Skip to content

Commit

Permalink
[clang][Interp] Add inline descriptor to global variables (#72892)
Browse files Browse the repository at this point in the history
Some time ago, I did a similar patch for local variables.

Initializing global variables can fail as well:
```c++
constexpr int a = 1/0;
static_assert(a == 0);
```
... would succeed in the new interpreter, because we never saved the
fact that `a` has not been successfully initialized.
  • Loading branch information
tbaederr committed Jan 31, 2024
1 parent f2816ff commit 5bb99ed
Show file tree
Hide file tree
Showing 16 changed files with 200 additions and 82 deletions.
17 changes: 15 additions & 2 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ bool ByteCodeExprGen<Emitter>::visitArrayElemInit(unsigned ElemIndex,
return false;
if (!this->visitInitializer(Init))
return false;
return this->emitPopPtr(Init);
return this->emitInitPtrPop(Init);
}

template <class Emitter>
Expand Down Expand Up @@ -854,13 +854,26 @@ bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) {
return this->visitInitList(E->inits(), E);

if (T->isArrayType()) {
// FIXME: Array fillers.
unsigned ElementIndex = 0;
for (const Expr *Init : E->inits()) {
if (!this->visitArrayElemInit(ElementIndex, Init))
return false;
++ElementIndex;
}

// Expand the filler expression.
// FIXME: This should go away.
if (const Expr *Filler = E->getArrayFiller()) {
const ConstantArrayType *CAT =
Ctx.getASTContext().getAsConstantArrayType(E->getType());
uint64_t NumElems = CAT->getSize().getZExtValue();

for (; ElementIndex != NumElems; ++ElementIndex) {
if (!this->visitArrayElemInit(ElementIndex, Filler))
return false;
}
}

return true;
}

Expand Down
8 changes: 7 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
if (!visitInitializer(Init))
return false;

if (!this->emitInitPtr(Init))
return false;

return this->emitPopPtr(Init);
}

Expand All @@ -191,6 +194,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
if (!visitInitializer(Init))
return false;

if (!this->emitInitPtr(Init))
return false;

return this->emitPopPtr(Init);
}

Expand All @@ -202,7 +208,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
if (!visitInitializer(I))
return false;

return this->emitPopPtr(I);
return this->emitInitPtrPop(I);
}

bool visitInitList(ArrayRef<const Expr *> Inits, const Expr *E);
Expand Down
25 changes: 13 additions & 12 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,18 +243,19 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
bool IsMutable)
: Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
MDSize(MD.value_or(0)),
AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst),
IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
MoveFn(getMoveArrayPrim(Type)) {
AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)),
IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
IsArray(true), CtorFn(getCtorArrayPrim(Type)),
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
assert(Source && "Missing source");
}

/// Primitive unknown-size arrays.
Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
UnknownSize)
: Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true),
Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
bool IsTemporary, UnknownSize)
: Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
MDSize(MD.value_or(0)),
AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true),
IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
MoveFn(getMoveArrayPrim(Type)) {
Expand All @@ -275,12 +276,12 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
}

/// Unknown-size arrays of composite elements.
Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem,
Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
bool IsTemporary, UnknownSize)
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
Size(UnknownSizeMark), MDSize(0),
AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem),
IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
Size(UnknownSizeMark), MDSize(MD.value_or(0)),
AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true),
IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
assert(Source && "Missing source");
}
Expand Down
11 changes: 8 additions & 3 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ struct InlineDescriptor {
unsigned IsFieldMutable : 1;

const Descriptor *Desc;

InlineDescriptor(const Descriptor *D)
: Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false),
IsBase(false), IsActive(false), IsFieldMutable(false), Desc(D) {}
};

/// Describes a memory block created by an allocation site.
Expand Down Expand Up @@ -128,15 +132,16 @@ struct Descriptor final {
bool IsConst, bool IsTemporary, bool IsMutable);

/// Allocates a descriptor for an array of primitives of unknown size.
Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize,
bool IsTemporary, UnknownSize);

/// Allocates a descriptor for an array of composites.
Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);

