Skip to content

Commit

Permalink
[clang][Interp] Handle global composite temporaries
Browse files Browse the repository at this point in the history
We only did this for primitive temporaries.

Unfortunately, the existing Pointer::toAPValue() won't do here, since
we're expected to set an rvalue on the LifetimeExtendedTemporaryDecl.

Differential Revision: https://reviews.llvm.org/D144457
  • Loading branch information
tbaederr committed Aug 20, 2023
1 parent ebbedc4 commit 8a58f0d
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 6 deletions.
19 changes: 14 additions & 5 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -891,19 +891,28 @@ bool ByteCodeExprGen<Emitter>::VisitMaterializeTemporaryExpr(
return this->discard(SubExpr);

if (E->getStorageDuration() == SD_Static) {
if (std::optional<unsigned> GlobalIndex = P.createGlobal(E)) {
const LifetimeExtendedTemporaryDecl *TempDecl =
E->getLifetimeExtendedTemporaryDecl();
std::optional<unsigned> GlobalIndex = P.createGlobal(E);
if (!GlobalIndex)
return false;

const LifetimeExtendedTemporaryDecl *TempDecl =
E->getLifetimeExtendedTemporaryDecl();
assert(TempDecl);

if (SubExprT) {
if (!this->visitInitializer(SubExpr))
return false;

if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E))
return false;
return this->emitGetPtrGlobal(*GlobalIndex, E);
}

return false;
// Non-primitive values.
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
return false;
if (!this->visitInitializer(SubExpr))
return false;
return this->emitInitGlobalTempComp(TempDecl, E);
}

// For everyhing else, use local variables.
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ struct Descriptor final {

/// Checks if the descriptor is of an array.
bool isArray() const { return IsArray; }
/// Checks if the descriptor is of a record.
bool isRecord() const { return !IsArray && ElemRecord; }
};

/// Bitfield tracking the initialisation status of elements of primitive arrays.
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,19 @@ bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
return true;
}

/// 1) Converts the value on top of the stack to an APValue
/// 2) Sets that APValue on \Temp
/// 3) Initialized global with index \I with that
inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC,
const LifetimeExtendedTemporaryDecl *Temp) {
assert(Temp);
const Pointer &P = S.Stk.peek<Pointer>();
APValue *Cached = Temp->getOrCreateValue(true);

*Cached = P.toRValue(S.getCtx());
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
if (S.checkingPotentialConstantExpression())
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ def InitGlobal : AccessOpcode;
def InitGlobalTemp : AccessOpcode {
let Args = [ArgUint32, ArgLETD];
}
// [Pointer] -> [Pointer]
def InitGlobalTempComp : Opcode {
let Args = [ArgLETD];
let Types = [];
let HasGroup = 0;
}
// [Value] -> []
def SetGlobal : AccessOpcode;

Expand Down
36 changes: 36 additions & 0 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@
//===----------------------------------------------------------------------===//

#include "Pointer.h"
#include "Boolean.h"
#include "Context.h"
#include "Floating.h"
#include "Function.h"
#include "Integral.h"
#include "InterpBlock.h"
#include "PrimType.h"
#include "Record.h"

using namespace clang;
using namespace clang::interp;
Expand Down Expand Up @@ -217,3 +222,34 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
}

APValue Pointer::toRValue(const Context &Ctx) const {
// Primitives.
if (getFieldDesc()->isPrimitive()) {
PrimType PT = *Ctx.classify(getType());
TYPE_SWITCH(PT, return deref<T>().toAPValue());
llvm_unreachable("Unhandled PrimType?");
}

APValue Result;
// Records.
if (getFieldDesc()->isRecord()) {
const Record *R = getRecord();
Result =
APValue(APValue::UninitStruct(), R->getNumBases(), R->getNumFields());

for (unsigned I = 0; I != R->getNumFields(); ++I) {
const Pointer &FieldPtr = this->atField(R->getField(I)->Offset);
Result.getStructField(I) = FieldPtr.toRValue(Ctx);
}

for (unsigned I = 0; I != R->getNumBases(); ++I) {
const Pointer &BasePtr = this->atField(R->getBase(I)->Offset);
Result.getStructBase(I) = BasePtr.toRValue(Ctx);
}
}

// TODO: Arrays

return Result;
}
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace interp {
class Block;
class DeadBlock;
class Pointer;
class Context;
enum PrimType : unsigned;

class Pointer;
Expand Down Expand Up @@ -87,6 +88,9 @@ class Pointer {
return reinterpret_cast<uintptr_t>(Pointee) + Offset;
}

/// Converts the pointer to an APValue that is an rvalue.
APValue toRValue(const Context &Ctx) const;

/// Offsets a pointer inside an array.
Pointer atIndex(unsigned Idx) const {
if (Base == RootPtrMark)
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/AST/Interp/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ class Record final {
}

unsigned getNumBases() const { return Bases.size(); }
const Base *getBase(unsigned I) const { return &Bases[I]; }
const Base *getBase(unsigned I) const {
assert(I < getNumBases());
return &Bases[I];
}

using const_virtual_iter = VirtualBaseList::const_iterator;
llvm::iterator_range<const_virtual_iter> virtual_bases() const {
Expand Down
15 changes: 15 additions & 0 deletions clang/test/AST/Interp/records.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ constexpr C c2 = C().get();
static_assert(c2.a == 100, "");
static_assert(c2.b == 200, "");


/// A global, composite temporary variable.
constexpr const C &c3 = C().get();

/// Same, but with a bitfield.
class D {
public:
unsigned a : 4;
constexpr D() : a(15) {}
constexpr D get() const {
return *this;
}
};
constexpr const D &d4 = D().get();

constexpr int getB() {
C c;
int &j = c.b;
Expand Down

0 comments on commit 8a58f0d

Please sign in to comment.