Skip to content

Commit 17d3029

Browse files
authored
[clang][bytecode] Make union activation more granular (#148835)
Only activate things if the syntactical structure suggests so. This adds a bunch of new opcodes to control whether to activate in stores, etc. Fixes #134789
1 parent 8710387 commit 17d3029

File tree

8 files changed

+492
-103
lines changed

8 files changed

+492
-103
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 141 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,34 @@ using APSInt = llvm::APSInt;
2525
namespace clang {
2626
namespace interp {
2727

28+
static bool refersToUnion(const Expr *E) {
29+
for (;;) {
30+
if (const auto *ME = dyn_cast<MemberExpr>(E)) {
31+
if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
32+
FD && FD->getParent()->isUnion())
33+
return true;
34+
E = ME->getBase();
35+
continue;
36+
}
37+
38+
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
39+
E = ASE->getBase()->IgnoreImplicit();
40+
continue;
41+
}
42+
43+
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
44+
ICE && (ICE->getCastKind() == CK_NoOp ||
45+
ICE->getCastKind() == CK_DerivedToBase ||
46+
ICE->getCastKind() == CK_UncheckedDerivedToBase)) {
47+
E = ICE->getSubExpr();
48+
continue;
49+
}
50+
51+
break;
52+
}
53+
return false;
54+
}
55+
2856
static std::optional<bool> getBoolValue(const Expr *E) {
2957
if (const auto *CE = dyn_cast_if_present<ConstantExpr>(E);
3058
CE && CE->hasAPValueResult() &&
@@ -880,22 +908,11 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
880908
return this->VisitPointerArithBinOp(BO);
881909
}
882910

883-
// Assignments require us to evalute the RHS first.
884-
if (BO->getOpcode() == BO_Assign) {
885-
886-
if (!visit(RHS) || !visit(LHS))
887-
return false;
888-
889-
// We don't support assignments in C.
890-
if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(BO))
891-
return false;
911+
if (BO->getOpcode() == BO_Assign)
912+
return this->visitAssignment(LHS, RHS, BO);
892913

893-
if (!this->emitFlip(*LT, *RT, BO))
894-
return false;
895-
} else {
896-
if (!visit(LHS) || !visit(RHS))
897-
return false;
898-
}
914+
if (!visit(LHS) || !visit(RHS))
915+
return false;
899916

900917
// For languages such as C, cast the result of one
901918
// of our comparision opcodes to T (which is usually int).
@@ -946,22 +963,6 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
946963
if (BO->getType()->isFloatingType())
947964
return Discard(this->emitDivf(getFPOptions(BO), BO));
948965
return Discard(this->emitDiv(*T, BO));
949-
case BO_Assign:
950-
if (DiscardResult)
951-
return LHS->refersToBitField() ? this->emitStoreBitFieldPop(*T, BO)
952-
: this->emitStorePop(*T, BO);
953-
if (LHS->refersToBitField()) {
954-
if (!this->emitStoreBitField(*T, BO))
955-
return false;
956-
} else {
957-
if (!this->emitStore(*T, BO))
958-
return false;
959-
}
960-
// Assignments aren't necessarily lvalues in C.
961-
// Load from them in that case.
962-
if (!BO->isLValue())
963-
return this->emitLoadPop(*T, BO);
964-
return true;
965966
case BO_And:
966967
return Discard(this->emitBitAnd(*T, BO));
967968
case BO_Or:
@@ -1790,26 +1791,37 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
17901791
return this->delegate(Inits[0]);
17911792

17921793
auto initPrimitiveField = [=](const Record::Field *FieldToInit,
1793-
const Expr *Init, PrimType T) -> bool {
1794+
const Expr *Init, PrimType T,
1795+
bool Activate = false) -> bool {
17941796
InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init));
17951797
InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset));
17961798
if (!this->visit(Init))
17971799
return false;
17981800

1799-
if (FieldToInit->isBitField())
1801+
bool BitField = FieldToInit->isBitField();
1802+
if (BitField && Activate)
1803+
return this->emitInitBitFieldActivate(T, FieldToInit, E);
1804+
if (BitField)
18001805
return this->emitInitBitField(T, FieldToInit, E);
1806+
if (Activate)
1807+
return this->emitInitFieldActivate(T, FieldToInit->Offset, E);
18011808
return this->emitInitField(T, FieldToInit->Offset, E);
18021809
};
18031810

18041811
auto initCompositeField = [=](const Record::Field *FieldToInit,
1805-
const Expr *Init) -> bool {
1812+
const Expr *Init,
1813+
bool Activate = false) -> bool {
18061814
InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init));
18071815
InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset));
18081816

