diff options
Diffstat (limited to 'clang/lib/AST/ByteCode/InterpBuiltin.cpp')
| -rw-r--r-- | clang/lib/AST/ByteCode/InterpBuiltin.cpp | 248 |
1 files changed, 240 insertions, 8 deletions
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 24b630d0455e..731c9290993f 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -17,6 +17,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/SipHash.h" namespace clang { @@ -197,9 +198,19 @@ static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC, const Pointer &A = getParam<Pointer>(Frame, 0); const Pointer &B = getParam<Pointer>(Frame, 1); - if (ID == Builtin::BIstrcmp) + if (ID == Builtin::BIstrcmp || ID == Builtin::BIstrncmp) diagnoseNonConstexprBuiltin(S, OpPC, ID); + uint64_t Limit = ~static_cast<uint64_t>(0); + if (ID == Builtin::BIstrncmp || ID == Builtin::BI__builtin_strncmp) + Limit = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2))) + .getZExtValue(); + + if (Limit == 0) { + pushInteger(S, 0, Call->getType()); + return true; + } + if (!CheckLive(S, OpPC, A, AK_Read) || !CheckLive(S, OpPC, B, AK_Read)) return false; @@ -212,7 +223,11 @@ static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC, unsigned IndexA = A.getIndex(); unsigned IndexB = B.getIndex(); int32_t Result = 0; - for (;; ++IndexA, ++IndexB) { + uint64_t Steps = 0; + for (;; ++IndexA, ++IndexB, ++Steps) { + + if (Steps >= Limit) + break; const Pointer &PA = A.atIndex(IndexA); const Pointer &PB = B.atIndex(IndexB); if (!CheckRange(S, OpPC, PA, AK_Read) || @@ -243,7 +258,7 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC, unsigned ID = Func->getBuiltinID(); const Pointer &StrPtr = getParam<Pointer>(Frame, 0); - if (ID == Builtin::BIstrlen) + if (ID == Builtin::BIstrlen || ID == Builtin::BIwcslen) diagnoseNonConstexprBuiltin(S, OpPC, ID); if (!CheckArray(S, OpPC, StrPtr)) @@ -256,6 +271,12 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC, return false; assert(StrPtr.getFieldDesc()->isPrimitiveArray()); + unsigned ElemSize = StrPtr.getFieldDesc()->getElemSize(); + + if (ID == Builtin::BI__builtin_wcslen || ID == Builtin::BIwcslen) { + [[maybe_unused]] const ASTContext &AC = S.getASTContext(); + assert(ElemSize == AC.getTypeSizeInChars(AC.getWCharType()).getQuantity()); + } size_t Len = 0; for (size_t I = StrPtr.getIndex();; ++I, ++Len) { @@ -264,7 +285,20 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC, if (!CheckRange(S, OpPC, ElemPtr, AK_Read)) return false; - uint8_t Val = ElemPtr.deref<uint8_t>(); + uint32_t Val; + switch (ElemSize) { + case 1: + Val = ElemPtr.deref<uint8_t>(); + break; + case 2: + Val = ElemPtr.deref<uint16_t>(); + break; + case 4: + Val = ElemPtr.deref<uint32_t>(); + break; + default: + llvm_unreachable("Unsupported char size"); + } if (Val == 0) break; } @@ -1510,9 +1544,10 @@ static bool interp__builtin_constant_p(InterpState &S, CodePtr OpPC, if (Res.isInvalid()) { C.cleanup(); Stk.clear(); + return returnInt(false); } - if (!Res.isInvalid() && !Res.empty()) { + if (!Res.empty()) { const APValue &LV = Res.toAPValue(); if (LV.isLValue()) { APValue::LValueBase Base = LV.getLValueBase(); @@ -1797,12 +1832,14 @@ static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC, return true; } + static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { assert(Call->getNumArgs() == 3); unsigned ID = Func->getBuiltinID(); Pointer DestPtr = getParam<Pointer>(Frame, 0); + const ASTContext &ASTCtx = S.getASTContext(); const Pointer &SrcPtr = getParam<Pointer>(Frame, 1); const APSInt &Size = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2))); @@ -1823,21 +1860,202 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, Pointer DiagPtr = (SrcPtr.isZero() ? SrcPtr : DestPtr); S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_null) << /*IsMove=*/Move << /*IsWchar=*/false << !SrcPtr.isZero() - << DiagPtr.toDiagnosticString(S.getASTContext()); + << DiagPtr.toDiagnosticString(ASTCtx); return false; } - // As a last resort, reject dummy pointers. + // Can't read from dummy pointers. if (DestPtr.isDummy() || SrcPtr.isDummy()) return false; - if (!DoBitCastPtr(S, OpPC, SrcPtr, DestPtr, Size.getZExtValue())) + QualType DestElemType; + size_t RemainingDestElems; + if (DestPtr.getFieldDesc()->isArray()) { + DestElemType = DestPtr.getFieldDesc()->getElemQualType(); + RemainingDestElems = DestPtr.isUnknownSizeArray() + ? 0 + : (DestPtr.getNumElems() - DestPtr.getIndex()); + } else { + DestElemType = DestPtr.getType(); + RemainingDestElems = 1; + } + unsigned DestElemSize = ASTCtx.getTypeSizeInChars(DestElemType).getQuantity(); + + if (Size.urem(DestElemSize) != 0) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_memcpy_unsupported) + << Move << /*IsWchar=*/false << 0 << DestElemType << Size + << DestElemSize; + return false; + } + + QualType SrcElemType; + size_t RemainingSrcElems; + if (SrcPtr.getFieldDesc()->isArray()) { + SrcElemType = SrcPtr.getFieldDesc()->getElemQualType(); + RemainingSrcElems = SrcPtr.isUnknownSizeArray() + ? 0 + : (SrcPtr.getNumElems() - SrcPtr.getIndex()); + } else { + SrcElemType = SrcPtr.getType(); + RemainingSrcElems = 1; + } + unsigned SrcElemSize = ASTCtx.getTypeSizeInChars(SrcElemType).getQuantity(); + + if (!ASTCtx.hasSameUnqualifiedType(DestElemType, SrcElemType)) { + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_type_pun) + << Move << SrcElemType << DestElemType; + return false; + } + + // Check if we have enough elements to read from and write to/ + size_t RemainingDestBytes = RemainingDestElems * DestElemSize; + size_t RemainingSrcBytes = RemainingSrcElems * SrcElemSize; + if (Size.ugt(RemainingDestBytes) || Size.ugt(RemainingSrcBytes)) { + APInt N = Size.udiv(DestElemSize); + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_memcpy_unsupported) + << Move << /*IsWChar*/ false << (Size.ugt(RemainingSrcBytes) ? 1 : 2) + << DestElemType << toString(N, 10, /*Signed=*/false); + return false; + } + + // Check for overlapping memory regions. + if (!Move && Pointer::pointToSameBlock(SrcPtr, DestPtr)) { + unsigned SrcIndex = SrcPtr.getIndex() * SrcPtr.elemSize(); + unsigned DstIndex = DestPtr.getIndex() * DestPtr.elemSize(); + unsigned N = Size.getZExtValue(); + + if ((SrcIndex <= DstIndex && (SrcIndex + N) > DstIndex) || + (DstIndex <= SrcIndex && (DstIndex + N) > SrcIndex)) { + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_overlap) + << /*IsWChar=*/false; + return false; + } + } + + assert(Size.getZExtValue() % DestElemSize == 0); + if (!DoMemcpy(S, OpPC, SrcPtr, DestPtr, Bytes(Size.getZExtValue()).toBits())) return false; S.Stk.push<Pointer>(DestPtr); return true; } +/// Determine if T is a character type for which we guarantee that +/// sizeof(T) == 1. +static bool isOneByteCharacterType(QualType T) { + return T->isCharType() || T->isChar8Type(); +} + +static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, const CallExpr *Call) { + assert(Call->getNumArgs() == 3); + unsigned ID = Func->getBuiltinID(); + const Pointer &PtrA = getParam<Pointer>(Frame, 0); + const Pointer &PtrB = getParam<Pointer>(Frame, 1); + const APSInt &Size = + peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2))); + + if (ID == Builtin::BImemcmp || ID == Builtin::BIbcmp || + ID == Builtin::BIwmemcmp) + diagnoseNonConstexprBuiltin(S, OpPC, ID); + + if (Size.isZero()) { + pushInteger(S, 0, Call->getType()); + return true; + } + + bool IsWide = + (ID == Builtin::BIwmemcmp || ID == Builtin::BI__builtin_wmemcmp); + + const ASTContext &ASTCtx = S.getASTContext(); + // FIXME: This is an arbitrary limitation the current constant interpreter + // had. We could remove this. + if (!IsWide && (!isOneByteCharacterType(PtrA.getType()) || + !isOneByteCharacterType(PtrB.getType()))) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_memcmp_unsupported) + << ("'" + ASTCtx.BuiltinInfo.getName(ID) + "'").str() << PtrA.getType() + << PtrB.getType(); + return false; + } + + if (PtrA.isDummy() || PtrB.isDummy()) + return false; + + // Now, read both pointers to a buffer and compare those. + BitcastBuffer BufferA( + Bits(ASTCtx.getTypeSize(PtrA.getFieldDesc()->getType()))); + readPointerToBuffer(S.getContext(), PtrA, BufferA, false); + // FIXME: The swapping here is UNDOING something we do when reading the + // data into the buffer. + if (ASTCtx.getTargetInfo().isBigEndian()) + swapBytes(BufferA.Data.get(), BufferA.byteSize().getQuantity()); + + BitcastBuffer BufferB( + Bits(ASTCtx.getTypeSize(PtrB.getFieldDesc()->getType()))); + readPointerToBuffer(S.getContext(), PtrB, BufferB, false); + // FIXME: The swapping here is UNDOING something we do when reading the + // data into the buffer. + if (ASTCtx.getTargetInfo().isBigEndian()) + swapBytes(BufferB.Data.get(), BufferB.byteSize().getQuantity()); + + size_t MinBufferSize = std::min(BufferA.byteSize().getQuantity(), + BufferB.byteSize().getQuantity()); + + unsigned ElemSize = 1; + if (IsWide) + ElemSize = ASTCtx.getTypeSizeInChars(ASTCtx.getWCharType()).getQuantity(); + // The Size given for the wide variants is in wide-char units. Convert it + // to bytes. + size_t ByteSize = Size.getZExtValue() * ElemSize; + size_t CmpSize = std::min(MinBufferSize, ByteSize); + + for (size_t I = 0; I != CmpSize; I += ElemSize) { + if (IsWide) { + INT_TYPE_SWITCH(*S.getContext().classify(ASTCtx.getWCharType()), { + T A = *reinterpret_cast<T *>(BufferA.Data.get() + I); + T B = *reinterpret_cast<T *>(BufferB.Data.get() + I); + if (A < B) { + pushInteger(S, -1, Call->getType()); + return true; + } else if (A > B) { + pushInteger(S, 1, Call->getType()); + return true; + } + }); + } else { + std::byte A = BufferA.Data[I]; + std::byte B = BufferB.Data[I]; + + if (A < B) { + pushInteger(S, -1, Call->getType()); + return true; + } else if (A > B) { + pushInteger(S, 1, Call->getType()); + return true; + } + } + } + + // We compared CmpSize bytes above. If the limiting factor was the Size + // passed, we're done and the result is equality (0). + if (ByteSize <= CmpSize) { + pushInteger(S, 0, Call->getType()); + return true; + } + + // However, if we read all the available bytes but were instructed to read + // even more, diagnose this as a "read of dereferenced one-past-the-end + // pointer". This is what would happen if we called CheckRead() on every array + // element. + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end) + << AK_Read << S.Current->getRange(OpPC); + return false; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call, uint32_t BuiltinID) { const InterpFrame *Frame = S.Current; @@ -1854,11 +2072,15 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, break; case Builtin::BI__builtin_strcmp: case Builtin::BIstrcmp: + case Builtin::BI__builtin_strncmp: + case Builtin::BIstrncmp: if (!interp__builtin_strcmp(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BI__builtin_strlen: case Builtin::BIstrlen: + case Builtin::BI__builtin_wcslen: + case Builtin::BIwcslen: if (!interp__builtin_strlen(S, OpPC, Frame, F, Call)) return false; break; @@ -2307,6 +2529,16 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case Builtin::BI__builtin_memcmp: + case Builtin::BImemcmp: + case Builtin::BI__builtin_bcmp: + case Builtin::BIbcmp: + case Builtin::BI__builtin_wmemcmp: + case Builtin::BIwmemcmp: + if (!interp__builtin_memcmp(S, OpPC, Frame, F, Call)) + return false; + break; + default: S.FFDiag(S.Current->getLocation(OpPC), diag::note_invalid_subexpr_in_const_expr) |
