Skip to content

Commit

Permalink
[clang][Interp] Start implementing unions and changing the active mem…
Browse files Browse the repository at this point in the history
…ber (llvm#102723)
  • Loading branch information
tbaederr authored and bwendling committed Aug 15, 2024
1 parent b8fe02b commit 666932a
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 34 deletions.
16 changes: 10 additions & 6 deletions clang/lib/AST/Interp/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4739,7 +4739,8 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
// Classify the return type.
ReturnType = this->classify(F->getReturnType());

auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
auto emitFieldInitializer = [&](const Record *R, const Record::Field *F,
unsigned FieldOffset,
const Expr *InitExpr) -> bool {
// We don't know what to do with these, so just return false.
if (InitExpr->getType().isNull())
Expand All @@ -4751,6 +4752,8 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {

if (F->isBitField())
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
if (R->isUnion())
return this->emitInitThisFieldActive(*T, FieldOffset, InitExpr);
return this->emitInitThisField(*T, FieldOffset, InitExpr);
}
// Non-primitive case. Get a pointer to the field-to-initialize
Expand All @@ -4762,7 +4765,7 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
if (!this->visitInitializer(InitExpr))
return false;

return this->emitPopPtr(InitExpr);
return this->emitFinishInitPop(InitExpr);
};

// Emit custom code if this is a lambda static invoker.
Expand All @@ -4786,7 +4789,7 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
if (const FieldDecl *Member = Init->getMember()) {
const Record::Field *F = R->getField(Member);

if (!emitFieldInitializer(F, F->Offset, InitExpr))
if (!emitFieldInitializer(R, F, F->Offset, InitExpr))
return false;
} else if (const Type *Base = Init->getBaseClass()) {
const auto *BaseDecl = Base->getAsCXXRecordDecl();
Expand Down Expand Up @@ -4814,11 +4817,11 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
assert(IFD->getChainingSize() >= 2);

unsigned NestedFieldOffset = 0;
const Record *FieldRecord = nullptr;
const Record::Field *NestedField = nullptr;
for (const NamedDecl *ND : IFD->chain()) {
const auto *FD = cast<FieldDecl>(ND);
const Record *FieldRecord =
this->P.getOrCreateRecord(FD->getParent());
FieldRecord = this->P.getOrCreateRecord(FD->getParent());
assert(FieldRecord);

NestedField = FieldRecord->getField(FD);
Expand All @@ -4828,7 +4831,8 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
}
assert(NestedField);

if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
if (!emitFieldInitializer(FieldRecord, NestedField, NestedFieldOffset,
InitExpr))
return false;
} else {
assert(Init->isDelegatingInitializer());
Expand Down
49 changes: 30 additions & 19 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace clang;
using namespace clang::interp;

template <typename T>
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool,
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
const Descriptor *) {
new (Ptr) T();
}
Expand All @@ -40,7 +40,7 @@ static void moveTy(Block *, const std::byte *Src, std::byte *Dst,
}

template <typename T>
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool,
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
const Descriptor *D) {
new (Ptr) InitMapPtr(std::nullopt);

Expand Down Expand Up @@ -83,7 +83,8 @@ static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst,
}

static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
bool IsMutable, bool IsActive, const Descriptor *D) {
bool IsMutable, bool IsActive, bool InUnion,
const Descriptor *D) {
const unsigned NumElems = D->getNumElems();
const unsigned ElemSize =
D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
Expand All @@ -102,9 +103,11 @@ static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
Desc->IsActive = IsActive;
Desc->IsConst = IsConst || D->IsConst;
Desc->IsFieldMutable = IsMutable || D->IsMutable;
Desc->InUnion = InUnion;

if (auto Fn = D->ElemDesc->CtorFn)
Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
D->ElemDesc);
Desc->InUnion || SD->isUnion(), D->ElemDesc);
}
}

Expand Down Expand Up @@ -146,25 +149,26 @@ static void moveArrayDesc(Block *B, const std::byte *Src, std::byte *Dst,
}