18091817
// Non-primitive case. Get a pointer to the field-to-initialize
18101818
// on the stack and recurse into visitInitializer().
18111819
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
18121820
return false;
1821+
1822+
if (Activate && !this->emitActivate(E))
1823+
return false;
1824+
18131825
if (!this->visitInitializer(Init))
18141826
return false;
18151827
return this->emitPopPtr(E);
@@ -1829,10 +1841,10 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
18291841

18301842
const Record::Field *FieldToInit = R->getField(FToInit);
18311843
if (std::optional<PrimType> T = classify(Init)) {
1832-
if (!initPrimitiveField(FieldToInit, Init, *T))
1844+
if (!initPrimitiveField(FieldToInit, Init, *T, /*Activate=*/true))
18331845
return false;
18341846
} else {
1835-
if (!initCompositeField(FieldToInit, Init))
1847+
if (!initCompositeField(FieldToInit, Init, /*Activate=*/true))
18361848
return false;
18371849
}
18381850
}
@@ -2023,7 +2035,8 @@ bool Compiler<Emitter>::visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
20232035

20242036
template <class Emitter>
20252037
bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
2026-
const FunctionDecl *FuncDecl) {
2038+
const FunctionDecl *FuncDecl,
2039+
bool Activate) {
20272040
assert(VarScope->getKind() == ScopeKind::Call);
20282041
llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args);
20292042

@@ -2046,6 +2059,11 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
20462059
return false;
20472060
}
20482061

2062+
if (ArgIndex == 1 && Activate) {
2063+
if (!this->emitActivate(Arg))
2064+
return false;
2065+
}
2066+
20492067
if (FuncDecl && NonNullArgs[ArgIndex]) {
20502068
PrimType ArgT = classify(Arg).value_or(PT_Ptr);
20512069
if (ArgT == PT_Ptr) {
@@ -4227,10 +4245,13 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
42274245
PrimType T = classifyPrim(D->getType());
42284246
if (!this->visitZeroInitializer(T, QT, E))
42294247
return false;
4248+
if (R->isUnion()) {
4249+
if (!this->emitInitFieldActivate(T, Field.Offset, E))
4250+
return false;
4251+
break;
4252+
}
42304253
if (!this->emitInitField(T, Field.Offset, E))
42314254
return false;
4232-
if (R->isUnion())
4233-
break;
42344255
continue;
42354256
}
42364257

@@ -4256,13 +4277,15 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
42564277
} else
42574278
return false;
42584279

4259-
if (!this->emitFinishInitPop(E))
4260-
return false;
4261-
42624280
// C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the
42634281
// object's first non-static named data member is zero-initialized
4264-
if (R->isUnion())
4282+
if (R->isUnion()) {
4283+
if (!this->emitFinishInitActivatePop(E))
4284+
return false;
42654285
break;
4286+
}
4287+
if (!this->emitFinishInitPop(E))
4288+
return false;
42664289
}
42674290

42684291
for (const Record::Base &B : R->bases()) {
@@ -4325,6 +4348,59 @@ bool Compiler<Emitter>::visitZeroArrayInitializer(QualType T, const Expr *E) {
43254348
return false;
43264349
}
43274350

4351+
template <class Emitter>
4352+
bool Compiler<Emitter>::visitAssignment(const Expr *LHS, const Expr *RHS,
4353+
const Expr *E) {
4354+
if (!classify(E->getType()))
4355+
return false;
4356+
4357+
if (!this->visit(RHS))
4358+
return false;
4359+
if (!this->visit(LHS))
4360+
return false;
4361+
4362+
// We don't support assignments in C.
4363+
if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(E))
4364+
return false;
4365+
4366+
PrimType RHT = classifyPrim(RHS);
4367+
bool Activates = refersToUnion(LHS);
4368+
bool BitField = LHS->refersToBitField();
4369+
4370+
if (!this->emitFlip(PT_Ptr, RHT, E))
4371+
return false;
4372+
4373+
if (DiscardResult) {
4374+
if (BitField && Activates)
4375+
return this->emitStoreBitFieldActivatePop(RHT, E);
4376+
if (BitField)
4377+
return this->emitStoreBitFieldPop(RHT, E);
4378+
if (Activates)
4379+
return this->emitStoreActivatePop(RHT, E);
4380+
// Otherwise, regular non-activating store.
4381+
return this->emitStorePop(RHT, E);
4382+
}
4383+
4384+
auto maybeLoad = [&](bool Result) -> bool {
4385+
if (!Result)
4386+
return false;
4387+
// Assignments aren't necessarily lvalues in C.
4388+
// Load from them in that case.
4389+
if (!E->isLValue())
4390+
return this->emitLoadPop(RHT, E);
4391+
return true;
4392+
};
4393+
4394+
if (BitField && Activates)
4395+
return maybeLoad(this->emitStoreBitFieldActivate(RHT, E));
4396+
if (BitField)
4397+
return maybeLoad(this->emitStoreBitField(RHT, E));
4398+
if (Activates)
4399+
return maybeLoad(this->emitStoreActivate(RHT, E));
4400+
// Otherwise, regular non-activating store.
4401+
return maybeLoad(this->emitStore(RHT, E));
4402+
}
4403+
43284404
template <class Emitter>
43294405
template <typename T>
43304406
bool Compiler<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
@@ -5067,7 +5143,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
50675143
return false;
50685144
}
50695145

