//===--- 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 "Interp.h" #include "PrimType.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetInfo.h" namespace clang { namespace interp { template static T getParam(const InterpFrame *Frame, unsigned Index) { assert(Frame->getFunction()->getNumParams() > Index); unsigned Offset = Frame->getFunction()->getParamOffset(Index); return Frame->getParam(Offset); } PrimType getIntPrimType(const InterpState &S) { const TargetInfo &TI = S.getCtx().getTargetInfo(); unsigned IntWidth = TI.getIntWidth(); if (IntWidth == 32) return PT_Sint32; else if (IntWidth == 16) return PT_Sint16; llvm_unreachable("Int isn't 16 or 32 bit?"); } PrimType getLongPrimType(const InterpState &S) { const TargetInfo &TI = S.getCtx().getTargetInfo(); unsigned LongWidth = TI.getLongWidth(); if (LongWidth == 64) return PT_Sint64; else if (LongWidth == 32) return PT_Sint32; else if (LongWidth == 16) return PT_Sint16; llvm_unreachable("long isn't 16, 32 or 64 bit?"); } /// Peek an integer value from the stack into an APSInt. static APSInt peekToAPSInt(InterpStack &Stk, PrimType T, size_t Offset = 0) { if (Offset == 0) Offset = align(primSize(T)); APSInt R; INT_TYPE_SWITCH(T, { T Val = Stk.peek(Offset); R = APSInt( APInt(Val.bitWidth(), static_cast(Val), T::isSigned())); }); return R; } /// Pushes \p Val to the stack, as a target-dependent 'int'. static void pushInt(InterpState &S, int32_t Val) { PrimType IntType = getIntPrimType(S); if (IntType == PT_Sint32) S.Stk.push>(Integral<32, true>::from(Val)); else if (IntType == PT_Sint16) S.Stk.push>(Integral<16, true>::from(Val)); else llvm_unreachable("Int isn't 16 or 32 bit?"); } static void pushAPSInt(InterpState &S, const APSInt &Val) { bool Signed = Val.isSigned(); if (Signed) { switch (Val.getBitWidth()) { case 64: S.Stk.push>( Integral<64, true>::from(Val.getSExtValue())); break; case 32: S.Stk.push>( Integral<32, true>::from(Val.getSExtValue())); break; case 16: S.Stk.push>( Integral<16, true>::from(Val.getSExtValue())); break; case 8: S.Stk.push>( Integral<8, true>::from(Val.getSExtValue())); break; default: llvm_unreachable("Invalid integer bitwidth"); } return; } // Unsigned. switch (Val.getBitWidth()) { case 64: S.Stk.push>( Integral<64, false>::from(Val.getZExtValue())); break; case 32: S.Stk.push>( Integral<32, false>::from(Val.getZExtValue())); break; case 16: S.Stk.push>( Integral<16, false>::from(Val.getZExtValue())); break; case 8: S.Stk.push>( Integral<8, false>::from(Val.getZExtValue())); break; default: llvm_unreachable("Invalid integer bitwidth"); } } /// Pushes \p Val to the stack, as a target-dependent 'long'. static void pushLong(InterpState &S, int64_t Val) { PrimType LongType = getLongPrimType(S); if (LongType == PT_Sint64) S.Stk.push>(Integral<64, true>::from(Val)); else if (LongType == PT_Sint32) S.Stk.push>(Integral<32, true>::from(Val)); else if (LongType == PT_Sint16) S.Stk.push>(Integral<16, true>::from(Val)); else llvm_unreachable("Long isn't 16, 32 or 64 bit?"); } static void pushSizeT(InterpState &S, uint64_t Val) { const TargetInfo &TI = S.getCtx().getTargetInfo(); unsigned SizeTWidth = TI.getTypeWidth(TI.getSizeType()); switch (SizeTWidth) { case 64: S.Stk.push>(Integral<64, false>::from(Val)); break; case 32: S.Stk.push>(Integral<32, false>::from(Val)); break; case 16: S.Stk.push>(Integral<16, false>::from(Val)); break; default: llvm_unreachable("We don't handle this size_t size."); } } static bool retPrimValue(InterpState &S, CodePtr OpPC, APValue &Result, std::optional &T) { if (!T) return RetVoid(S, OpPC, Result); #define RET_CASE(X) \ case X: \ return Ret(S, OpPC, Result); switch (*T) { RET_CASE(PT_Ptr); RET_CASE(PT_FnPtr); RET_CASE(PT_Float); RET_CASE(PT_Bool); RET_CASE(PT_Sint8); RET_CASE(PT_Uint8); RET_CASE(PT_Sint16); RET_CASE(PT_Uint16); RET_CASE(PT_Sint32); RET_CASE(PT_Uint32); RET_CASE(PT_Sint64); RET_CASE(PT_Uint64); default: llvm_unreachable("Unsupported return type for builtin function"); } #undef RET_CASE } static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC, const InterpFrame *Frame) { const Pointer &A = getParam(Frame, 0); const Pointer &B = getParam(Frame, 1); if (!CheckLive(S, OpPC, A, AK_Read) || !CheckLive(S, OpPC, B, AK_Read)) return false; assert(A.getFieldDesc()->isPrimitiveArray()); assert(B.getFieldDesc()->isPrimitiveArray()); unsigned IndexA = A.getIndex(); unsigned IndexB = B.getIndex(); int32_t Result = 0; for (;; ++IndexA, ++IndexB) { 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; } uint8_t CA = PA.deref(); uint8_t CB = PB.deref(); if (CA > CB) { Result = 1; break; } else if (CA < CB) { Result = -1; break; } if (CA == 0 || CB == 0) break; } pushInt(S, Result); return true; } static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC, const InterpFrame *Frame) { const Pointer &StrPtr = getParam(Frame, 0); if (!CheckArray(S, OpPC, StrPtr)) return false; if (!CheckLive(S, OpPC, StrPtr, AK_Read)) return false; if (!CheckDummy(S, OpPC, StrPtr)) return false; assert(StrPtr.getFieldDesc()->isPrimitiveArray()); 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; uint8_t Val = ElemPtr.deref(); if (Val == 0) break; } pushSizeT(S, Len); return true; } static bool interp__builtin_nan(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F, bool Signaling) { const Pointer &Arg = getParam(Frame, 0); 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() == 0) break; Str += Elem.deref(); } // 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.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType()); Floating Result; if (S.getCtx().getTargetInfo().isNan2008()) { if (Signaling) Result = Floating( llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill)); else Result = Floating( 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 = Floating( llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, &Fill)); else Result = Floating( llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill)); } S.Stk.push(Result); return true; } static bool interp__builtin_inf(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const llvm::fltSemantics &TargetSemantics = S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType()); S.Stk.push(Floating::getInf(TargetSemantics)); return true; } static bool interp__builtin_copysign(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const Floating &Arg1 = getParam(Frame, 0); const Floating &Arg2 = getParam(Frame, 1); APFloat Copy = Arg1.getAPFloat(); Copy.copySign(Arg2.getAPFloat()); S.Stk.push(Floating(Copy)); return true; } static bool interp__builtin_fmin(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const Floating &LHS = getParam(Frame, 0); const Floating &RHS = getParam(Frame, 1); Floating Result; // When comparing zeroes, return -0.0 if one of the zeroes is negative. if (LHS.isZero() && RHS.isZero() && RHS.isNegative()) Result = RHS; else if (LHS.isNan() || RHS < LHS) Result = RHS; else Result = LHS; S.Stk.push(Result); return true; } static bool interp__builtin_fmax(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func) { const Floating &LHS = getParam(Frame, 0); const Floating &RHS = getParam(Frame, 1); Floating Result; // When comparing zeroes, return +0.0 if one of the zeroes is positive. if (LHS.isZero() && RHS.isZero() && LHS.isNegative()) Result = RHS; else if (LHS.isNan() || RHS > LHS) Result = RHS; else Result = LHS; S.Stk.push(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 Function *F) { const Floating &Arg = S.Stk.peek(); pushInt(S, Arg.isNan()); return true; } static bool interp__builtin_issignaling(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const Floating &Arg = S.Stk.peek(); pushInt(S, Arg.isSignaling()); return true; } static bool interp__builtin_isinf(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F, bool CheckSign) { const Floating &Arg = S.Stk.peek(); bool IsInf = Arg.isInf(); if (CheckSign) pushInt(S, IsInf ? (Arg.isNegative() ? -1 : 1) : 0); else pushInt(S, Arg.isInf()); return true; } static bool interp__builtin_isfinite(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const Floating &Arg = S.Stk.peek(); pushInt(S, Arg.isFinite()); return true; } static bool interp__builtin_isnormal(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const Floating &Arg = S.Stk.peek(); pushInt(S, Arg.isNormal()); return true; } static bool interp__builtin_issubnormal(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const Floating &Arg = S.Stk.peek(); pushInt(S, Arg.isDenormal()); return true; } static bool interp__builtin_iszero(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *F) { const Floating &Arg = S.Stk.peek(); pushInt(S, Arg.isZero()); 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 Function *Func, const CallExpr *Call) { PrimType FPClassArgT = *S.getContext().classify(Call->getArg(1)->getType()); APSInt FPClassArg = peekToAPSInt(S.Stk, FPClassArgT); const Floating &F = S.Stk.peek(align(primSize(FPClassArgT) + primSize(PT_Float))); int32_t Result = static_cast((F.classify() & FPClassArg).getZExtValue()); pushInt(S, Result); return true; } /// Five int values followed by one floating value. static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func) { const Floating &Val = S.Stk.peek(); 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); unsigned IntSize = primSize(getIntPrimType(S)); unsigned Offset = align(primSize(PT_Float)) + ((1 + (4 - Index)) * align(IntSize)); APSInt I = peekToAPSInt(S.Stk, getIntPrimType(S), Offset); pushInt(S, I.getZExtValue()); return true; } // 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 Function *Func) { const Floating &Val = getParam(Frame, 0); S.Stk.push(Floating::abs(Val)); return true; } static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); APSInt Val = peekToAPSInt(S.Stk, ArgT); pushInt(S, Val.popcount()); return true; } static bool interp__builtin_parity(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); APSInt Val = peekToAPSInt(S.Stk, ArgT); pushInt(S, Val.popcount() % 2); return true; } static bool interp__builtin_clrsb(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); APSInt Val = peekToAPSInt(S.Stk, ArgT); pushInt(S, Val.getBitWidth() - Val.getSignificantBits()); return true; } static bool interp__builtin_bitreverse(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); APSInt Val = peekToAPSInt(S.Stk, ArgT); pushAPSInt(S, APSInt(Val.reverseBits(), /*IsUnsigned=*/true)); return true; } static bool interp__builtin_classify_type(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, 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(ResultClass); pushInt(S, ReturnVal); 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 Function *Func, 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()); unsigned Offset = align(primSize(getLongPrimType(S))) * 2; if (NumArgs == 3) Offset += align(primSize(PT_Float)); APSInt Val = peekToAPSInt(S.Stk, ArgT, Offset); pushLong(S, Val.getSExtValue()); return true; } /// rotateleft(value, amount) static bool interp__builtin_rotate(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call, bool Right) { PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); assert(ArgT == *S.getContext().classify(Call->getArg(1)->getType())); APSInt Amount = peekToAPSInt(S.Stk, ArgT); APSInt Value = peekToAPSInt(S.Stk, ArgT, align(primSize(ArgT)) * 2); APSInt Result; if (Right) Result = APSInt(Value.rotr(Amount.urem(Value.getBitWidth())), /*IsUnsigned=*/true); else // Left. Result = APSInt(Value.rotl(Amount.urem(Value.getBitWidth())), /*IsUnsigned=*/true); pushAPSInt(S, Result); return true; } static bool interp__builtin_ffs(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); APSInt Value = peekToAPSInt(S.Stk, ArgT); uint64_t N = Value.countr_zero(); pushInt(S, N == Value.getBitWidth() ? 0 : N + 1); return true; } static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { PrimType PtrT = S.getContext().classify(Call->getArg(0)->getType()).value_or(PT_Ptr); if (PtrT == PT_FnPtr) { const FunctionPointer &Arg = S.Stk.peek(); S.Stk.push(Arg); } else if (PtrT == PT_Ptr) { const Pointer &Arg = S.Stk.peek(); S.Stk.push(Arg); } else { assert(false && "Unsupported pointer type passed to __builtin_addressof()"); } return true; } bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call) { InterpFrame *Frame = S.Current; APValue Dummy; std::optional ReturnT = S.getContext().classify(Call->getType()); // If classify failed, we assume void. assert(ReturnT || Call->getType()->isVoidType()); switch (F->getBuiltinID()) { case Builtin::BI__builtin_is_constant_evaluated: S.Stk.push(Boolean::from(S.inConstantContext())); break; case Builtin::BI__builtin_assume: break; case Builtin::BI__builtin_strcmp: if (!interp__builtin_strcmp(S, OpPC, Frame)) return false; break; case Builtin::BI__builtin_strlen: if (!interp__builtin_strlen(S, OpPC, Frame)) return false; break; 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: if (!interp__builtin_nan(S, OpPC, Frame, F, /*Signaling=*/false)) return false; break; 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: if (!interp__builtin_nan(S, OpPC, Frame, F, /*Signaling=*/true)) return false; break; 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: if (!interp__builtin_inf(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_copysign: case Builtin::BI__builtin_copysignf: case Builtin::BI__builtin_copysignl: case Builtin::BI__builtin_copysignf128: if (!interp__builtin_copysign(S, OpPC, Frame, F)) return false; break; 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: if (!interp__builtin_fmin(S, OpPC, Frame, F)) return false; break; 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: if (!interp__builtin_fmax(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_isnan: if (!interp__builtin_isnan(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_issignaling: if (!interp__builtin_issignaling(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_isinf: if (!interp__builtin_isinf(S, OpPC, Frame, F, /*Sign=*/false)) return false; break; case Builtin::BI__builtin_isinf_sign: if (!interp__builtin_isinf(S, OpPC, Frame, F, /*Sign=*/true)) return false; break; case Builtin::BI__builtin_isfinite: if (!interp__builtin_isfinite(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_isnormal: if (!interp__builtin_isnormal(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_issubnormal: if (!interp__builtin_issubnormal(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_iszero: if (!interp__builtin_iszero(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_isfpclass: if (!interp__builtin_isfpclass(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BI__builtin_fpclassify: if (!interp__builtin_fpclassify(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_fabs: case Builtin::BI__builtin_fabsf: case Builtin::BI__builtin_fabsl: case Builtin::BI__builtin_fabsf128: if (!interp__builtin_fabs(S, OpPC, Frame, F)) return false; break; case Builtin::BI__builtin_popcount: case Builtin::BI__builtin_popcountl: case Builtin::BI__builtin_popcountll: case Builtin::BI__popcnt16: // Microsoft variants of popcount case Builtin::BI__popcnt: case Builtin::BI__popcnt64: if (!interp__builtin_popcount(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BI__builtin_parity: case Builtin::BI__builtin_parityl: case Builtin::BI__builtin_parityll: if (!interp__builtin_parity(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BI__builtin_clrsb: case Builtin::BI__builtin_clrsbl: case Builtin::BI__builtin_clrsbll: if (!interp__builtin_clrsb(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BI__builtin_bitreverse8: case Builtin::BI__builtin_bitreverse16: case Builtin::BI__builtin_bitreverse32: case Builtin::BI__builtin_bitreverse64: if (!interp__builtin_bitreverse(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BI__builtin_classify_type: if (!interp__builtin_classify_type(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BI__builtin_expect: case Builtin::BI__builtin_expect_with_probability: if (!interp__builtin_expect(S, OpPC, Frame, F, Call)) return false; break; 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: if (!interp__builtin_rotate(S, OpPC, Frame, F, Call, /*Right=*/false)) return false; break; 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: if (!interp__builtin_rotate(S, OpPC, Frame, F, Call, /*Right=*/true)) return false; break; case Builtin::BI__builtin_ffs: case Builtin::BI__builtin_ffsl: case Builtin::BI__builtin_ffsll: if (!interp__builtin_ffs(S, OpPC, Frame, F, Call)) return false; break; case Builtin::BIaddressof: case Builtin::BI__addressof: case Builtin::BI__builtin_addressof: if (!interp__builtin_addressof(S, OpPC, Frame, F, Call)) return false; break; default: return false; } return retPrimValue(S, OpPC, Dummy, ReturnT); } bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E, llvm::ArrayRef 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 RecordType *RT = CurrentType->getAs(); if (!RT) return false; RecordDecl *RD = RT->getDecl(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = S.getCtx().getASTRecordLayout(RD); unsigned FieldIndex = MemberDecl->getFieldIndex(); assert(FieldIndex < RL.getFieldCount() && "offsetof field in wrong type"); Result += S.getCtx().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.getCtx().getAsArrayType(CurrentType); if (!AT) return false; CurrentType = AT->getElementType(); CharUnits ElementSize = S.getCtx().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 RecordType *RT = CurrentType->getAs(); if (!RT) return false; const RecordDecl *RD = RT->getDecl(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = S.getCtx().getASTRecordLayout(RD); // Find the base class itself. CurrentType = BaseSpec->getType(); const RecordType *BaseRT = CurrentType->getAs(); if (!BaseRT) return false; // Add the offset to the base. Result += RL.getBaseClassOffset(cast(BaseRT->getDecl())); 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::from(IntValue.getSExtValue())); FieldPtr.initialize(); return true; } } // namespace interp } // namespace clang