Skip to content

Commit

Permalink
[clang][Interp] Implement __builtin_addressof (#77303)
Browse files Browse the repository at this point in the history
We don't need to do anything here, since the input is already a Pointer.
The only complexity is that we pre-classify the parameters as PT_Ptr,
but they might end up being of a different pointer type, e.g. PT_FnPtr.
  • Loading branch information
tbaederr committed Jan 11, 2024
1 parent 3643d11 commit e3993e0
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 3 deletions.
12 changes: 12 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,18 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) {
if (CurFunc->isUnevaluatedBuiltin())
return;

// Some builtin functions require us to only look at the call site, since
// the classified parameter types do not match.
if (CurFunc->isBuiltin()) {
const auto *CE =
cast<CallExpr>(S.Current->Caller->getExpr(S.Current->getRetPC()));
for (int32_t I = CE->getNumArgs() - 1; I >= 0; --I) {
const Expr *A = CE->getArg(I);
popArg(S, A);
}
return;
}

if (S.Current->Caller && CurFunc->isVariadic()) {
// CallExpr we're look for is at the return PC of the current function, i.e.
// in the caller.
Expand Down
33 changes: 30 additions & 3 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ static bool retPrimValue(InterpState &S, CodePtr OpPC, APValue &Result,
case X: \
return Ret<X>(S, OpPC, Result);
switch (*T) {
RET_CASE(PT_Ptr);
RET_CASE(PT_FnPtr);
RET_CASE(PT_Float);
RET_CASE(PT_Bool);
RET_CASE(PT_Sint8);
Expand Down Expand Up @@ -613,15 +615,34 @@ static bool interp__builtin_ffs(InterpState &S, CodePtr OpPC,
return true;
}

static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType PtrT =
S.getContext().classify(Call->getArg(0)->getType()).value_or(PT_Ptr);

if (PtrT == PT_FnPtr) {
const FunctionPointer &Arg = S.Stk.peek<FunctionPointer>();
S.Stk.push<FunctionPointer>(Arg);
} else if (PtrT == PT_Ptr) {
const Pointer &Arg = S.Stk.peek<Pointer>();
S.Stk.push<Pointer>(Arg);
} else {
assert(false && "Unsupported pointer type passed to __builtin_addressof()");
}
return true;
}

bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call) {
InterpFrame *Frame = S.Current;
APValue Dummy;

QualType ReturnType = Call->getCallReturnType(S.getCtx());
std::optional<PrimType> ReturnT = S.getContext().classify(ReturnType);
std::optional<PrimType> ReturnT = S.getContext().classify(Call->getType());

// If classify failed, we assume void.
assert(ReturnT || ReturnType->isVoidType());
assert(ReturnT || Call->getType()->isVoidType());

switch (F->getBuiltinID()) {
case Builtin::BI__builtin_is_constant_evaluated:
Expand Down Expand Up @@ -820,6 +841,12 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
if (!interp__builtin_ffs(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
if (!interp__builtin_addressof(S, OpPC, Frame, F, Call))
return false;
break;

default:
return false;
Expand Down
24 changes: 24 additions & 0 deletions clang/test/AST/Interp/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,27 @@ namespace Packs {
static_assert(foo<int, char>() == 2, "");
static_assert(foo<>() == 0, "");
}

namespace AddressOf {
struct S {} s;
static_assert(__builtin_addressof(s) == &s, "");

struct T { constexpr T *operator&() const { return nullptr; } int n; } t;
constexpr T *pt = __builtin_addressof(t);
static_assert(&pt->n == &t.n, "");

struct U { int n : 5; } u;
int *pbf = __builtin_addressof(u.n); // expected-error {{address of bit-field requested}} \
// ref-error {{address of bit-field requested}}

S *ptmp = __builtin_addressof(S{}); // expected-error {{taking the address of a temporary}} \
// expected-warning {{temporary whose address is used as value of local variable 'ptmp' will be destroyed at the end of the full-expression}} \
// ref-error {{taking the address of a temporary}} \
// ref-warning {{temporary whose address is used as value of local variable 'ptmp' will be destroyed at the end of the full-expression}}

constexpr int foo() {return 1;}
static_assert(__builtin_addressof(foo) == foo, "");

constexpr _Complex float F = {3, 4};
static_assert(__builtin_addressof(F) == &F, "");
}

0 comments on commit e3993e0

Please sign in to comment.