Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4538,6 +4538,9 @@ bool Compiler<Emitter>::visitAPValueInitializer(const APValue &Val,
template <class Emitter>
bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E,
unsigned BuiltinID) {
if (BuiltinID == Builtin::BI__builtin_constant_p)
return this->emitBCP(classifyPrim(E), E->getArg(0), E);

const Function *Func = getFunction(E->getDirectCallee());
if (!Func)
return false;
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/ByteCode/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,14 @@ class Function final {
return ParamTypes[ParamIndex];
}

std::optional<unsigned> findParam(const ValueDecl *D) const {
for (auto &[K, V] : Params) {
if (V.second->asValueDecl() == D)
return K;
}
return std::nullopt;
}

private:
/// Construct a function representing an actual function.
Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "Function.h"
#include "FunctionPointer.h"
#include "InterpBuiltinBitCast.h"
#include "InterpBuiltinConstantP.h"
#include "InterpFrame.h"
#include "InterpStack.h"
#include "InterpState.h"
Expand Down Expand Up @@ -3040,6 +3041,16 @@ bool GetTypeid(InterpState &S, CodePtr OpPC, const Type *TypePtr,
bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType);
bool DiagTypeid(InterpState &S, CodePtr OpPC);

/// __builtin_constant_p.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool BCP(InterpState &S, CodePtr OpPC, const Expr *E) {
BCPVisitor BV{S};
BV.VisitStmt(const_cast<Expr *>(E));

S.Stk.push<T>(T::from(BV.Result));
return true;
}

//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
76 changes: 0 additions & 76 deletions clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1505,77 +1505,6 @@ static bool interp__builtin_ptrauth_string_discriminator(
return true;
}

// FIXME: This implementation is not complete.
// The Compiler instance we create cannot access the current stack frame, local
// variables, function parameters, etc. We also need protection from
// side-effects, fatal errors, etc.
static bool interp__builtin_constant_p(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
const Expr *Arg = Call->getArg(0);
QualType ArgType = Arg->getType();

auto returnInt = [&S, Call](bool Value) -> bool {
pushInteger(S, Value, Call->getType());
return true;
};

// __builtin_constant_p always has one operand. The rules which gcc follows
// are not precisely documented, but are as follows:
//
// - If the operand is of integral, floating, complex or enumeration type,
// and can be folded to a known value of that type, it returns 1.
// - If the operand can be folded to a pointer to the first character
// of a string literal (or such a pointer cast to an integral type)
// or to a null pointer or an integer cast to a pointer, it returns 1.
//
// Otherwise, it returns 0.
//
// FIXME: GCC also intends to return 1 for literals of aggregate types, but
// its support for this did not work prior to GCC 9 and is not yet well
// understood.
if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() ||
ArgType->isAnyComplexType() || ArgType->isPointerType() ||
ArgType->isNullPtrType()) {
InterpStack Stk;
Compiler<EvalEmitter> C(S.Ctx, S.P, S, Stk);
auto Res = C.interpretExpr(Arg, /*ConvertResultToRValue=*/Arg->isGLValue());
if (Res.isInvalid()) {
C.cleanup();
Stk.clear();
return returnInt(false);
}

if (!Res.empty()) {
const APValue &LV = Res.toAPValue();
if (LV.isLValue()) {
APValue::LValueBase Base = LV.getLValueBase();
if (Base.isNull()) {
// A null base is acceptable.
return returnInt(true);
} else if (const auto *E = Base.dyn_cast<const Expr *>()) {
if (!isa<StringLiteral>(E))
return returnInt(false);
return returnInt(LV.getLValueOffset().isZero());
} else if (Base.is<TypeInfoLValue>()) {
// Surprisingly, GCC considers __builtin_constant_p(&typeid(int)) to
// evaluate to true.
return returnInt(true);
} else {
// Any other base is not constant enough for GCC.
return returnInt(false);
}
}
}

// Otherwise, any constant value is good enough.
return returnInt(true);
}

return returnInt(false);
}

static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
Expand Down Expand Up @@ -2483,11 +2412,6 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
return false;
break;

case Builtin::BI__builtin_constant_p:
if (!interp__builtin_constant_p(S, OpPC, Frame, F, Call))
return false;
break;

case Builtin::BI__noop:
pushInteger(S, 0, Call->getType());
break;
Expand Down
183 changes: 183 additions & 0 deletions clang/lib/AST/ByteCode/InterpBuiltinConstantP.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//===--------------- InterpBuiltinConstantP.cpp -----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "InterpBuiltinConstantP.h"
#include "Compiler.h"
#include "EvalEmitter.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"

using namespace clang;
using namespace interp;

