Skip to content

Commit 7304936

Browse files
committed
[clang][Interp] Add preliminary __builtin_constant_p implementation
This is not perfect or complete, but it helps us pass the simple tests and those tests where __builtin_constant_p is not the main subject of testing.
1 parent f67fa3b commit 7304936

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

clang/lib/AST/Interp/ByteCodeEmitter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ using namespace clang::interp;
2828
/// but that is not correct for our use cases.
2929
static bool isUnevaluatedBuiltin(unsigned BuiltinID) {
3030
return BuiltinID == Builtin::BI__builtin_classify_type ||
31-
BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size;
31+
BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size ||
32+
BuiltinID == Builtin::BI__builtin_constant_p;
3233
}
3334

3435
Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===----------------------------------------------------------------------===//
88
#include "../ExprConstShared.h"
99
#include "Boolean.h"
10+
#include "Compiler.h"
11+
#include "EvalEmitter.h"
1012
#include "Interp.h"
1113
#include "PrimType.h"
1214
#include "clang/AST/OSLog.h"
@@ -1127,6 +1129,73 @@ static bool interp__builtin_ptrauth_string_discriminator(
11271129
return true;
11281130
}
11291131

1132+
// FIXME: This implementation is not complete.
1133+
// The Compiler instance we create cannot access the current stack frame, local
1134+
// variables, function parameters, etc. We also need protection from
1135+
// side-effects, fatal errors, etc.
1136+
static bool interp__builtin_constant_p(InterpState &S, CodePtr OpPC,
1137+
const InterpFrame *Frame,
1138+
const Function *Func,
1139+
const CallExpr *Call) {
1140+
const Expr *Arg = Call->getArg(0);
1141+
QualType ArgType = Arg->getType();
1142+
1143+
auto returnInt = [&S, Call](bool Value) -> bool {
1144+
pushInteger(S, Value, Call->getType());
1145+
return true;
1146+
};
1147+
1148+
// __builtin_constant_p always has one operand. The rules which gcc follows
1149+
// are not precisely documented, but are as follows:
1150+
//
1151+
// - If the operand is of integral, floating, complex or enumeration type,
1152+
// and can be folded to a known value of that type, it returns 1.
1153+
// - If the operand can be folded to a pointer to the first character
1154+
// of a string literal (or such a pointer cast to an integral type)
1155+
// or to a null pointer or an integer cast to a pointer, it returns 1.
1156+
//
1157+
// Otherwise, it returns 0.
1158+
//
1159+
// FIXME: GCC also intends to return 1 for literals of aggregate types, but
1160+
// its support for this did not work prior to GCC 9 and is not yet well
1161+
// understood.
1162+
if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() ||
1163+
ArgType->isAnyComplexType() || ArgType->isPointerType() ||
1164+
ArgType->isNullPtrType()) {
1165+
InterpStack Stk;
1166+
Compiler<EvalEmitter> C(S.Ctx, S.P, S, Stk);
1167+
auto Res = C.interpretExpr(Arg, /*ConvertResultToRValue=*/Arg->isGLValue());
1168+
if (Res.isInvalid()) {
1169+
C.cleanup();
1170+
Stk.clear();
1171+
}
1172+
1173+
const APValue &LV = Res.toAPValue();
1174+
if (!Res.isInvalid() && LV.isLValue()) {
1175+
APValue::LValueBase Base = LV.getLValueBase();
1176+
if (Base.isNull()) {
1177+
// A null base is acceptable.
1178+
return returnInt(true);
1179+
} else if (const auto *E = Base.dyn_cast<const Expr *>()) {
1180+
if (!isa<StringLiteral>(E))
1181+
return returnInt(false);
1182+
return returnInt(LV.getLValueOffset().isZero());
1183+
} else if (Base.is<TypeInfoLValue>()) {
1184+
// Surprisingly, GCC considers __builtin_constant_p(&typeid(int)) to
1185+
// evaluate to true.
1186+
return returnInt(true);
1187+
} else {
1188+
// Any other base is not constant enough for GCC.
1189+
return returnInt(false);
1190+
}
1191+
}
1192+
1193+
return returnInt(!Res.isInvalid() && !Res.empty());
1194+
}
1195+
1196+
return returnInt(false);
1197+
}
1198+
11301199
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
11311200
const CallExpr *Call) {
11321201
const InterpFrame *Frame = S.Current;
@@ -1456,6 +1525,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
14561525
return false;
14571526
break;
14581527

1528+
case Builtin::BI__builtin_constant_p:
1529+
if (!interp__builtin_constant_p(S, OpPC, Frame, F, Call))
1530+
return false;
1531+
break;
1532+
14591533
default:
14601534
S.FFDiag(S.Current->getLocation(OpPC),
14611535
diag::note_invalid_subexpr_in_const_expr)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s
2+
// RUN: %clang_cc1 -verify=ref,both %s
3+
4+
5+
static_assert(__builtin_constant_p(12), "");
6+
static_assert(__builtin_constant_p(1.0), "");
7+
8+
constexpr int I = 100;
9+
static_assert(__builtin_constant_p(I), "");
10+
static_assert(__builtin_constant_p(I + 10), "");
11+
static_assert(__builtin_constant_p(I + 10.0), "");
12+
static_assert(__builtin_constant_p(nullptr), "");
13+
static_assert(__builtin_constant_p(&I), ""); // both-error {{failed due to requirement}}
14+
static_assert(__builtin_constant_p((void)I), ""); // both-error {{failed due to requirement}}

0 commit comments

Comments
 (0)