Skip to content

Commit 3fe2be7

Browse files
committed
[clang][Interp] Call destructors of local variables
Differential Revision: https://reviews.llvm.org/D154581
1 parent 14b039c commit 3fe2be7

File tree

11 files changed

+163
-76
lines changed

11 files changed

+163
-76
lines changed

clang/lib/AST/Interp/Context.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,16 @@ const llvm::fltSemantics &Context::getFloatSemantics(QualType T) const {
160160
bool Context::Run(State &Parent, const Function *Func, APValue &Result) {
161161
InterpState State(Parent, *P, Stk, *this);
162162
State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {});
163-
if (Interpret(State, Result))
163+
if (Interpret(State, Result)) {
164+
assert(Stk.empty());
164165
return true;
166+
}
167+
168+
// We explicitly delete our state here, so the Stk.clear() call
169+
// below doesn't violently free values the destructor would
170+
// otherwise access.
171+
State.~InterpState();
172+
165173
Stk.clear();
166174
return false;
167175
}

clang/lib/AST/Interp/Descriptor.cpp

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,21 @@ static void moveTy(Block *, const std::byte *Src, std::byte *Dst,
4040
template <typename T>
4141
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool,
4242
const Descriptor *D) {
43+
new (Ptr) InitMapPtr(std::nullopt);
44+
45+
Ptr += sizeof(InitMapPtr);
4346
for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
4447
new (&reinterpret_cast<T *>(Ptr)[I]) T();
4548
}
4649
}
4750

4851
template <typename T>
4952
static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
50-
InitMap *IM = *reinterpret_cast<InitMap **>(Ptr);
51-
if (IM != (InitMap *)-1)
52-
free(IM);
53+
InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr);
5354

54-
Ptr += sizeof(InitMap *);
55+
if (IMP)
56+
IMP = std::nullopt;
57+
Ptr += sizeof(InitMapPtr);
5558
for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
5659
reinterpret_cast<T *>(Ptr)[I].~T();
5760
}
@@ -209,7 +212,8 @@ static BlockMoveFn getMovePrim(PrimType Type) {
209212
}
210213

211214
static BlockCtorFn getCtorArrayPrim(PrimType Type) {
212-
COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy<T>, return nullptr);
215+
TYPE_SWITCH(Type, return ctorArrayTy<T>);
216+
llvm_unreachable("unknown Expr");
213217
}
214218

215219
static BlockDtorFn getDtorArrayPrim(PrimType Type) {
@@ -218,7 +222,8 @@ static BlockDtorFn getDtorArrayPrim(PrimType Type) {
218222
}
219223

220224
static BlockMoveFn getMoveArrayPrim(PrimType Type) {
221-
COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr);
225+
TYPE_SWITCH(Type, return moveArrayTy<T>);
226+
llvm_unreachable("unknown Expr");
222227
}
223228

224229
/// Primitives.
@@ -238,7 +243,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
238243
bool IsMutable)
239244
: Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
240245
MDSize(MD.value_or(0)),
241-
AllocSize(align(Size) + sizeof(InitMap *) + MDSize), IsConst(IsConst),
246+
AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst),
242247
IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
243248
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
244249
MoveFn(getMoveArrayPrim(Type)) {
@@ -249,9 +254,10 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
249254
Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
250255
UnknownSize)
251256
: Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
252-
AllocSize(alignof(void *)), IsConst(true), IsMutable(false),
253-
IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)),
254-
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
257+
AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true),
258+
IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
259+
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
260+
MoveFn(getMoveArrayPrim(Type)) {
255261
assert(Source && "Missing source");
256262
}
257263

@@ -272,10 +278,10 @@ Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
272278
Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
273279
UnknownSize)
274280
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
275-
Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)),
276-
ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary),
277-
IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
278-
MoveFn(moveArrayDesc) {
281+
Size(UnknownSizeMark), MDSize(0),
282+
AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem),
283+
IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
284+
CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
279285
assert(Source && "Missing source");
280286
}
281287

@@ -314,21 +320,12 @@ SourceLocation Descriptor::getLocation() const {
314320
llvm_unreachable("Invalid descriptor type");
315321
}
316322

317-
InitMap::InitMap(unsigned N) : UninitFields(N) {
318-
std::fill_n(data(), (N + PER_FIELD - 1) / PER_FIELD, 0);
323+
InitMap::InitMap(unsigned N)
324+
: UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
325+
std::fill_n(data(), numFields(N), 0);
319326
}
320327

