Skip to content

Commit ff80fc0

Browse files
committed
[clang][Interp] Implement __builtin_isnan()
The previous version was using llvm::reverse(CallExpr::arguments()), which causes problems when clang is compiled with GCC. Differential Revision: https://reviews.llvm.org/D155369
1 parent 7dbc7b1 commit ff80fc0

File tree

6 files changed

+57
-3
lines changed

6 files changed

+57
-3
lines changed

clang/lib/AST/Interp/Function.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "Function.h"
10-
#include "Program.h"
1110
#include "Opcode.h"
11+
#include "Program.h"
1212
#include "clang/AST/Decl.h"
1313
#include "clang/AST/DeclCXX.h"
14+
#include "clang/Basic/Builtins.h"
1415

1516
using namespace clang;
1617
using namespace clang::interp;
@@ -47,3 +48,9 @@ bool Function::isVirtual() const {
4748
return M->isVirtual();
4849
return false;
4950
}
51+
52+
bool Function::needsRuntimeArgPop(const ASTContext &Ctx) const {
53+
if (!isBuiltin())
54+
return false;
55+
return Ctx.BuiltinInfo.hasCustomTypechecking(getBuiltinID());
56+
}

clang/lib/AST/Interp/Function.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ class Function final {
171171

172172
unsigned getBuiltinID() const { return F->getBuiltinID(); }
173173

174+
bool isBuiltin() const { return F->getBuiltinID() != 0; }
175+
176+
/// Does this function need its arguments to be classified at runtime
177+
/// rather than at bytecode-compile-time?
178+
bool needsRuntimeArgPop(const ASTContext &Ctx) const;
179+
174180
unsigned getNumParams() const { return ParamTypes.size(); }
175181

176182
unsigned getParamOffset(unsigned ParamIndex) const {

clang/lib/AST/Interp/Interp.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,19 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
122122
namespace clang {
123123
namespace interp {
124124

125+
bool popBuiltinArgs(InterpState &S, CodePtr OpPC) {
126+
assert(S.Current && S.Current->getFunction()->needsRuntimeArgPop(S.getCtx()));
127+
const Expr *E = S.Current->getExpr(OpPC);
128+
assert(isa<CallExpr>(E));
129+
const CallExpr *CE = cast<CallExpr>(E);
130+
for (int32_t I = CE->getNumArgs() - 1; I >= 0; --I) {
131+
const Expr *A = CE->getArg(I);
132+
PrimType Ty = S.getContext().classify(A->getType()).value_or(PT_Ptr);
133+
TYPE_SWITCH(Ty, S.Stk.discard<T>());
134+
}
135+
return true;
136+
}
137+
125138
bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
126139
if (!Ptr.isExtern())
127140
return true;

clang/lib/AST/Interp/Interp.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ enum class ArithOp { Add, Sub };
181181
// Returning values
182182
//===----------------------------------------------------------------------===//
183183

184+
/// Pop arguments of builtins defined as func-name(...).
185+
bool popBuiltinArgs(InterpState &S, CodePtr OpPC);
186+
184187
template <PrimType Name, class T = typename PrimConv<Name>::T>
185188
bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
186189
const T &Ret = S.Stk.pop<T>();
@@ -197,8 +200,16 @@ bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
197200
}
198201

199202
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
200-
if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
201-
S.Current->popArgs();
203+
if (!S.checkingPotentialConstantExpression() || S.Current->Caller) {
204+
// Certain builtin functions are declared as func-name(...), so the
205+
// parameters are checked in Sema and only available through the CallExpr.
206+
// The interp::Function we create for them has 0 parameters, so we need to
207+
// remove them from the stack by checking the CallExpr.
208+
if (S.Current && S.Current->getFunction()->needsRuntimeArgPop(S.getCtx()))
209+
popBuiltinArgs(S, PC);
210+
else
211+
S.Current->popArgs();
212+
}
202213

203214
if (InterpFrame *Caller = S.Current->Caller) {
204215
PC = S.Current->getRetPC();

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ static bool interp__builtin_fmin(InterpState &S, CodePtr OpPC,
162162
return true;
163163
}
164164

165+
/// Defined as __builtin_isnan(...), to accommodate the fact that it can
166+
/// take a float, double, long double, etc.
167+
/// But for us, that's all a Floating anyway.
168+
static bool interp__builtin_isnan(InterpState &S, CodePtr OpPC,
169+
const InterpFrame *Frame, const Function *F) {
170+
const Floating &Arg = S.Stk.peek<Floating>();
171+
172+
S.Stk.push<Integral<32, true>>(Integral<32, true>::from(Arg.isNan()));
173+
return true;
174+
}
175+
165176
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F) {
166177
InterpFrame *Frame = S.Current;
167178
APValue Dummy;
@@ -223,6 +234,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F) {
223234
return Ret<PT_Float>(S, OpPC, Dummy);
224235
break;
225236

237+
case Builtin::BI__builtin_isnan:
238+
if (interp__builtin_isnan(S, OpPC, Frame, F))
239+
return Ret<PT_Sint32>(S, OpPC, Dummy);
240+
break;
241+
226242
default:
227243
return false;
228244
}

clang/test/Sema/constant-builtins-fmin.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s
23
// expected-no-diagnostics
34

45
constexpr double NaN = __builtin_nan("");

0 commit comments

Comments
 (0)