Skip to content

Commit 211b51e

Browse files
authored
[clang][bytecode] Propagate IsVolatile bit to subobjects (llvm#137293)
For ```c++ struct S { constexpr S(int=0) : i(1) {} int i; }; constexpr volatile S vs; ``` reading from `vs.i` is not allowed, even though `i` is not volatile qualified. Propagate the IsVolatile bit down the hierarchy, so we know reading from `vs.i` is a volatile read.
1 parent 5a64510 commit 211b51e

File tree

11 files changed

+98
-66
lines changed

11 files changed

+98
-66
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
364364
Desc = P.createDescriptor(SubExpr, *T);
365365
else
366366
Desc = P.createDescriptor(SubExpr, PointeeType.getTypePtr(),
367-
std::nullopt, true, false,
368-
/*IsMutable=*/false, nullptr);
367+
std::nullopt, /*IsConst=*/true);
369368
}
370369

371370
uint64_t Val = Ctx.getASTContext().getTargetNullPointerValue(CE->getType());
@@ -417,8 +416,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
417416
Desc = nullptr;
418417
else
419418
Desc = P.createDescriptor(CE, PtrType->getPointeeType().getTypePtr(),
420-
Descriptor::InlineDescMD, true, false,
421-
/*IsMutable=*/false, nullptr);
419+
Descriptor::InlineDescMD, /*IsConst=*/true);
422420

423421
if (!this->emitGetIntPtr(T, Desc, CE))
424422
return false;
@@ -3400,14 +3398,13 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
34003398
Desc = nullptr; // We're not going to use it in this case.
34013399
else
34023400
Desc = P.createDescriptor(E, *ElemT, /*SourceTy=*/nullptr,
3403-
Descriptor::InlineDescMD,
3404-
/*IsConst=*/false, /*IsTemporary=*/false,
3405-
/*IsMutable=*/false);
3401+
Descriptor::InlineDescMD);
34063402
} else {
34073403
Desc = P.createDescriptor(
34083404
E, ElementType.getTypePtr(),
34093405
E->isArray() ? std::nullopt : Descriptor::InlineDescMD,
3410-
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, Init);
3406+
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false,
3407+
/*IsVolatile=*/false, Init);
34113408
}
34123409
}
34133410

@@ -4355,7 +4352,7 @@ Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
43554352

43564353
Descriptor *D = P.createDescriptor(
43574354
Src, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(),
4358-
IsTemporary, /*IsMutable=*/false, Init);
4355+
IsTemporary, /*IsMutable=*/false, /*IsVolatile=*/false, Init);
43594356
if (!D)
43604357
return std::nullopt;
43614358
D->IsConstexprUnknown = IsConstexprUnknown;
@@ -4377,7 +4374,7 @@ std::optional<unsigned> Compiler<Emitter>::allocateTemporary(const Expr *E) {
43774374

43784375
Descriptor *D = P.createDescriptor(
43794376
E, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(),
4380-
/*IsTemporary=*/true, /*IsMutable=*/false, /*Init=*/nullptr);
4377+
/*IsTemporary=*/true);
43814378

43824379
if (!D)
43834380
return std::nullopt;

clang/lib/AST/ByteCode/Descriptor.cpp

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ using namespace clang;
2222
using namespace clang::interp;
2323

2424
template <typename T>
25-
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
25+
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool,
2626
const Descriptor *) {
2727
new (Ptr) T();
2828
}
@@ -41,7 +41,7 @@ static void moveTy(Block *, std::byte *Src, std::byte *Dst,
4141
}
4242

