| //===--- InterpBuiltin.cpp - Interpreter for the constexpr VM ---*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| #include "../ExprConstShared.h" |
| #include "Boolean.h" |
| #include "EvalEmitter.h" |
| #include "InterpBuiltinBitCast.h" |
| #include "InterpHelpers.h" |
| #include "PrimType.h" |
| #include "Program.h" |
| #include "clang/AST/InferAlloc.h" |
| #include "clang/AST/OSLog.h" |
| #include "clang/AST/RecordLayout.h" |
| #include "clang/Basic/Builtins.h" |
| #include "clang/Basic/TargetBuiltins.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/AllocToken.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/SipHash.h" |
| |
| namespace clang { |
| namespace interp { |
| |
| [[maybe_unused]] static bool isNoopBuiltin(unsigned ID) { |
| switch (ID) { |
| case Builtin::BIas_const: |
| case Builtin::BIforward: |
| case Builtin::BIforward_like: |
| case Builtin::BImove: |
| case Builtin::BImove_if_noexcept: |
| case Builtin::BIaddressof: |
| case Builtin::BI__addressof: |
| case Builtin::BI__builtin_addressof: |
| case Builtin::BI__builtin_launder: |
| return true; |
| default: |
| return false; |
| } |
| return false; |
| } |
| |
| static void discard(InterpStack &Stk, PrimType T) { |
| TYPE_SWITCH(T, { Stk.discard<T>(); }); |
| } |
| |
| static APSInt popToAPSInt(InterpStack &Stk, PrimType T) { |
| INT_TYPE_SWITCH(T, return Stk.pop<T>().toAPSInt()); |
| } |
| |
| static APSInt popToAPSInt(InterpState &S, const Expr *E) { |
| return popToAPSInt(S.Stk, *S.getContext().classify(E->getType())); |
| } |
| static APSInt popToAPSInt(InterpState &S, QualType T) { |
| return popToAPSInt(S.Stk, *S.getContext().classify(T)); |
| } |
| |
| /// Pushes \p Val on the stack as the type given by \p QT. |
| static void pushInteger(InterpState &S, const APSInt &Val, QualType QT) { |
| assert(QT->isSignedIntegerOrEnumerationType() || |
| QT->isUnsignedIntegerOrEnumerationType()); |
| OptPrimType T = S.getContext().classify(QT); |
| assert(T); |
| |
| unsigned BitWidth = S.getASTContext().getTypeSize(QT); |
| |
| if (T == PT_IntAPS) { |
| auto Result = S.allocAP<IntegralAP<true>>(BitWidth); |
| Result.copy(Val); |
| S.Stk.push<IntegralAP<true>>(Result); |
| return; |
| } |
| |
| if (T == PT_IntAP) { |
| auto Result = S.allocAP<IntegralAP<false>>(BitWidth); |
| Result.copy(Val); |
| S.Stk.push<IntegralAP<false>>(Result); |
| return; |
| } |
| |
| if (QT->isSignedIntegerOrEnumerationType()) { |
| int64_t V = Val.getSExtValue(); |
| INT_TYPE_SWITCH(*T, { S.Stk.push<T>(T::from(V, BitWidth)); }); |
| } else { |
| assert(QT->isUnsignedIntegerOrEnumerationType()); |
| uint64_t V = Val.getZExtValue(); |
| INT_TYPE_SWITCH(*T, { S.Stk.push<T>(T::from(V, BitWidth)); }); |
| } |
| } |
| |
| template <typename T> |
| static void pushInteger(InterpState &S, T Val, QualType QT) { |
| if constexpr (std::is_same_v<T, APInt>) |
| pushInteger(S, APSInt(Val, !std::is_signed_v<T>), QT); |
| else if constexpr (std::is_same_v<T, APSInt>) |
| pushInteger(S, Val, QT); |
| else |
| pushInteger(S, |
| APSInt(APInt(sizeof(T) * 8, static_cast<uint64_t>(Val), |
| std::is_signed_v<T>), |
| !std::is_signed_v<T>), |
| QT); |
| } |
| |
| static void assignInteger(InterpState &S, const Pointer &Dest, PrimType ValueT, |
| const APSInt &Value) { |
| |
| if (ValueT == PT_IntAPS) { |
| Dest.deref<IntegralAP<true>>() = |
| S.allocAP<IntegralAP<true>>(Value.getBitWidth()); |
| Dest.deref<IntegralAP<true>>().copy(Value); |
| } else if (ValueT == PT_IntAP) { |
| Dest.deref<IntegralAP<false>>() = |
| S.allocAP<IntegralAP<false>>(Value.getBitWidth()); |
| Dest.deref<IntegralAP<false>>().copy(Value); |
| } else { |
| INT_TYPE_SWITCH_NO_BOOL( |
| ValueT, { Dest.deref<T>() = T::from(static_cast<T>(Value)); }); |
| } |
| } |
| |
| static QualType getElemType(const Pointer &P) { |
| const Descriptor *Desc = P.getFieldDesc(); |
| QualType T = Desc->getType(); |
| if (Desc->isPrimitive()) |
| return T; |
| if (T->isPointerType()) |
| return T->getAs<PointerType>()->getPointeeType(); |
| if (Desc->isArray()) |
| return Desc->getElemQualType(); |
| if (const auto *AT = T->getAsArrayTypeUnsafe()) |
| return AT->getElementType(); |
| return T; |
| } |
| |
| static void diagnoseNonConstexprBuiltin(InterpState &S, CodePtr OpPC, |
| unsigned ID) { |
| if (!S.diagnosing()) |
| return; |
| |
| auto Loc = S.Current->getSource(OpPC); |
| if (S.getLangOpts().CPlusPlus11) |
| S.CCEDiag(Loc, diag::note_constexpr_invalid_function) |
| << /*isConstexpr=*/0 << /*isConstructor=*/0 |
| << S.getASTContext().BuiltinInfo.getQuotedName(ID); |
| else |
| S.CCEDiag(Loc, diag::note_invalid_subexpr_in_const_expr); |
| } |
| |
| static llvm::APSInt convertBoolVectorToInt(const Pointer &Val) { |
| assert(Val.getFieldDesc()->isPrimitiveArray() && |
| Val.getFieldDesc()->getElemQualType()->isBooleanType() && |
| "Not a boolean vector"); |
| unsigned NumElems = Val.getNumElems(); |
| |
| // Each element is one bit, so create an integer with NumElts bits. |
| llvm::APSInt Result(NumElems, 0); |
| for (unsigned I = 0; I != NumElems; ++I) { |
| if (Val.elem<bool>(I)) |
| Result.setBit(I); |
| } |
| |
| return Result; |
| } |
| |
| static bool interp__builtin_is_constant_evaluated(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| unsigned Depth = S.Current->getDepth(); |
| auto isStdCall = [](const FunctionDecl *F) -> bool { |
| return F && F->isInStdNamespace() && F->getIdentifier() && |
| F->getIdentifier()->isStr("is_constant_evaluated"); |
| }; |
| const InterpFrame *Caller = Frame->Caller; |
| // The current frame is the one for __builtin_is_constant_evaluated. |
| // The one above that, potentially the one for std::is_constant_evaluated(). |
| if (S.inConstantContext() && !S.checkingPotentialConstantExpression() && |
| S.getEvalStatus().Diag && |
| (Depth == 0 || (Depth == 1 && isStdCall(Frame->getCallee())))) { |
| if (Caller && isStdCall(Frame->getCallee())) { |
| const Expr *E = Caller->getExpr(Caller->getRetPC()); |
| S.report(E->getExprLoc(), |
| diag::warn_is_constant_evaluated_always_true_constexpr) |
| << "std::is_constant_evaluated" << E->getSourceRange(); |
| } else { |
| S.report(Call->getExprLoc(), |
| diag::warn_is_constant_evaluated_always_true_constexpr) |
| << "__builtin_is_constant_evaluated" << Call->getSourceRange(); |
| } |
| } |
| |
| S.Stk.push<Boolean>(Boolean::from(S.inConstantContext())); |
| return true; |
| } |
| |
| // __builtin_assume(int) |
| static bool interp__builtin_assume(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| assert(Call->getNumArgs() == 1); |
| discard(S.Stk, *S.getContext().classify(Call->getArg(0))); |
| return true; |
| } |
| |
| static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, unsigned ID) { |
| uint64_t Limit = ~static_cast<uint64_t>(0); |
| if (ID == Builtin::BIstrncmp || ID == Builtin::BI__builtin_strncmp || |
| ID == Builtin::BIwcsncmp || ID == Builtin::BI__builtin_wcsncmp) |
| Limit = popToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2))) |
| .getZExtValue(); |
| |
| const Pointer &B = S.Stk.pop<Pointer>(); |
| const Pointer &A = S.Stk.pop<Pointer>(); |
| if (ID == Builtin::BIstrcmp || ID == Builtin::BIstrncmp || |
| ID == Builtin::BIwcscmp || ID == Builtin::BIwcsncmp) |
| diagnoseNonConstexprBuiltin(S, OpPC, ID); |
| |
| 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; |
| |
| if (A.isDummy() || B.isDummy()) |
| return false; |
| if (!A.isBlockPointer() || !B.isBlockPointer()) |
| return false; |
| |
| bool IsWide = ID == Builtin::BIwcscmp || ID == Builtin::BIwcsncmp || |
| ID == Builtin::BI__builtin_wcscmp || |
| ID == Builtin::BI__builtin_wcsncmp; |
| assert(A.getFieldDesc()->isPrimitiveArray()); |
| assert(B.getFieldDesc()->isPrimitiveArray()); |
| |
| // Different element types shouldn't happen, but with casts they can. |
| if (!S.getASTContext().hasSameUnqualifiedType(getElemType(A), getElemType(B))) |
| return false; |
| |
| PrimType ElemT = *S.getContext().classify(getElemType(A)); |
| |
| auto returnResult = [&](int V) -> bool { |
| pushInteger(S, V, Call->getType()); |
| return true; |
| }; |
| |
| unsigned IndexA = A.getIndex(); |
| unsigned IndexB = B.getIndex(); |
| 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) || |
| !CheckRange(S, OpPC, PB, AK_Read)) { |
| return false; |
| } |
| |
| if (IsWide) { |
| INT_TYPE_SWITCH(ElemT, { |
| T CA = PA.deref<T>(); |
| T CB = PB.deref<T>(); |
| if (CA > CB) |
| return returnResult(1); |
| if (CA < CB) |
| return returnResult(-1); |
| if (CA.isZero() || CB.isZero()) |
| return returnResult(0); |
| }); |
| continue; |
| } |
| |
| uint8_t CA = PA.deref<uint8_t>(); |
| uint8_t CB = PB.deref<uint8_t>(); |
| |
| if (CA > CB) |
| return returnResult(1); |
| if (CA < CB) |
| return returnResult(-1); |
| if (CA == 0 || CB == 0) |
| return returnResult(0); |
| } |
| |
| return returnResult(0); |
| } |
| |
| static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, unsigned ID) { |
| const Pointer &StrPtr = S.Stk.pop<Pointer>(); |
| |
| if (ID == Builtin::BIstrlen || ID == Builtin::BIwcslen) |
| diagnoseNonConstexprBuiltin(S, OpPC, ID); |
| |
| if (!CheckArray(S, OpPC, StrPtr)) |
| return false; |
| |
| if (!CheckLive(S, OpPC, StrPtr, AK_Read)) |
| return false; |
| |
| if (!CheckDummy(S, OpPC, StrPtr.block(), AK_Read)) |
| return false; |
| |
| if (!StrPtr.getFieldDesc()->isPrimitiveArray()) |
| 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) { |
| const Pointer &ElemPtr = StrPtr.atIndex(I); |
| |
| if (!CheckRange(S, OpPC, ElemPtr, AK_Read)) |
| return false; |
| |
| 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; |
| } |
| |
| pushInteger(S, Len, Call->getType()); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_nan(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, const CallExpr *Call, |
| bool Signaling) { |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| |
| if (!CheckLoad(S, OpPC, Arg)) |
| return false; |
| |
| assert(Arg.getFieldDesc()->isPrimitiveArray()); |
| |
| // Convert the given string to an integer using StringRef's API. |
| llvm::APInt Fill; |
| std::string Str; |
| assert(Arg.getNumElems() >= 1); |
| for (unsigned I = 0;; ++I) { |
| const Pointer &Elem = Arg.atIndex(I); |
| |
| if (!CheckLoad(S, OpPC, Elem)) |
| return false; |
| |
| if (Elem.deref<int8_t>() == 0) |
| break; |
| |
| Str += Elem.deref<char>(); |
| } |
| |
| // Treat empty strings as if they were zero. |
| if (Str.empty()) |
| Fill = llvm::APInt(32, 0); |
| else if (StringRef(Str).getAsInteger(0, Fill)) |
| return false; |
| |
| const llvm::fltSemantics &TargetSemantics = |
| S.getASTContext().getFloatTypeSemantics( |
| Call->getDirectCallee()->getReturnType()); |
| |
| Floating Result = S.allocFloat(TargetSemantics); |
| if (S.getASTContext().getTargetInfo().isNan2008()) { |
| if (Signaling) |
| Result.copy( |
| llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill)); |
| else |
| Result.copy( |
| llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, &Fill)); |
| } else { |
| // Prior to IEEE 754-2008, architectures were allowed to choose whether |
| // the first bit of their significand was set for qNaN or sNaN. MIPS chose |
| // a different encoding to what became a standard in 2008, and for pre- |
| // 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as |
| // sNaN. This is now known as "legacy NaN" encoding. |
| if (Signaling) |
| Result.copy( |
| llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, &Fill)); |
| else |
| Result.copy( |
| llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill)); |
| } |
| |
| S.Stk.push<Floating>(Result); |
| return true; |
| } |
| |
| static bool interp__builtin_inf(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const llvm::fltSemantics &TargetSemantics = |
| S.getASTContext().getFloatTypeSemantics( |
| Call->getDirectCallee()->getReturnType()); |
| |
| Floating Result = S.allocFloat(TargetSemantics); |
| Result.copy(APFloat::getInf(TargetSemantics)); |
| S.Stk.push<Floating>(Result); |
| return true; |
| } |
| |
| static bool interp__builtin_copysign(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame) { |
| const Floating &Arg2 = S.Stk.pop<Floating>(); |
| const Floating &Arg1 = S.Stk.pop<Floating>(); |
| Floating Result = S.allocFloat(Arg1.getSemantics()); |
| |
| APFloat Copy = Arg1.getAPFloat(); |
| Copy.copySign(Arg2.getAPFloat()); |
| Result.copy(Copy); |
| S.Stk.push<Floating>(Result); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_fmin(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, bool IsNumBuiltin) { |
| const Floating &RHS = S.Stk.pop<Floating>(); |
| const Floating &LHS = S.Stk.pop<Floating>(); |
| Floating Result = S.allocFloat(LHS.getSemantics()); |
| |
| if (IsNumBuiltin) |
| Result.copy(llvm::minimumnum(LHS.getAPFloat(), RHS.getAPFloat())); |
| else |
| Result.copy(minnum(LHS.getAPFloat(), RHS.getAPFloat())); |
| S.Stk.push<Floating>(Result); |
| return true; |
| } |
| |
| static bool interp__builtin_fmax(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, bool IsNumBuiltin) { |
| const Floating &RHS = S.Stk.pop<Floating>(); |
| const Floating &LHS = S.Stk.pop<Floating>(); |
| Floating Result = S.allocFloat(LHS.getSemantics()); |
| |
| if (IsNumBuiltin) |
| Result.copy(llvm::maximumnum(LHS.getAPFloat(), RHS.getAPFloat())); |
| else |
| Result.copy(maxnum(LHS.getAPFloat(), RHS.getAPFloat())); |
| S.Stk.push<Floating>(Result); |
| return true; |
| } |
| |
| /// Defined as __builtin_isnan(...), to accommodate the fact that it can |
| /// take a float, double, long double, etc. |
| /// But for us, that's all a Floating anyway. |
| static bool interp__builtin_isnan(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| |
| pushInteger(S, Arg.isNan(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_issignaling(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| |
| pushInteger(S, Arg.isSignaling(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_isinf(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, bool CheckSign, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| APFloat F = Arg.getAPFloat(); |
| bool IsInf = F.isInfinity(); |
| |
| if (CheckSign) |
| pushInteger(S, IsInf ? (F.isNegative() ? -1 : 1) : 0, Call->getType()); |
| else |
| pushInteger(S, IsInf, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_isfinite(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| |
| pushInteger(S, Arg.isFinite(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_isnormal(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| |
| pushInteger(S, Arg.isNormal(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_issubnormal(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| |
| pushInteger(S, Arg.isDenormal(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_iszero(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| |
| pushInteger(S, Arg.isZero(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_signbit(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg = S.Stk.pop<Floating>(); |
| |
| pushInteger(S, Arg.isNegative(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp_floating_comparison(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, unsigned ID) { |
| const Floating &RHS = S.Stk.pop<Floating>(); |
| const Floating &LHS = S.Stk.pop<Floating>(); |
| |
| pushInteger( |
| S, |
| [&] { |
| switch (ID) { |
| case Builtin::BI__builtin_isgreater: |
| return LHS > RHS; |
| case Builtin::BI__builtin_isgreaterequal: |
| return LHS >= RHS; |
| case Builtin::BI__builtin_isless: |
| return LHS < RHS; |
| case Builtin::BI__builtin_islessequal: |
| return LHS <= RHS; |
| case Builtin::BI__builtin_islessgreater: { |
| ComparisonCategoryResult Cmp = LHS.compare(RHS); |
| return Cmp == ComparisonCategoryResult::Less || |
| Cmp == ComparisonCategoryResult::Greater; |
| } |
| case Builtin::BI__builtin_isunordered: |
| return LHS.compare(RHS) == ComparisonCategoryResult::Unordered; |
| default: |
| llvm_unreachable("Unexpected builtin ID: Should be a floating point " |
| "comparison function"); |
| } |
| }(), |
| Call->getType()); |
| return true; |
| } |
| |
| /// First parameter to __builtin_isfpclass is the floating value, the |
| /// second one is an integral value. |
| static bool interp__builtin_isfpclass(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| APSInt FPClassArg = popToAPSInt(S, Call->getArg(1)); |
| const Floating &F = S.Stk.pop<Floating>(); |
| |
| int32_t Result = static_cast<int32_t>( |
| (F.classify() & std::move(FPClassArg)).getZExtValue()); |
| pushInteger(S, Result, Call->getType()); |
| |
| return true; |
| } |
| |
| /// Five int values followed by one floating value. |
| /// __builtin_fpclassify(int, int, int, int, int, float) |
| static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Val = S.Stk.pop<Floating>(); |
| |
| PrimType IntT = *S.getContext().classify(Call->getArg(0)); |
| APSInt Values[5]; |
| for (unsigned I = 0; I != 5; ++I) |
| Values[4 - I] = popToAPSInt(S.Stk, IntT); |
| |
| unsigned Index; |
| switch (Val.getCategory()) { |
| case APFloat::fcNaN: |
| Index = 0; |
| break; |
| case APFloat::fcInfinity: |
| Index = 1; |
| break; |
| case APFloat::fcNormal: |
| Index = Val.isDenormal() ? 3 : 2; |
| break; |
| case APFloat::fcZero: |
| Index = 4; |
| break; |
| } |
| |
| // The last argument is first on the stack. |
| assert(Index <= 4); |
| |
| pushInteger(S, Values[Index], Call->getType()); |
| return true; |
| } |
| |
| static inline Floating abs(InterpState &S, const Floating &In) { |
| if (!In.isNegative()) |
| return In; |
| |
| Floating Output = S.allocFloat(In.getSemantics()); |
| APFloat New = In.getAPFloat(); |
| New.changeSign(); |
| Output.copy(New); |
| return Output; |
| } |
| |
| // The C standard says "fabs raises no floating-point exceptions, |
| // even if x is a signaling NaN. The returned value is independent of |
| // the current rounding direction mode." Therefore constant folding can |
| // proceed without regard to the floating point settings. |
| // Reference, WG14 N2478 F.10.4.3 |
| static bool interp__builtin_fabs(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame) { |
| const Floating &Val = S.Stk.pop<Floating>(); |
| S.Stk.push<Floating>(abs(S, Val)); |
| return true; |
| } |
| |
| static bool interp__builtin_abs(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| APSInt Val = popToAPSInt(S, Call->getArg(0)); |
| if (Val == |
| APSInt(APInt::getSignedMinValue(Val.getBitWidth()), /*IsUnsigned=*/false)) |
| return false; |
| if (Val.isNegative()) |
| Val.negate(); |
| pushInteger(S, Val, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| APSInt Val; |
| if (Call->getArg(0)->getType()->isExtVectorBoolType()) { |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| Val = convertBoolVectorToInt(Arg); |
| } else { |
| Val = popToAPSInt(S, Call->getArg(0)); |
| } |
| pushInteger(S, Val.popcount(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_classify_type(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| // This is an unevaluated call, so there are no arguments on the stack. |
| assert(Call->getNumArgs() == 1); |
| const Expr *Arg = Call->getArg(0); |
| |
| GCCTypeClass ResultClass = |
| EvaluateBuiltinClassifyType(Arg->getType(), S.getLangOpts()); |
| int32_t ReturnVal = static_cast<int32_t>(ResultClass); |
| pushInteger(S, ReturnVal, Call->getType()); |
| return true; |
| } |
| |
| // __builtin_expect(long, long) |
| // __builtin_expect_with_probability(long, long, double) |
| static bool interp__builtin_expect(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| // The return value is simply the value of the first parameter. |
| // We ignore the probability. |
| unsigned NumArgs = Call->getNumArgs(); |
| assert(NumArgs == 2 || NumArgs == 3); |
| |
| PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); |
| if (NumArgs == 3) |
| S.Stk.discard<Floating>(); |
| discard(S.Stk, ArgT); |
| |
| APSInt Val = popToAPSInt(S.Stk, ArgT); |
| pushInteger(S, Val, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| #ifndef NDEBUG |
| assert(Call->getArg(0)->isLValue()); |
| PrimType PtrT = S.getContext().classify(Call->getArg(0)).value_or(PT_Ptr); |
| assert(PtrT == PT_Ptr && |
| "Unsupported pointer type passed to __builtin_addressof()"); |
| #endif |
| return true; |
| } |
| |
| static bool interp__builtin_move(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| return Call->getDirectCallee()->isConstexpr(); |
| } |
| |
| static bool interp__builtin_eh_return_data_regno(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| APSInt Arg = popToAPSInt(S, Call->getArg(0)); |
| |
| int Result = S.getASTContext().getTargetInfo().getEHDataRegisterNumber( |
| Arg.getZExtValue()); |
| pushInteger(S, Result, Call->getType()); |
| return true; |
| } |
| |
| // Two integral values followed by a pointer (lhs, rhs, resultOut) |
| static bool interp__builtin_overflowop(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, |
| unsigned BuiltinOp) { |
| const Pointer &ResultPtr = S.Stk.pop<Pointer>(); |
| if (ResultPtr.isDummy() || !ResultPtr.isBlockPointer()) |
| return false; |
| |
| PrimType RHST = *S.getContext().classify(Call->getArg(1)->getType()); |
| PrimType LHST = *S.getContext().classify(Call->getArg(0)->getType()); |
| APSInt RHS = popToAPSInt(S.Stk, RHST); |
| APSInt LHS = popToAPSInt(S.Stk, LHST); |
| QualType ResultType = Call->getArg(2)->getType()->getPointeeType(); |
| PrimType ResultT = *S.getContext().classify(ResultType); |
| bool Overflow; |
| |
| APSInt Result; |
| if (BuiltinOp == Builtin::BI__builtin_add_overflow || |
| BuiltinOp == Builtin::BI__builtin_sub_overflow || |
| BuiltinOp == Builtin::BI__builtin_mul_overflow) { |
| bool IsSigned = LHS.isSigned() || RHS.isSigned() || |
| ResultType->isSignedIntegerOrEnumerationType(); |
| bool AllSigned = LHS.isSigned() && RHS.isSigned() && |
| ResultType->isSignedIntegerOrEnumerationType(); |
| uint64_t LHSSize = LHS.getBitWidth(); |
| uint64_t RHSSize = RHS.getBitWidth(); |
| uint64_t ResultSize = S.getASTContext().getTypeSize(ResultType); |
| uint64_t MaxBits = std::max(std::max(LHSSize, RHSSize), ResultSize); |
| |
| // Add an additional bit if the signedness isn't uniformly agreed to. We |
| // could do this ONLY if there is a signed and an unsigned that both have |
| // MaxBits, but the code to check that is pretty nasty. The issue will be |
| // caught in the shrink-to-result later anyway. |
| if (IsSigned && !AllSigned) |
| ++MaxBits; |
| |
| LHS = APSInt(LHS.extOrTrunc(MaxBits), !IsSigned); |
| RHS = APSInt(RHS.extOrTrunc(MaxBits), !IsSigned); |
| Result = APSInt(MaxBits, !IsSigned); |
| } |
| |
| // Find largest int. |
| switch (BuiltinOp) { |
| default: |
| llvm_unreachable("Invalid value for BuiltinOp"); |
| case Builtin::BI__builtin_add_overflow: |
| case Builtin::BI__builtin_sadd_overflow: |
| case Builtin::BI__builtin_saddl_overflow: |
| case Builtin::BI__builtin_saddll_overflow: |
| case Builtin::BI__builtin_uadd_overflow: |
| case Builtin::BI__builtin_uaddl_overflow: |
| case Builtin::BI__builtin_uaddll_overflow: |
| Result = LHS.isSigned() ? LHS.sadd_ov(RHS, Overflow) |
| : LHS.uadd_ov(RHS, Overflow); |
| break; |
| case Builtin::BI__builtin_sub_overflow: |
| case Builtin::BI__builtin_ssub_overflow: |
| case Builtin::BI__builtin_ssubl_overflow: |
| case Builtin::BI__builtin_ssubll_overflow: |
| case Builtin::BI__builtin_usub_overflow: |
| case Builtin::BI__builtin_usubl_overflow: |
| case Builtin::BI__builtin_usubll_overflow: |
| Result = LHS.isSigned() ? LHS.ssub_ov(RHS, Overflow) |
| : LHS.usub_ov(RHS, Overflow); |
| break; |
| case Builtin::BI__builtin_mul_overflow: |
| case Builtin::BI__builtin_smul_overflow: |
| case Builtin::BI__builtin_smull_overflow: |
| case Builtin::BI__builtin_smulll_overflow: |
| case Builtin::BI__builtin_umul_overflow: |
| case Builtin::BI__builtin_umull_overflow: |
| case Builtin::BI__builtin_umulll_overflow: |
| Result = LHS.isSigned() ? LHS.smul_ov(RHS, Overflow) |
| : LHS.umul_ov(RHS, Overflow); |
| break; |
| } |
| |
| // In the case where multiple sizes are allowed, truncate and see if |
| // the values are the same. |
| if (BuiltinOp == Builtin::BI__builtin_add_overflow || |
| BuiltinOp == Builtin::BI__builtin_sub_overflow || |
| BuiltinOp == Builtin::BI__builtin_mul_overflow) { |
| // APSInt doesn't have a TruncOrSelf, so we use extOrTrunc instead, |
| // since it will give us the behavior of a TruncOrSelf in the case where |
| // its parameter <= its size. We previously set Result to be at least the |
| // type-size of the result, so getTypeSize(ResultType) <= Resu |
| APSInt Temp = Result.extOrTrunc(S.getASTContext().getTypeSize(ResultType)); |
| Temp.setIsSigned(ResultType->isSignedIntegerOrEnumerationType()); |
| |
| if (!APSInt::isSameValue(Temp, Result)) |
| Overflow = true; |
| Result = std::move(Temp); |
| } |
| |
| // Write Result to ResultPtr and put Overflow on the stack. |
| assignInteger(S, ResultPtr, ResultT, Result); |
| if (ResultPtr.canBeInitialized()) |
| ResultPtr.initialize(); |
| |
| assert(Call->getDirectCallee()->getReturnType()->isBooleanType()); |
| S.Stk.push<Boolean>(Overflow); |
| return true; |
| } |
| |
| /// Three integral values followed by a pointer (lhs, rhs, carry, carryOut). |
| static bool interp__builtin_carryop(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, unsigned BuiltinOp) { |
| const Pointer &CarryOutPtr = S.Stk.pop<Pointer>(); |
| PrimType LHST = *S.getContext().classify(Call->getArg(0)->getType()); |
| PrimType RHST = *S.getContext().classify(Call->getArg(1)->getType()); |
| APSInt CarryIn = popToAPSInt(S.Stk, LHST); |
| APSInt RHS = popToAPSInt(S.Stk, RHST); |
| APSInt LHS = popToAPSInt(S.Stk, LHST); |
| |
| if (CarryOutPtr.isDummy() || !CarryOutPtr.isBlockPointer()) |
| return false; |
| |
| APSInt CarryOut; |
| |
| APSInt Result; |
| // Copy the number of bits and sign. |
| Result = LHS; |
| CarryOut = LHS; |
| |
| bool FirstOverflowed = false; |
| bool SecondOverflowed = false; |
| switch (BuiltinOp) { |
| default: |
| llvm_unreachable("Invalid value for BuiltinOp"); |
| case Builtin::BI__builtin_addcb: |
| case Builtin::BI__builtin_addcs: |
| case Builtin::BI__builtin_addc: |
| case Builtin::BI__builtin_addcl: |
| case Builtin::BI__builtin_addcll: |
| Result = |
| LHS.uadd_ov(RHS, FirstOverflowed).uadd_ov(CarryIn, SecondOverflowed); |
| break; |
| case Builtin::BI__builtin_subcb: |
| case Builtin::BI__builtin_subcs: |
| case Builtin::BI__builtin_subc: |
| case Builtin::BI__builtin_subcl: |
| case Builtin::BI__builtin_subcll: |
| Result = |
| LHS.usub_ov(RHS, FirstOverflowed).usub_ov(CarryIn, SecondOverflowed); |
| break; |
| } |
| // It is possible for both overflows to happen but CGBuiltin uses an OR so |
| // this is consistent. |
| CarryOut = (uint64_t)(FirstOverflowed | SecondOverflowed); |
| |
| QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType(); |
| PrimType CarryOutT = *S.getContext().classify(CarryOutType); |
| assignInteger(S, CarryOutPtr, CarryOutT, CarryOut); |
| CarryOutPtr.initialize(); |
| |
| assert(Call->getType() == Call->getArg(0)->getType()); |
| pushInteger(S, Result, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_clz(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, const CallExpr *Call, |
| unsigned BuiltinOp) { |
| |
| std::optional<APSInt> Fallback; |
| if (BuiltinOp == Builtin::BI__builtin_clzg && Call->getNumArgs() == 2) |
| Fallback = popToAPSInt(S, Call->getArg(1)); |
| |
| APSInt Val; |
| if (Call->getArg(0)->getType()->isExtVectorBoolType()) { |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| Val = convertBoolVectorToInt(Arg); |
| } else { |
| Val = popToAPSInt(S, Call->getArg(0)); |
| } |
| |
| // When the argument is 0, the result of GCC builtins is undefined, whereas |
| // for Microsoft intrinsics, the result is the bit-width of the argument. |
| bool ZeroIsUndefined = BuiltinOp != Builtin::BI__lzcnt16 && |
| BuiltinOp != Builtin::BI__lzcnt && |
| BuiltinOp != Builtin::BI__lzcnt64; |
| |
| if (Val == 0) { |
| if (Fallback) { |
| pushInteger(S, *Fallback, Call->getType()); |
| return true; |
| } |
| |
| if (ZeroIsUndefined) |
| return false; |
| } |
| |
| pushInteger(S, Val.countl_zero(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_ctz(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, const CallExpr *Call, |
| unsigned BuiltinID) { |
| std::optional<APSInt> Fallback; |
| if (BuiltinID == Builtin::BI__builtin_ctzg && Call->getNumArgs() == 2) |
| Fallback = popToAPSInt(S, Call->getArg(1)); |
| |
| APSInt Val; |
| if (Call->getArg(0)->getType()->isExtVectorBoolType()) { |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| Val = convertBoolVectorToInt(Arg); |
| } else { |
| Val = popToAPSInt(S, Call->getArg(0)); |
| } |
| |
| if (Val == 0) { |
| if (Fallback) { |
| pushInteger(S, *Fallback, Call->getType()); |
| return true; |
| } |
| return false; |
| } |
| |
| pushInteger(S, Val.countr_zero(), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_bswap(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const APSInt &Val = popToAPSInt(S, Call->getArg(0)); |
| assert(Val.getActiveBits() <= 64); |
| |
| pushInteger(S, Val.byteSwap(), Call->getType()); |
| return true; |
| } |
| |
| /// bool __atomic_always_lock_free(size_t, void const volatile*) |
| /// bool __atomic_is_lock_free(size_t, void const volatile*) |
| static bool interp__builtin_atomic_lock_free(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, |
| unsigned BuiltinOp) { |
| auto returnBool = [&S](bool Value) -> bool { |
| S.Stk.push<Boolean>(Value); |
| return true; |
| }; |
| |
| const Pointer &Ptr = S.Stk.pop<Pointer>(); |
| const APSInt &SizeVal = popToAPSInt(S, Call->getArg(0)); |
| |
| // For __atomic_is_lock_free(sizeof(_Atomic(T))), if the size is a power |
| // of two less than or equal to the maximum inline atomic width, we know it |
| // is lock-free. If the size isn't a power of two, or greater than the |
| // maximum alignment where we promote atomics, we know it is not lock-free |
| // (at least not in the sense of atomic_is_lock_free). Otherwise, |
| // the answer can only be determined at runtime; for example, 16-byte |
| // atomics have lock-free implementations on some, but not all, |
| // x86-64 processors. |
| |
| // Check power-of-two. |
| CharUnits Size = CharUnits::fromQuantity(SizeVal.getZExtValue()); |
| if (Size.isPowerOfTwo()) { |
| // Check against inlining width. |
| unsigned InlineWidthBits = |
| S.getASTContext().getTargetInfo().getMaxAtomicInlineWidth(); |
| if (Size <= S.getASTContext().toCharUnitsFromBits(InlineWidthBits)) { |
| |
| // OK, we will inline appropriately-aligned operations of this size, |
| // and _Atomic(T) is appropriately-aligned. |
| if (Size == CharUnits::One()) |
| return returnBool(true); |
| |
| // Same for null pointers. |
| assert(BuiltinOp != Builtin::BI__c11_atomic_is_lock_free); |
| if (Ptr.isZero()) |
| return returnBool(true); |
| |
| if (Ptr.isIntegralPointer()) { |
| uint64_t IntVal = Ptr.getIntegerRepresentation(); |
| if (APSInt(APInt(64, IntVal, false), true).isAligned(Size.getAsAlign())) |
| return returnBool(true); |
| } |
| |
| const Expr *PtrArg = Call->getArg(1); |
| // Otherwise, check if the type's alignment against Size. |
| if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PtrArg)) { |
| // Drop the potential implicit-cast to 'const volatile void*', getting |
| // the underlying type. |
| if (ICE->getCastKind() == CK_BitCast) |
| PtrArg = ICE->getSubExpr(); |
| } |
| |
| if (const auto *PtrTy = PtrArg->getType()->getAs<PointerType>()) { |
| QualType PointeeType = PtrTy->getPointeeType(); |
| if (!PointeeType->isIncompleteType() && |
| S.getASTContext().getTypeAlignInChars(PointeeType) >= Size) { |
| // OK, we will inline operations on this object. |
| return returnBool(true); |
| } |
| } |
| } |
| } |
| |
| if (BuiltinOp == Builtin::BI__atomic_always_lock_free) |
| return returnBool(false); |
| |
| return false; |
| } |
| |
| /// bool __c11_atomic_is_lock_free(size_t) |
| static bool interp__builtin_c11_atomic_is_lock_free(InterpState &S, |
| CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const APSInt &SizeVal = popToAPSInt(S, Call->getArg(0)); |
| |
| CharUnits Size = CharUnits::fromQuantity(SizeVal.getZExtValue()); |
| if (Size.isPowerOfTwo()) { |
| // Check against inlining width. |
| unsigned InlineWidthBits = |
| S.getASTContext().getTargetInfo().getMaxAtomicInlineWidth(); |
| if (Size <= S.getASTContext().toCharUnitsFromBits(InlineWidthBits)) { |
| S.Stk.push<Boolean>(true); |
| return true; |
| } |
| } |
| |
| return false; // returnBool(false); |
| } |
| |
| /// __builtin_complex(Float A, float B); |
| static bool interp__builtin_complex(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg2 = S.Stk.pop<Floating>(); |
| const Floating &Arg1 = S.Stk.pop<Floating>(); |
| Pointer &Result = S.Stk.peek<Pointer>(); |
| |
| Result.elem<Floating>(0) = Arg1; |
| Result.elem<Floating>(1) = Arg2; |
| Result.initializeAllElements(); |
| |
| return true; |
| } |
| |
| /// __builtin_is_aligned() |
| /// __builtin_align_up() |
| /// __builtin_align_down() |
| /// The first parameter is either an integer or a pointer. |
| /// The second parameter is the requested alignment as an integer. |
| static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, |
| unsigned BuiltinOp) { |
| const APSInt &Alignment = popToAPSInt(S, Call->getArg(1)); |
| |
| if (Alignment < 0 || !Alignment.isPowerOf2()) { |
| S.FFDiag(Call, diag::note_constexpr_invalid_alignment) << Alignment; |
| return false; |
| } |
| unsigned SrcWidth = S.getASTContext().getIntWidth(Call->getArg(0)->getType()); |
| APSInt MaxValue(APInt::getOneBitSet(SrcWidth, SrcWidth - 1)); |
| if (APSInt::compareValues(Alignment, MaxValue) > 0) { |
| S.FFDiag(Call, diag::note_constexpr_alignment_too_big) |
| << MaxValue << Call->getArg(0)->getType() << Alignment; |
| return false; |
| } |
| |
| // The first parameter is either an integer or a pointer. |
| PrimType FirstArgT = *S.Ctx.classify(Call->getArg(0)); |
| |
| if (isIntegralType(FirstArgT)) { |
| const APSInt &Src = popToAPSInt(S.Stk, FirstArgT); |
| APInt AlignMinusOne = Alignment.extOrTrunc(Src.getBitWidth()) - 1; |
| if (BuiltinOp == Builtin::BI__builtin_align_up) { |
| APSInt AlignedVal = |
| APSInt((Src + AlignMinusOne) & ~AlignMinusOne, Src.isUnsigned()); |
| pushInteger(S, AlignedVal, Call->getType()); |
| } else if (BuiltinOp == Builtin::BI__builtin_align_down) { |
| APSInt AlignedVal = APSInt(Src & ~AlignMinusOne, Src.isUnsigned()); |
| pushInteger(S, AlignedVal, Call->getType()); |
| } else { |
| assert(*S.Ctx.classify(Call->getType()) == PT_Bool); |
| S.Stk.push<Boolean>((Src & AlignMinusOne) == 0); |
| } |
| return true; |
| } |
| assert(FirstArgT == PT_Ptr); |
| const Pointer &Ptr = S.Stk.pop<Pointer>(); |
| if (!Ptr.isBlockPointer()) |
| return false; |
| |
| unsigned PtrOffset = Ptr.getIndex(); |
| CharUnits BaseAlignment = |
| S.getASTContext().getDeclAlign(Ptr.getDeclDesc()->asValueDecl()); |
| CharUnits PtrAlign = |
| BaseAlignment.alignmentAtOffset(CharUnits::fromQuantity(PtrOffset)); |
| |
| if (BuiltinOp == Builtin::BI__builtin_is_aligned) { |
| if (PtrAlign.getQuantity() >= Alignment) { |
| S.Stk.push<Boolean>(true); |
| return true; |
| } |
| // If the alignment is not known to be sufficient, some cases could still |
| // be aligned at run time. However, if the requested alignment is less or |
| // equal to the base alignment and the offset is not aligned, we know that |
| // the run-time value can never be aligned. |
| if (BaseAlignment.getQuantity() >= Alignment && |
| PtrAlign.getQuantity() < Alignment) { |
| S.Stk.push<Boolean>(false); |
| return true; |
| } |
| |
| S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_compute) |
| << Alignment; |
| return false; |
| } |
| |
| assert(BuiltinOp == Builtin::BI__builtin_align_down || |
| BuiltinOp == Builtin::BI__builtin_align_up); |
| |
| // For align_up/align_down, we can return the same value if the alignment |
| // is known to be greater or equal to the requested value. |
| if (PtrAlign.getQuantity() >= Alignment) { |
| S.Stk.push<Pointer>(Ptr); |
| return true; |
| } |
| |
| // The alignment could be greater than the minimum at run-time, so we cannot |
| // infer much about the resulting pointer value. One case is possible: |
| // For `_Alignas(32) char buf[N]; __builtin_align_down(&buf[idx], 32)` we |
| // can infer the correct index if the requested alignment is smaller than |
| // the base alignment so we can perform the computation on the offset. |
| if (BaseAlignment.getQuantity() >= Alignment) { |
| assert(Alignment.getBitWidth() <= 64 && |
| "Cannot handle > 64-bit address-space"); |
| uint64_t Alignment64 = Alignment.getZExtValue(); |
| CharUnits NewOffset = |
| CharUnits::fromQuantity(BuiltinOp == Builtin::BI__builtin_align_down |
| ? llvm::alignDown(PtrOffset, Alignment64) |
| : llvm::alignTo(PtrOffset, Alignment64)); |
| |
| S.Stk.push<Pointer>(Ptr.atIndex(NewOffset.getQuantity())); |
| return true; |
| } |
| |
| // Otherwise, we cannot constant-evaluate the result. |
| S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_adjust) << Alignment; |
| return false; |
| } |
| |
| /// __builtin_assume_aligned(Ptr, Alignment[, ExtraOffset]) |
| static bool interp__builtin_assume_aligned(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| assert(Call->getNumArgs() == 2 || Call->getNumArgs() == 3); |
| |
| std::optional<APSInt> ExtraOffset; |
| if (Call->getNumArgs() == 3) |
| ExtraOffset = popToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(2))); |
| |
| APSInt Alignment = popToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1))); |
| const Pointer &Ptr = S.Stk.pop<Pointer>(); |
| |
| CharUnits Align = CharUnits::fromQuantity(Alignment.getZExtValue()); |
| |
| // If there is a base object, then it must have the correct alignment. |
| if (Ptr.isBlockPointer()) { |
| CharUnits BaseAlignment; |
| if (const auto *VD = Ptr.getDeclDesc()->asValueDecl()) |
| BaseAlignment = S.getASTContext().getDeclAlign(VD); |
| else if (const auto *E = Ptr.getDeclDesc()->asExpr()) |
| BaseAlignment = GetAlignOfExpr(S.getASTContext(), E, UETT_AlignOf); |
| |
| if (BaseAlignment < Align) { |
| S.CCEDiag(Call->getArg(0), |
| diag::note_constexpr_baa_insufficient_alignment) |
| << 0 << BaseAlignment.getQuantity() << Align.getQuantity(); |
| return false; |
| } |
| } |
| |
| APValue AV = Ptr.toAPValue(S.getASTContext()); |
| CharUnits AVOffset = AV.getLValueOffset(); |
| if (ExtraOffset) |
| AVOffset -= CharUnits::fromQuantity(ExtraOffset->getZExtValue()); |
| if (AVOffset.alignTo(Align) != AVOffset) { |
| if (Ptr.isBlockPointer()) |
| S.CCEDiag(Call->getArg(0), |
| diag::note_constexpr_baa_insufficient_alignment) |
| << 1 << AVOffset.getQuantity() << Align.getQuantity(); |
| else |
| S.CCEDiag(Call->getArg(0), |
| diag::note_constexpr_baa_value_insufficient_alignment) |
| << AVOffset.getQuantity() << Align.getQuantity(); |
| return false; |
| } |
| |
| S.Stk.push<Pointer>(Ptr); |
| return true; |
| } |
| |
| /// (CarryIn, LHS, RHS, Result) |
| static bool interp__builtin_ia32_addcarry_subborrow(InterpState &S, |
| CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, |
| unsigned BuiltinOp) { |
| if (Call->getNumArgs() != 4 || !Call->getArg(0)->getType()->isIntegerType() || |
| !Call->getArg(1)->getType()->isIntegerType() || |
| !Call->getArg(2)->getType()->isIntegerType()) |
| return false; |
| |
| const Pointer &CarryOutPtr = S.Stk.pop<Pointer>(); |
| |
| APSInt RHS = popToAPSInt(S, Call->getArg(2)); |
| APSInt LHS = popToAPSInt(S, Call->getArg(1)); |
| APSInt CarryIn = popToAPSInt(S, Call->getArg(0)); |
| |
| bool IsAdd = BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u32 || |
| BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u64; |
| |
| unsigned BitWidth = LHS.getBitWidth(); |
| unsigned CarryInBit = CarryIn.ugt(0) ? 1 : 0; |
| APInt ExResult = |
| IsAdd ? (LHS.zext(BitWidth + 1) + (RHS.zext(BitWidth + 1) + CarryInBit)) |
| : (LHS.zext(BitWidth + 1) - (RHS.zext(BitWidth + 1) + CarryInBit)); |
| |
| APInt Result = ExResult.extractBits(BitWidth, 0); |
| APSInt CarryOut = |
| APSInt(ExResult.extractBits(1, BitWidth), /*IsUnsigned=*/true); |
| |
| QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType(); |
| PrimType CarryOutT = *S.getContext().classify(CarryOutType); |
| assignInteger(S, CarryOutPtr, CarryOutT, APSInt(std::move(Result), true)); |
| |
| pushInteger(S, CarryOut, Call->getType()); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_os_log_format_buffer_size(InterpState &S, |
| CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| analyze_os_log::OSLogBufferLayout Layout; |
| analyze_os_log::computeOSLogBufferLayout(S.getASTContext(), Call, Layout); |
| pushInteger(S, Layout.size().getQuantity(), Call->getType()); |
| return true; |
| } |
| |
| static bool |
| interp__builtin_ptrauth_string_discriminator(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const auto &Ptr = S.Stk.pop<Pointer>(); |
| assert(Ptr.getFieldDesc()->isPrimitiveArray()); |
| |
| // This should be created for a StringLiteral, so should alway shold at least |
| // one array element. |
| assert(Ptr.getFieldDesc()->getNumElems() >= 1); |
| StringRef R(&Ptr.deref<char>(), Ptr.getFieldDesc()->getNumElems() - 1); |
| uint64_t Result = getPointerAuthStableSipHash(R); |
| pushInteger(S, Result, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_infer_alloc_token(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const ASTContext &ASTCtx = S.getASTContext(); |
| uint64_t BitWidth = ASTCtx.getTypeSize(ASTCtx.getSizeType()); |
| auto Mode = |
| ASTCtx.getLangOpts().AllocTokenMode.value_or(llvm::DefaultAllocTokenMode); |
| uint64_t MaxTokens = |
| ASTCtx.getLangOpts().AllocTokenMax.value_or(~0ULL >> (64 - BitWidth)); |
| |
| // We do not read any of the arguments; discard them. |
| for (int I = Call->getNumArgs() - 1; I >= 0; --I) |
| discard(S.Stk, *S.getContext().classify(Call->getArg(I))); |
| |
| // Note: Type inference from a surrounding cast is not supported in |
| // constexpr evaluation. |
| QualType AllocType = infer_alloc::inferPossibleType(Call, ASTCtx, nullptr); |
| if (AllocType.isNull()) { |
| S.CCEDiag(Call, |
| diag::note_constexpr_infer_alloc_token_type_inference_failed); |
| return false; |
| } |
| |
| auto ATMD = infer_alloc::getAllocTokenMetadata(AllocType, ASTCtx); |
| if (!ATMD) { |
| S.CCEDiag(Call, diag::note_constexpr_infer_alloc_token_no_metadata); |
| return false; |
| } |
| |
| auto MaybeToken = llvm::getAllocToken(Mode, *ATMD, MaxTokens); |
| if (!MaybeToken) { |
| S.CCEDiag(Call, diag::note_constexpr_infer_alloc_token_stateful_mode); |
| return false; |
| } |
| |
| pushInteger(S, llvm::APInt(BitWidth, *MaybeToken), ASTCtx.getSizeType()); |
| return true; |
| } |
| |
| static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| // A call to __operator_new is only valid within std::allocate<>::allocate. |
| // Walk up the call stack to find the appropriate caller and get the |
| // element type from it. |
| auto [NewCall, ElemType] = S.getStdAllocatorCaller("allocate"); |
| |
| if (ElemType.isNull()) { |
| S.FFDiag(Call, S.getLangOpts().CPlusPlus20 |
| ? diag::note_constexpr_new_untyped |
| : diag::note_constexpr_new); |
| return false; |
| } |
| assert(NewCall); |
| |
| if (ElemType->isIncompleteType() || ElemType->isFunctionType()) { |
| S.FFDiag(Call, diag::note_constexpr_new_not_complete_object_type) |
| << (ElemType->isIncompleteType() ? 0 : 1) << ElemType; |
| return false; |
| } |
| |
| // We only care about the first parameter (the size), so discard all the |
| // others. |
| { |
| unsigned NumArgs = Call->getNumArgs(); |
| assert(NumArgs >= 1); |
| |
| // The std::nothrow_t arg never gets put on the stack. |
| if (Call->getArg(NumArgs - 1)->getType()->isNothrowT()) |
| --NumArgs; |
| auto Args = ArrayRef(Call->getArgs(), Call->getNumArgs()); |
| // First arg is needed. |
| Args = Args.drop_front(); |
| |
| // Discard the rest. |
| for (const Expr *Arg : Args) |
| discard(S.Stk, *S.getContext().classify(Arg)); |
| } |
| |
| APSInt Bytes = popToAPSInt(S, Call->getArg(0)); |
| CharUnits ElemSize = S.getASTContext().getTypeSizeInChars(ElemType); |
| assert(!ElemSize.isZero()); |
| // Divide the number of bytes by sizeof(ElemType), so we get the number of |
| // elements we should allocate. |
| APInt NumElems, Remainder; |
| APInt ElemSizeAP(Bytes.getBitWidth(), ElemSize.getQuantity()); |
| APInt::udivrem(Bytes, ElemSizeAP, NumElems, Remainder); |
| if (Remainder != 0) { |
| // This likely indicates a bug in the implementation of 'std::allocator'. |
| S.FFDiag(Call, diag::note_constexpr_operator_new_bad_size) |
| << Bytes << APSInt(ElemSizeAP, true) << ElemType; |
| return false; |
| } |
| |
| // NB: The same check we're using in CheckArraySize() |
| if (NumElems.getActiveBits() > |
| ConstantArrayType::getMaxSizeBits(S.getASTContext()) || |
| NumElems.ugt(Descriptor::MaxArrayElemBytes / ElemSize.getQuantity())) { |
| // FIXME: NoThrow check? |
| const SourceInfo &Loc = S.Current->getSource(OpPC); |
| S.FFDiag(Loc, diag::note_constexpr_new_too_large) |
| << NumElems.getZExtValue(); |
| return false; |
| } |
| |
| if (!CheckArraySize(S, OpPC, NumElems.getZExtValue())) |
| return false; |
| |
| bool IsArray = NumElems.ugt(1); |
| OptPrimType ElemT = S.getContext().classify(ElemType); |
| DynamicAllocator &Allocator = S.getAllocator(); |
| if (ElemT) { |
| Block *B = |
| Allocator.allocate(NewCall, *ElemT, NumElems.getZExtValue(), |
| S.Ctx.getEvalID(), DynamicAllocator::Form::Operator); |
| assert(B); |
| S.Stk.push<Pointer>(Pointer(B).atIndex(0)); |
| return true; |
| } |
| |
| assert(!ElemT); |
| |
| // Composite arrays |
| if (IsArray) { |
| const Descriptor *Desc = |
| S.P.createDescriptor(NewCall, ElemType.getTypePtr(), std::nullopt); |
| Block *B = |
| Allocator.allocate(Desc, NumElems.getZExtValue(), S.Ctx.getEvalID(), |
| DynamicAllocator::Form::Operator); |
| assert(B); |
| S.Stk.push<Pointer>(Pointer(B).atIndex(0)); |
| return true; |
| } |
| |
| // Records. Still allocate them as single-element arrays. |
| QualType AllocType = S.getASTContext().getConstantArrayType( |
| ElemType, NumElems, nullptr, ArraySizeModifier::Normal, 0); |
| |
| const Descriptor *Desc = S.P.createDescriptor(NewCall, AllocType.getTypePtr(), |
| Descriptor::InlineDescMD); |
| Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(), |
| DynamicAllocator::Form::Operator); |
| assert(B); |
| S.Stk.push<Pointer>(Pointer(B).atIndex(0).narrow()); |
| return true; |
| } |
| |
| static bool interp__builtin_operator_delete(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Expr *Source = nullptr; |
| const Block *BlockToDelete = nullptr; |
| |
| if (S.checkingPotentialConstantExpression()) { |
| S.Stk.discard<Pointer>(); |
| return false; |
| } |
| |
| // This is permitted only within a call to std::allocator<T>::deallocate. |
| if (!S.getStdAllocatorCaller("deallocate")) { |
| S.FFDiag(Call); |
| S.Stk.discard<Pointer>(); |
| return true; |
| } |
| |
| { |
| const Pointer &Ptr = S.Stk.pop<Pointer>(); |
| |
| if (Ptr.isZero()) { |
| S.CCEDiag(Call, diag::note_constexpr_deallocate_null); |
| return true; |
| } |
| |
| Source = Ptr.getDeclDesc()->asExpr(); |
| BlockToDelete = Ptr.block(); |
| |
| if (!BlockToDelete->isDynamic()) { |
| S.FFDiag(Call, diag::note_constexpr_delete_not_heap_alloc) |
| << Ptr.toDiagnosticString(S.getASTContext()); |
| if (const auto *D = Ptr.getFieldDesc()->asDecl()) |
| S.Note(D->getLocation(), diag::note_declared_at); |
| } |
| } |
| assert(BlockToDelete); |
| |
| DynamicAllocator &Allocator = S.getAllocator(); |
| const Descriptor *BlockDesc = BlockToDelete->getDescriptor(); |
| std::optional<DynamicAllocator::Form> AllocForm = |
| Allocator.getAllocationForm(Source); |
| |
| if (!Allocator.deallocate(Source, BlockToDelete, S)) { |
| // Nothing has been deallocated, this must be a double-delete. |
| const SourceInfo &Loc = S.Current->getSource(OpPC); |
| S.FFDiag(Loc, diag::note_constexpr_double_delete); |
| return false; |
| } |
| assert(AllocForm); |
| |
| return CheckNewDeleteForms( |
| S, OpPC, *AllocForm, DynamicAllocator::Form::Operator, BlockDesc, Source); |
| } |
| |
| static bool interp__builtin_arithmetic_fence(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const Floating &Arg0 = S.Stk.pop<Floating>(); |
| S.Stk.push<Floating>(Arg0); |
| return true; |
| } |
| |
| static bool interp__builtin_vector_reduce(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, unsigned ID) { |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| assert(Arg.getFieldDesc()->isPrimitiveArray()); |
| |
| QualType ElemType = Arg.getFieldDesc()->getElemQualType(); |
| assert(Call->getType() == ElemType); |
| PrimType ElemT = *S.getContext().classify(ElemType); |
| unsigned NumElems = Arg.getNumElems(); |
| |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| T Result = Arg.elem<T>(0); |
| unsigned BitWidth = Result.bitWidth(); |
| for (unsigned I = 1; I != NumElems; ++I) { |
| T Elem = Arg.elem<T>(I); |
| T PrevResult = Result; |
| |
| if (ID == Builtin::BI__builtin_reduce_add) { |
| if (T::add(Result, Elem, BitWidth, &Result)) { |
| unsigned OverflowBits = BitWidth + 1; |
| (void)handleOverflow(S, OpPC, |
| (PrevResult.toAPSInt(OverflowBits) + |
| Elem.toAPSInt(OverflowBits))); |
| return false; |
| } |
| } else if (ID == Builtin::BI__builtin_reduce_mul) { |
| if (T::mul(Result, Elem, BitWidth, &Result)) { |
| unsigned OverflowBits = BitWidth * 2; |
| (void)handleOverflow(S, OpPC, |
| (PrevResult.toAPSInt(OverflowBits) * |
| Elem.toAPSInt(OverflowBits))); |
| return false; |
| } |
| |
| } else if (ID == Builtin::BI__builtin_reduce_and) { |
| (void)T::bitAnd(Result, Elem, BitWidth, &Result); |
| } else if (ID == Builtin::BI__builtin_reduce_or) { |
| (void)T::bitOr(Result, Elem, BitWidth, &Result); |
| } else if (ID == Builtin::BI__builtin_reduce_xor) { |
| (void)T::bitXor(Result, Elem, BitWidth, &Result); |
| } else if (ID == Builtin::BI__builtin_reduce_min) { |
| if (Elem < Result) |
| Result = Elem; |
| } else if (ID == Builtin::BI__builtin_reduce_max) { |
| if (Elem > Result) |
| Result = Elem; |
| } else { |
| llvm_unreachable("Unhandled vector reduce builtin"); |
| } |
| } |
| pushInteger(S, Result.toAPSInt(), Call->getType()); |
| }); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_elementwise_abs(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, |
| unsigned BuiltinID) { |
| assert(Call->getNumArgs() == 1); |
| QualType Ty = Call->getArg(0)->getType(); |
| if (Ty->isIntegerType()) { |
| APSInt Val = popToAPSInt(S, Call->getArg(0)); |
| pushInteger(S, Val.abs(), Call->getType()); |
| return true; |
| } |
| |
| if (Ty->isFloatingType()) { |
| Floating Val = S.Stk.pop<Floating>(); |
| Floating Result = abs(S, Val); |
| S.Stk.push<Floating>(Result); |
| return true; |
| } |
| |
| // Otherwise, the argument must be a vector. |
| assert(Call->getArg(0)->getType()->isVectorType()); |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| assert(Arg.getFieldDesc()->isPrimitiveArray()); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| assert(Dst.getFieldDesc()->isPrimitiveArray()); |
| assert(Arg.getFieldDesc()->getNumElems() == |
| Dst.getFieldDesc()->getNumElems()); |
| |
| QualType ElemType = Arg.getFieldDesc()->getElemQualType(); |
| PrimType ElemT = *S.getContext().classify(ElemType); |
| unsigned NumElems = Arg.getNumElems(); |
| // we can either have a vector of integer or a vector of floating point |
| for (unsigned I = 0; I != NumElems; ++I) { |
| if (ElemType->isIntegerType()) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| Dst.elem<T>(I) = T::from(static_cast<T>( |
| APSInt(Arg.elem<T>(I).toAPSInt().abs(), |
| ElemType->isUnsignedIntegerOrEnumerationType()))); |
| }); |
| } else { |
| Floating Val = Arg.elem<Floating>(I); |
| Dst.elem<Floating>(I) = abs(S, Val); |
| } |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| /// Can be called with an integer or vector as the first and only parameter. |
| static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, |
| unsigned BuiltinID) { |
| assert(Call->getNumArgs() == 1); |
| if (Call->getArg(0)->getType()->isIntegerType()) { |
| APSInt Val = popToAPSInt(S, Call->getArg(0)); |
| |
| if (BuiltinID == Builtin::BI__builtin_elementwise_popcount) { |
| pushInteger(S, Val.popcount(), Call->getType()); |
| } else { |
| pushInteger(S, Val.reverseBits(), Call->getType()); |
| } |
| return true; |
| } |
| // Otherwise, the argument must be a vector. |
| assert(Call->getArg(0)->getType()->isVectorType()); |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| assert(Arg.getFieldDesc()->isPrimitiveArray()); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| assert(Dst.getFieldDesc()->isPrimitiveArray()); |
| assert(Arg.getFieldDesc()->getNumElems() == |
| Dst.getFieldDesc()->getNumElems()); |
| |
| QualType ElemType = Arg.getFieldDesc()->getElemQualType(); |
| PrimType ElemT = *S.getContext().classify(ElemType); |
| unsigned NumElems = Arg.getNumElems(); |
| |
| // FIXME: Reading from uninitialized vector elements? |
| for (unsigned I = 0; I != NumElems; ++I) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| if (BuiltinID == Builtin::BI__builtin_elementwise_popcount) { |
| Dst.elem<T>(I) = T::from(Arg.elem<T>(I).toAPSInt().popcount()); |
| } else { |
| Dst.elem<T>(I) = |
| T::from(Arg.elem<T>(I).toAPSInt().reverseBits().getZExtValue()); |
| } |
| }); |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| /// Can be called with an integer or vector as the first and only parameter. |
| static bool interp__builtin_elementwise_countzeroes(InterpState &S, |
| CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, |
| unsigned BuiltinID) { |
| bool HasZeroArg = Call->getNumArgs() == 2; |
| bool IsCTTZ = BuiltinID == Builtin::BI__builtin_elementwise_ctzg; |
| assert(Call->getNumArgs() == 1 || HasZeroArg); |
| if (Call->getArg(0)->getType()->isIntegerType()) { |
| PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); |
| APSInt Val = popToAPSInt(S.Stk, ArgT); |
| std::optional<APSInt> ZeroVal; |
| if (HasZeroArg) { |
| ZeroVal = Val; |
| Val = popToAPSInt(S.Stk, ArgT); |
| } |
| |
| if (Val.isZero()) { |
| if (ZeroVal) { |
| pushInteger(S, *ZeroVal, Call->getType()); |
| return true; |
| } |
| // If we haven't been provided the second argument, the result is |
| // undefined |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_countzeroes_zero) |
| << /*IsTrailing=*/IsCTTZ; |
| return false; |
| } |
| |
| if (BuiltinID == Builtin::BI__builtin_elementwise_clzg) { |
| pushInteger(S, Val.countLeadingZeros(), Call->getType()); |
| } else { |
| pushInteger(S, Val.countTrailingZeros(), Call->getType()); |
| } |
| return true; |
| } |
| // Otherwise, the argument must be a vector. |
| const ASTContext &ASTCtx = S.getASTContext(); |
| Pointer ZeroArg; |
| if (HasZeroArg) { |
| assert(Call->getArg(1)->getType()->isVectorType() && |
| ASTCtx.hasSameUnqualifiedType(Call->getArg(0)->getType(), |
| Call->getArg(1)->getType())); |
| (void)ASTCtx; |
| ZeroArg = S.Stk.pop<Pointer>(); |
| assert(ZeroArg.getFieldDesc()->isPrimitiveArray()); |
| } |
| assert(Call->getArg(0)->getType()->isVectorType()); |
| const Pointer &Arg = S.Stk.pop<Pointer>(); |
| assert(Arg.getFieldDesc()->isPrimitiveArray()); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| assert(Dst.getFieldDesc()->isPrimitiveArray()); |
| assert(Arg.getFieldDesc()->getNumElems() == |
| Dst.getFieldDesc()->getNumElems()); |
| |
| QualType ElemType = Arg.getFieldDesc()->getElemQualType(); |
| PrimType ElemT = *S.getContext().classify(ElemType); |
| unsigned NumElems = Arg.getNumElems(); |
| |
| // FIXME: Reading from uninitialized vector elements? |
| for (unsigned I = 0; I != NumElems; ++I) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| APInt EltVal = Arg.atIndex(I).deref<T>().toAPSInt(); |
| if (EltVal.isZero()) { |
| if (HasZeroArg) { |
| Dst.atIndex(I).deref<T>() = ZeroArg.atIndex(I).deref<T>(); |
| } else { |
| // If we haven't been provided the second argument, the result is |
| // undefined |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_countzeroes_zero) |
| << /*IsTrailing=*/IsCTTZ; |
| return false; |
| } |
| } else if (IsCTTZ) { |
| Dst.atIndex(I).deref<T>() = T::from(EltVal.countTrailingZeros()); |
| } else { |
| Dst.atIndex(I).deref<T>() = T::from(EltVal.countLeadingZeros()); |
| } |
| Dst.atIndex(I).initialize(); |
| }); |
| } |
| |
| return true; |
| } |
| |
| static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call, unsigned ID) { |
| assert(Call->getNumArgs() == 3); |
| const ASTContext &ASTCtx = S.getASTContext(); |
| APSInt Size = popToAPSInt(S, Call->getArg(2)); |
| const Pointer SrcPtr = S.Stk.pop<Pointer>(); |
| const Pointer DestPtr = S.Stk.pop<Pointer>(); |
| |
| assert(!Size.isSigned() && "memcpy and friends take an unsigned size"); |
| |
| if (ID == Builtin::BImemcpy || ID == Builtin::BImemmove) |
| diagnoseNonConstexprBuiltin(S, OpPC, ID); |
| |
| bool Move = |
| (ID == Builtin::BI__builtin_memmove || ID == Builtin::BImemmove || |
| ID == Builtin::BI__builtin_wmemmove || ID == Builtin::BIwmemmove); |
| bool WChar = ID == Builtin::BIwmemcpy || ID == Builtin::BIwmemmove || |
| ID == Builtin::BI__builtin_wmemcpy || |
| ID == Builtin::BI__builtin_wmemmove; |
| |
| // If the size is zero, we treat this as always being a valid no-op. |
| if (Size.isZero()) { |
| S.Stk.push<Pointer>(DestPtr); |
| return true; |
| } |
| |
| if (SrcPtr.isZero() || DestPtr.isZero()) { |
| Pointer DiagPtr = (SrcPtr.isZero() ? SrcPtr : DestPtr); |
| S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_null) |
| << /*IsMove=*/Move << /*IsWchar=*/WChar << !SrcPtr.isZero() |
| << DiagPtr.toDiagnosticString(ASTCtx); |
| return false; |
| } |
| |
| // Diagnose integral src/dest pointers specially. |
| if (SrcPtr.isIntegralPointer() || DestPtr.isIntegralPointer()) { |
| std::string DiagVal = "(void *)"; |
| DiagVal += SrcPtr.isIntegralPointer() |
| ? std::to_string(SrcPtr.getIntegerRepresentation()) |
| : std::to_string(DestPtr.getIntegerRepresentation()); |
| S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_null) |
| << Move << WChar << DestPtr.isIntegralPointer() << DiagVal; |
| return false; |
| } |
| |
| // Can't read from dummy pointers. |
| if (DestPtr.isDummy() || SrcPtr.isDummy()) |
| return false; |
| |
| if (DestPtr.getType()->isIncompleteType()) { |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_memcpy_incomplete_type) |
| << Move << DestPtr.getType(); |
| return false; |
| } |
| if (SrcPtr.getType()->isIncompleteType()) { |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_memcpy_incomplete_type) |
| << Move << SrcPtr.getType(); |
| return false; |
| } |
| |
| QualType DestElemType = getElemType(DestPtr); |
| if (DestElemType->isIncompleteType()) { |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_memcpy_incomplete_type) |
| << Move << DestElemType; |
| return false; |
| } |
| |
| size_t RemainingDestElems; |
| if (DestPtr.getFieldDesc()->isArray()) { |
| RemainingDestElems = DestPtr.isUnknownSizeArray() |
| ? 0 |
| : (DestPtr.getNumElems() - DestPtr.getIndex()); |
| } else { |
| RemainingDestElems = 1; |
| } |
| unsigned DestElemSize = ASTCtx.getTypeSizeInChars(DestElemType).getQuantity(); |
| |
| if (WChar) { |
| uint64_t WCharSize = |
| ASTCtx.getTypeSizeInChars(ASTCtx.getWCharType()).getQuantity(); |
| Size *= APSInt(APInt(Size.getBitWidth(), WCharSize, /*IsSigned=*/false), |
| /*IsUnsigend=*/true); |
| } |
| |
| if (Size.urem(DestElemSize) != 0) { |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_memcpy_unsupported) |
| << Move << WChar << 0 << DestElemType << Size << DestElemSize; |
| return false; |
| } |
| |
| QualType SrcElemType = getElemType(SrcPtr); |
| size_t RemainingSrcElems; |
| if (SrcPtr.getFieldDesc()->isArray()) { |
| RemainingSrcElems = SrcPtr.isUnknownSizeArray() |
| ? 0 |
| : (SrcPtr.getNumElems() - SrcPtr.getIndex()); |
| } else { |
| 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; |
| } |
| |
| if (!DestElemType.isTriviallyCopyableType(ASTCtx)) { |
| S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_nontrivial) |
| << Move << 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 << WChar << (Size.ugt(RemainingSrcBytes) ? 1 : 2) |
| << DestElemType << toString(N, 10, /*Signed=*/false); |
| return false; |
| } |
| |
| // Check for overlapping memory regions. |
| if (!Move && Pointer::pointToSameBlock(SrcPtr, DestPtr)) { |
| // Remove base casts. |
| Pointer SrcP = SrcPtr; |
| while (SrcP.isBaseClass()) |
| SrcP = SrcP.getBase(); |
| |
| Pointer DestP = DestPtr; |
| while (DestP.isBaseClass()) |
| DestP = DestP.getBase(); |
| |
| unsigned SrcIndex = SrcP.expand().getIndex() * SrcP.elemSize(); |
| unsigned DstIndex = DestP.expand().getIndex() * DestP.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 CallExpr *Call, unsigned ID) { |
| assert(Call->getNumArgs() == 3); |
| const APSInt &Size = popToAPSInt(S, Call->getArg(2)); |
| const Pointer &PtrB = S.Stk.pop<Pointer>(); |
| const Pointer &PtrA = S.Stk.pop<Pointer>(); |
| |
| if (ID == Builtin::BImemcmp || ID == Builtin::BIbcmp || |
| ID == Builtin::BIwmemcmp) |
| diagnoseNonConstexprBuiltin(S, OpPC, ID); |
| |
| if (Size.isZero()) { |
| pushInteger(S, 0, Call->getType()); |
| return true; |
| } |
| |
| if (!PtrA.isBlockPointer() || !PtrB.isBlockPointer()) |
| return false; |
| |
| bool IsWide = |
| (ID == Builtin::BIwmemcmp || ID == Builtin::BI__builtin_wmemcmp); |
| |
| const ASTContext &ASTCtx = S.getASTContext(); |
| QualType ElemTypeA = getElemType(PtrA); |
| QualType ElemTypeB = getElemType(PtrB); |
| // FIXME: This is an arbitrary limitation the current constant interpreter |
| // had. We could remove this. |
| if (!IsWide && (!isOneByteCharacterType(ElemTypeA) || |
| !isOneByteCharacterType(ElemTypeB))) { |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_memcmp_unsupported) |
| << ASTCtx.BuiltinInfo.getQuotedName(ID) << 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(ElemTypeA) * PtrA.getNumElems())); |
| 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(ElemTypeB) * PtrB.getNumElems())); |
| 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; |
| } |
| 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; |
| } |
| 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 CheckLoad() on every array |
| // element. |
| S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end) |
| << AK_Read << S.Current->getRange(OpPC); |
| return false; |
| } |
| |
| // __builtin_memchr(ptr, int, int) |
| // __builtin_strchr(ptr, int) |
| static bool interp__builtin_memchr(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, unsigned ID) { |
| if (ID == Builtin::BImemchr || ID == Builtin::BIwcschr || |
| ID == Builtin::BIstrchr || ID == Builtin::BIwmemchr) |
| diagnoseNonConstexprBuiltin(S, OpPC, ID); |
| |
| std::optional<APSInt> MaxLength; |
| if (Call->getNumArgs() == 3) |
| MaxLength = popToAPSInt(S, Call->getArg(2)); |
| |
| APSInt Desired = popToAPSInt(S, Call->getArg(1)); |
| const Pointer &Ptr = S.Stk.pop<Pointer>(); |
| |
| if (MaxLength && MaxLength->isZero()) { |
| S.Stk.push<Pointer>(); |
| return true; |
| } |
| |
| if (Ptr.isDummy()) { |
| if (Ptr.getType()->isIncompleteType()) |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_ltor_incomplete_type) |
| << Ptr.getType(); |
| return false; |
| } |
| |
| // Null is only okay if the given size is 0. |
| if (Ptr.isZero()) { |
| S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null) |
| << AK_Read; |
| return false; |
| } |
| |
| QualType ElemTy = Ptr.getFieldDesc()->isArray() |
| ? Ptr.getFieldDesc()->getElemQualType() |
| : Ptr.getFieldDesc()->getType(); |
| bool IsRawByte = ID == Builtin::BImemchr || ID == Builtin::BI__builtin_memchr; |
| |
| // Give up on byte-oriented matching against multibyte elements. |
| if (IsRawByte && !isOneByteCharacterType(ElemTy)) { |
| S.FFDiag(S.Current->getSource(OpPC), |
| diag::note_constexpr_memchr_unsupported) |
| << S.getASTContext().BuiltinInfo.getQuotedName(ID) << ElemTy; |
| return false; |
| } |
| |
| if (ID == Builtin::BIstrchr || ID == Builtin::BI__builtin_strchr) { |
| int64_t DesiredTrunc; |
| if (S.getASTContext().CharTy->isSignedIntegerType()) |
| DesiredTrunc = |
| Desired.trunc(S.getASTContext().getCharWidth()).getSExtValue(); |
| else |
| DesiredTrunc = |
| Desired.trunc(S.getASTContext().getCharWidth()).getZExtValue(); |
| // strchr compares directly to the passed integer, and therefore |
| // always fails if given an int that is not a char. |
| if (Desired != DesiredTrunc) { |
| S.Stk.push<Pointer>(); |
| return true; |
| } |
| } |
| |
| uint64_t DesiredVal; |
| if (ID == Builtin::BIwmemchr || ID == Builtin::BI__builtin_wmemchr || |
| ID == Builtin::BIwcschr || ID == Builtin::BI__builtin_wcschr) { |
| // wcschr and wmemchr are given a wchar_t to look for. Just use it. |
| DesiredVal = Desired.getZExtValue(); |
| } else { |
| DesiredVal = Desired.trunc(S.getASTContext().getCharWidth()).getZExtValue(); |
| } |
| |
| bool StopAtZero = |
| (ID == Builtin::BIstrchr || ID == Builtin::BI__builtin_strchr || |
| ID == Builtin::BIwcschr || ID == Builtin::BI__builtin_wcschr); |
| |
| PrimType ElemT = |
| IsRawByte ? PT_Sint8 : *S.getContext().classify(getElemType(Ptr)); |
| |
| size_t Index = Ptr.getIndex(); |
| size_t Step = 0; |
| for (;;) { |
| const Pointer &ElemPtr = |
| (Index + Step) > 0 ? Ptr.atIndex(Index + Step) : Ptr; |
| |
| if (!CheckLoad(S, OpPC, ElemPtr)) |
| return false; |
| |
| uint64_t V; |
| INT_TYPE_SWITCH_NO_BOOL( |
| ElemT, { V = static_cast<uint64_t>(ElemPtr.deref<T>().toUnsigned()); }); |
| |
| if (V == DesiredVal) { |
| S.Stk.push<Pointer>(ElemPtr); |
| return true; |
| } |
| |
| if (StopAtZero && V == 0) |
| break; |
| |
| ++Step; |
| if (MaxLength && Step == MaxLength->getZExtValue()) |
| break; |
| } |
| |
| S.Stk.push<Pointer>(); |
| return true; |
| } |
| |
| static std::optional<unsigned> computeFullDescSize(const ASTContext &ASTCtx, |
| const Descriptor *Desc) { |
| if (Desc->isPrimitive()) |
| return ASTCtx.getTypeSizeInChars(Desc->getType()).getQuantity(); |
| if (Desc->isArray()) |
| return ASTCtx.getTypeSizeInChars(Desc->getElemQualType()).getQuantity() * |
| Desc->getNumElems(); |
| if (Desc->isRecord()) { |
| // Can't use Descriptor::getType() as that may return a pointer type. Look |
| // at the decl directly. |
| return ASTCtx |
| .getTypeSizeInChars( |
| ASTCtx.getCanonicalTagType(Desc->ElemRecord->getDecl())) |
| .getQuantity(); |
| } |
| |
| return std::nullopt; |
| } |
| |
| /// Compute the byte offset of \p Ptr in the full declaration. |
| static unsigned computePointerOffset(const ASTContext &ASTCtx, |
| const Pointer &Ptr) { |
| unsigned Result = 0; |
| |
| Pointer P = Ptr; |
| while (P.isField() || P.isArrayElement()) { |
| P = P.expand(); |
| const Descriptor *D = P.getFieldDesc(); |
| |
| if (P.isArrayElement()) { |
| unsigned ElemSize = |
| ASTCtx.getTypeSizeInChars(D->getElemQualType()).getQuantity(); |
| if (P.isOnePastEnd()) |
| Result += ElemSize * P.getNumElems(); |
| else |
| Result += ElemSize * P.getIndex(); |
| P = P.expand().getArray(); |
| } else if (P.isBaseClass()) { |
| const auto *RD = cast<CXXRecordDecl>(D->asDecl()); |
| bool IsVirtual = Ptr.isVirtualBaseClass(); |
| P = P.getBase(); |
| const Record *BaseRecord = P.getRecord(); |
| |
| const ASTRecordLayout &Layout = |
| ASTCtx.getASTRecordLayout(cast<CXXRecordDecl>(BaseRecord->getDecl())); |
| if (IsVirtual) |
| Result += Layout.getVBaseClassOffset(RD).getQuantity(); |
| else |
| Result += Layout.getBaseClassOffset(RD).getQuantity(); |
| } else if (P.isField()) { |
| const FieldDecl *FD = P.getField(); |
| const ASTRecordLayout &Layout = |
| ASTCtx.getASTRecordLayout(FD->getParent()); |
| unsigned FieldIndex = FD->getFieldIndex(); |
| uint64_t FieldOffset = |
| ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)) |
| .getQuantity(); |
| Result += FieldOffset; |
| P = P.getBase(); |
| } else |
| llvm_unreachable("Unhandled descriptor type"); |
| } |
| |
| return Result; |
| } |
| |
| /// Does Ptr point to the last subobject? |
| static bool pointsToLastObject(const Pointer &Ptr) { |
| Pointer P = Ptr; |
| while (!P.isRoot()) { |
| |
| if (P.isArrayElement()) { |
| P = P.expand().getArray(); |
| continue; |
| } |
| if (P.isBaseClass()) { |
| if (P.getRecord()->getNumFields() > 0) |
| return false; |
| P = P.getBase(); |
| continue; |
| } |
| |
| Pointer Base = P.getBase(); |
| if (const Record *R = Base.getRecord()) { |
| assert(P.getField()); |
| if (P.getField()->getFieldIndex() != R->getNumFields() - 1) |
| return false; |
| } |
| P = Base; |
| } |
| |
| return true; |
| } |
| |
| /// Does Ptr point to the last object AND to a flexible array member? |
| static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const Pointer &Ptr) { |
| auto isFlexibleArrayMember = [&](const Descriptor *FieldDesc) { |
| using FAMKind = LangOptions::StrictFlexArraysLevelKind; |
| FAMKind StrictFlexArraysLevel = |
| Ctx.getLangOpts().getStrictFlexArraysLevel(); |
| |
| if (StrictFlexArraysLevel == FAMKind::Default) |
| return true; |
| |
| unsigned NumElems = FieldDesc->getNumElems(); |
| if (NumElems == 0 && StrictFlexArraysLevel != FAMKind::IncompleteOnly) |
| return true; |
| |
| if (NumElems == 1 && StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete) |
| return true; |
| return false; |
| }; |
| |
| const Descriptor *FieldDesc = Ptr.getFieldDesc(); |
| if (!FieldDesc->isArray()) |
| return false; |
| |
| return Ptr.isDummy() && pointsToLastObject(Ptr) && |
| isFlexibleArrayMember(FieldDesc); |
| } |
| |
| static bool interp__builtin_object_size(InterpState &S, CodePtr OpPC, |
| const InterpFrame *Frame, |
| const CallExpr *Call) { |
| const ASTContext &ASTCtx = S.getASTContext(); |
| // From the GCC docs: |
| // Kind is an integer constant from 0 to 3. If the least significant bit is |
| // clear, objects are whole variables. If it is set, a closest surrounding |
| // subobject is considered the object a pointer points to. The second bit |
| // determines if maximum or minimum of remaining bytes is computed. |
| unsigned Kind = popToAPSInt(S, Call->getArg(1)).getZExtValue(); |
| assert(Kind <= 3 && "unexpected kind"); |
| bool UseFieldDesc = (Kind & 1u); |
| bool ReportMinimum = (Kind & 2u); |
| const Pointer &Ptr = S.Stk.pop<Pointer>(); |
| |
| if (Call->getArg(0)->HasSideEffects(ASTCtx)) { |
| // "If there are any side effects in them, it returns (size_t) -1 |
| // for type 0 or 1 and (size_t) 0 for type 2 or 3." |
| pushInteger(S, Kind <= 1 ? -1 : 0, Call->getType()); |
| return true; |
| } |
| |
| if (Ptr.isZero() || !Ptr.isBlockPointer()) |
| return false; |
| |
| // We can't load through pointers. |
| if (Ptr.isDummy() && Ptr.getType()->isPointerType()) |
| return false; |
| |
| bool DetermineForCompleteObject = Ptr.getFieldDesc() == Ptr.getDeclDesc(); |
| const Descriptor *DeclDesc = Ptr.getDeclDesc(); |
| assert(DeclDesc); |
| |
| if (!UseFieldDesc || DetermineForCompleteObject) { |
| // Lower bound, so we can't fall back to this. |
| if (ReportMinimum && !DetermineForCompleteObject) |
| return false; |
| |
| // Can't read beyond the pointer decl desc. |
| if (!UseFieldDesc && !ReportMinimum && DeclDesc->getType()->isPointerType()) |
| return false; |
| } else { |
| if (isUserWritingOffTheEnd(ASTCtx, Ptr.expand())) { |
| // If we cannot determine the size of the initial allocation, then we |
| // can't given an accurate upper-bound. However, we are still able to give |
| // conservative lower-bounds for Type=3. |
| if (Kind == 1) |
| return false; |
| } |
| } |
| |
| const Descriptor *Desc = UseFieldDesc ? Ptr.getFieldDesc() : DeclDesc; |
| assert(Desc); |
| |
| std::optional<unsigned> FullSize = computeFullDescSize(ASTCtx, Desc); |
| if (!FullSize) |
| return false; |
| |
| unsigned ByteOffset; |
| if (UseFieldDesc) { |
| if (Ptr.isBaseClass()) |
| ByteOffset = computePointerOffset(ASTCtx, Ptr.getBase()) - |
| computePointerOffset(ASTCtx, Ptr); |
| else { |
| if (Ptr.inArray()) |
| ByteOffset = |
| computePointerOffset(ASTCtx, Ptr) - |
| computePointerOffset(ASTCtx, Ptr.expand().atIndex(0).narrow()); |
| else |
| ByteOffset = 0; |
| } |
| } else |
| ByteOffset = computePointerOffset(ASTCtx, Ptr); |
| |
| assert(ByteOffset <= *FullSize); |
| unsigned Result = *FullSize - ByteOffset; |
| |
| pushInteger(S, Result, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call) { |
| |
| if (!S.inConstantContext()) |
| return false; |
| |
| const Pointer &Ptr = S.Stk.pop<Pointer>(); |
| |
| auto Error = [&](int Diag) { |
| bool CalledFromStd = false; |
| const auto *Callee = S.Current->getCallee(); |
| if (Callee && Callee->isInStdNamespace()) { |
| const IdentifierInfo *Identifier = Callee->getIdentifier(); |
| CalledFromStd = Identifier && Identifier->isStr("is_within_lifetime"); |
| } |
| S.CCEDiag(CalledFromStd |
| ? S.Current->Caller->getSource(S.Current->getRetPC()) |
| : S.Current->getSource(OpPC), |
| diag::err_invalid_is_within_lifetime) |
| << (CalledFromStd ? "std::is_within_lifetime" |
| : "__builtin_is_within_lifetime") |
| << Diag; |
| return false; |
| }; |
| |
| if (Ptr.isZero()) |
| return Error(0); |
| if (Ptr.isOnePastEnd()) |
| return Error(1); |
| |
| bool Result = Ptr.getLifetime() != Lifetime::Ended; |
| if (!Ptr.isActive()) { |
| Result = false; |
| } else { |
| if (!CheckLive(S, OpPC, Ptr, AK_Read)) |
| return false; |
| if (!CheckMutable(S, OpPC, Ptr)) |
| return false; |
| if (!CheckDummy(S, OpPC, Ptr.block(), AK_Read)) |
| return false; |
| } |
| |
| // Check if we're currently running an initializer. |
| if (llvm::is_contained(S.InitializingBlocks, Ptr.block())) |
| return Error(2); |
| if (S.EvaluatingDecl && Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl) |
| return Error(2); |
| |
| pushInteger(S, Result, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_elementwise_int_unaryop( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<APInt(const APSInt &)> Fn) { |
| assert(Call->getNumArgs() == 1); |
| assert(Call->getType()->isIntegerType()); |
| |
| // Single integer case. |
| if (!Call->getArg(0)->getType()->isVectorType()) { |
| APSInt Src = popToAPSInt(S, Call->getArg(0)); |
| APInt Result = Fn(Src); |
| pushInteger(S, APSInt(std::move(Result), !Src.isSigned()), Call->getType()); |
| return true; |
| } |
| |
| // TODO: Add vector integer handling. |
| return false; |
| } |
| |
| static bool interp__builtin_elementwise_int_binop( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<APInt(const APSInt &, const APSInt &)> Fn) { |
| assert(Call->getNumArgs() == 2); |
| |
| // Single integer case. |
| if (!Call->getArg(0)->getType()->isVectorType()) { |
| assert(!Call->getArg(1)->getType()->isVectorType()); |
| APSInt RHS = popToAPSInt(S, Call->getArg(1)); |
| APSInt LHS = popToAPSInt(S, Call->getArg(0)); |
| APInt Result = Fn(LHS, RHS); |
| pushInteger(S, APSInt(std::move(Result), !LHS.isSigned()), Call->getType()); |
| return true; |
| } |
| |
| const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); |
| assert(VT->getElementType()->isIntegralOrEnumerationType()); |
| PrimType ElemT = *S.getContext().classify(VT->getElementType()); |
| unsigned NumElems = VT->getNumElements(); |
| bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); |
| |
| // Vector + Scalar case. |
| if (!Call->getArg(1)->getType()->isVectorType()) { |
| assert(Call->getArg(1)->getType()->isIntegralOrEnumerationType()); |
| |
| APSInt RHS = popToAPSInt(S, Call->getArg(1)); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| for (unsigned I = 0; I != NumElems; ++I) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| Dst.elem<T>(I) = static_cast<T>( |
| APSInt(Fn(LHS.elem<T>(I).toAPSInt(), RHS), DestUnsigned)); |
| }); |
| } |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| // Vector case. |
| assert(Call->getArg(0)->getType()->isVectorType() && |
| Call->getArg(1)->getType()->isVectorType()); |
| assert(VT->getElementType() == |
| Call->getArg(1)->getType()->castAs<VectorType>()->getElementType()); |
| assert(VT->getNumElements() == |
| Call->getArg(1)->getType()->castAs<VectorType>()->getNumElements()); |
| assert(VT->getElementType()->isIntegralOrEnumerationType()); |
| |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| for (unsigned I = 0; I != NumElems; ++I) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| APSInt Elem1 = LHS.elem<T>(I).toAPSInt(); |
| APSInt Elem2 = RHS.elem<T>(I).toAPSInt(); |
| Dst.elem<T>(I) = static_cast<T>(APSInt(Fn(Elem1, Elem2), DestUnsigned)); |
| }); |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| static bool |
| interp__builtin_x86_pack(InterpState &S, CodePtr, const CallExpr *E, |
| llvm::function_ref<APInt(const APSInt &)> PackFn) { |
| const auto *VT0 = E->getArg(0)->getType()->castAs<VectorType>(); |
| [[maybe_unused]] const auto *VT1 = |
| E->getArg(1)->getType()->castAs<VectorType>(); |
| assert(VT0 && VT1 && "pack builtin VT0 and VT1 must be VectorType"); |
| assert(VT0->getElementType() == VT1->getElementType() && |
| VT0->getNumElements() == VT1->getNumElements() && |
| "pack builtin VT0 and VT1 ElementType must be same"); |
| |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| const ASTContext &ASTCtx = S.getASTContext(); |
| unsigned SrcBits = ASTCtx.getIntWidth(VT0->getElementType()); |
| unsigned LHSVecLen = VT0->getNumElements(); |
| unsigned SrcPerLane = 128 / SrcBits; |
| unsigned Lanes = LHSVecLen * SrcBits / 128; |
| |
| PrimType SrcT = *S.getContext().classify(VT0->getElementType()); |
| PrimType DstT = *S.getContext().classify(getElemType(Dst)); |
| bool IsUnsigend = getElemType(Dst)->isUnsignedIntegerType(); |
| |
| for (unsigned Lane = 0; Lane != Lanes; ++Lane) { |
| unsigned BaseSrc = Lane * SrcPerLane; |
| unsigned BaseDst = Lane * (2 * SrcPerLane); |
| |
| for (unsigned I = 0; I != SrcPerLane; ++I) { |
| INT_TYPE_SWITCH_NO_BOOL(SrcT, { |
| APSInt A = LHS.elem<T>(BaseSrc + I).toAPSInt(); |
| APSInt B = RHS.elem<T>(BaseSrc + I).toAPSInt(); |
| |
| assignInteger(S, Dst.atIndex(BaseDst + I), DstT, |
| APSInt(PackFn(A), IsUnsigend)); |
| assignInteger(S, Dst.atIndex(BaseDst + SrcPerLane + I), DstT, |
| APSInt(PackFn(B), IsUnsigend)); |
| }); |
| } |
| } |
| |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_elementwise_maxmin(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, |
| unsigned BuiltinID) { |
| assert(Call->getNumArgs() == 2); |
| |
| QualType Arg0Type = Call->getArg(0)->getType(); |
| |
| // TODO: Support floating-point types. |
| if (!(Arg0Type->isIntegerType() || |
| (Arg0Type->isVectorType() && |
| Arg0Type->castAs<VectorType>()->getElementType()->isIntegerType()))) |
| return false; |
| |
| if (!Arg0Type->isVectorType()) { |
| assert(!Call->getArg(1)->getType()->isVectorType()); |
| APSInt RHS = popToAPSInt(S, Call->getArg(1)); |
| APSInt LHS = popToAPSInt(S, Arg0Type); |
| APInt Result; |
| if (BuiltinID == Builtin::BI__builtin_elementwise_max) { |
| Result = std::max(LHS, RHS); |
| } else if (BuiltinID == Builtin::BI__builtin_elementwise_min) { |
| Result = std::min(LHS, RHS); |
| } else { |
| llvm_unreachable("Wrong builtin ID"); |
| } |
| |
| pushInteger(S, APSInt(Result, !LHS.isSigned()), Call->getType()); |
| return true; |
| } |
| |
| // Vector case. |
| assert(Call->getArg(0)->getType()->isVectorType() && |
| Call->getArg(1)->getType()->isVectorType()); |
| const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); |
| assert(VT->getElementType() == |
| Call->getArg(1)->getType()->castAs<VectorType>()->getElementType()); |
| assert(VT->getNumElements() == |
| Call->getArg(1)->getType()->castAs<VectorType>()->getNumElements()); |
| assert(VT->getElementType()->isIntegralOrEnumerationType()); |
| |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| PrimType ElemT = *S.getContext().classify(VT->getElementType()); |
| unsigned NumElems = VT->getNumElements(); |
| for (unsigned I = 0; I != NumElems; ++I) { |
| APSInt Elem1; |
| APSInt Elem2; |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| Elem1 = LHS.elem<T>(I).toAPSInt(); |
| Elem2 = RHS.elem<T>(I).toAPSInt(); |
| }); |
| |
| APSInt Result; |
| if (BuiltinID == Builtin::BI__builtin_elementwise_max) { |
| Result = APSInt(std::max(Elem1, Elem2), |
| Call->getType()->isUnsignedIntegerOrEnumerationType()); |
| } else if (BuiltinID == Builtin::BI__builtin_elementwise_min) { |
| Result = APSInt(std::min(Elem1, Elem2), |
| Call->getType()->isUnsignedIntegerOrEnumerationType()); |
| } else { |
| llvm_unreachable("Wrong builtin ID"); |
| } |
| |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, |
| { Dst.elem<T>(I) = static_cast<T>(Result); }); |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_pmul( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &, |
| const APSInt &)> |
| Fn) { |
| assert(Call->getArg(0)->getType()->isVectorType() && |
| Call->getArg(1)->getType()->isVectorType()); |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); |
| PrimType ElemT = *S.getContext().classify(VT->getElementType()); |
| unsigned NumElems = VT->getNumElements(); |
| const auto *DestVT = Call->getType()->castAs<VectorType>(); |
| PrimType DestElemT = *S.getContext().classify(DestVT->getElementType()); |
| bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); |
| |
| unsigned DstElem = 0; |
| for (unsigned I = 0; I != NumElems; I += 2) { |
| APSInt Result; |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| APSInt LoLHS = LHS.elem<T>(I).toAPSInt(); |
| APSInt HiLHS = LHS.elem<T>(I + 1).toAPSInt(); |
| APSInt LoRHS = RHS.elem<T>(I).toAPSInt(); |
| APSInt HiRHS = RHS.elem<T>(I + 1).toAPSInt(); |
| Result = APSInt(Fn(LoLHS, HiLHS, LoRHS, HiRHS), DestUnsigned); |
| }); |
| |
| INT_TYPE_SWITCH_NO_BOOL(DestElemT, |
| { Dst.elem<T>(DstElem) = static_cast<T>(Result); }); |
| ++DstElem; |
| } |
| |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp_builtin_horizontal_int_binop( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<APInt(const APSInt &, const APSInt &)> Fn) { |
| const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); |
| PrimType ElemT = *S.getContext().classify(VT->getElementType()); |
| bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); |
| |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| unsigned NumElts = VT->getNumElements(); |
| unsigned EltBits = S.getASTContext().getIntWidth(VT->getElementType()); |
| unsigned EltsPerLane = 128 / EltBits; |
| unsigned Lanes = NumElts * EltBits / 128; |
| unsigned DestIndex = 0; |
| |
| for (unsigned Lane = 0; Lane < Lanes; ++Lane) { |
| unsigned LaneStart = Lane * EltsPerLane; |
| for (unsigned I = 0; I < EltsPerLane; I += 2) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| APSInt Elem1 = LHS.elem<T>(LaneStart + I).toAPSInt(); |
| APSInt Elem2 = LHS.elem<T>(LaneStart + I + 1).toAPSInt(); |
| APSInt ResL = APSInt(Fn(Elem1, Elem2), DestUnsigned); |
| Dst.elem<T>(DestIndex++) = static_cast<T>(ResL); |
| }); |
| } |
| |
| for (unsigned I = 0; I < EltsPerLane; I += 2) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| APSInt Elem1 = RHS.elem<T>(LaneStart + I).toAPSInt(); |
| APSInt Elem2 = RHS.elem<T>(LaneStart + I + 1).toAPSInt(); |
| APSInt ResR = APSInt(Fn(Elem1, Elem2), DestUnsigned); |
| Dst.elem<T>(DestIndex++) = static_cast<T>(ResR); |
| }); |
| } |
| } |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp_builtin_horizontal_fp_binop( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<APFloat(const APFloat &, const APFloat &, |
| llvm::RoundingMode)> |
| Fn) { |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts()); |
| llvm::RoundingMode RM = getRoundingMode(FPO); |
| const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); |
| |
| unsigned NumElts = VT->getNumElements(); |
| unsigned EltBits = S.getASTContext().getTypeSize(VT->getElementType()); |
| unsigned NumLanes = NumElts * EltBits / 128; |
| unsigned NumElemsPerLane = NumElts / NumLanes; |
| unsigned HalfElemsPerLane = NumElemsPerLane / 2; |
| |
| for (unsigned L = 0; L != NumElts; L += NumElemsPerLane) { |
| using T = PrimConv<PT_Float>::T; |
| for (unsigned E = 0; E != HalfElemsPerLane; ++E) { |
| APFloat Elem1 = LHS.elem<T>(L + (2 * E) + 0).getAPFloat(); |
| APFloat Elem2 = LHS.elem<T>(L + (2 * E) + 1).getAPFloat(); |
| Dst.elem<T>(L + E) = static_cast<T>(Fn(Elem1, Elem2, RM)); |
| } |
| for (unsigned E = 0; E != HalfElemsPerLane; ++E) { |
| APFloat Elem1 = RHS.elem<T>(L + (2 * E) + 0).getAPFloat(); |
| APFloat Elem2 = RHS.elem<T>(L + (2 * E) + 1).getAPFloat(); |
| Dst.elem<T>(L + E + HalfElemsPerLane) = |
| static_cast<T>(Fn(Elem1, Elem2, RM)); |
| } |
| } |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_elementwise_triop_fp( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<APFloat(const APFloat &, const APFloat &, |
| const APFloat &, llvm::RoundingMode)> |
| Fn) { |
| assert(Call->getNumArgs() == 3); |
| |
| FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts()); |
| llvm::RoundingMode RM = getRoundingMode(FPO); |
| QualType Arg1Type = Call->getArg(0)->getType(); |
| QualType Arg2Type = Call->getArg(1)->getType(); |
| QualType Arg3Type = Call->getArg(2)->getType(); |
| |
| // Non-vector floating point types. |
| if (!Arg1Type->isVectorType()) { |
| assert(!Arg2Type->isVectorType()); |
| assert(!Arg3Type->isVectorType()); |
| (void)Arg2Type; |
| (void)Arg3Type; |
| |
| const Floating &Z = S.Stk.pop<Floating>(); |
| const Floating &Y = S.Stk.pop<Floating>(); |
| const Floating &X = S.Stk.pop<Floating>(); |
| APFloat F = Fn(X.getAPFloat(), Y.getAPFloat(), Z.getAPFloat(), RM); |
| Floating Result = S.allocFloat(X.getSemantics()); |
| Result.copy(F); |
| S.Stk.push<Floating>(Result); |
| return true; |
| } |
| |
| // Vector type. |
| assert(Arg1Type->isVectorType() && Arg2Type->isVectorType() && |
| Arg3Type->isVectorType()); |
| |
| const VectorType *VecTy = Arg1Type->castAs<VectorType>(); |
| QualType ElemQT = VecTy->getElementType(); |
| unsigned NumElems = VecTy->getNumElements(); |
| |
| assert(ElemQT == Arg2Type->castAs<VectorType>()->getElementType() && |
| ElemQT == Arg3Type->castAs<VectorType>()->getElementType()); |
| assert(NumElems == Arg2Type->castAs<VectorType>()->getNumElements() && |
| NumElems == Arg3Type->castAs<VectorType>()->getNumElements()); |
| assert(ElemQT->isRealFloatingType()); |
| (void)ElemQT; |
| |
| const Pointer &VZ = S.Stk.pop<Pointer>(); |
| const Pointer &VY = S.Stk.pop<Pointer>(); |
| const Pointer &VX = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| for (unsigned I = 0; I != NumElems; ++I) { |
| using T = PrimConv<PT_Float>::T; |
| APFloat X = VX.elem<T>(I).getAPFloat(); |
| APFloat Y = VY.elem<T>(I).getAPFloat(); |
| APFloat Z = VZ.elem<T>(I).getAPFloat(); |
| APFloat F = Fn(X, Y, Z, RM); |
| Dst.elem<Floating>(I) = Floating(F); |
| } |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| /// AVX512 predicated move: "Result = Mask[] ? LHS[] : RHS[]". |
| static bool interp__builtin_select(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call) { |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| APSInt Mask = popToAPSInt(S, Call->getArg(0)); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| assert(LHS.getNumElems() == RHS.getNumElems()); |
| assert(LHS.getNumElems() == Dst.getNumElems()); |
| unsigned NumElems = LHS.getNumElems(); |
| PrimType ElemT = LHS.getFieldDesc()->getPrimType(); |
| PrimType DstElemT = Dst.getFieldDesc()->getPrimType(); |
| |
| for (unsigned I = 0; I != NumElems; ++I) { |
| if (ElemT == PT_Float) { |
| assert(DstElemT == PT_Float); |
| Dst.elem<Floating>(I) = |
| Mask[I] ? LHS.elem<Floating>(I) : RHS.elem<Floating>(I); |
| } else { |
| APSInt Elem; |
| INT_TYPE_SWITCH(ElemT, { |
| Elem = Mask[I] ? LHS.elem<T>(I).toAPSInt() : RHS.elem<T>(I).toAPSInt(); |
| }); |
| INT_TYPE_SWITCH_NO_BOOL(DstElemT, |
| { Dst.elem<T>(I) = static_cast<T>(Elem); }); |
| } |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_blend(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call) { |
| APSInt Mask = popToAPSInt(S, Call->getArg(2)); |
| const Pointer &TrueVec = S.Stk.pop<Pointer>(); |
| const Pointer &FalseVec = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| assert(FalseVec.getNumElems() == TrueVec.getNumElems()); |
| assert(FalseVec.getNumElems() == Dst.getNumElems()); |
| unsigned NumElems = FalseVec.getNumElems(); |
| PrimType ElemT = FalseVec.getFieldDesc()->getPrimType(); |
| PrimType DstElemT = Dst.getFieldDesc()->getPrimType(); |
| |
| for (unsigned I = 0; I != NumElems; ++I) { |
| bool MaskBit = Mask[I % 8]; |
| if (ElemT == PT_Float) { |
| assert(DstElemT == PT_Float); |
| Dst.elem<Floating>(I) = |
| MaskBit ? TrueVec.elem<Floating>(I) : FalseVec.elem<Floating>(I); |
| } else { |
| assert(DstElemT == ElemT); |
| INT_TYPE_SWITCH_NO_BOOL(DstElemT, { |
| Dst.elem<T>(I) = |
| static_cast<T>(MaskBit ? TrueVec.elem<T>(I).toAPSInt() |
| : FalseVec.elem<T>(I).toAPSInt()); |
| }); |
| } |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_pshufb(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call) { |
| assert(Call->getNumArgs() == 2 && "masked forms handled via select*"); |
| const Pointer &Control = S.Stk.pop<Pointer>(); |
| const Pointer &Src = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| unsigned NumElems = Dst.getNumElems(); |
| assert(NumElems == Control.getNumElems()); |
| assert(NumElems == Dst.getNumElems()); |
| |
| for (unsigned Idx = 0; Idx != NumElems; ++Idx) { |
| uint8_t Ctlb = static_cast<uint8_t>(Control.elem<int8_t>(Idx)); |
| |
| if (Ctlb & 0x80) { |
| Dst.elem<int8_t>(Idx) = 0; |
| } else { |
| unsigned LaneBase = (Idx / 16) * 16; |
| unsigned SrcOffset = Ctlb & 0x0F; |
| unsigned SrcIdx = LaneBase + SrcOffset; |
| |
| Dst.elem<int8_t>(Idx) = Src.elem<int8_t>(SrcIdx); |
| } |
| } |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_pshuf(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, bool IsShufHW) { |
| assert(Call->getNumArgs() == 2 && "masked forms handled via select*"); |
| APSInt ControlImm = popToAPSInt(S, Call->getArg(1)); |
| const Pointer &Src = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| unsigned NumElems = Dst.getNumElems(); |
| PrimType ElemT = Dst.getFieldDesc()->getPrimType(); |
| |
| unsigned ElemBits = static_cast<unsigned>(primSize(ElemT) * 8); |
| if (ElemBits != 16 && ElemBits != 32) |
| return false; |
| |
| unsigned LaneElts = 128u / ElemBits; |
| assert(LaneElts && (NumElems % LaneElts == 0)); |
| |
| uint8_t Ctl = static_cast<uint8_t>(ControlImm.getZExtValue()); |
| |
| for (unsigned Idx = 0; Idx != NumElems; Idx++) { |
| unsigned LaneBase = (Idx / LaneElts) * LaneElts; |
| unsigned LaneIdx = Idx % LaneElts; |
| unsigned SrcIdx = Idx; |
| unsigned Sel = (Ctl >> (2 * (LaneIdx & 0x3))) & 0x3; |
| if (ElemBits == 32) { |
| SrcIdx = LaneBase + Sel; |
| } else { |
| constexpr unsigned HalfSize = 4; |
| bool InHigh = LaneIdx >= HalfSize; |
| if (!IsShufHW && !InHigh) { |
| SrcIdx = LaneBase + Sel; |
| } else if (IsShufHW && InHigh) { |
| SrcIdx = LaneBase + HalfSize + Sel; |
| } |
| } |
| |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { Dst.elem<T>(Idx) = Src.elem<T>(SrcIdx); }); |
| } |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_test_op( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<bool(const APInt &A, const APInt &B)> Fn) { |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| |
| assert(LHS.getNumElems() == RHS.getNumElems()); |
| |
| unsigned SourceLen = LHS.getNumElems(); |
| QualType ElemQT = getElemType(LHS); |
| OptPrimType ElemPT = S.getContext().classify(ElemQT); |
| unsigned LaneWidth = S.getASTContext().getTypeSize(ElemQT); |
| |
| APInt AWide(LaneWidth * SourceLen, 0); |
| APInt BWide(LaneWidth * SourceLen, 0); |
| |
| for (unsigned I = 0; I != SourceLen; ++I) { |
| APInt ALane; |
| APInt BLane; |
| |
| if (ElemQT->isIntegerType()) { // Get value. |
| INT_TYPE_SWITCH_NO_BOOL(*ElemPT, { |
| ALane = LHS.elem<T>(I).toAPSInt(); |
| BLane = RHS.elem<T>(I).toAPSInt(); |
| }); |
| } else if (ElemQT->isFloatingType()) { // Get only sign bit. |
| using T = PrimConv<PT_Float>::T; |
| ALane = LHS.elem<T>(I).getAPFloat().bitcastToAPInt().isNegative(); |
| BLane = RHS.elem<T>(I).getAPFloat().bitcastToAPInt().isNegative(); |
| } else { // Must be integer or floating type. |
| return false; |
| } |
| AWide.insertBits(ALane, I * LaneWidth); |
| BWide.insertBits(BLane, I * LaneWidth); |
| } |
| pushInteger(S, Fn(AWide, BWide), Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_movmsk_op(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call) { |
| assert(Call->getNumArgs() == 1); |
| |
| const Pointer &Source = S.Stk.pop<Pointer>(); |
| |
| unsigned SourceLen = Source.getNumElems(); |
| QualType ElemQT = getElemType(Source); |
| OptPrimType ElemT = S.getContext().classify(ElemQT); |
| unsigned ResultLen = |
| S.getASTContext().getTypeSize(Call->getType()); // Always 32-bit integer. |
| APInt Result(ResultLen, 0); |
| |
| for (unsigned I = 0; I != SourceLen; ++I) { |
| APInt Elem; |
| if (ElemQT->isIntegerType()) { |
| INT_TYPE_SWITCH_NO_BOOL(*ElemT, { Elem = Source.elem<T>(I).toAPSInt(); }); |
| } else if (ElemQT->isRealFloatingType()) { |
| using T = PrimConv<PT_Float>::T; |
| Elem = Source.elem<T>(I).getAPFloat().bitcastToAPInt(); |
| } else { |
| return false; |
| } |
| Result.setBitVal(I, Elem.isNegative()); |
| } |
| pushInteger(S, Result, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_elementwise_triop( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &)> |
| Fn) { |
| assert(Call->getNumArgs() == 3); |
| |
| QualType Arg0Type = Call->getArg(0)->getType(); |
| QualType Arg2Type = Call->getArg(2)->getType(); |
| // Non-vector integer types. |
| if (!Arg0Type->isVectorType()) { |
| const APSInt &Op2 = popToAPSInt(S, Arg2Type); |
| const APSInt &Op1 = popToAPSInt(S, Call->getArg(1)); |
| const APSInt &Op0 = popToAPSInt(S, Arg0Type); |
| APSInt Result = APSInt(Fn(Op0, Op1, Op2), Op0.isUnsigned()); |
| pushInteger(S, Result, Call->getType()); |
| return true; |
| } |
| |
| const auto *VecT = Arg0Type->castAs<VectorType>(); |
| PrimType ElemT = *S.getContext().classify(VecT->getElementType()); |
| unsigned NumElems = VecT->getNumElements(); |
| bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); |
| |
| // Vector + Vector + Scalar case. |
| if (!Arg2Type->isVectorType()) { |
| APSInt Op2 = popToAPSInt(S, Arg2Type); |
| |
| const Pointer &Op1 = S.Stk.pop<Pointer>(); |
| const Pointer &Op0 = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| for (unsigned I = 0; I != NumElems; ++I) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| Dst.elem<T>(I) = static_cast<T>(APSInt( |
| Fn(Op0.elem<T>(I).toAPSInt(), Op1.elem<T>(I).toAPSInt(), Op2), |
| DestUnsigned)); |
| }); |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| // Vector type. |
| const Pointer &Op2 = S.Stk.pop<Pointer>(); |
| const Pointer &Op1 = S.Stk.pop<Pointer>(); |
| const Pointer &Op0 = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| for (unsigned I = 0; I != NumElems; ++I) { |
| APSInt Val0, Val1, Val2; |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| Val0 = Op0.elem<T>(I).toAPSInt(); |
| Val1 = Op1.elem<T>(I).toAPSInt(); |
| Val2 = Op2.elem<T>(I).toAPSInt(); |
| }); |
| APSInt Result = APSInt(Fn(Val0, Val1, Val2), Val0.isUnsigned()); |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, |
| { Dst.elem<T>(I) = static_cast<T>(Result); }); |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_x86_extract_vector(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, |
| unsigned ID) { |
| assert(Call->getNumArgs() == 2); |
| |
| APSInt ImmAPS = popToAPSInt(S, Call->getArg(1)); |
| uint64_t Index = ImmAPS.getZExtValue(); |
| |
| const Pointer &Src = S.Stk.pop<Pointer>(); |
| if (!Src.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| if (!Dst.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| unsigned SrcElems = Src.getNumElems(); |
| unsigned DstElems = Dst.getNumElems(); |
| |
| unsigned NumLanes = SrcElems / DstElems; |
| unsigned Lane = static_cast<unsigned>(Index % NumLanes); |
| unsigned ExtractPos = Lane * DstElems; |
| |
| PrimType ElemT = Src.getFieldDesc()->getPrimType(); |
| |
| TYPE_SWITCH(ElemT, { |
| for (unsigned I = 0; I != DstElems; ++I) { |
| Dst.elem<T>(I) = Src.elem<T>(ExtractPos + I); |
| } |
| }); |
| |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_x86_extract_vector_masked(InterpState &S, |
| CodePtr OpPC, |
| const CallExpr *Call, |
| unsigned ID) { |
| assert(Call->getNumArgs() == 4); |
| |
| APSInt MaskAPS = popToAPSInt(S, Call->getArg(3)); |
| const Pointer &Merge = S.Stk.pop<Pointer>(); |
| APSInt ImmAPS = popToAPSInt(S, Call->getArg(1)); |
| const Pointer &Src = S.Stk.pop<Pointer>(); |
| |
| if (!Src.getFieldDesc()->isPrimitiveArray() || |
| !Merge.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| if (!Dst.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| unsigned SrcElems = Src.getNumElems(); |
| unsigned DstElems = Dst.getNumElems(); |
| |
| unsigned NumLanes = SrcElems / DstElems; |
| unsigned Lane = static_cast<unsigned>(ImmAPS.getZExtValue() % NumLanes); |
| unsigned Base = Lane * DstElems; |
| |
| PrimType ElemT = Src.getFieldDesc()->getPrimType(); |
| |
| TYPE_SWITCH(ElemT, { |
| for (unsigned I = 0; I != DstElems; ++I) { |
| if (MaskAPS[I]) |
| Dst.elem<T>(I) = Src.elem<T>(Base + I); |
| else |
| Dst.elem<T>(I) = Merge.elem<T>(I); |
| } |
| }); |
| |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_x86_insert_subvector(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, |
| unsigned ID) { |
| assert(Call->getNumArgs() == 3); |
| |
| APSInt ImmAPS = popToAPSInt(S, Call->getArg(2)); |
| uint64_t Index = ImmAPS.getZExtValue(); |
| |
| const Pointer &SubVec = S.Stk.pop<Pointer>(); |
| if (!SubVec.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| const Pointer &BaseVec = S.Stk.pop<Pointer>(); |
| if (!BaseVec.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| unsigned BaseElements = BaseVec.getNumElems(); |
| unsigned SubElements = SubVec.getNumElems(); |
| |
| assert(SubElements != 0 && BaseElements != 0 && |
| (BaseElements % SubElements) == 0); |
| |
| unsigned NumLanes = BaseElements / SubElements; |
| unsigned Lane = static_cast<unsigned>(Index % NumLanes); |
| unsigned InsertPos = Lane * SubElements; |
| |
| PrimType ElemT = BaseVec.getFieldDesc()->getPrimType(); |
| |
| TYPE_SWITCH(ElemT, { |
| for (unsigned I = 0; I != BaseElements; ++I) |
| Dst.elem<T>(I) = BaseVec.elem<T>(I); |
| for (unsigned I = 0; I != SubElements; ++I) |
| Dst.elem<T>(InsertPos + I) = SubVec.elem<T>(I); |
| }); |
| |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_phminposuw(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call) { |
| assert(Call->getNumArgs() == 1); |
| |
| const Pointer &Source = S.Stk.pop<Pointer>(); |
| const Pointer &Dest = S.Stk.peek<Pointer>(); |
| |
| unsigned SourceLen = Source.getNumElems(); |
| QualType ElemQT = getElemType(Source); |
| OptPrimType ElemT = S.getContext().classify(ElemQT); |
| unsigned ElemBitWidth = S.getASTContext().getTypeSize(ElemQT); |
| |
| bool DestUnsigned = Call->getCallReturnType(S.getASTContext()) |
| ->castAs<VectorType>() |
| ->getElementType() |
| ->isUnsignedIntegerOrEnumerationType(); |
| |
| INT_TYPE_SWITCH_NO_BOOL(*ElemT, { |
| APSInt MinIndex(ElemBitWidth, DestUnsigned); |
| APSInt MinVal = Source.elem<T>(0).toAPSInt(); |
| |
| for (unsigned I = 1; I != SourceLen; ++I) { |
| APSInt Val = Source.elem<T>(I).toAPSInt(); |
| if (MinVal.ugt(Val)) { |
| MinVal = Val; |
| MinIndex = I; |
| } |
| } |
| |
| Dest.elem<T>(0) = static_cast<T>(MinVal); |
| Dest.elem<T>(1) = static_cast<T>(MinIndex); |
| for (unsigned I = 2; I != SourceLen; ++I) { |
| Dest.elem<T>(I) = static_cast<T>(APSInt(ElemBitWidth, DestUnsigned)); |
| } |
| }); |
| Dest.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_pternlog(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, bool MaskZ) { |
| assert(Call->getNumArgs() == 5); |
| |
| APInt U = popToAPSInt(S, Call->getArg(4)); // Lane mask |
| APInt Imm = popToAPSInt(S, Call->getArg(3)); // Ternary truth table |
| const Pointer &C = S.Stk.pop<Pointer>(); |
| const Pointer &B = S.Stk.pop<Pointer>(); |
| const Pointer &A = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| unsigned DstLen = A.getNumElems(); |
| QualType ElemQT = getElemType(A); |
| OptPrimType ElemT = S.getContext().classify(ElemQT); |
| unsigned LaneWidth = S.getASTContext().getTypeSize(ElemQT); |
| bool DstUnsigned = ElemQT->isUnsignedIntegerOrEnumerationType(); |
| |
| INT_TYPE_SWITCH_NO_BOOL(*ElemT, { |
| for (unsigned I = 0; I != DstLen; ++I) { |
| APInt ALane = A.elem<T>(I).toAPSInt(); |
| APInt BLane = B.elem<T>(I).toAPSInt(); |
| APInt CLane = C.elem<T>(I).toAPSInt(); |
| APInt RLane(LaneWidth, 0); |
| if (U[I]) { // If lane not masked, compute ternary logic. |
| for (unsigned Bit = 0; Bit != LaneWidth; ++Bit) { |
| unsigned ABit = ALane[Bit]; |
| unsigned BBit = BLane[Bit]; |
| unsigned CBit = CLane[Bit]; |
| unsigned Idx = (ABit << 2) | (BBit << 1) | (CBit); |
| RLane.setBitVal(Bit, Imm[Idx]); |
| } |
| Dst.elem<T>(I) = static_cast<T>(APSInt(RLane, DstUnsigned)); |
| } else if (MaskZ) { // If zero masked, zero the lane. |
| Dst.elem<T>(I) = static_cast<T>(APSInt(RLane, DstUnsigned)); |
| } else { // Just masked, put in A lane. |
| Dst.elem<T>(I) = static_cast<T>(APSInt(ALane, DstUnsigned)); |
| } |
| } |
| }); |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_vec_ext(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, unsigned ID) { |
| assert(Call->getNumArgs() == 2); |
| |
| APSInt ImmAPS = popToAPSInt(S, Call->getArg(1)); |
| const Pointer &Vec = S.Stk.pop<Pointer>(); |
| if (!Vec.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| unsigned NumElems = Vec.getNumElems(); |
| unsigned Index = |
| static_cast<unsigned>(ImmAPS.getZExtValue() & (NumElems - 1)); |
| |
| PrimType ElemT = Vec.getFieldDesc()->getPrimType(); |
| // FIXME(#161685): Replace float+int split with a numeric-only type switch |
| if (ElemT == PT_Float) { |
| S.Stk.push<Floating>(Vec.elem<Floating>(Index)); |
| return true; |
| } |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| APSInt V = Vec.elem<T>(Index).toAPSInt(); |
| pushInteger(S, V, Call->getType()); |
| }); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_vec_set(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, unsigned ID) { |
| assert(Call->getNumArgs() == 3); |
| |
| APSInt ImmAPS = popToAPSInt(S, Call->getArg(2)); |
| APSInt ValAPS = popToAPSInt(S, Call->getArg(1)); |
| |
| const Pointer &Base = S.Stk.pop<Pointer>(); |
| if (!Base.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| unsigned NumElems = Base.getNumElems(); |
| unsigned Index = |
| static_cast<unsigned>(ImmAPS.getZExtValue() & (NumElems - 1)); |
| |
| PrimType ElemT = Base.getFieldDesc()->getPrimType(); |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| for (unsigned I = 0; I != NumElems; ++I) |
| Dst.elem<T>(I) = Base.elem<T>(I); |
| Dst.elem<T>(Index) = static_cast<T>(ValAPS); |
| }); |
| |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool evalICmpImm(uint8_t Imm, const APSInt &A, const APSInt &B, |
| bool IsUnsigned) { |
| switch (Imm & 0x7) { |
| case 0x00: // _MM_CMPINT_EQ |
| return (A == B); |
| case 0x01: // _MM_CMPINT_LT |
| return IsUnsigned ? A.ult(B) : A.slt(B); |
| case 0x02: // _MM_CMPINT_LE |
| return IsUnsigned ? A.ule(B) : A.sle(B); |
| case 0x03: // _MM_CMPINT_FALSE |
| return false; |
| case 0x04: // _MM_CMPINT_NE |
| return (A != B); |
| case 0x05: // _MM_CMPINT_NLT |
| return IsUnsigned ? A.ugt(B) : A.sgt(B); |
| case 0x06: // _MM_CMPINT_NLE |
| return IsUnsigned ? A.uge(B) : A.sge(B); |
| case 0x07: // _MM_CMPINT_TRUE |
| return true; |
| default: |
| llvm_unreachable("Invalid Op"); |
| } |
| } |
| |
| static bool interp__builtin_ia32_cmp_mask(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call, unsigned ID, |
| bool IsUnsigned) { |
| assert(Call->getNumArgs() == 4); |
| |
| APSInt Mask = popToAPSInt(S, Call->getArg(3)); |
| APSInt Opcode = popToAPSInt(S, Call->getArg(2)); |
| unsigned CmpOp = static_cast<unsigned>(Opcode.getZExtValue()); |
| const Pointer &RHS = S.Stk.pop<Pointer>(); |
| const Pointer &LHS = S.Stk.pop<Pointer>(); |
| |
| assert(LHS.getNumElems() == RHS.getNumElems()); |
| |
| APInt RetMask = APInt::getZero(LHS.getNumElems()); |
| unsigned VectorLen = LHS.getNumElems(); |
| PrimType ElemT = LHS.getFieldDesc()->getPrimType(); |
| |
| for (unsigned ElemNum = 0; ElemNum < VectorLen; ++ElemNum) { |
| APSInt A, B; |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| A = LHS.elem<T>(ElemNum).toAPSInt(); |
| B = RHS.elem<T>(ElemNum).toAPSInt(); |
| }); |
| RetMask.setBitVal(ElemNum, |
| Mask[ElemNum] && evalICmpImm(CmpOp, A, B, IsUnsigned)); |
| } |
| pushInteger(S, RetMask, Call->getType()); |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_vpconflict(InterpState &S, CodePtr OpPC, |
| const CallExpr *Call) { |
| assert(Call->getNumArgs() == 1); |
| |
| QualType Arg0Type = Call->getArg(0)->getType(); |
| const auto *VecT = Arg0Type->castAs<VectorType>(); |
| PrimType ElemT = *S.getContext().classify(VecT->getElementType()); |
| unsigned NumElems = VecT->getNumElements(); |
| bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); |
| const Pointer &Src = S.Stk.pop<Pointer>(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| for (unsigned I = 0; I != NumElems; ++I) { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { |
| APSInt ElemI = Src.elem<T>(I).toAPSInt(); |
| APInt ConflictMask(ElemI.getBitWidth(), 0); |
| for (unsigned J = 0; J != I; ++J) { |
| APSInt ElemJ = Src.elem<T>(J).toAPSInt(); |
| ConflictMask.setBitVal(J, ElemI == ElemJ); |
| } |
| Dst.elem<T>(I) = static_cast<T>(APSInt(ConflictMask, DestUnsigned)); |
| }); |
| } |
| Dst.initializeAllElements(); |
| return true; |
| } |
| |
| static bool interp__builtin_x86_byteshift( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, unsigned ID, |
| llvm::function_ref<APInt(const Pointer &, unsigned Lane, unsigned I, |
| unsigned Shift)> |
| Fn) { |
| assert(Call->getNumArgs() == 2); |
| |
| APSInt ImmAPS = popToAPSInt(S, Call->getArg(1)); |
| uint64_t Shift = ImmAPS.getZExtValue() & 0xff; |
| |
| const Pointer &Src = S.Stk.pop<Pointer>(); |
| if (!Src.getFieldDesc()->isPrimitiveArray()) |
| return false; |
| |
| unsigned NumElems = Src.getNumElems(); |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| PrimType ElemT = Src.getFieldDesc()->getPrimType(); |
| |
| for (unsigned Lane = 0; Lane != NumElems; Lane += 16) { |
| for (unsigned I = 0; I != 16; ++I) { |
| unsigned Base = Lane + I; |
| APSInt Result = APSInt(Fn(Src, Lane, I, Shift)); |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, |
| { Dst.elem<T>(Base) = static_cast<T>(Result); }); |
| } |
| } |
| |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| static bool interp__builtin_ia32_shuffle_generic( |
| InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| llvm::function_ref<std::pair<unsigned, int>(unsigned, unsigned)> |
| GetSourceIndex) { |
| |
| assert(Call->getNumArgs() == 3); |
| |
| unsigned ShuffleMask = 0; |
| Pointer A, MaskVector, B; |
| |
| QualType Arg2Type = Call->getArg(2)->getType(); |
| bool IsVectorMask = false; |
| if (Arg2Type->isVectorType()) { |
| IsVectorMask = true; |
| B = S.Stk.pop<Pointer>(); |
| MaskVector = S.Stk.pop<Pointer>(); |
| A = S.Stk.pop<Pointer>(); |
| } else if (Arg2Type->isIntegerType()) { |
| ShuffleMask = popToAPSInt(S, Call->getArg(2)).getZExtValue(); |
| B = S.Stk.pop<Pointer>(); |
| A = S.Stk.pop<Pointer>(); |
| } else { |
| return false; |
| } |
| |
| QualType Arg0Type = Call->getArg(0)->getType(); |
| const auto *VecT = Arg0Type->castAs<VectorType>(); |
| PrimType ElemT = *S.getContext().classify(VecT->getElementType()); |
| unsigned NumElems = VecT->getNumElements(); |
| |
| const Pointer &Dst = S.Stk.peek<Pointer>(); |
| |
| PrimType MaskElemT = PT_Uint32; |
| if (IsVectorMask) { |
| QualType Arg1Type = Call->getArg(1)->getType(); |
| const auto *MaskVecT = Arg1Type->castAs<VectorType>(); |
| QualType MaskElemType = MaskVecT->getElementType(); |
| MaskElemT = *S.getContext().classify(MaskElemType); |
| } |
| |
| for (unsigned DstIdx = 0; DstIdx != NumElems; ++DstIdx) { |
| if (IsVectorMask) { |
| INT_TYPE_SWITCH(MaskElemT, { |
| ShuffleMask = static_cast<unsigned>(MaskVector.elem<T>(DstIdx)); |
| }); |
| } |
| auto [SrcVecIdx, SrcIdx] = GetSourceIndex(DstIdx, ShuffleMask); |
| |
| if (SrcIdx < 0) { |
| // Zero out this element |
| if (ElemT == PT_Float) { |
| Dst.elem<Floating>(DstIdx) = Floating( |
| S.getASTContext().getFloatTypeSemantics(VecT->getElementType())); |
| } else { |
| INT_TYPE_SWITCH_NO_BOOL(ElemT, { Dst.elem<T>(DstIdx) = T::from(0); }); |
| } |
| } else { |
| const Pointer &Src = (SrcVecIdx == 0) ? A : B; |
| TYPE_SWITCH(ElemT, { Dst.elem<T>(DstIdx) = Src.elem<T>(SrcIdx); }); |
| } |
| } |
| Dst.initializeAllElements(); |
| |
| return true; |
| } |
| |
| bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, |
| uint32_t BuiltinID) { |
| if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID)) |
| return Invalid(S, OpPC); |
| |
| const InterpFrame *Frame = S.Current; |
| switch (BuiltinID) { |
| case Builtin::BI__builtin_is_constant_evaluated: |
| return interp__builtin_is_constant_evaluated(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_assume: |
| case Builtin::BI__assume: |
| return interp__builtin_assume(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_strcmp: |
| case Builtin::BIstrcmp: |
| case Builtin::BI__builtin_strncmp: |
| case Builtin::BIstrncmp: |
| case Builtin::BI__builtin_wcsncmp: |
| case Builtin::BIwcsncmp: |
| case Builtin::BI__builtin_wcscmp: |
| case Builtin::BIwcscmp: |
| return interp__builtin_strcmp(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_strlen: |
| case Builtin::BIstrlen: |
| case Builtin::BI__builtin_wcslen: |
| case Builtin::BIwcslen: |
| return interp__builtin_strlen(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_nan: |
| case Builtin::BI__builtin_nanf: |
| case Builtin::BI__builtin_nanl: |
| case Builtin::BI__builtin_nanf16: |
| case Builtin::BI__builtin_nanf128: |
| return interp__builtin_nan(S, OpPC, Frame, Call, /*Signaling=*/false); |
| |
| case Builtin::BI__builtin_nans: |
| case Builtin::BI__builtin_nansf: |
| case Builtin::BI__builtin_nansl: |
| case Builtin::BI__builtin_nansf16: |
| case Builtin::BI__builtin_nansf128: |
| return interp__builtin_nan(S, OpPC, Frame, Call, /*Signaling=*/true); |
| |
| case Builtin::BI__builtin_huge_val: |
| case Builtin::BI__builtin_huge_valf: |
| case Builtin::BI__builtin_huge_vall: |
| case Builtin::BI__builtin_huge_valf16: |
| case Builtin::BI__builtin_huge_valf128: |
| case Builtin::BI__builtin_inf: |
| case Builtin::BI__builtin_inff: |
| case Builtin::BI__builtin_infl: |
| case Builtin::BI__builtin_inff16: |
| case Builtin::BI__builtin_inff128: |
| return interp__builtin_inf(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_copysign: |
| case Builtin::BI__builtin_copysignf: |
| case Builtin::BI__builtin_copysignl: |
| case Builtin::BI__builtin_copysignf128: |
| return interp__builtin_copysign(S, OpPC, Frame); |
| |
| case Builtin::BI__builtin_fmin: |
| case Builtin::BI__builtin_fminf: |
| case Builtin::BI__builtin_fminl: |
| case Builtin::BI__builtin_fminf16: |
| case Builtin::BI__builtin_fminf128: |
| return interp__builtin_fmin(S, OpPC, Frame, /*IsNumBuiltin=*/false); |
| |
| case Builtin::BI__builtin_fminimum_num: |
| case Builtin::BI__builtin_fminimum_numf: |
| case Builtin::BI__builtin_fminimum_numl: |
| case Builtin::BI__builtin_fminimum_numf16: |
| case Builtin::BI__builtin_fminimum_numf128: |
| return interp__builtin_fmin(S, OpPC, Frame, /*IsNumBuiltin=*/true); |
| |
| case Builtin::BI__builtin_fmax: |
| case Builtin::BI__builtin_fmaxf: |
| case Builtin::BI__builtin_fmaxl: |
| case Builtin::BI__builtin_fmaxf16: |
| case Builtin::BI__builtin_fmaxf128: |
| return interp__builtin_fmax(S, OpPC, Frame, /*IsNumBuiltin=*/false); |
| |
| case Builtin::BI__builtin_fmaximum_num: |
| case Builtin::BI__builtin_fmaximum_numf: |
| case Builtin::BI__builtin_fmaximum_numl: |
| case Builtin::BI__builtin_fmaximum_numf16: |
| case Builtin::BI__builtin_fmaximum_numf128: |
| return interp__builtin_fmax(S, OpPC, Frame, /*IsNumBuiltin=*/true); |
| |
| case Builtin::BI__builtin_isnan: |
| return interp__builtin_isnan(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_issignaling: |
| return interp__builtin_issignaling(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_isinf: |
| return interp__builtin_isinf(S, OpPC, Frame, /*Sign=*/false, Call); |
| |
| case Builtin::BI__builtin_isinf_sign: |
| return interp__builtin_isinf(S, OpPC, Frame, /*Sign=*/true, Call); |
| |
| case Builtin::BI__builtin_isfinite: |
| return interp__builtin_isfinite(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_isnormal: |
| return interp__builtin_isnormal(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_issubnormal: |
| return interp__builtin_issubnormal(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_iszero: |
| return interp__builtin_iszero(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_signbit: |
| case Builtin::BI__builtin_signbitf: |
| case Builtin::BI__builtin_signbitl: |
| return interp__builtin_signbit(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_isgreater: |
| case Builtin::BI__builtin_isgreaterequal: |
| case Builtin::BI__builtin_isless: |
| case Builtin::BI__builtin_islessequal: |
| case Builtin::BI__builtin_islessgreater: |
| case Builtin::BI__builtin_isunordered: |
| return interp_floating_comparison(S, OpPC, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_isfpclass: |
| return interp__builtin_isfpclass(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_fpclassify: |
| return interp__builtin_fpclassify(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_fabs: |
| case Builtin::BI__builtin_fabsf: |
| case Builtin::BI__builtin_fabsl: |
| case Builtin::BI__builtin_fabsf128: |
| return interp__builtin_fabs(S, OpPC, Frame); |
| |
| case Builtin::BI__builtin_abs: |
| case Builtin::BI__builtin_labs: |
| case Builtin::BI__builtin_llabs: |
| return interp__builtin_abs(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_popcount: |
| case Builtin::BI__builtin_popcountl: |
| case Builtin::BI__builtin_popcountll: |
| case Builtin::BI__builtin_popcountg: |
| case Builtin::BI__popcnt16: // Microsoft variants of popcount |
| case Builtin::BI__popcnt: |
| case Builtin::BI__popcnt64: |
| return interp__builtin_popcount(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_parity: |
| case Builtin::BI__builtin_parityl: |
| case Builtin::BI__builtin_parityll: |
| return interp__builtin_elementwise_int_unaryop( |
| S, OpPC, Call, [](const APSInt &Val) { |
| return APInt(Val.getBitWidth(), Val.popcount() % 2); |
| }); |
| case Builtin::BI__builtin_clrsb: |
| case Builtin::BI__builtin_clrsbl: |
| case Builtin::BI__builtin_clrsbll: |
| return interp__builtin_elementwise_int_unaryop( |
| S, OpPC, Call, [](const APSInt &Val) { |
| return APInt(Val.getBitWidth(), |
| Val.getBitWidth() - Val.getSignificantBits()); |
| }); |
| case Builtin::BI__builtin_bitreverse8: |
| case Builtin::BI__builtin_bitreverse16: |
| case Builtin::BI__builtin_bitreverse32: |
| case Builtin::BI__builtin_bitreverse64: |
| return interp__builtin_elementwise_int_unaryop( |
| S, OpPC, Call, [](const APSInt &Val) { return Val.reverseBits(); }); |
| |
| case Builtin::BI__builtin_classify_type: |
| return interp__builtin_classify_type(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_expect: |
| case Builtin::BI__builtin_expect_with_probability: |
| return interp__builtin_expect(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_rotateleft8: |
| case Builtin::BI__builtin_rotateleft16: |
| case Builtin::BI__builtin_rotateleft32: |
| case Builtin::BI__builtin_rotateleft64: |
| case Builtin::BI_rotl8: // Microsoft variants of rotate left |
| case Builtin::BI_rotl16: |
| case Builtin::BI_rotl: |
| case Builtin::BI_lrotl: |
| case Builtin::BI_rotl64: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &Value, const APSInt &Amount) { |
| return Value.rotl(Amount); |
| }); |
| |
| case Builtin::BI__builtin_rotateright8: |
| case Builtin::BI__builtin_rotateright16: |
| case Builtin::BI__builtin_rotateright32: |
| case Builtin::BI__builtin_rotateright64: |
| case Builtin::BI_rotr8: // Microsoft variants of rotate right |
| case Builtin::BI_rotr16: |
| case Builtin::BI_rotr: |
| case Builtin::BI_lrotr: |
| case Builtin::BI_rotr64: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &Value, const APSInt &Amount) { |
| return Value.rotr(Amount); |
| }); |
| |
| case Builtin::BI__builtin_ffs: |
| case Builtin::BI__builtin_ffsl: |
| case Builtin::BI__builtin_ffsll: |
| return interp__builtin_elementwise_int_unaryop( |
| S, OpPC, Call, [](const APSInt &Val) { |
| return APInt(Val.getBitWidth(), |
| Val.isZero() ? 0u : Val.countTrailingZeros() + 1u); |
| }); |
| |
| case Builtin::BIaddressof: |
| case Builtin::BI__addressof: |
| case Builtin::BI__builtin_addressof: |
| assert(isNoopBuiltin(BuiltinID)); |
| return interp__builtin_addressof(S, OpPC, Frame, Call); |
| |
| case Builtin::BIas_const: |
| case Builtin::BIforward: |
| case Builtin::BIforward_like: |
| case Builtin::BImove: |
| case Builtin::BImove_if_noexcept: |
| assert(isNoopBuiltin(BuiltinID)); |
| return interp__builtin_move(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_eh_return_data_regno: |
| return interp__builtin_eh_return_data_regno(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_launder: |
| assert(isNoopBuiltin(BuiltinID)); |
| return true; |
| |
| case Builtin::BI__builtin_add_overflow: |
| case Builtin::BI__builtin_sub_overflow: |
| case Builtin::BI__builtin_mul_overflow: |
| case Builtin::BI__builtin_sadd_overflow: |
| case Builtin::BI__builtin_uadd_overflow: |
| case Builtin::BI__builtin_uaddl_overflow: |
| case Builtin::BI__builtin_uaddll_overflow: |
| case Builtin::BI__builtin_usub_overflow: |
| case Builtin::BI__builtin_usubl_overflow: |
| case Builtin::BI__builtin_usubll_overflow: |
| case Builtin::BI__builtin_umul_overflow: |
| case Builtin::BI__builtin_umull_overflow: |
| case Builtin::BI__builtin_umulll_overflow: |
| case Builtin::BI__builtin_saddl_overflow: |
| case Builtin::BI__builtin_saddll_overflow: |
| case Builtin::BI__builtin_ssub_overflow: |
| case Builtin::BI__builtin_ssubl_overflow: |
| case Builtin::BI__builtin_ssubll_overflow: |
| case Builtin::BI__builtin_smul_overflow: |
| case Builtin::BI__builtin_smull_overflow: |
| case Builtin::BI__builtin_smulll_overflow: |
| return interp__builtin_overflowop(S, OpPC, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_addcb: |
| case Builtin::BI__builtin_addcs: |
| case Builtin::BI__builtin_addc: |
| case Builtin::BI__builtin_addcl: |
| case Builtin::BI__builtin_addcll: |
| case Builtin::BI__builtin_subcb: |
| case Builtin::BI__builtin_subcs: |
| case Builtin::BI__builtin_subc: |
| case Builtin::BI__builtin_subcl: |
| case Builtin::BI__builtin_subcll: |
| return interp__builtin_carryop(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_clz: |
| case Builtin::BI__builtin_clzl: |
| case Builtin::BI__builtin_clzll: |
| case Builtin::BI__builtin_clzs: |
| case Builtin::BI__builtin_clzg: |
| case Builtin::BI__lzcnt16: // Microsoft variants of count leading-zeroes |
| case Builtin::BI__lzcnt: |
| case Builtin::BI__lzcnt64: |
| return interp__builtin_clz(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_ctz: |
| case Builtin::BI__builtin_ctzl: |
| case Builtin::BI__builtin_ctzll: |
| case Builtin::BI__builtin_ctzs: |
| case Builtin::BI__builtin_ctzg: |
| return interp__builtin_ctz(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_elementwise_clzg: |
| case Builtin::BI__builtin_elementwise_ctzg: |
| return interp__builtin_elementwise_countzeroes(S, OpPC, Frame, Call, |
| BuiltinID); |
| |
| case Builtin::BI__builtin_bswap16: |
| case Builtin::BI__builtin_bswap32: |
| case Builtin::BI__builtin_bswap64: |
| return interp__builtin_bswap(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__atomic_always_lock_free: |
| case Builtin::BI__atomic_is_lock_free: |
| return interp__builtin_atomic_lock_free(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__c11_atomic_is_lock_free: |
| return interp__builtin_c11_atomic_is_lock_free(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_complex: |
| return interp__builtin_complex(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_is_aligned: |
| case Builtin::BI__builtin_align_up: |
| case Builtin::BI__builtin_align_down: |
| return interp__builtin_is_aligned_up_down(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_assume_aligned: |
| return interp__builtin_assume_aligned(S, OpPC, Frame, Call); |
| |
| case clang::X86::BI__builtin_ia32_bextr_u32: |
| case clang::X86::BI__builtin_ia32_bextr_u64: |
| case clang::X86::BI__builtin_ia32_bextri_u32: |
| case clang::X86::BI__builtin_ia32_bextri_u64: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &Val, const APSInt &Idx) { |
| unsigned BitWidth = Val.getBitWidth(); |
| uint64_t Shift = Idx.extractBitsAsZExtValue(8, 0); |
| uint64_t Length = Idx.extractBitsAsZExtValue(8, 8); |
| if (Length > BitWidth) { |
| Length = BitWidth; |
| } |
| |
| // Handle out of bounds cases. |
| if (Length == 0 || Shift >= BitWidth) |
| return APInt(BitWidth, 0); |
| |
| uint64_t Result = Val.getZExtValue() >> Shift; |
| Result &= llvm::maskTrailingOnes<uint64_t>(Length); |
| return APInt(BitWidth, Result); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_bzhi_si: |
| case clang::X86::BI__builtin_ia32_bzhi_di: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &Val, const APSInt &Idx) { |
| unsigned BitWidth = Val.getBitWidth(); |
| uint64_t Index = Idx.extractBitsAsZExtValue(8, 0); |
| APSInt Result = Val; |
| |
| if (Index < BitWidth) |
| Result.clearHighBits(BitWidth - Index); |
| |
| return Result; |
| }); |
| |
| case clang::X86::BI__builtin_ia32_lzcnt_u16: |
| case clang::X86::BI__builtin_ia32_lzcnt_u32: |
| case clang::X86::BI__builtin_ia32_lzcnt_u64: |
| return interp__builtin_elementwise_int_unaryop( |
| S, OpPC, Call, [](const APSInt &Src) { |
| return APInt(Src.getBitWidth(), Src.countLeadingZeros()); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_tzcnt_u16: |
| case clang::X86::BI__builtin_ia32_tzcnt_u32: |
| case clang::X86::BI__builtin_ia32_tzcnt_u64: |
| return interp__builtin_elementwise_int_unaryop( |
| S, OpPC, Call, [](const APSInt &Src) { |
| return APInt(Src.getBitWidth(), Src.countTrailingZeros()); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_pdep_si: |
| case clang::X86::BI__builtin_ia32_pdep_di: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &Val, const APSInt &Mask) { |
| unsigned BitWidth = Val.getBitWidth(); |
| APInt Result = APInt::getZero(BitWidth); |
| |
| for (unsigned I = 0, P = 0; I != BitWidth; ++I) { |
| if (Mask[I]) |
| Result.setBitVal(I, Val[P++]); |
| } |
| |
| return Result; |
| }); |
| |
| case clang::X86::BI__builtin_ia32_pext_si: |
| case clang::X86::BI__builtin_ia32_pext_di: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &Val, const APSInt &Mask) { |
| unsigned BitWidth = Val.getBitWidth(); |
| APInt Result = APInt::getZero(BitWidth); |
| |
| for (unsigned I = 0, P = 0; I != BitWidth; ++I) { |
| if (Mask[I]) |
| Result.setBitVal(P++, Val[I]); |
| } |
| |
| return Result; |
| }); |
| |
| case clang::X86::BI__builtin_ia32_addcarryx_u32: |
| case clang::X86::BI__builtin_ia32_addcarryx_u64: |
| case clang::X86::BI__builtin_ia32_subborrow_u32: |
| case clang::X86::BI__builtin_ia32_subborrow_u64: |
| return interp__builtin_ia32_addcarry_subborrow(S, OpPC, Frame, Call, |
| BuiltinID); |
| |
| case Builtin::BI__builtin_os_log_format_buffer_size: |
| return interp__builtin_os_log_format_buffer_size(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_ptrauth_string_discriminator: |
| return interp__builtin_ptrauth_string_discriminator(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_infer_alloc_token: |
| return interp__builtin_infer_alloc_token(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__noop: |
| pushInteger(S, 0, Call->getType()); |
| return true; |
| |
| case Builtin::BI__builtin_operator_new: |
| return interp__builtin_operator_new(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_operator_delete: |
| return interp__builtin_operator_delete(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__arithmetic_fence: |
| return interp__builtin_arithmetic_fence(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_reduce_add: |
| case Builtin::BI__builtin_reduce_mul: |
| case Builtin::BI__builtin_reduce_and: |
| case Builtin::BI__builtin_reduce_or: |
| case Builtin::BI__builtin_reduce_xor: |
| case Builtin::BI__builtin_reduce_min: |
| case Builtin::BI__builtin_reduce_max: |
| return interp__builtin_vector_reduce(S, OpPC, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_elementwise_popcount: |
| case Builtin::BI__builtin_elementwise_bitreverse: |
| return interp__builtin_elementwise_popcount(S, OpPC, Frame, Call, |
| BuiltinID); |
| |
| case Builtin::BI__builtin_elementwise_abs: |
| return interp__builtin_elementwise_abs(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_memcpy: |
| case Builtin::BImemcpy: |
| case Builtin::BI__builtin_wmemcpy: |
| case Builtin::BIwmemcpy: |
| case Builtin::BI__builtin_memmove: |
| case Builtin::BImemmove: |
| case Builtin::BI__builtin_wmemmove: |
| case Builtin::BIwmemmove: |
| return interp__builtin_memcpy(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_memcmp: |
| case Builtin::BImemcmp: |
| case Builtin::BI__builtin_bcmp: |
| case Builtin::BIbcmp: |
| case Builtin::BI__builtin_wmemcmp: |
| case Builtin::BIwmemcmp: |
| return interp__builtin_memcmp(S, OpPC, Frame, Call, BuiltinID); |
| |
| case Builtin::BImemchr: |
| case Builtin::BI__builtin_memchr: |
| case Builtin::BIstrchr: |
| case Builtin::BI__builtin_strchr: |
| case Builtin::BIwmemchr: |
| case Builtin::BI__builtin_wmemchr: |
| case Builtin::BIwcschr: |
| case Builtin::BI__builtin_wcschr: |
| case Builtin::BI__builtin_char_memchr: |
| return interp__builtin_memchr(S, OpPC, Call, BuiltinID); |
| |
| case Builtin::BI__builtin_object_size: |
| case Builtin::BI__builtin_dynamic_object_size: |
| return interp__builtin_object_size(S, OpPC, Frame, Call); |
| |
| case Builtin::BI__builtin_is_within_lifetime: |
| return interp__builtin_is_within_lifetime(S, OpPC, Call); |
| |
| case Builtin::BI__builtin_elementwise_add_sat: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { |
| return LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS); |
| }); |
| |
| case Builtin::BI__builtin_elementwise_sub_sat: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { |
| return LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS); |
| }); |
| case X86::BI__builtin_ia32_extract128i256: |
| case X86::BI__builtin_ia32_vextractf128_pd256: |
| case X86::BI__builtin_ia32_vextractf128_ps256: |
| case X86::BI__builtin_ia32_vextractf128_si256: |
| return interp__builtin_x86_extract_vector(S, OpPC, Call, BuiltinID); |
| |
| case X86::BI__builtin_ia32_extractf32x4_256_mask: |
| case X86::BI__builtin_ia32_extractf32x4_mask: |
| case X86::BI__builtin_ia32_extractf32x8_mask: |
| case X86::BI__builtin_ia32_extractf64x2_256_mask: |
| case X86::BI__builtin_ia32_extractf64x2_512_mask: |
| case X86::BI__builtin_ia32_extractf64x4_mask: |
| case X86::BI__builtin_ia32_extracti32x4_256_mask: |
| case X86::BI__builtin_ia32_extracti32x4_mask: |
| case X86::BI__builtin_ia32_extracti32x8_mask: |
| case X86::BI__builtin_ia32_extracti64x2_256_mask: |
| case X86::BI__builtin_ia32_extracti64x2_512_mask: |
| case X86::BI__builtin_ia32_extracti64x4_mask: |
| return interp__builtin_x86_extract_vector_masked(S, OpPC, Call, BuiltinID); |
| |
| case clang::X86::BI__builtin_ia32_pmulhrsw128: |
| case clang::X86::BI__builtin_ia32_pmulhrsw256: |
| case clang::X86::BI__builtin_ia32_pmulhrsw512: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { |
| return (llvm::APIntOps::mulsExtended(LHS, RHS).ashr(14) + 1) |
| .extractBits(16, 1); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_movmskps: |
| case clang::X86::BI__builtin_ia32_movmskpd: |
| case clang::X86::BI__builtin_ia32_pmovmskb128: |
| case clang::X86::BI__builtin_ia32_pmovmskb256: |
| case clang::X86::BI__builtin_ia32_movmskps256: |
| case clang::X86::BI__builtin_ia32_movmskpd256: { |
| return interp__builtin_ia32_movmsk_op(S, OpPC, Call); |
| } |
| |
| case X86::BI__builtin_ia32_psignb128: |
| case X86::BI__builtin_ia32_psignb256: |
| case X86::BI__builtin_ia32_psignw128: |
| case X86::BI__builtin_ia32_psignw256: |
| case X86::BI__builtin_ia32_psignd128: |
| case X86::BI__builtin_ia32_psignd256: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APInt &AElem, const APInt &BElem) { |
| if (BElem.isZero()) |
| return APInt::getZero(AElem.getBitWidth()); |
| if (BElem.isNegative()) |
| return -AElem; |
| return AElem; |
| }); |
| |
| case clang::X86::BI__builtin_ia32_pavgb128: |
| case clang::X86::BI__builtin_ia32_pavgw128: |
| case clang::X86::BI__builtin_ia32_pavgb256: |
| case clang::X86::BI__builtin_ia32_pavgw256: |
| case clang::X86::BI__builtin_ia32_pavgb512: |
| case clang::X86::BI__builtin_ia32_pavgw512: |
| return interp__builtin_elementwise_int_binop(S, OpPC, Call, |
| llvm::APIntOps::avgCeilU); |
| |
| case clang::X86::BI__builtin_ia32_pmaddubsw128: |
| case clang::X86::BI__builtin_ia32_pmaddubsw256: |
| case clang::X86::BI__builtin_ia32_pmaddubsw512: |
| return interp__builtin_ia32_pmul( |
| S, OpPC, Call, |
| [](const APSInt &LoLHS, const APSInt &HiLHS, const APSInt &LoRHS, |
| const APSInt &HiRHS) { |
| unsigned BitWidth = 2 * LoLHS.getBitWidth(); |
| return (LoLHS.zext(BitWidth) * LoRHS.sext(BitWidth)) |
| .sadd_sat((HiLHS.zext(BitWidth) * HiRHS.sext(BitWidth))); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_pmaddwd128: |
| case clang::X86::BI__builtin_ia32_pmaddwd256: |
| case clang::X86::BI__builtin_ia32_pmaddwd512: |
| return interp__builtin_ia32_pmul( |
| S, OpPC, Call, |
| [](const APSInt &LoLHS, const APSInt &HiLHS, const APSInt &LoRHS, |
| const APSInt &HiRHS) { |
| unsigned BitWidth = 2 * LoLHS.getBitWidth(); |
| return (LoLHS.sext(BitWidth) * LoRHS.sext(BitWidth)) + |
| (HiLHS.sext(BitWidth) * HiRHS.sext(BitWidth)); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_pmulhuw128: |
| case clang::X86::BI__builtin_ia32_pmulhuw256: |
| case clang::X86::BI__builtin_ia32_pmulhuw512: |
| return interp__builtin_elementwise_int_binop(S, OpPC, Call, |
| llvm::APIntOps::mulhu); |
| |
| case clang::X86::BI__builtin_ia32_pmulhw128: |
| case clang::X86::BI__builtin_ia32_pmulhw256: |
| case clang::X86::BI__builtin_ia32_pmulhw512: |
| return interp__builtin_elementwise_int_binop(S, OpPC, Call, |
| llvm::APIntOps::mulhs); |
| |
| case clang::X86::BI__builtin_ia32_psllv2di: |
| case clang::X86::BI__builtin_ia32_psllv4di: |
| case clang::X86::BI__builtin_ia32_psllv4si: |
| case clang::X86::BI__builtin_ia32_psllv8di: |
| case clang::X86::BI__builtin_ia32_psllv8hi: |
| case clang::X86::BI__builtin_ia32_psllv8si: |
| case clang::X86::BI__builtin_ia32_psllv16hi: |
| case clang::X86::BI__builtin_ia32_psllv16si: |
| case clang::X86::BI__builtin_ia32_psllv32hi: |
| case clang::X86::BI__builtin_ia32_psllwi128: |
| case clang::X86::BI__builtin_ia32_psllwi256: |
| case clang::X86::BI__builtin_ia32_psllwi512: |
| case clang::X86::BI__builtin_ia32_pslldi128: |
| case clang::X86::BI__builtin_ia32_pslldi256: |
| case clang::X86::BI__builtin_ia32_pslldi512: |
| case clang::X86::BI__builtin_ia32_psllqi128: |
| case clang::X86::BI__builtin_ia32_psllqi256: |
| case clang::X86::BI__builtin_ia32_psllqi512: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { |
| if (RHS.uge(LHS.getBitWidth())) { |
| return APInt::getZero(LHS.getBitWidth()); |
| } |
| return LHS.shl(RHS.getZExtValue()); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_psrav4si: |
| case clang::X86::BI__builtin_ia32_psrav8di: |
| case clang::X86::BI__builtin_ia32_psrav8hi: |
| case clang::X86::BI__builtin_ia32_psrav8si: |
| case clang::X86::BI__builtin_ia32_psrav16hi: |
| case clang::X86::BI__builtin_ia32_psrav16si: |
| case clang::X86::BI__builtin_ia32_psrav32hi: |
| case clang::X86::BI__builtin_ia32_psravq128: |
| case clang::X86::BI__builtin_ia32_psravq256: |
| case clang::X86::BI__builtin_ia32_psrawi128: |
| case clang::X86::BI__builtin_ia32_psrawi256: |
| case clang::X86::BI__builtin_ia32_psrawi512: |
| case clang::X86::BI__builtin_ia32_psradi128: |
| case clang::X86::BI__builtin_ia32_psradi256: |
| case clang::X86::BI__builtin_ia32_psradi512: |
| case clang::X86::BI__builtin_ia32_psraqi128: |
| case clang::X86::BI__builtin_ia32_psraqi256: |
| case clang::X86::BI__builtin_ia32_psraqi512: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { |
| if (RHS.uge(LHS.getBitWidth())) { |
| return LHS.ashr(LHS.getBitWidth() - 1); |
| } |
| return LHS.ashr(RHS.getZExtValue()); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_psrlv2di: |
| case clang::X86::BI__builtin_ia32_psrlv4di: |
| case clang::X86::BI__builtin_ia32_psrlv4si: |
| case clang::X86::BI__builtin_ia32_psrlv8di: |
| case clang::X86::BI__builtin_ia32_psrlv8hi: |
| case clang::X86::BI__builtin_ia32_psrlv8si: |
| case clang::X86::BI__builtin_ia32_psrlv16hi: |
| case clang::X86::BI__builtin_ia32_psrlv16si: |
| case clang::X86::BI__builtin_ia32_psrlv32hi: |
| case clang::X86::BI__builtin_ia32_psrlwi128: |
| case clang::X86::BI__builtin_ia32_psrlwi256: |
| case clang::X86::BI__builtin_ia32_psrlwi512: |
| case clang::X86::BI__builtin_ia32_psrldi128: |
| case clang::X86::BI__builtin_ia32_psrldi256: |
| case clang::X86::BI__builtin_ia32_psrldi512: |
| case clang::X86::BI__builtin_ia32_psrlqi128: |
| case clang::X86::BI__builtin_ia32_psrlqi256: |
| case clang::X86::BI__builtin_ia32_psrlqi512: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { |
| if (RHS.uge(LHS.getBitWidth())) { |
| return APInt::getZero(LHS.getBitWidth()); |
| } |
| return LHS.lshr(RHS.getZExtValue()); |
| }); |
| case clang::X86::BI__builtin_ia32_packsswb128: |
| case clang::X86::BI__builtin_ia32_packsswb256: |
| case clang::X86::BI__builtin_ia32_packsswb512: |
| case clang::X86::BI__builtin_ia32_packssdw128: |
| case clang::X86::BI__builtin_ia32_packssdw256: |
| case clang::X86::BI__builtin_ia32_packssdw512: |
| return interp__builtin_x86_pack(S, OpPC, Call, [](const APSInt &Src) { |
| return APInt(Src).truncSSat(Src.getBitWidth() / 2); |
| }); |
| case clang::X86::BI__builtin_ia32_packusdw128: |
| case clang::X86::BI__builtin_ia32_packusdw256: |
| case clang::X86::BI__builtin_ia32_packusdw512: |
| case clang::X86::BI__builtin_ia32_packuswb128: |
| case clang::X86::BI__builtin_ia32_packuswb256: |
| case clang::X86::BI__builtin_ia32_packuswb512: |
| return interp__builtin_x86_pack(S, OpPC, Call, [](const APSInt &Src) { |
| unsigned DstBits = Src.getBitWidth() / 2; |
| if (Src.isNegative()) |
| return APInt::getZero(DstBits); |
| if (Src.isIntN(DstBits)) |
| return APInt(Src).trunc(DstBits); |
| return APInt::getAllOnes(DstBits); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_vprotbi: |
| case clang::X86::BI__builtin_ia32_vprotdi: |
| case clang::X86::BI__builtin_ia32_vprotqi: |
| case clang::X86::BI__builtin_ia32_vprotwi: |
| case clang::X86::BI__builtin_ia32_prold128: |
| case clang::X86::BI__builtin_ia32_prold256: |
| case clang::X86::BI__builtin_ia32_prold512: |
| case clang::X86::BI__builtin_ia32_prolq128: |
| case clang::X86::BI__builtin_ia32_prolq256: |
| case clang::X86::BI__builtin_ia32_prolq512: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotl(RHS); }); |
| |
| case clang::X86::BI__builtin_ia32_prord128: |
| case clang::X86::BI__builtin_ia32_prord256: |
| case clang::X86::BI__builtin_ia32_prord512: |
| case clang::X86::BI__builtin_ia32_prorq128: |
| case clang::X86::BI__builtin_ia32_prorq256: |
| case clang::X86::BI__builtin_ia32_prorq512: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotr(RHS); }); |
| |
| case Builtin::BI__builtin_elementwise_max: |
| case Builtin::BI__builtin_elementwise_min: |
| return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID); |
| |
| case clang::X86::BI__builtin_ia32_phaddw128: |
| case clang::X86::BI__builtin_ia32_phaddw256: |
| case clang::X86::BI__builtin_ia32_phaddd128: |
| case clang::X86::BI__builtin_ia32_phaddd256: |
| return interp_builtin_horizontal_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS + RHS; }); |
| case clang::X86::BI__builtin_ia32_phaddsw128: |
| case clang::X86::BI__builtin_ia32_phaddsw256: |
| return interp_builtin_horizontal_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS.sadd_sat(RHS); }); |
| case clang::X86::BI__builtin_ia32_phsubw128: |
| case clang::X86::BI__builtin_ia32_phsubw256: |
| case clang::X86::BI__builtin_ia32_phsubd128: |
| case clang::X86::BI__builtin_ia32_phsubd256: |
| return interp_builtin_horizontal_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS - RHS; }); |
| case clang::X86::BI__builtin_ia32_phsubsw128: |
| case clang::X86::BI__builtin_ia32_phsubsw256: |
| return interp_builtin_horizontal_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS.ssub_sat(RHS); }); |
| case clang::X86::BI__builtin_ia32_haddpd: |
| case clang::X86::BI__builtin_ia32_haddps: |
| case clang::X86::BI__builtin_ia32_haddpd256: |
| case clang::X86::BI__builtin_ia32_haddps256: |
| return interp_builtin_horizontal_fp_binop( |
| S, OpPC, Call, |
| [](const APFloat &LHS, const APFloat &RHS, llvm::RoundingMode RM) { |
| APFloat F = LHS; |
| F.add(RHS, RM); |
| return F; |
| }); |
| case clang::X86::BI__builtin_ia32_hsubpd: |
| case clang::X86::BI__builtin_ia32_hsubps: |
| case clang::X86::BI__builtin_ia32_hsubpd256: |
| case clang::X86::BI__builtin_ia32_hsubps256: |
| return interp_builtin_horizontal_fp_binop( |
| S, OpPC, Call, |
| [](const APFloat &LHS, const APFloat &RHS, llvm::RoundingMode RM) { |
| APFloat F = LHS; |
| F.subtract(RHS, RM); |
| return F; |
| }); |
| |
| case clang::X86::BI__builtin_ia32_pmuldq128: |
| case clang::X86::BI__builtin_ia32_pmuldq256: |
| case clang::X86::BI__builtin_ia32_pmuldq512: |
| return interp__builtin_ia32_pmul( |
| S, OpPC, Call, |
| [](const APSInt &LoLHS, const APSInt &HiLHS, const APSInt &LoRHS, |
| const APSInt &HiRHS) { |
| return llvm::APIntOps::mulsExtended(LoLHS, LoRHS); |
| }); |
| |
| case clang::X86::BI__builtin_ia32_pmuludq128: |
| case clang::X86::BI__builtin_ia32_pmuludq256: |
| case clang::X86::BI__builtin_ia32_pmuludq512: |
| return interp__builtin_ia32_pmul( |
| S, OpPC, Call, |
| [](const APSInt &LoLHS, const APSInt &HiLHS, const APSInt &LoRHS, |
| const APSInt &HiRHS) { |
| return llvm::APIntOps::muluExtended(LoLHS, LoRHS); |
| }); |
| |
| case Builtin::BI__builtin_elementwise_fma: |
| return interp__builtin_elementwise_triop_fp( |
| S, OpPC, Call, |
| [](const APFloat &X, const APFloat &Y, const APFloat &Z, |
| llvm::RoundingMode RM) { |
| APFloat F = X; |
| F.fusedMultiplyAdd(Y, Z, RM); |
| return F; |
| }); |
| |
| case X86::BI__builtin_ia32_vpmadd52luq128: |
| case X86::BI__builtin_ia32_vpmadd52luq256: |
| case X86::BI__builtin_ia32_vpmadd52luq512: |
| return interp__builtin_elementwise_triop( |
| S, OpPC, Call, [](const APSInt &A, const APSInt &B, const APSInt &C) { |
| return A + (B.trunc(52) * C.trunc(52)).zext(64); |
| }); |
| case X86::BI__builtin_ia32_vpmadd52huq128: |
| case X86::BI__builtin_ia32_vpmadd52huq256: |
| case X86::BI__builtin_ia32_vpmadd52huq512: |
| return interp__builtin_elementwise_triop( |
| S, OpPC, Call, [](const APSInt &A, const APSInt &B, const APSInt &C) { |
| return A + llvm::APIntOps::mulhu(B.trunc(52), C.trunc(52)).zext(64); |
| }); |
| |
| case X86::BI__builtin_ia32_vpshldd128: |
| case X86::BI__builtin_ia32_vpshldd256: |
| case X86::BI__builtin_ia32_vpshldd512: |
| case X86::BI__builtin_ia32_vpshldq128: |
| case X86::BI__builtin_ia32_vpshldq256: |
| case X86::BI__builtin_ia32_vpshldq512: |
| case X86::BI__builtin_ia32_vpshldw128: |
| case X86::BI__builtin_ia32_vpshldw256: |
| case X86::BI__builtin_ia32_vpshldw512: |
| return interp__builtin_elementwise_triop( |
| S, OpPC, Call, |
| [](const APSInt &Hi, const APSInt &Lo, const APSInt &Amt) { |
| return llvm::APIntOps::fshl(Hi, Lo, Amt); |
| }); |
| |
| case X86::BI__builtin_ia32_vpshrdd128: |
| case X86::BI__builtin_ia32_vpshrdd256: |
| case X86::BI__builtin_ia32_vpshrdd512: |
| case X86::BI__builtin_ia32_vpshrdq128: |
| case X86::BI__builtin_ia32_vpshrdq256: |
| case X86::BI__builtin_ia32_vpshrdq512: |
| case X86::BI__builtin_ia32_vpshrdw128: |
| case X86::BI__builtin_ia32_vpshrdw256: |
| case X86::BI__builtin_ia32_vpshrdw512: |
| // NOTE: Reversed Hi/Lo operands. |
| return interp__builtin_elementwise_triop( |
| S, OpPC, Call, |
| [](const APSInt &Lo, const APSInt &Hi, const APSInt &Amt) { |
| return llvm::APIntOps::fshr(Hi, Lo, Amt); |
| }); |
| case X86::BI__builtin_ia32_vpconflictsi_128: |
| case X86::BI__builtin_ia32_vpconflictsi_256: |
| case X86::BI__builtin_ia32_vpconflictsi_512: |
| case X86::BI__builtin_ia32_vpconflictdi_128: |
| case X86::BI__builtin_ia32_vpconflictdi_256: |
| case X86::BI__builtin_ia32_vpconflictdi_512: |
| return interp__builtin_ia32_vpconflict(S, OpPC, Call); |
| case clang::X86::BI__builtin_ia32_blendpd: |
| case clang::X86::BI__builtin_ia32_blendpd256: |
| case clang::X86::BI__builtin_ia32_blendps: |
| case clang::X86::BI__builtin_ia32_blendps256: |
| case clang::X86::BI__builtin_ia32_pblendw128: |
| case clang::X86::BI__builtin_ia32_pblendw256: |
| case clang::X86::BI__builtin_ia32_pblendd128: |
| case clang::X86::BI__builtin_ia32_pblendd256: |
| return interp__builtin_blend(S, OpPC, Call); |
| |
| case clang::X86::BI__builtin_ia32_blendvpd: |
| case clang::X86::BI__builtin_ia32_blendvpd256: |
| case clang::X86::BI__builtin_ia32_blendvps: |
| case clang::X86::BI__builtin_ia32_blendvps256: |
| return interp__builtin_elementwise_triop_fp( |
| S, OpPC, Call, |
| [](const APFloat &F, const APFloat &T, const APFloat &C, |
| llvm::RoundingMode) { return C.isNegative() ? T : F; }); |
| |
| case clang::X86::BI__builtin_ia32_pblendvb128: |
| case clang::X86::BI__builtin_ia32_pblendvb256: |
| return interp__builtin_elementwise_triop( |
| S, OpPC, Call, [](const APSInt &F, const APSInt &T, const APSInt &C) { |
| return ((APInt)C).isNegative() ? T : F; |
| }); |
| case X86::BI__builtin_ia32_ptestz128: |
| case X86::BI__builtin_ia32_ptestz256: |
| case X86::BI__builtin_ia32_vtestzps: |
| case X86::BI__builtin_ia32_vtestzps256: |
| case X86::BI__builtin_ia32_vtestzpd: |
| case X86::BI__builtin_ia32_vtestzpd256: |
| return interp__builtin_ia32_test_op( |
| S, OpPC, Call, |
| [](const APInt &A, const APInt &B) { return (A & B) == 0; }); |
| case X86::BI__builtin_ia32_ptestc128: |
| case X86::BI__builtin_ia32_ptestc256: |
| case X86::BI__builtin_ia32_vtestcps: |
| case X86::BI__builtin_ia32_vtestcps256: |
| case X86::BI__builtin_ia32_vtestcpd: |
| case X86::BI__builtin_ia32_vtestcpd256: |
| return interp__builtin_ia32_test_op( |
| S, OpPC, Call, |
| [](const APInt &A, const APInt &B) { return (~A & B) == 0; }); |
| case X86::BI__builtin_ia32_ptestnzc128: |
| case X86::BI__builtin_ia32_ptestnzc256: |
| case X86::BI__builtin_ia32_vtestnzcps: |
| case X86::BI__builtin_ia32_vtestnzcps256: |
| case X86::BI__builtin_ia32_vtestnzcpd: |
| case X86::BI__builtin_ia32_vtestnzcpd256: |
| return interp__builtin_ia32_test_op( |
| S, OpPC, Call, [](const APInt &A, const APInt &B) { |
| return ((A & B) != 0) && ((~A & B) != 0); |
| }); |
| case X86::BI__builtin_ia32_selectb_128: |
| case X86::BI__builtin_ia32_selectb_256: |
| case X86::BI__builtin_ia32_selectb_512: |
| case X86::BI__builtin_ia32_selectw_128: |
| case X86::BI__builtin_ia32_selectw_256: |
| case X86::BI__builtin_ia32_selectw_512: |
| case X86::BI__builtin_ia32_selectd_128: |
| case X86::BI__builtin_ia32_selectd_256: |
| case X86::BI__builtin_ia32_selectd_512: |
| case X86::BI__builtin_ia32_selectq_128: |
| case X86::BI__builtin_ia32_selectq_256: |
| case X86::BI__builtin_ia32_selectq_512: |
| case X86::BI__builtin_ia32_selectph_128: |
| case X86::BI__builtin_ia32_selectph_256: |
| case X86::BI__builtin_ia32_selectph_512: |
| case X86::BI__builtin_ia32_selectpbf_128: |
| case X86::BI__builtin_ia32_selectpbf_256: |
| case X86::BI__builtin_ia32_selectpbf_512: |
| case X86::BI__builtin_ia32_selectps_128: |
| case X86::BI__builtin_ia32_selectps_256: |
| case X86::BI__builtin_ia32_selectps_512: |
| case X86::BI__builtin_ia32_selectpd_128: |
| case X86::BI__builtin_ia32_selectpd_256: |
| case X86::BI__builtin_ia32_selectpd_512: |
| return interp__builtin_select(S, OpPC, Call); |
| |
| case X86::BI__builtin_ia32_shufps: |
| case X86::BI__builtin_ia32_shufps256: |
| case X86::BI__builtin_ia32_shufps512: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| unsigned NumElemPerLane = 4; |
| unsigned NumSelectableElems = NumElemPerLane / 2; |
| unsigned BitsPerElem = 2; |
| unsigned IndexMask = 0x3; |
| unsigned MaskBits = 8; |
| unsigned Lane = DstIdx / NumElemPerLane; |
| unsigned ElemInLane = DstIdx % NumElemPerLane; |
| unsigned LaneOffset = Lane * NumElemPerLane; |
| unsigned SrcIdx = ElemInLane >= NumSelectableElems ? 1 : 0; |
| unsigned BitIndex = (DstIdx * BitsPerElem) % MaskBits; |
| unsigned Index = (ShuffleMask >> BitIndex) & IndexMask; |
| return std::pair<unsigned, int>{SrcIdx, |
| static_cast<int>(LaneOffset + Index)}; |
| }); |
| case X86::BI__builtin_ia32_shufpd: |
| case X86::BI__builtin_ia32_shufpd256: |
| case X86::BI__builtin_ia32_shufpd512: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| unsigned NumElemPerLane = 2; |
| unsigned NumSelectableElems = NumElemPerLane / 2; |
| unsigned BitsPerElem = 1; |
| unsigned IndexMask = 0x1; |
| unsigned MaskBits = 8; |
| unsigned Lane = DstIdx / NumElemPerLane; |
| unsigned ElemInLane = DstIdx % NumElemPerLane; |
| unsigned LaneOffset = Lane * NumElemPerLane; |
| unsigned SrcIdx = ElemInLane >= NumSelectableElems ? 1 : 0; |
| unsigned BitIndex = (DstIdx * BitsPerElem) % MaskBits; |
| unsigned Index = (ShuffleMask >> BitIndex) & IndexMask; |
| return std::pair<unsigned, int>{SrcIdx, |
| static_cast<int>(LaneOffset + Index)}; |
| }); |
| case X86::BI__builtin_ia32_insertps128: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned Mask) { |
| // Bits [3:0]: zero mask - if bit is set, zero this element |
| if ((Mask & (1 << DstIdx)) != 0) { |
| return std::pair<unsigned, int>{0, -1}; |
| } |
| // Bits [7:6]: select element from source vector Y (0-3) |
| // Bits [5:4]: select destination position (0-3) |
| unsigned SrcElem = (Mask >> 6) & 0x3; |
| unsigned DstElem = (Mask >> 4) & 0x3; |
| if (DstIdx == DstElem) { |
| // Insert element from source vector (B) at this position |
| return std::pair<unsigned, int>{1, static_cast<int>(SrcElem)}; |
| } else { |
| // Copy from destination vector (A) |
| return std::pair<unsigned, int>{0, static_cast<int>(DstIdx)}; |
| } |
| }); |
| case X86::BI__builtin_ia32_vpermi2varq128: |
| case X86::BI__builtin_ia32_vpermi2varpd128: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| int Offset = ShuffleMask & 0x1; |
| unsigned SrcIdx = (ShuffleMask >> 1) & 0x1; |
| return std::pair<unsigned, int>{SrcIdx, Offset}; |
| }); |
| case X86::BI__builtin_ia32_vpermi2vard128: |
| case X86::BI__builtin_ia32_vpermi2varps128: |
| case X86::BI__builtin_ia32_vpermi2varq256: |
| case X86::BI__builtin_ia32_vpermi2varpd256: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| int Offset = ShuffleMask & 0x3; |
| unsigned SrcIdx = (ShuffleMask >> 2) & 0x1; |
| return std::pair<unsigned, int>{SrcIdx, Offset}; |
| }); |
| case X86::BI__builtin_ia32_vpermi2varhi128: |
| case X86::BI__builtin_ia32_vpermi2vard256: |
| case X86::BI__builtin_ia32_vpermi2varps256: |
| case X86::BI__builtin_ia32_vpermi2varq512: |
| case X86::BI__builtin_ia32_vpermi2varpd512: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| int Offset = ShuffleMask & 0x7; |
| unsigned SrcIdx = (ShuffleMask >> 3) & 0x1; |
| return std::pair<unsigned, int>{SrcIdx, Offset}; |
| }); |
| case X86::BI__builtin_ia32_vpermi2varqi128: |
| case X86::BI__builtin_ia32_vpermi2varhi256: |
| case X86::BI__builtin_ia32_vpermi2vard512: |
| case X86::BI__builtin_ia32_vpermi2varps512: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| int Offset = ShuffleMask & 0xF; |
| unsigned SrcIdx = (ShuffleMask >> 4) & 0x1; |
| return std::pair<unsigned, int>{SrcIdx, Offset}; |
| }); |
| case X86::BI__builtin_ia32_vpermi2varqi256: |
| case X86::BI__builtin_ia32_vpermi2varhi512: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| int Offset = ShuffleMask & 0x1F; |
| unsigned SrcIdx = (ShuffleMask >> 5) & 0x1; |
| return std::pair<unsigned, int>{SrcIdx, Offset}; |
| }); |
| case X86::BI__builtin_ia32_vpermi2varqi512: |
| return interp__builtin_ia32_shuffle_generic( |
| S, OpPC, Call, [](unsigned DstIdx, unsigned ShuffleMask) { |
| int Offset = ShuffleMask & 0x3F; |
| unsigned SrcIdx = (ShuffleMask >> 6) & 0x1; |
| return std::pair<unsigned, int>{SrcIdx, Offset}; |
| }); |
| case X86::BI__builtin_ia32_pshufb128: |
| case X86::BI__builtin_ia32_pshufb256: |
| case X86::BI__builtin_ia32_pshufb512: |
| return interp__builtin_ia32_pshufb(S, OpPC, Call); |
| |
| case X86::BI__builtin_ia32_pshuflw: |
| case X86::BI__builtin_ia32_pshuflw256: |
| case X86::BI__builtin_ia32_pshuflw512: |
| return interp__builtin_ia32_pshuf(S, OpPC, Call, false); |
| |
| case X86::BI__builtin_ia32_pshufhw: |
| case X86::BI__builtin_ia32_pshufhw256: |
| case X86::BI__builtin_ia32_pshufhw512: |
| return interp__builtin_ia32_pshuf(S, OpPC, Call, true); |
| |
| case X86::BI__builtin_ia32_pshufd: |
| case X86::BI__builtin_ia32_pshufd256: |
| case X86::BI__builtin_ia32_pshufd512: |
| return interp__builtin_ia32_pshuf(S, OpPC, Call, false); |
| |
| case X86::BI__builtin_ia32_kandqi: |
| case X86::BI__builtin_ia32_kandhi: |
| case X86::BI__builtin_ia32_kandsi: |
| case X86::BI__builtin_ia32_kanddi: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS & RHS; }); |
| |
| case X86::BI__builtin_ia32_kandnqi: |
| case X86::BI__builtin_ia32_kandnhi: |
| case X86::BI__builtin_ia32_kandnsi: |
| case X86::BI__builtin_ia32_kandndi: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return ~LHS & RHS; }); |
| |
| case X86::BI__builtin_ia32_korqi: |
| case X86::BI__builtin_ia32_korhi: |
| case X86::BI__builtin_ia32_korsi: |
| case X86::BI__builtin_ia32_kordi: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS | RHS; }); |
| |
| case X86::BI__builtin_ia32_kxnorqi: |
| case X86::BI__builtin_ia32_kxnorhi: |
| case X86::BI__builtin_ia32_kxnorsi: |
| case X86::BI__builtin_ia32_kxnordi: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return ~(LHS ^ RHS); }); |
| |
| case X86::BI__builtin_ia32_kxorqi: |
| case X86::BI__builtin_ia32_kxorhi: |
| case X86::BI__builtin_ia32_kxorsi: |
| case X86::BI__builtin_ia32_kxordi: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS ^ RHS; }); |
| |
| case X86::BI__builtin_ia32_knotqi: |
| case X86::BI__builtin_ia32_knothi: |
| case X86::BI__builtin_ia32_knotsi: |
| case X86::BI__builtin_ia32_knotdi: |
| return interp__builtin_elementwise_int_unaryop( |
| S, OpPC, Call, [](const APSInt &Src) { return ~Src; }); |
| |
| case X86::BI__builtin_ia32_kaddqi: |
| case X86::BI__builtin_ia32_kaddhi: |
| case X86::BI__builtin_ia32_kaddsi: |
| case X86::BI__builtin_ia32_kadddi: |
| return interp__builtin_elementwise_int_binop( |
| S, OpPC, Call, |
| [](const APSInt &LHS, const APSInt &RHS) { return LHS + RHS; }); |
| |
| case X86::BI__builtin_ia32_phminposuw128: |
| return interp__builtin_ia32_phminposuw(S, OpPC, Call); |
| |
| case X86::BI__builtin_ia32_pternlogd128_mask: |
| case X86::BI__builtin_ia32_pternlogd256_mask: |
| case X86::BI__builtin_ia32_pternlogd512_mask: |
| case X86::BI__builtin_ia32_pternlogq128_mask: |
| case X86::BI__builtin_ia32_pternlogq256_mask: |
| case X86::BI__builtin_ia32_pternlogq512_mask: |
| return interp__builtin_ia32_pternlog(S, OpPC, Call, /*MaskZ=*/false); |
| case X86::BI__builtin_ia32_pternlogd128_maskz: |
| case X86::BI__builtin_ia32_pternlogd256_maskz: |
| case X86::BI__builtin_ia32_pternlogd512_maskz: |
| case X86::BI__builtin_ia32_pternlogq128_maskz: |
| case X86::BI__builtin_ia32_pternlogq256_maskz: |
| case X86::BI__builtin_ia32_pternlogq512_maskz: |
| return interp__builtin_ia32_pternlog(S, OpPC, Call, /*MaskZ=*/true); |
| case Builtin::BI__builtin_elementwise_fshl: |
| return interp__builtin_elementwise_triop(S, OpPC, Call, |
| llvm::APIntOps::fshl); |
| case Builtin::BI__builtin_elementwise_fshr: |
| return interp__builtin_elementwise_triop(S, OpPC, Call, |
| llvm::APIntOps::fshr); |
| |
| case X86::BI__builtin_ia32_insertf32x4_256: |
| case X86::BI__builtin_ia32_inserti32x4_256: |
| case X86::BI__builtin_ia32_insertf64x2_256: |
| case X86::BI__builtin_ia32_inserti64x2_256: |
| case X86::BI__builtin_ia32_insertf32x4: |
| case X86::BI__builtin_ia32_inserti32x4: |
| case X86::BI__builtin_ia32_insertf64x2_512: |
| case X86::BI__builtin_ia32_inserti64x2_512: |
| case X86::BI__builtin_ia32_insertf32x8: |
| case X86::BI__builtin_ia32_inserti32x8: |
| case X86::BI__builtin_ia32_insertf64x4: |
| case X86::BI__builtin_ia32_inserti64x4: |
| case X86::BI__builtin_ia32_vinsertf128_ps256: |
| case X86::BI__builtin_ia32_vinsertf128_pd256: |
| case X86::BI__builtin_ia32_vinsertf128_si256: |
| case X86::BI__builtin_ia32_insert128i256: |
| return interp__builtin_x86_insert_subvector(S, OpPC, Call, BuiltinID); |
| |
| case X86::BI__builtin_ia32_vec_ext_v4hi: |
| case X86::BI__builtin_ia32_vec_ext_v16qi: |
| case X86::BI__builtin_ia32_vec_ext_v8hi: |
| case X86::BI__builtin_ia32_vec_ext_v4si: |
| case X86::BI__builtin_ia32_vec_ext_v2di: |
| case X86::BI__builtin_ia32_vec_ext_v32qi: |
| case X86::BI__builtin_ia32_vec_ext_v16hi: |
| case X86::BI__builtin_ia32_vec_ext_v8si: |
| case X86::BI__builtin_ia32_vec_ext_v4di: |
| case X86::BI__builtin_ia32_vec_ext_v4sf: |
| return interp__builtin_vec_ext(S, OpPC, Call, BuiltinID); |
| |
| case X86::BI__builtin_ia32_vec_set_v4hi: |
| case X86::BI__builtin_ia32_vec_set_v16qi: |
| case X86::BI__builtin_ia32_vec_set_v8hi: |
| case X86::BI__builtin_ia32_vec_set_v4si: |
| case X86::BI__builtin_ia32_vec_set_v2di: |
| case X86::BI__builtin_ia32_vec_set_v32qi: |
| case X86::BI__builtin_ia32_vec_set_v16hi: |
| case X86::BI__builtin_ia32_vec_set_v8si: |
| case X86::BI__builtin_ia32_vec_set_v4di: |
| return interp__builtin_vec_set(S, OpPC, Call, BuiltinID); |
| |
| case X86::BI__builtin_ia32_cmpb128_mask: |
| case X86::BI__builtin_ia32_cmpw128_mask: |
| case X86::BI__builtin_ia32_cmpd128_mask: |
| case X86::BI__builtin_ia32_cmpq128_mask: |
| case X86::BI__builtin_ia32_cmpb256_mask: |
| case X86::BI__builtin_ia32_cmpw256_mask: |
| case X86::BI__builtin_ia32_cmpd256_mask: |
| case X86::BI__builtin_ia32_cmpq256_mask: |
| case X86::BI__builtin_ia32_cmpb512_mask: |
| case X86::BI__builtin_ia32_cmpw512_mask: |
| case X86::BI__builtin_ia32_cmpd512_mask: |
| case X86::BI__builtin_ia32_cmpq512_mask: |
| return interp__builtin_ia32_cmp_mask(S, OpPC, Call, BuiltinID, |
| /*IsUnsigned=*/false); |
| |
| case X86::BI__builtin_ia32_ucmpb128_mask: |
| case X86::BI__builtin_ia32_ucmpw128_mask: |
| case X86::BI__builtin_ia32_ucmpd128_mask: |
| case X86::BI__builtin_ia32_ucmpq128_mask: |
| case X86::BI__builtin_ia32_ucmpb256_mask: |
| case X86::BI__builtin_ia32_ucmpw256_mask: |
| case X86::BI__builtin_ia32_ucmpd256_mask: |
| case X86::BI__builtin_ia32_ucmpq256_mask: |
| case X86::BI__builtin_ia32_ucmpb512_mask: |
| case X86::BI__builtin_ia32_ucmpw512_mask: |
| case X86::BI__builtin_ia32_ucmpd512_mask: |
| case X86::BI__builtin_ia32_ucmpq512_mask: |
| return interp__builtin_ia32_cmp_mask(S, OpPC, Call, BuiltinID, |
| /*IsUnsigned=*/true); |
| case X86::BI__builtin_ia32_pslldqi128_byteshift: |
| case X86::BI__builtin_ia32_pslldqi256_byteshift: |
| case X86::BI__builtin_ia32_pslldqi512_byteshift: |
| // These SLLDQ intrinsics always operate on byte elements (8 bits). |
| // The lane width is hardcoded to 16 to match the SIMD register size, |
| // but the algorithm processes one byte per iteration, |
| // so APInt(8, ...) is correct and intentional. |
| return interp__builtin_x86_byteshift( |
| S, OpPC, Call, BuiltinID, |
| [](const Pointer &Src, unsigned Lane, unsigned I, unsigned Shift) { |
| if (I < Shift) { |
| return APInt(8, 0); |
| } |
| return APInt(8, Src.elem<uint8_t>(Lane + I - Shift)); |
| }); |
| |
| case X86::BI__builtin_ia32_psrldqi128_byteshift: |
| case X86::BI__builtin_ia32_psrldqi256_byteshift: |
| case X86::BI__builtin_ia32_psrldqi512_byteshift: |
| // These SRLDQ intrinsics always operate on byte elements (8 bits). |
| // The lane width is hardcoded to 16 to match the SIMD register size, |
| // but the algorithm processes one byte per iteration, |
| // so APInt(8, ...) is correct and intentional. |
| return interp__builtin_x86_byteshift( |
| S, OpPC, Call, BuiltinID, |
| [](const Pointer &Src, unsigned Lane, unsigned I, unsigned Shift) { |
| if (I + Shift < 16) { |
| return APInt(8, Src.elem<uint8_t>(Lane + I + Shift)); |
| } |
| |
| return APInt(8, 0); |
| }); |
| |
| default: |
| S.FFDiag(S.Current->getLocation(OpPC), |
| diag::note_invalid_subexpr_in_const_expr) |
| << S.Current->getRange(OpPC); |
| |
| return false; |
| } |
| |
| llvm_unreachable("Unhandled builtin ID"); |
| } |
| |
| bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E, |
| ArrayRef<int64_t> ArrayIndices, int64_t &IntResult) { |
| CharUnits Result; |
| unsigned N = E->getNumComponents(); |
| assert(N > 0); |
| |
| unsigned ArrayIndex = 0; |
| QualType CurrentType = E->getTypeSourceInfo()->getType(); |
| for (unsigned I = 0; I != N; ++I) { |
| const OffsetOfNode &Node = E->getComponent(I); |
| switch (Node.getKind()) { |
| case OffsetOfNode::Field: { |
| const FieldDecl *MemberDecl = Node.getField(); |
| const auto *RD = CurrentType->getAsRecordDecl(); |
| if (!RD || RD->isInvalidDecl()) |
| return false; |
| const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD); |
| unsigned FieldIndex = MemberDecl->getFieldIndex(); |
| assert(FieldIndex < RL.getFieldCount() && "offsetof field in wrong type"); |
| Result += |
| S.getASTContext().toCharUnitsFromBits(RL.getFieldOffset(FieldIndex)); |
| CurrentType = MemberDecl->getType().getNonReferenceType(); |
| break; |
| } |
| case OffsetOfNode::Array: { |
| // When generating bytecode, we put all the index expressions as Sint64 on |
| // the stack. |
| int64_t Index = ArrayIndices[ArrayIndex]; |
| const ArrayType *AT = S.getASTContext().getAsArrayType(CurrentType); |
| if (!AT) |
| return false; |
| CurrentType = AT->getElementType(); |
| CharUnits ElementSize = S.getASTContext().getTypeSizeInChars(CurrentType); |
| Result += Index * ElementSize; |
| ++ArrayIndex; |
| break; |
| } |
| case OffsetOfNode::Base: { |
| const CXXBaseSpecifier *BaseSpec = Node.getBase(); |
| if (BaseSpec->isVirtual()) |
| return false; |
| |
| // Find the layout of the class whose base we are looking into. |
| const auto *RD = CurrentType->getAsCXXRecordDecl(); |
| if (!RD || RD->isInvalidDecl()) |
| return false; |
| const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD); |
| |
| // Find the base class itself. |
| CurrentType = BaseSpec->getType(); |
| const auto *BaseRD = CurrentType->getAsCXXRecordDecl(); |
| if (!BaseRD) |
| return false; |
| |
| // Add the offset to the base. |
| Result += RL.getBaseClassOffset(BaseRD); |
| break; |
| } |
| case OffsetOfNode::Identifier: |
| llvm_unreachable("Dependent OffsetOfExpr?"); |
| } |
| } |
| |
| IntResult = Result.getQuantity(); |
| |
| return true; |
| } |
| |
| bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC, |
| const Pointer &Ptr, const APSInt &IntValue) { |
| |
| const Record *R = Ptr.getRecord(); |
| assert(R); |
| assert(R->getNumFields() == 1); |
| |
| unsigned FieldOffset = R->getField(0u)->Offset; |
| const Pointer &FieldPtr = Ptr.atField(FieldOffset); |
| PrimType FieldT = *S.getContext().classify(FieldPtr.getType()); |
| |
| INT_TYPE_SWITCH(FieldT, |
| FieldPtr.deref<T>() = T::from(IntValue.getSExtValue())); |
| FieldPtr.initialize(); |
| return true; |
| } |
| |
| static void zeroAll(Pointer &Dest) { |
| const Descriptor *Desc = Dest.getFieldDesc(); |
| |
| if (Desc->isPrimitive()) { |
| TYPE_SWITCH(Desc->getPrimType(), { |
| Dest.deref<T>().~T(); |
| new (&Dest.deref<T>()) T(); |
| }); |
| return; |
| } |
| |
| if (Desc->isRecord()) { |
| const Record *R = Desc->ElemRecord; |
| for (const Record::Field &F : R->fields()) { |
| Pointer FieldPtr = Dest.atField(F.Offset); |
| zeroAll(FieldPtr); |
| } |
| return; |
| } |
| |
| if (Desc->isPrimitiveArray()) { |
| for (unsigned I = 0, N = Desc->getNumElems(); I != N; ++I) { |
| TYPE_SWITCH(Desc->getPrimType(), { |
| Dest.deref<T>().~T(); |
| new (&Dest.deref<T>()) T(); |
| }); |
| } |
| return; |
| } |
| |
| if (Desc->isCompositeArray()) { |
| for (unsigned I = 0, N = Desc->getNumElems(); I != N; ++I) { |
| Pointer ElemPtr = Dest.atIndex(I).narrow(); |
| zeroAll(ElemPtr); |
| } |
| return; |
| } |
| } |
| |
| static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src, |
| Pointer &Dest, bool Activate); |
| static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src, |
| Pointer &Dest, bool Activate = false) { |
| [[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc(); |
| const Descriptor *DestDesc = Dest.getFieldDesc(); |
| |
| auto copyField = [&](const Record::Field &F, bool Activate) -> bool { |
| Pointer DestField = Dest.atField(F.Offset); |
| if (OptPrimType FT = S.Ctx.classify(F.Decl->getType())) { |
| TYPE_SWITCH(*FT, { |
| DestField.deref<T>() = Src.atField(F.Offset).deref<T>(); |
| if (Src.atField(F.Offset).isInitialized()) |
| DestField.initialize(); |
| if (Activate) |
| DestField.activate(); |
| }); |
| return true; |
| } |
| // Composite field. |
| return copyComposite(S, OpPC, Src.atField(F.Offset), DestField, Activate); |
| }; |
| |
| assert(SrcDesc->isRecord()); |
| assert(SrcDesc->ElemRecord == DestDesc->ElemRecord); |
| const Record *R = DestDesc->ElemRecord; |
| for (const Record::Field &F : R->fields()) { |
| if (R->isUnion()) { |
| // For unions, only copy the active field. Zero all others. |
| const Pointer &SrcField = Src.atField(F.Offset); |
| if (SrcField.isActive()) { |
| if (!copyField(F, /*Activate=*/true)) |
| return false; |
| } else { |
| if (!CheckMutable(S, OpPC, Src.atField(F.Offset))) |
| return false; |
| Pointer DestField = Dest.atField(F.Offset); |
| zeroAll(DestField); |
| } |
| } else { |
| if (!copyField(F, Activate)) |
| return false; |
| } |
| } |
| |
| for (const Record::Base &B : R->bases()) { |
| Pointer DestBase = Dest.atField(B.Offset); |
| if (!copyRecord(S, OpPC, Src.atField(B.Offset), DestBase, Activate)) |
| return false; |
| } |
| |
| Dest.initialize(); |
| return true; |
| } |
| |
| static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src, |
| Pointer &Dest, bool Activate = false) { |
| assert(Src.isLive() && Dest.isLive()); |
| |
| [[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc(); |
| const Descriptor *DestDesc = Dest.getFieldDesc(); |
| |
| assert(!DestDesc->isPrimitive() && !SrcDesc->isPrimitive()); |
| |
| if (DestDesc->isPrimitiveArray()) { |
| assert(SrcDesc->isPrimitiveArray()); |
| assert(SrcDesc->getNumElems() == DestDesc->getNumElems()); |
| PrimType ET = DestDesc->getPrimType(); |
| for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) { |
| Pointer DestElem = Dest.atIndex(I); |
| TYPE_SWITCH(ET, { |
| DestElem.deref<T>() = Src.elem<T>(I); |
| DestElem.initialize(); |
| }); |
| } |
| return true; |
| } |
| |
| if (DestDesc->isCompositeArray()) { |
| assert(SrcDesc->isCompositeArray()); |
| assert(SrcDesc->getNumElems() == DestDesc->getNumElems()); |
| for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) { |
| const Pointer &SrcElem = Src.atIndex(I).narrow(); |
| Pointer DestElem = Dest.atIndex(I).narrow(); |
| if (!copyComposite(S, OpPC, SrcElem, DestElem, Activate)) |
| return false; |
| } |
| return true; |
| } |
| |
| if (DestDesc->isRecord()) |
| return copyRecord(S, OpPC, Src, Dest, Activate); |
| return Invalid(S, OpPC); |
| } |
| |
| bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) { |
| return copyComposite(S, OpPC, Src, Dest); |
| } |
| |
| } // namespace interp |
| } // namespace clang |