Index: clang/lib/AST/Interp/Function.h =================================================================== --- clang/lib/AST/Interp/Function.h +++ clang/lib/AST/Interp/Function.h @@ -171,6 +171,12 @@ 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 { Index: clang/lib/AST/Interp/Function.cpp =================================================================== --- clang/lib/AST/Interp/Function.cpp +++ 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 @@ return M->isVirtual(); return false; } + +bool Function::needsRuntimeArgPop(const ASTContext &Ctx) const { + if (!isBuiltin()) + return false; + return Ctx.BuiltinInfo.hasCustomTypechecking(getBuiltinID()); +} Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -200,6 +200,9 @@ // 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(); @@ -216,8 +219,16 @@ } 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(); Index: clang/lib/AST/Interp/Interp.cpp =================================================================== --- clang/lib/AST/Interp/Interp.cpp +++ clang/lib/AST/Interp/Interp.cpp @@ -122,6 +122,18 @@ 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 (const auto *A : llvm::reverse(CE->arguments())) { + 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; Index: clang/lib/AST/Interp/InterpBuiltin.cpp =================================================================== --- clang/lib/AST/Interp/InterpBuiltin.cpp +++ clang/lib/AST/Interp/InterpBuiltin.cpp @@ -158,6 +158,20 @@ 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) { + // FIXME: We are not going through getParam() here, because the function + // doesn't have any parameters. Wait for a pattern to emerge and maybe + // refactor this into a different function that checks things are valid. + 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; @@ -213,6 +227,11 @@ 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; } Index: clang/test/Sema/constant-builtins-fmin.cpp =================================================================== --- clang/test/Sema/constant-builtins-fmin.cpp +++ 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("");