4343
template <typename T>
44-
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
44+
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool,
4545
const Descriptor *D) {
4646
new (Ptr) InitMapPtr(std::nullopt);
4747

@@ -82,8 +82,8 @@ static void moveArrayTy(Block *, std::byte *Src, std::byte *Dst,
8282
}
8383

8484
static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
85-
bool IsMutable, bool IsActive, bool InUnion,
86-
const Descriptor *D) {
85+
bool IsMutable, bool IsVolatile, bool IsActive,
86+
bool InUnion, const Descriptor *D) {
8787
const unsigned NumElems = D->getNumElems();
8888
const unsigned ElemSize =
8989
D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
@@ -104,9 +104,10 @@ static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
104104
Desc->IsFieldMutable = IsMutable || D->IsMutable;
105105
Desc->InUnion = InUnion;
106106
Desc->IsArrayElement = true;
107+
Desc->IsVolatile = IsVolatile;
107108

108109
if (auto Fn = D->ElemDesc->CtorFn)
109-
Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
110+
Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsVolatile, IsActive,
110111
Desc->InUnion || SD->isUnion(), D->ElemDesc);
111112
}
112113
}
@@ -149,8 +150,8 @@ static void moveArrayDesc(Block *B, std::byte *Src, std::byte *Dst,
149150
}
150151

151152
static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
152-
bool IsActive, bool IsUnionField, bool InUnion,
153-
const Descriptor *D, unsigned FieldOffset) {
153+
bool IsVolatile, bool IsActive, bool IsUnionField,
154+
bool InUnion, const Descriptor *D, unsigned FieldOffset) {
154155
auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
155156
Desc->Offset = FieldOffset;
156157
Desc->Desc = D;
@@ -160,15 +161,17 @@ static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
160161
Desc->InUnion = InUnion;
161162
Desc->IsConst = IsConst || D->IsConst;
162163
Desc->IsFieldMutable = IsMutable || D->IsMutable;
164+
Desc->IsVolatile = IsVolatile || D->IsVolatile;
163165

164166
if (auto Fn = D->CtorFn)
165167
Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
166-
Desc->IsActive, InUnion || D->isUnion(), D);
168+
Desc->IsVolatile, Desc->IsActive, InUnion || D->isUnion(), D);
167169
}
168170

169171
static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
170-
bool IsActive, bool InUnion, const Descriptor *D,
171-
unsigned FieldOffset, bool IsVirtualBase) {
172+
bool IsVolatile, bool IsActive, bool InUnion,
173+
const Descriptor *D, unsigned FieldOffset,
174+
bool IsVirtualBase) {
172175
assert(D);
173176
assert(D->ElemRecord);
174177
assert(!D->ElemRecord->isUnion()); // Unions cannot be base classes.
@@ -183,28 +186,32 @@ static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
183186
Desc->IsConst = IsConst || D->IsConst;
184187
Desc->IsFieldMutable = IsMutable || D->IsMutable;
185188
Desc->InUnion = InUnion;
189+
Desc->IsVolatile = false;
186190

187191
for (const auto &V : D->ElemRecord->bases())
188-
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
189-
V.Desc, V.Offset, false);
192+
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsVolatile, IsActive,
193+
InUnion, V.Desc, V.Offset, false);
190194
for (const auto &F : D->ElemRecord->fields())
191-
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
192-
InUnion, F.Desc, F.Offset);
195+
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsVolatile, IsActive,
196+
InUnion, InUnion, F.Desc, F.Offset);
193197
}
194198

195199
static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
196-
bool IsActive, bool InUnion, const Descriptor *D) {
200+
bool IsVolatile, bool IsActive, bool InUnion,
201+
const Descriptor *D) {
197202
for (const auto &V : D->ElemRecord->bases())
198-
initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
199-
false);
203+
initBase(B, Ptr, IsConst, IsMutable, IsVolatile, IsActive, InUnion, V.Desc,
204+
V.Offset,
205+
/*IsVirtualBase=*/false);
200206
for (const auto &F : D->ElemRecord->fields()) {
201207
bool IsUnionField = D->isUnion();
202-
initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField,
208+
initField(B, Ptr, IsConst, IsMutable, IsVolatile, IsActive, IsUnionField,
203209
InUnion || IsUnionField, F.Desc, F.Offset);
204210
}
205211
for (const auto &V : D->ElemRecord->virtual_bases())
206-
initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
207-
true);
212+
initBase(B, Ptr, IsConst, IsMutable, IsVolatile, IsActive, InUnion, V.Desc,
213+
V.Offset,
214+
/*IsVirtualBase=*/true);
208215
}
209216

