diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp index 75312999d23d6..e409002171c4b 100644 --- a/clang/lib/AST/Interp/Function.cpp +++ b/clang/lib/AST/Interp/Function.cpp @@ -7,10 +7,11 @@ //===----------------------------------------------------------------------===// #include "Function.h" -#include "Program.h" #include "Opcode.h" +#include "Program.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/Basic/Builtins.h" using namespace clang; using namespace clang::interp; @@ -47,3 +48,9 @@ bool Function::isVirtual() const { return M->isVirtual(); return false; } + +bool Function::needsRuntimeArgPop(const ASTContext &Ctx) const { + if (!isBuiltin()) + return false; + return Ctx.BuiltinInfo.hasCustomTypechecking(getBuiltinID()); +} diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h index 644d4cd53b1e1..5444c9f59cda7 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -171,6 +171,12 @@ class Function final { unsigned getBuiltinID() const { return F->getBuiltinID(); } + bool isBuiltin() const { return F->getBuiltinID() != 0; } + + /// Does this function need its arguments to be classified at runtime + /// rather than at bytecode-compile-time? + bool needsRuntimeArgPop(const ASTContext &Ctx) const; + unsigned getNumParams() const { return ParamTypes.size(); } unsigned getParamOffset(unsigned ParamIndex) const { diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index b891306e6aa40..2b7f3bf35aa59 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -122,6 +122,19 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { namespace clang { namespace interp { +bool popBuiltinArgs(InterpState &S, CodePtr OpPC) { + assert(S.Current && S.Current->getFunction()->needsRuntimeArgPop(S.getCtx())); + const Expr *E = S.Current->getExpr(OpPC); + assert(isa(E)); + const CallExpr *CE = cast(E); + for (int32_t I = CE->getNumArgs() - 1; I >= 0; --I) { + const Expr *A = CE->getArg(I); + PrimType Ty = S.getContext().classify(A->getType()).value_or(PT_Ptr); + TYPE_SWITCH(Ty, S.Stk.discard()); + } + return true; +} + bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!Ptr.isExtern()) return true; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 9eae79ba5d1e7..4da5985e3b3d6 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -181,6 +181,9 @@ enum class ArithOp { Add, Sub }; // Returning values //===----------------------------------------------------------------------===// +/// Pop arguments of builtins defined as func-name(...). +bool popBuiltinArgs(InterpState &S, CodePtr OpPC); + template ::T> bool Ret(InterpState &S, CodePtr &PC, APValue &Result) { const T &Ret = S.Stk.pop(); @@ -197,8 +200,16 @@ bool Ret(InterpState &S, CodePtr &PC, APValue &Result) { } assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); - if (!S.checkingPotentialConstantExpression() || S.Current->Caller) - S.Current->popArgs(); + if (!S.checkingPotentialConstantExpression() || S.Current->Caller) { + // Certain builtin functions are declared as func-name(...), so the + // parameters are checked in Sema and only available through the CallExpr. + // The interp::Function we create for them has 0 parameters, so we need to + // remove them from the stack by checking the CallExpr. + if (S.Current && S.Current->getFunction()->needsRuntimeArgPop(S.getCtx())) + popBuiltinArgs(S, PC); + else + S.Current->popArgs(); + } if (InterpFrame *Caller = S.Current->Caller) { PC = S.Current->getRetPC(); diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index b023d09df4878..01c41339bba8c 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -162,6 +162,17 @@ static bool interp__builtin_fmin(InterpState &S, CodePtr OpPC, 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; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F) { InterpFrame *Frame = S.Current; APValue Dummy; @@ -223,6 +234,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *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; + default: return false; } diff --git a/clang/test/Sema/constant-builtins-fmin.cpp b/clang/test/Sema/constant-builtins-fmin.cpp index bf3e81e21e617..8c80f7550666e 100644 --- a/clang/test/Sema/constant-builtins-fmin.cpp +++ b/clang/test/Sema/constant-builtins-fmin.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s // expected-no-diagnostics constexpr double NaN = __builtin_nan("");