Skip to content

Commit 7fe5944

Browse files
committed
[clang][bytecode] Implement __builtin_constant_p differently
1 parent 61b806f commit 7fe5944

File tree

10 files changed

+357
-78
lines changed

10 files changed

+357
-78
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4538,6 +4538,9 @@ bool Compiler<Emitter>::visitAPValueInitializer(const APValue &Val,
45384538
template <class Emitter>
45394539
bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E,
45404540
unsigned BuiltinID) {
4541+
if (BuiltinID == Builtin::BI__builtin_constant_p)
4542+
return this->emitBCP(classifyPrim(E), E->getArg(0), E);
4543+
45414544
const Function *Func = getFunction(E->getDirectCallee());
45424545
if (!Func)
45434546
return false;

clang/lib/AST/ByteCode/Function.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,14 @@ class Function final {
226226
return ParamTypes[ParamIndex];
227227
}
228228

229+
std::optional<unsigned> findParam(const ValueDecl *D) const {
230+
for (auto &[K, V] : Params) {
231+
if (V.second->asValueDecl() == D)
232+
return K;
233+
}
234+
return std::nullopt;
235+
}
236+
229237
private:
230238
/// Construct a function representing an actual function.
231239
Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,

clang/lib/AST/ByteCode/Interp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "Function.h"
2323
#include "FunctionPointer.h"
2424
#include "InterpBuiltinBitCast.h"
25+
#include "InterpBuiltinConstantP.h"
2526
#include "InterpFrame.h"
2627
#include "InterpStack.h"
2728
#include "InterpState.h"
@@ -3040,6 +3041,16 @@ bool GetTypeid(InterpState &S, CodePtr OpPC, const Type *TypePtr,
30403041
bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType);
30413042
bool DiagTypeid(InterpState &S, CodePtr OpPC);
30423043

3044+
/// __builtin_constant_p.
3045+
template <PrimType Name, class T = typename PrimConv<Name>::T>
3046+
bool BCP(InterpState &S, CodePtr OpPC, const Expr *E) {
3047+
BCPVisitor BV{S};
3048+
BV.VisitStmt(const_cast<Expr *>(E));
3049+
3050+
S.Stk.push<T>(T::from(BV.Result));
3051+
return true;
3052+
}
3053+
30433054
//===----------------------------------------------------------------------===//
30443055
// Read opcode arguments
30453056
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,77 +1505,6 @@ static bool interp__builtin_ptrauth_string_discriminator(
15051505
return true;
15061506
}
15071507

1508-
// FIXME: This implementation is not complete.
1509-
// The Compiler instance we create cannot access the current stack frame, local
1510-
// variables, function parameters, etc. We also need protection from
1511-
// side-effects, fatal errors, etc.
1512-
static bool interp__builtin_constant_p(InterpState &S, CodePtr OpPC,
1513-
const InterpFrame *Frame,
1514-
const Function *Func,
1515-
const CallExpr *Call) {
1516-
const Expr *Arg = Call->getArg(0);
1517-
QualType ArgType = Arg->getType();
1518-
1519-
auto returnInt = [&S, Call](bool Value) -> bool {
1520-
pushInteger(S, Value, Call->getType());
1521-
return true;
1522-
};
1523-
1524-
// __builtin_constant_p always has one operand. The rules which gcc follows
1525-
// are not precisely documented, but are as follows:
1526-
//
1527-
// - If the operand is of integral, floating, complex or enumeration type,
1528-
// and can be folded to a known value of that type, it returns 1.
1529-
// - If the operand can be folded to a pointer to the first character
1530-
// of a string literal (or such a pointer cast to an integral type)
1531-
// or to a null pointer or an integer cast to a pointer, it returns 1.
1532-
//
1533-
// Otherwise, it returns 0.
1534-
//
1535-
// FIXME: GCC also intends to return 1 for literals of aggregate types, but
1536-
// its support for this did not work prior to GCC 9 and is not yet well
1537-
// understood.
1538-
if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() ||
1539-
ArgType->isAnyComplexType() || ArgType->isPointerType() ||
1540-
ArgType->isNullPtrType()) {
1541-
InterpStack Stk;
1542-
Compiler<EvalEmitter> C(S.Ctx, S.P, S, Stk);
1543-
auto Res = C.interpretExpr(Arg, /*ConvertResultToRValue=*/Arg->isGLValue());
1544-
if (Res.isInvalid()) {
1545-
C.cleanup();
1546-
Stk.clear();
1547-
return returnInt(false);
1548-
}
1549-
1550-
if (!Res.empty()) {
1551-
const APValue &LV = Res.toAPValue();
1552-
if (LV.isLValue()) {
1553-
APValue::LValueBase Base = LV.getLValueBase();
1554-
if (Base.isNull()) {
1555-
// A null base is acceptable.
1556-
return returnInt(true);
1557-
} else if (const auto *E = Base.dyn_cast<const Expr *>()) {
1558-
if (!isa<StringLiteral>(E))
1559-
return returnInt(false);
1560-
return returnInt(LV.getLValueOffset().isZero());
1561-
} else if (Base.is<TypeInfoLValue>()) {
1562-
// Surprisingly, GCC considers __builtin_constant_p(&typeid(int)) to
1563-
// evaluate to true.
1564-
return returnInt(true);
1565-
} else {
1566-
// Any other base is not constant enough for GCC.
1567-
return returnInt(false);
1568-
}
1569-
}
1570-
}
1571-
1572-
// Otherwise, any constant value is good enough.
1573-
return returnInt(true);
1574-
}
1575-
1576-
return returnInt(false);
1577-
}
1578-
15791508
static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
15801509
const InterpFrame *Frame,
15811510
const Function *Func,
@@ -2483,11 +2412,6 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
24832412
return false;
24842413
break;
24852414