210217
static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D,
@@ -332,12 +339,12 @@ static BlockMoveFn getMoveArrayPrim(PrimType Type) {
332339
/// Primitives.
333340
Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type,
334341
MetadataSize MD, bool IsConst, bool IsTemporary,
335-
bool IsMutable)
342+
bool IsMutable, bool IsVolatile)
336343
: Source(D), SourceType(SourceTy), ElemSize(primSize(Type)), Size(ElemSize),
337344
MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type),
338345
IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
339-
CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
340-
MoveFn(getMovePrim(Type)) {
346+
IsVolatile(IsVolatile), CtorFn(getCtorPrim(Type)),
347+
DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) {
341348
assert(AllocSize >= Size);
342349
assert(Source && "Missing source");
343350
}
@@ -396,12 +403,13 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
396403

397404
/// Composite records.
398405
Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD,
399-
bool IsConst, bool IsTemporary, bool IsMutable)
406+
bool IsConst, bool IsTemporary, bool IsMutable,
407+
bool IsVolatile)
400408
: Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
401409
Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
402410
ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
403-
IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord),
404-
MoveFn(moveRecord) {
411+
IsTemporary(IsTemporary), IsVolatile(IsVolatile), CtorFn(ctorRecord),
412+
DtorFn(dtorRecord), MoveFn(moveRecord) {
405413
assert(Source && "Missing source");
406414
}
407415

clang/lib/AST/ByteCode/Descriptor.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;
3333
/// inline descriptors of all fields and array elements. It also initializes
3434
/// all the fields which contain non-trivial types.
3535
using BlockCtorFn = void (*)(Block *Storage, std::byte *FieldPtr, bool IsConst,
36-
bool IsMutable, bool IsActive, bool InUnion,
37-
const Descriptor *FieldDesc);
36+
bool IsMutable, bool IsVolatile, bool IsActive,
37+
bool InUnion, const Descriptor *FieldDesc);
3838

3939
/// Invoked when a block is destroyed. Invokes the destructors of all
4040
/// non-trivial nested fields of arrays and records.
@@ -104,6 +104,8 @@ struct InlineDescriptor {
104104
/// Flag indicating if the field is an element of a composite array.
105105
LLVM_PREFERRED_TYPE(bool)
106106
unsigned IsArrayElement : 1;
107+
LLVM_PREFERRED_TYPE(bool)
108+
unsigned IsVolatile : 1;
107109

108110
Lifetime LifeState;
109111

@@ -112,7 +114,8 @@ struct InlineDescriptor {
112114
InlineDescriptor(const Descriptor *D)
113115
: Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false),
114116
IsBase(false), IsActive(false), IsFieldMutable(false),
115-
IsArrayElement(false), LifeState(Lifetime::Started), Desc(D) {}
117+
IsArrayElement(false), IsVolatile(false), LifeState(Lifetime::Started),
118+
Desc(D) {}
116119

117120
void dump() const { dump(llvm::errs()); }
118121
void dump(llvm::raw_ostream &OS) const;
@@ -164,6 +167,7 @@ struct Descriptor final {
164167
const bool IsMutable = false;
165168
/// Flag indicating if the block is a temporary.
166169
const bool IsTemporary = false;
170+
const bool IsVolatile = false;
167171
/// Flag indicating if the block is an array.
168172
const bool IsArray = false;
169173
/// Flag indicating if this is a dummy descriptor.
@@ -177,7 +181,8 @@ struct Descriptor final {
177181

178182
/// Allocates a descriptor for a primitive.
179183
Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type,
180-
MetadataSize MD, bool IsConst, bool IsTemporary, bool IsMutable);
184+
MetadataSize MD, bool IsConst, bool IsTemporary, bool IsMutable,
185+
bool IsVolatile);
181186

182187
/// Allocates a descriptor for an array of primitives.
183188
Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems,
@@ -198,7 +203,7 @@ struct Descriptor final {
198203

199204
/// Allocates a descriptor for a record.
200205
Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst,
201-
bool IsTemporary, bool IsMutable);
206+
bool IsTemporary, bool IsMutable, bool IsVolatile);
202207