static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
bool IsActive, bool IsUnion, const Descriptor *D,
unsigned FieldOffset) {
bool IsActive, bool IsUnionField, bool InUnion,
const Descriptor *D, unsigned FieldOffset) {
auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
Desc->Offset = FieldOffset;
Desc->Desc = D;
Desc->IsInitialized = D->IsArray;
Desc->IsBase = false;
Desc->IsActive = IsActive && !IsUnion;
Desc->IsActive = IsActive && !IsUnionField;
Desc->InUnion = InUnion;
Desc->IsConst = IsConst || D->IsConst;
Desc->IsFieldMutable = IsMutable || D->IsMutable;

if (auto Fn = D->CtorFn)
Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
Desc->IsActive, D);
Desc->IsActive, InUnion || D->isUnion(), D);
}

static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
bool IsActive, const Descriptor *D, unsigned FieldOffset,
bool IsVirtualBase) {
bool IsActive, bool InUnion, const Descriptor *D,
unsigned FieldOffset, bool IsVirtualBase) {
assert(D);
assert(D->ElemRecord);

Expand All @@ -180,21 +184,26 @@ static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
Desc->IsFieldMutable = IsMutable || D->IsMutable;

for (const auto &V : D->ElemRecord->bases())
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, V.Desc,
V.Offset, false);
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
V.Desc, V.Offset, false);
for (const auto &F : D->ElemRecord->fields())
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, IsUnion,
F.Desc, F.Offset);
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
IsUnion, F.Desc, F.Offset);
}

static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
bool IsActive, const Descriptor *D) {
bool IsActive, bool InUnion, const Descriptor *D) {
for (const auto &V : D->ElemRecord->bases())
initBase(B, Ptr, IsConst, IsMutable, IsActive, V.Desc, V.Offset, false);
for (const auto &F : D->ElemRecord->fields())
initField(B, Ptr, IsConst, IsMutable, IsActive, D->ElemRecord->isUnion(), F.Desc, F.Offset);
initBase(B, Ptr, IsConst, IsMutable, IsActive, false, V.Desc, V.Offset,
false);
for (const auto &F : D->ElemRecord->fields()) {
bool IsUnionField = D->isUnion();
initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField,
InUnion || IsUnionField, F.Desc, F.Offset);
}
for (const auto &V : D->ElemRecord->virtual_bases())
initBase(B, Ptr, IsConst, IsMutable, IsActive, V.Desc, V.Offset, true);
initBase(B, Ptr, IsConst, IsMutable, IsActive, false, V.Desc, V.Offset,
true);
}

static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D,
Expand Down Expand Up @@ -403,6 +412,8 @@ SourceLocation Descriptor::getLocation() const {
llvm_unreachable("Invalid descriptor type");
}

bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); }

InitMap::InitMap(unsigned N)
: UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
std::fill_n(data(), numFields(N), 0);
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;
/// inline descriptors of all fields and array elements. It also initializes
/// all the fields which contain non-trivial types.
using BlockCtorFn = void (*)(Block *Storage, std::byte *FieldPtr, bool IsConst,
bool IsMutable, bool IsActive,
bool IsMutable, bool IsActive, bool InUnion,
const Descriptor *FieldDesc);