2486-
case Builtin::BI__builtin_constant_p:
2487-
if (!interp__builtin_constant_p(S, OpPC, Frame, F, Call))
2488-
return false;
2489-
break;
2490-
24912415
case Builtin::BI__noop:
24922416
pushInteger(S, 0, Call->getType());
24932417
break;
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//===--------------- InterpBuiltinConstantP.cpp -----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
#include "InterpBuiltinConstantP.h"
9+
#include "Compiler.h"
10+
#include "EvalEmitter.h"
11+
#include "clang/AST/Expr.h"
12+
#include "clang/AST/ExprCXX.h"
13+
14+
using namespace clang;
15+
using namespace interp;
16+
17+
bool BCPVisitor::VisitStmt(Stmt *S) {
18+
switch (S->getStmtClass()) {
19+
case Stmt::DeclRefExprClass:
20+
return VisitDeclRefExpr(cast<DeclRefExpr>(S));
21+
case Stmt::ImplicitCastExprClass:
22+
case Stmt::CStyleCastExprClass:
23+
return VisitCastExpr(cast<CastExpr>(S));
24+
case Stmt::CallExprClass:
25+
return VisitCallExpr(cast<CallExpr>(S));
26+
case Stmt::UnaryOperatorClass:
27+
return VisitUnaryOperator(cast<UnaryOperator>(S));
28+
case Stmt::BinaryOperatorClass:
29+
return VisitBinaryOperator(cast<BinaryOperator>(S));
30+
case Stmt::ParenExprClass:
31+
return VisitStmt(cast<ParenExpr>(S)->getSubExpr());
32+
case Stmt::ConditionalOperatorClass:
33+
return VisitConditionalOperator(cast<ConditionalOperator>(S));
34+
case Stmt::MemberExprClass:
35+
return VisitMemberExpr(cast<MemberExpr>(S));
36+
37+
case Stmt::IntegerLiteralClass:
38+
case Stmt::FloatingLiteralClass:
39+
case Stmt::CXXNullPtrLiteralExprClass:
40+
return true;
41+
default:
42+
return Fail();
43+
}
44+
45+
llvm_unreachable("All handled above");
46+
}
47+
48+
bool BCPVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
49+
const ValueDecl *D = E->getDecl();
50+
51+
// Local variable?
52+
if (auto LocalOffset = findLocal(D)) {
53+
Result = pointerChainIsLive(Frame->getLocalPointer(*LocalOffset));
54+
} else if (auto G = S.P.getGlobal(D)) {
55+
// Fine.
56+
} else if (auto P = findParam(D)) {
57+
Result = pointerChainIsLive(Frame->getParamPointer(*P));
58+
} else {
59+
Result = false;
60+
}
61+
62+
return Result;
63+
}
64+
65+
bool BCPVisitor::VisitUnaryOperator(UnaryOperator *E) {
66+
switch (E->getOpcode()) {
67+
case UO_AddrOf:
68+
Result = isa<CXXTypeidExpr>(E->getSubExpr());
69+
break;
70+
case UO_PostInc:
71+
case UO_PreInc:
72+
case UO_PostDec:
73+
case UO_PreDec:
74+
return Fail();
75+
default:;
76+
}
77+
return Result;
78+
}
79+
80+
bool BCPVisitor::VisitBinaryOperator(BinaryOperator *E) {
81+
if (E->isCommaOp())
82+
return VisitStmt(E->getRHS());
83+
84+
return VisitStmt(E->getLHS()) && VisitStmt(E->getRHS());
85+
}
86+
87+
bool BCPVisitor::VisitCastExpr(CastExpr *E) {
88+
if (E->getCastKind() == CK_ToVoid)
89+
return Fail();
90+
return VisitStmt(E->getSubExpr());
91+
}
92+
93+
bool BCPVisitor::VisitCallExpr(CallExpr *E) {
94+
// FIXME: We're not passing any arguments to the function call.
95+
Compiler<EvalEmitter> C(S.getContext(), S.P, S, S.Stk);
96+
97+
auto OldDiag = S.getEvalStatus().Diag;
98+
S.getEvalStatus().Diag = nullptr;
99+
auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());
100+
101+
S.getEvalStatus().Diag = OldDiag;
102+
Result = !Res.isInvalid();
103+
return Result;
104+
}
105+
106+
bool BCPVisitor::VisitConditionalOperator(ConditionalOperator *E) {
107+
return VisitStmt(E->getCond()) && VisitStmt(E->getTrueExpr()) &&
108+
VisitStmt(E->getFalseExpr());
109+
}
110+
111+
bool BCPVisitor::VisitMemberExpr(MemberExpr *E) {
112+
if (!isa<DeclRefExpr>(E->getBase()))
113+
return Fail();
114+
115+
const auto *BaseDecl = cast<DeclRefExpr>(E->getBase())->getDecl();
116+
const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
117+
if (!FD)
118+
return Fail();
119+
120+
if (!VisitStmt(E->getBase()))
121+
return Fail();
122+
123+
Pointer BasePtr = getPointer(BaseDecl);
124+
const Record *R = BasePtr.getRecord();
125+
assert(R);
126+
Pointer FieldPtr = BasePtr.atField(R->getField(FD)->Offset);
127+
if (!pointerChainIsLive(FieldPtr))
128+
return Fail();
129+
return true;
130+
}
131+
132+
std::optional<unsigned> BCPVisitor::findLocal(const ValueDecl *D) const {
133+
const auto *Func = Frame->getFunction();
134+
if (!Func)
135+
return std::nullopt;
136+
for (auto &Scope : Func->scopes()) {
137+
for (auto &Local : Scope.locals()) {
138+
if (Local.Desc->asValueDecl() == D) {
139+
return Local.Offset;
140+
}
141+
}
142+
}
143+
return std::nullopt;
144+
}
145+
146+
std::optional<unsigned> BCPVisitor::findParam(const ValueDecl *D) const {
147+
const auto *Func = Frame->getFunction();
148+
if (!Func || !Frame->Caller)
149+
return std::nullopt;
150+
151+
return Func->findParam(D);
152+
}
153+
154+
bool BCPVisitor::pointerChainIsLive(const Pointer &P) const {
155+
Pointer Ptr = P;
156+
for (;;) {
157+
if (!Ptr.isLive() || !Ptr.isInitialized() || Ptr.isExtern() ||
158+
Ptr.isDummy())
159+
return false;
160+
161+
if (Ptr.isZero())
162+
return true;
163+
164+
const Descriptor *Desc = Ptr.getFieldDesc();
165+
if (!Desc->isPrimitive() || Desc->getPrimType() != PT_Ptr)
166+
return true;
167+
168+
Ptr = Ptr.deref<Pointer>();
169+
}
170+
171+
return true;
172+
}
173+
174+
Pointer BCPVisitor::getPointer(const ValueDecl *D) const {
175+
if (auto LocalOffset = findLocal(D))
176+
return Frame->getLocalPointer(*LocalOffset);
177+
if (auto G = S.P.getGlobal(D))
178+
return S.P.getPtrGlobal(*G);
179+
if (auto P = findParam(D))
180+
return Frame->getParamPointer(*P);
181+
182+
llvm_unreachable("One of the ifs before should've worked.");
183+
}

0 commit comments

Comments
 (0)