5070-
if (!this->visitCallArgs(Args, FuncDecl))
5146+
if (!this->visitCallArgs(Args, FuncDecl, IsAssignmentOperatorCall))
50715147
return false;
50725148

50735149
// Undo the argument reversal we did earlier.
@@ -5851,7 +5927,8 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
58515927
assert(!ReturnType);
58525928

58535929
auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
5854-
const Expr *InitExpr) -> bool {
5930+
const Expr *InitExpr,
5931+
bool Activate = false) -> bool {
58555932
// We don't know what to do with these, so just return false.
58565933
if (InitExpr->getType().isNull())
58575934
return false;
@@ -5860,8 +5937,13 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
58605937
if (!this->visit(InitExpr))
58615938
return false;
58625939

5863-
if (F->isBitField())
5940+
bool BitField = F->isBitField();
5941+
if (BitField && Activate)
5942+
return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr);
5943+
if (BitField)
58645944
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
5945+
if (Activate)
5946+
return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr);
58655947
return this->emitInitThisField(*T, FieldOffset, InitExpr);
58665948
}
58675949
// Non-primitive case. Get a pointer to the field-to-initialize
@@ -5870,6 +5952,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
58705952
if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
58715953
return false;
58725954

5955+
if (Activate && !this->emitActivate(InitExpr))
5956+
return false;
5957+
58735958
if (!this->visitInitializer(InitExpr))
58745959
return false;
58755960

@@ -5880,8 +5965,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
58805965
const Record *R = this->getRecord(RD);
58815966
if (!R)
58825967
return false;
5968+
bool IsUnion = R->isUnion();
58835969

5884-
if (R->isUnion() && Ctor->isCopyOrMoveConstructor()) {
5970+
if (IsUnion && Ctor->isCopyOrMoveConstructor()) {
58855971
if (R->getNumFields() == 0)
58865972
return this->emitRetVoid(Ctor);
58875973
// union copy and move ctors are special.
@@ -5908,7 +5994,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
59085994
if (const FieldDecl *Member = Init->getMember()) {
59095995
const Record::Field *F = R->getField(Member);
59105996

5911-
if (!emitFieldInitializer(F, F->Offset, InitExpr))
5997+
if (!emitFieldInitializer(F, F->Offset, InitExpr, IsUnion))
59125998
return false;
59135999
} else if (const Type *Base = Init->getBaseClass()) {
59146000
const auto *BaseDecl = Base->getAsCXXRecordDecl();
@@ -5928,11 +6014,15 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
59286014
return false;
59296015
}
59306016

6017+
if (IsUnion && !this->emitActivate(InitExpr))
6018+
return false;
6019+
59316020
if (!this->visitInitializer(InitExpr))
59326021
return false;
59336022
if (!this->emitFinishInitPop(InitExpr))
59346023
return false;
59356024
} else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) {
6025+
59366026
assert(IFD->getChainingSize() >= 2);
59376027

59386028
unsigned NestedFieldOffset = 0;
@@ -5944,12 +6034,14 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
59446034

59456035
NestedField = FieldRecord->getField(FD);
59466036
assert(NestedField);
6037+
IsUnion = IsUnion || FieldRecord->isUnion();
59476038

59486039
NestedFieldOffset += NestedField->Offset;
59496040
}
59506041
assert(NestedField);
59516042

5952-
if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
6043+
if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr,
6044+
IsUnion))
59536045
return false;
59546046

59556047
// Mark all chain links as initialized.

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
307307
const Expr *E);
308308
bool visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
309309
std::optional<PrimType> InitT);
310-
bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl);
310+
bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl,
311+
bool Activate);
311312

312313
/// Creates a local primitive value.
313314
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst,
@@ -342,6 +343,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
342343
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
343344
bool visitZeroRecordInitializer(const Record *R, const Expr *E);
344345
bool visitZeroArrayInitializer(QualType T, const Expr *E);
346+
bool visitAssignment(const Expr *LHS, const Expr *RHS, const Expr *E);
345347

346348
/// Emits an APSInt constant.
347349
bool emitConst(const llvm::APSInt &Value, PrimType Ty, const Expr *E);

0 commit comments

Comments
 (0)