/// Invoked when a block is destroyed. Invokes the destructors of all
Expand Down Expand Up @@ -83,11 +83,15 @@ struct InlineDescriptor {
/// Flag indicating if the field is an embedded base class.
LLVM_PREFERRED_TYPE(bool)
unsigned IsBase : 1;
/// Flag inidcating if the field is a virtual base class.
LLVM_PREFERRED_TYPE(bool)
unsigned IsVirtualBase : 1;
/// Flag indicating if the field is the active member of a union.
LLVM_PREFERRED_TYPE(bool)
unsigned IsActive : 1;
/// Flat indicating if this field is in a union (even if nested).
unsigned InUnion : 1;
LLVM_PREFERRED_TYPE(bool)
/// Flag indicating if the field is mutable (if in a record).
LLVM_PREFERRED_TYPE(bool)
unsigned IsFieldMutable : 1;
Expand Down Expand Up @@ -250,6 +254,8 @@ struct Descriptor final {
bool isArray() const { return IsArray; }
/// Checks if the descriptor is of a record.
bool isRecord() const { return !IsArray && ElemRecord; }
/// Checks if the descriptor is of a union.
bool isUnion() const;
/// Checks if this is a dummy descriptor.
bool isDummy() const { return IsDummy; }

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
OS << " primitive-array";
else if (isCompositeArray())
OS << " composite-array";
else if (isUnion())
OS << " union";
else if (isRecord())
OS << " record";
else if (isPrimitive())
Expand All @@ -250,6 +252,7 @@ LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
OS << "IsInitialized: " << IsInitialized << "\n";
OS << "IsBase: " << IsBase << "\n";
OS << "IsActive: " << IsActive << "\n";
OS << "InUnion: " << InUnion << "\n";
OS << "IsFieldMutable: " << IsFieldMutable << "\n";
OS << "Desc: ";
if (Desc)
Expand Down
21 changes: 18 additions & 3 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,31 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

// Get the inactive field descriptor.
const FieldDecl *InactiveField = Ptr.getField();
assert(InactiveField);

// Walk up the pointer chain to find the union which is not active.
// Walk up the pointer chain to find the closest union.
Pointer U = Ptr.getBase();
while (!U.isActive()) {
while (!U.getFieldDesc()->isUnion())
U = U.getBase();
}

// Find the active field of the union.
const Record *R = U.getRecord();
assert(R && R->isUnion() && "Not a union");

// Consider:
// union U {
// struct {
// int x;
// int y;
// } a;
// }
//
// When activating x, we will also activate a. If we now try to read
// from y, we will get to CheckActive, because y is not active. In that
// case we return here and let later code handle this.
if (!llvm::is_contained(R->getDecl()->fields(), InactiveField))
return true;

const FieldDecl *ActiveField = nullptr;
for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
const Pointer &Field = U.atField(R->getField(I)->Offset);
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1719,8 +1719,10 @@ bool Store(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (Ptr.canBeInitialized())
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
}
Ptr.deref<T>() = Value;
return true;
}
Expand All @@ -1731,8 +1733,10 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (Ptr.canBeInitialized())
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
}
Ptr.deref<T>() = Value;
return true;
}
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/Interp/InterpBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,10 @@ class Block final {
void invokeCtor() {
assert(!IsInitialized);
std::memset(rawData(), 0, Desc->getAllocSize());
if (Desc->CtorFn)
if (Desc->CtorFn) {
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
/*isActive=*/true, Desc);
/*isActive=*/true, /*InUnion=*/false, Desc);
}
IsInitialized = true;
}

Expand Down
27 changes: 26 additions & 1 deletion clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,37 @@ void Pointer::initialize() const {
void Pointer::activate() const {
// Field has its bit in an inline descriptor.
assert(PointeeStorage.BS.Base != 0 &&
"Only composite fields can be initialised");
"Only composite fields can be activated");

if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))
return;
if (!getInlineDesc()->InUnion)
return;

getInlineDesc()->IsActive = true;

// Get the union, iterate over its fields and DEactivate all others.
Pointer UnionPtr = getBase();
while (!UnionPtr.getFieldDesc()->isUnion())
UnionPtr = UnionPtr.getBase();

const Record *UnionRecord = UnionPtr.getRecord();
for (const Record::Field &F : UnionRecord->fields()) {
Pointer FieldPtr = UnionPtr.atField(F.Offset);
if (FieldPtr == *this) {
} else {
FieldPtr.getInlineDesc()->IsActive = false;
// FIXME: Recurse.
}
}

Pointer B = getBase();
while (!B.getFieldDesc()->isUnion()) {
// FIXME: Need to de-activate other fields of parent records.
B.getInlineDesc()->IsActive = true;
assert(B.isActive());
B = B.getBase();
}
}

void Pointer::deactivate() const {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,12 @@ class Pointer {
return getFieldDesc()->IsArray;
return false;
}
bool inUnion() const {
if (isBlockPointer())
return getInlineDesc()->InUnion;
return false;
};

/// Checks if the structure is a primitive array.
bool inPrimitiveArray() const {
if (isBlockPointer())
Expand Down
Loading

0 comments on commit 666932a

Please sign in to comment.