/// Allocates a descriptor for an array of composites of unknown size.
Descriptor(const DeclTy &D, const Descriptor *Elem, bool IsTemporary,
UnknownSize);
Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
bool IsTemporary, UnknownSize);

/// Allocates a descriptor for a record.
Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst,
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,23 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}

bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (Ptr.isInitialized())
return true;

assert(S.getLangOpts().CPlusPlus);
const auto *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl());
if ((!VD->hasConstantInitialization() &&
VD->mightBeUsableInConstantExpressions(S.getCtx())) ||
(S.getLangOpts().OpenCL && !S.getLangOpts().CPlusPlus11 &&
!VD->hasICEInitializer(S.getCtx()))) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
S.Note(VD->getLocation(), diag::note_declared_at);
}
return false;
}

bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_Read))
return false;
Expand Down
40 changes: 29 additions & 11 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);
/// Check if a global variable is initialized.
bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

/// Checks if a value can be stored in a block.
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
Expand Down Expand Up @@ -1003,13 +1005,18 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
const Block *B = S.P.getGlobal(I);

if (!CheckConstant(S, OpPC, B->getDescriptor()))
const Pointer &Ptr = S.P.getPtrGlobal(I);
if (!CheckConstant(S, OpPC, Ptr.getFieldDesc()))
return false;
if (B->isExtern())
if (Ptr.isExtern())
return false;
S.Stk.push<T>(B->deref<T>());

// If a global variable is uninitialized, that means the initializer we've
// compiled for it wasn't a constant expression. Diagnose that.
if (!CheckGlobalInitialized(S, OpPC, Ptr))
return false;

S.Stk.push<T>(Ptr.deref<T>());
return true;
}

Expand All @@ -1029,7 +1036,9 @@ bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
const Pointer &P = S.P.getGlobal(I);
P.deref<T>() = S.Stk.pop<T>();
P.initialize();
return true;
}

Expand All @@ -1045,7 +1054,10 @@ bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
APValue *Cached = Temp->getOrCreateValue(true);
*Cached = APV;

S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
const Pointer &P = S.P.getGlobal(I);
P.deref<T>() = S.Stk.pop<T>();
P.initialize();

return true;
}

Expand Down Expand Up @@ -1267,6 +1279,12 @@ inline bool InitPtrPop(InterpState &S, CodePtr OpPC) {
return true;
}

inline bool InitPtr(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
Ptr.initialize();
return true;
}

inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
const Pointer &Ptr) {
Pointer Base = Ptr;
Expand Down Expand Up @@ -1323,7 +1341,7 @@ bool Store(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
Expand All @@ -1335,7 +1353,7 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
Expand All @@ -1347,7 +1365,7 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
Expand All @@ -1362,7 +1380,7 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
Expand Down
11 changes: 2 additions & 9 deletions clang/lib/AST/Interp/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,7 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
for (auto &Local : Scope.locals()) {
Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
B->invokeCtor();
InlineDescriptor *ID = localInlineDesc(Local.Offset);
ID->Desc = Local.Desc;
ID->IsActive = true;
ID->Offset = sizeof(InlineDescriptor);
ID->IsBase = false;
ID->IsFieldMutable = false;
ID->IsConst = false;
ID->IsInitialized = false;
new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
}
}
}
Expand Down Expand Up @@ -201,7 +194,7 @@ const FunctionDecl *InterpFrame::getCallee() const {

Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
assert(Offset < Func->getFrameSize() && "Invalid local offset.");
return Pointer(localBlock(Offset), sizeof(InlineDescriptor));
return Pointer(localBlock(Offset));
}

Pointer InterpFrame::getParamPointer(unsigned Off) {
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,8 @@ def GetPtrBasePop : Opcode {
let Args = [ArgUint32];
}

def InitPtrPop : Opcode {
let Args = [];
}
def InitPtrPop : Opcode;
def InitPtr : Opcode;

def GetPtrDerivedPop : Opcode {
let Args = [ArgUint32];
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
using namespace clang;
using namespace clang::interp;

Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
Pointer::Pointer(Block *Pointee)
: Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
Pointee->getDescriptor()->getMetadataSize()) {}

Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
: Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
Expand Down

0 comments on commit 5bb99ed

Please sign in to comment.