321-
InitMap::T *InitMap::data() {
322-
auto *Start = reinterpret_cast<char *>(this) + align(sizeof(InitMap));
323-
return reinterpret_cast<T *>(Start);
324-
}
325-
326-
const InitMap::T *InitMap::data() const {
327-
auto *Start = reinterpret_cast<const char *>(this) + align(sizeof(InitMap));
328-
return reinterpret_cast<const T *>(Start);
329-
}
330-
331-
bool InitMap::initialize(unsigned I) {
328+
bool InitMap::initializeElement(unsigned I) {
332329
unsigned Bucket = I / PER_FIELD;
333330
T Mask = T(1) << (I % PER_FIELD);
334331
if (!(data()[Bucket] & Mask)) {
@@ -338,13 +335,7 @@ bool InitMap::initialize(unsigned I) {
338335
return UninitFields == 0;
339336
}
340337

341-
bool InitMap::isInitialized(unsigned I) const {
338+
bool InitMap::isElementInitialized(unsigned I) const {
342339
unsigned Bucket = I / PER_FIELD;
343340
return data()[Bucket] & (T(1) << (I % PER_FIELD));
344341
}
345-
346-
InitMap *InitMap::allocate(unsigned N) {
347-
const size_t NumFields = ((N + PER_FIELD - 1) / PER_FIELD);
348-
const size_t Size = align(sizeof(InitMap)) + NumFields * PER_FIELD;
349-
return new (malloc(Size)) InitMap(N);
350-
}

clang/lib/AST/Interp/Descriptor.h

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ namespace clang {
2020
namespace interp {
2121
class Block;
2222
class Record;
23+
struct InitMap;
2324
struct Descriptor;
2425
enum PrimType : unsigned;
2526

2627
using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
28+
using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;
2729

2830
/// Invoked whenever a block is created. The constructor method fills in the
2931
/// inline descriptors of all fields and array elements. It also initializes
@@ -193,36 +195,36 @@ struct Descriptor final {
193195
};
194196

195197
/// Bitfield tracking the initialisation status of elements of primitive arrays.
196-
/// A pointer to this is embedded at the end of all primitive arrays.
197-
/// If the map was not yet created and nothing was initialized, the pointer to
198-
/// this structure is 0. If the object was fully initialized, the pointer is -1.
199198
struct InitMap final {
200199
private:
201200
/// Type packing bits.
202201
using T = uint64_t;
203202
/// Bits stored in a single field.
204203
static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;
205204

205+
public:
206206
/// Initializes the map with no fields set.
207-
InitMap(unsigned N);
207+
explicit InitMap(unsigned N);
208+
209+
private:
210+
friend class Pointer;
208211

209212
/// Returns a pointer to storage.
210-
T *data();
211-
const T *data() const;
213+
T *data() { return Data.get(); }
214+
const T *data() const { return Data.get(); }
212215

213-
public:
214216
/// Initializes an element. Returns true when object if fully initialized.
215-
bool initialize(unsigned I);
217+
bool initializeElement(unsigned I);
216218

217219
/// Checks if an element was initialized.
218-
bool isInitialized(unsigned I) const;
220+
bool isElementInitialized(unsigned I) const;
219221

220-
/// Allocates a map holding N elements.
221-
static InitMap *allocate(unsigned N);
222-
223-
private:
224-
/// Number of fields initialized.
222+
static constexpr size_t numFields(unsigned N) {
223+
return (N + PER_FIELD - 1) / PER_FIELD;
224+
}
225+
/// Number of fields not initialized.
225226
unsigned UninitFields;
227+
std::unique_ptr<T[]> Data;
226228
};
227229

228230
} // namespace interp

clang/lib/AST/Interp/EvalEmitter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
2525
new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
2626
}
2727

28+
EvalEmitter::~EvalEmitter() {
29+
for (auto &[K, V] : Locals) {
30+
Block *B = reinterpret_cast<Block *>(V.get());
31+
if (B->isInitialized())
32+
B->invokeDtor();
33+
}
34+
}
35+
2836
llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
2937
if (this->visitExpr(E))
3038
return true;

clang/lib/AST/Interp/EvalEmitter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class EvalEmitter : public SourceMapper {
4040
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
4141
APValue &Result);
4242

43-
virtual ~EvalEmitter() {}
43+
virtual ~EvalEmitter();
4444

4545
/// Define a label.
4646
void emitLabel(LabelTy Label);

clang/lib/AST/Interp/InterpBlock.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Block final {
7171
unsigned getSize() const { return Desc->getAllocSize(); }
7272
/// Returns the declaration ID.
7373
std::optional<unsigned> getDeclID() const { return DeclID; }
74+
bool isInitialized() const { return IsInitialized; }
7475

7576
/// Returns a pointer to the stored data.
7677
/// You are allowed to read Desc->getSize() bytes from this address.
@@ -104,12 +105,14 @@ class Block final {
104105
if (Desc->CtorFn)
105106
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
106107
/*isActive=*/true, Desc);
108+
IsInitialized = true;
107109
}
108110

109111
/// Invokes the Destructor.
110112
void invokeDtor() {
111113
if (Desc->DtorFn)
112114
Desc->DtorFn(this, data(), Desc);
115+
IsInitialized = false;
113116
}
114117

115118
protected:
@@ -139,8 +142,12 @@ class Block final {
139142
bool IsStatic = false;
140143
/// Flag indicating if the block is an extern.
141144
bool IsExtern = false;
142-
/// Flag indicating if the pointer is dead.
145+
/// Flag indicating if the pointer is dead. This is only ever
146+
/// set once, when converting the Block to a DeadBlock.
143147
bool IsDead = false;
148+
/// Flag indicating if the block contents have been initialized
149+
/// via invokeCtor.
150+
bool IsInitialized = false;
144151
/// Pointer to the stack slot descriptor.
145152
Descriptor *Desc;
146153
};

clang/lib/AST/Interp/InterpFrame.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC)
7272
InterpFrame::~InterpFrame() {
7373
for (auto &Param : Params)
7474
S.deallocate(reinterpret_cast<Block *>(Param.second.get()));
75+
76+
// When destroying the InterpFrame, call the Dtor for all block
77+
// that haven't been destroyed via a destroy() op yet.
78+
// This happens when the execution is interruped midway-through.
79+
if (Func) {
80+
for (auto &Scope : Func->scopes()) {
81+
for (auto &Local : Scope.locals()) {
82+
Block *B = localBlock(Local.Offset);
83+
if (B->isInitialized())
84+
B->invokeDtor();
85+
}
86+
}
87+
}
7588
}
7689

7790
void InterpFrame::destroy(unsigned Idx) {

clang/lib/AST/Interp/InterpState.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ void InterpState::deallocate(Block *B) {
6565
std::memcpy(D->rawData(), B->rawData(), Desc->getMetadataSize());
6666
}
6767

68+
// We moved the contents over to the DeadBlock.
69+
B->IsInitialized = false;
6870
} else {
69-
// Free storage, if necessary.
70-
if (Desc->DtorFn)
71-
Desc->DtorFn(B, B->data(), Desc);
71+
B->invokeDtor();
7272
}
7373
}

clang/lib/AST/Interp/Pointer.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,16 @@ bool Pointer::isInitialized() const {
164164
if (Desc->isPrimitiveArray()) {
165165
if (isStatic() && Base == 0)
166166
return true;
167-
// Primitive array field are stored in a bitset.
168-
InitMap *Map = getInitMap();
169-
if (!Map)
167+
168+
InitMapPtr &IM = getInitMap();
169+
170+
if (!IM)
170171
return false;
171-
if (Map == (InitMap *)-1)
172+
173+
if (IM->first)
172174
return true;
173-
return Map->isInitialized(getIndex());
175+
176+
return IM->second->isElementInitialized(getIndex());
174177
}
175178

176179
// Field has its bit in an inline descriptor.
@@ -187,15 +190,20 @@ void Pointer::initialize() const {
187190
if (isStatic() && Base == 0)
188191
return;
189192

190-
// Primitive array initializer.
191-
InitMap *&Map = getInitMap();
192-
if (Map == (InitMap *)-1)
193+
InitMapPtr &IM = getInitMap();
194+
if (!IM)
195+
IM =
196+
std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));
197+
198+
assert(IM);
199+
200+
// All initialized.
201+
if (IM->first)
193202
return;
194-
if (Map == nullptr)
195-
Map = InitMap::allocate(Desc->getNumElems());
196-
if (Map->initialize(getIndex())) {
197-
free(Map);
198-
Map = (InitMap *)-1;
203+
204+
if (IM->second->initializeElement(getIndex())) {
205+
IM->first = true;
206+
IM->second.reset();
199207
}
200208
return;
201209
}

0 commit comments

Comments
 (0)