203208
/// Allocates a dummy descriptor.
204209
Descriptor(const DeclTy &D, MetadataSize MD = std::nullopt);

clang/lib/AST/ByteCode/DynamicAllocator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
8484
ID->IsFieldMutable = false;
8585
ID->IsConst = false;
8686
ID->IsInitialized = false;
87+
ID->IsVolatile = false;
8788

8889
B->IsDynamic = true;
8990

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -634,32 +634,35 @@ static bool CheckVolatile(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
634634
AccessKinds AK) {
635635
assert(Ptr.isLive());
636636

637-
// FIXME: This check here might be kinda expensive. Maybe it would be better
638-
// to have another field in InlineDescriptor for this?
639-
if (!Ptr.isBlockPointer())
640-
return true;
641-
642-
QualType PtrType = Ptr.getType();
643-
if (!PtrType.isVolatileQualified())
637+
if (!Ptr.isVolatile())
644638
return true;
645639

646640
if (!S.getLangOpts().CPlusPlus)
647641
return Invalid(S, OpPC);
648642

643+
// The reason why Ptr is volatile might be further up the hierarchy.
644+
// Find that pointer.
645+
Pointer P = Ptr;
646+
while (!P.isRoot()) {
647+
if (P.getType().isVolatileQualified())
648+
break;
649+
P = P.getBase();
650+
}
651+
649652
const NamedDecl *ND = nullptr;
650653
int DiagKind;
651654
SourceLocation Loc;
652-
if (const auto *F = Ptr.getField()) {
655+
if (const auto *F = P.getField()) {
653656
DiagKind = 2;
654657
Loc = F->getLocation();
655658
ND = F;
656-
} else if (auto *VD = Ptr.getFieldDesc()->asValueDecl()) {
659+
} else if (auto *VD = P.getFieldDesc()->asValueDecl()) {
657660
DiagKind = 1;
658661
Loc = VD->getLocation();
659662
ND = VD;
660663
} else {
661664
DiagKind = 0;
662-
if (const auto *E = Ptr.getFieldDesc()->asExpr())
665+
if (const auto *E = P.getFieldDesc()->asExpr())
663666
Loc = E->getExprLoc();
664667
}
665668

clang/lib/AST/ByteCode/InterpBlock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class Block final {
114114
std::memset(rawData(), 0, Desc->getAllocSize());
115115
if (Desc->CtorFn) {
116116
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
117+
Desc->IsVolatile,
117118
/*isActive=*/true, /*InUnion=*/false, Desc);
118119
}
119120
IsInitialized = true;

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,11 +1595,9 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
15951595

15961596
assert(!ElemT);
15971597
// Structs etc.
1598-
const Descriptor *Desc = S.P.createDescriptor(
1599-
NewCall, ElemType.getTypePtr(),
1600-
IsArray ? std::nullopt : Descriptor::InlineDescMD,
1601-
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false,
1602-
/*Init=*/nullptr);
1598+
const Descriptor *Desc =
1599+
S.P.createDescriptor(NewCall, ElemType.getTypePtr(),
1600+
IsArray ? std::nullopt : Descriptor::InlineDescMD);
16031601

16041602
if (IsArray) {
16051603
Block *B =

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,13 @@ class Pointer {
577577
return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
578578
}
579579

580+
/// Checks if an object or a subfield is volatile.
581+
bool isVolatile() const {
582+
if (!isBlockPointer())
583+
return false;
584+
return isRoot() ? getDeclDesc()->IsVolatile : getInlineDesc()->IsVolatile;
585+
}
586+
580587
/// Returns the declaration ID.
581588
std::optional<unsigned> getDeclID() const {
582589
if (isBlockPointer()) {

0 commit comments

Comments
 (0)