//===--- 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 "Boolean.h" #include "Interp.h" #include "PrimType.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); } /// Peek an integer value from the stack into an APSInt. static APSInt peekToAPSInt(InterpStack &Stk, PrimType T) { APSInt R; INT_TYPE_SWITCH(T, { T Val = Stk.peek(); R = APSInt(APInt(T::bitWidth(), static_cast(Val), T::isSigned())); }); return R; } 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; } S.Stk.push>(Integral<32, true>::from(Result)); 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(); S.Stk.push>(Integral<32, true>::from(Arg.isNan())); 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) S.Stk.push>( Integral<32, true>::from(IsInf ? (Arg.isNegative() ? -1 : 1) : 0)); else S.Stk.push>(Integral<32, true>::from(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(); S.Stk.push>(Integral<32, true>::from(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(); S.Stk.push>(Integral<32, true>::from(Arg.isNormal())); 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()); S.Stk.push>(Integral<32, true>::from(Result)); return true; } /// Five int32 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. unsigned Offset = align(primSize(PT_Float)) + ((1 + (4 - Index)) * align(primSize(PT_Sint32))); const Integral<32, true> &I = S.Stk.peek>(Offset); S.Stk.push>(I); 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; } bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call) { InterpFrame *Frame = S.Current; APValue Dummy; switch (F->getBuiltinID()) { case Builtin::BI__builtin_is_constant_evaluated: S.Stk.push(Boolean::from(S.inConstantContext())); return Ret(S, OpPC, Dummy); case Builtin::BI__builtin_assume: return RetVoid(S, OpPC, Dummy); case Builtin::BI__builtin_strcmp: if (interp__builtin_strcmp(S, OpPC, Frame)) return Ret(S, OpPC, Dummy); 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 Ret(S, OpPC, Dummy); 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 Ret(S, OpPC, Dummy); 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 Ret(S, OpPC, Dummy); 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 Ret(S, OpPC, Dummy); 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 Ret(S, OpPC, Dummy); 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 Ret(S, OpPC, Dummy); break; case Builtin::BI__builtin_isnan: if (interp__builtin_isnan(S, OpPC, Frame, F)) return Ret(S, OpPC, Dummy); break; case Builtin::BI__builtin_isinf: if (interp__builtin_isinf(S, OpPC, Frame, F, /*Sign=*/false)) return Ret(S, OpPC, Dummy); break; case Builtin::BI__builtin_isinf_sign: if (interp__builtin_isinf(S, OpPC, Frame, F, /*Sign=*/true)) return Ret(S, OpPC, Dummy); break; case Builtin::BI__builtin_isfinite: if (interp__builtin_isfinite(S, OpPC, Frame, F)) return Ret(S, OpPC, Dummy); break; case Builtin::BI__builtin_isnormal: if (interp__builtin_isnormal(S, OpPC, Frame, F)) return Ret(S, OpPC, Dummy); break; case Builtin::BI__builtin_isfpclass: if (interp__builtin_isfpclass(S, OpPC, Frame, F, Call)) return Ret(S, OpPC, Dummy); break; case Builtin::BI__builtin_fpclassify: if (interp__builtin_fpclassify(S, OpPC, Frame, F)) return Ret(S, OpPC, Dummy); 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 Ret(S, OpPC, Dummy); break; default: return false; } return false; } } // namespace interp } // namespace clang