bool BCPVisitor::VisitStmt(Stmt *S) {
switch (S->getStmtClass()) {
case Stmt::DeclRefExprClass:
return VisitDeclRefExpr(cast<DeclRefExpr>(S));
case Stmt::ImplicitCastExprClass:
case Stmt::CStyleCastExprClass:
return VisitCastExpr(cast<CastExpr>(S));
case Stmt::CallExprClass:
return VisitCallExpr(cast<CallExpr>(S));
case Stmt::UnaryOperatorClass:
return VisitUnaryOperator(cast<UnaryOperator>(S));
case Stmt::BinaryOperatorClass:
return VisitBinaryOperator(cast<BinaryOperator>(S));
case Stmt::ParenExprClass:
return VisitStmt(cast<ParenExpr>(S)->getSubExpr());
case Stmt::ConditionalOperatorClass:
return VisitConditionalOperator(cast<ConditionalOperator>(S));
case Stmt::MemberExprClass:
return VisitMemberExpr(cast<MemberExpr>(S));

case Stmt::IntegerLiteralClass:
case Stmt::FloatingLiteralClass:
case Stmt::CXXNullPtrLiteralExprClass:
return true;
default:
return Fail();
}

llvm_unreachable("All handled above");
}

bool BCPVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
const ValueDecl *D = E->getDecl();

// Local variable?
if (auto LocalOffset = findLocal(D)) {
Result = pointerChainIsLive(Frame->getLocalPointer(*LocalOffset));
} else if (auto G = S.P.getGlobal(D)) {
// Fine.
} else if (auto P = findParam(D)) {
Result = pointerChainIsLive(Frame->getParamPointer(*P));
} else {
Result = false;
}

return Result;
}

bool BCPVisitor::VisitUnaryOperator(UnaryOperator *E) {
switch (E->getOpcode()) {
case UO_AddrOf:
Result = isa<CXXTypeidExpr>(E->getSubExpr());
break;
case UO_PostInc:
case UO_PreInc:
case UO_PostDec:
case UO_PreDec:
return Fail();
default:;
}
return Result;
}

bool BCPVisitor::VisitBinaryOperator(BinaryOperator *E) {
if (E->isCommaOp())
return VisitStmt(E->getRHS());

return VisitStmt(E->getLHS()) && VisitStmt(E->getRHS());
}

bool BCPVisitor::VisitCastExpr(CastExpr *E) {
if (E->getCastKind() == CK_ToVoid)
return Fail();
return VisitStmt(E->getSubExpr());
}

bool BCPVisitor::VisitCallExpr(CallExpr *E) {
// FIXME: We're not passing any arguments to the function call.
Compiler<EvalEmitter> C(S.getContext(), S.P, S, S.Stk);

auto OldDiag = S.getEvalStatus().Diag;
S.getEvalStatus().Diag = nullptr;
auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());

S.getEvalStatus().Diag = OldDiag;
Result = !Res.isInvalid();
return Result;
}

bool BCPVisitor::VisitConditionalOperator(ConditionalOperator *E) {
return VisitStmt(E->getCond()) && VisitStmt(E->getTrueExpr()) &&
VisitStmt(E->getFalseExpr());
}

bool BCPVisitor::VisitMemberExpr(MemberExpr *E) {
if (!isa<DeclRefExpr>(E->getBase()))
return Fail();

const auto *BaseDecl = cast<DeclRefExpr>(E->getBase())->getDecl();
const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
if (!FD)
return Fail();

if (!VisitStmt(E->getBase()))
return Fail();

Pointer BasePtr = getPointer(BaseDecl);
const Record *R = BasePtr.getRecord();
assert(R);
Pointer FieldPtr = BasePtr.atField(R->getField(FD)->Offset);
if (!pointerChainIsLive(FieldPtr))
return Fail();
return true;
}

std::optional<unsigned> BCPVisitor::findLocal(const ValueDecl *D) const {
const auto *Func = Frame->getFunction();
if (!Func)
return std::nullopt;
for (auto &Scope : Func->scopes()) {
for (auto &Local : Scope.locals()) {
if (Local.Desc->asValueDecl() == D) {
return Local.Offset;
}
}
}
return std::nullopt;
}

std::optional<unsigned> BCPVisitor::findParam(const ValueDecl *D) const {
const auto *Func = Frame->getFunction();
if (!Func || !Frame->Caller)
return std::nullopt;

return Func->findParam(D);
}

bool BCPVisitor::pointerChainIsLive(const Pointer &P) const {
Pointer Ptr = P;
for (;;) {
if (!Ptr.isLive() || !Ptr.isInitialized() || Ptr.isExtern() ||
Ptr.isDummy())
return false;

if (Ptr.isZero())
return true;

const Descriptor *Desc = Ptr.getFieldDesc();
if (!Desc->isPrimitive() || Desc->getPrimType() != PT_Ptr)
return true;

Ptr = Ptr.deref<Pointer>();
}

return true;
}

Pointer BCPVisitor::getPointer(const ValueDecl *D) const {
if (auto LocalOffset = findLocal(D))
return Frame->getLocalPointer(*LocalOffset);
if (auto G = S.P.getGlobal(D))
return S.P.getPtrGlobal(*G);
if (auto P = findParam(D))
return Frame->getParamPointer(*P);

llvm_unreachable("One of the ifs before should've worked.");
}
Loading
Loading