aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/AST/ByteCode/Compiler.cpp
diff options
context:
space:
mode:
authorTimm Baeder <tbaeder@redhat.com>2025-07-16 09:03:33 +0200
committerGitHub <noreply@github.com>2025-07-16 09:03:33 +0200
commit17d3029331a65ea54ad1bdbf79add03d5a71dcd4 (patch)
treeed5ac24acee81c602ddd4dac04d0644431d70032 /clang/lib/AST/ByteCode/Compiler.cpp
parent871038759afb491c16fb2cd14b78e51e410efbf3 (diff)
downloadllvm-17d3029331a65ea54ad1bdbf79add03d5a71dcd4.zip
llvm-17d3029331a65ea54ad1bdbf79add03d5a71dcd4.tar.gz
llvm-17d3029331a65ea54ad1bdbf79add03d5a71dcd4.tar.bz2
[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
Diffstat (limited to 'clang/lib/AST/ByteCode/Compiler.cpp')
-rw-r--r--clang/lib/AST/ByteCode/Compiler.cpp190
1 files changed, 141 insertions, 49 deletions
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index afa3b7e..ea473730 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -25,6 +25,34 @@ using APSInt = llvm::APSInt;
namespace clang {
namespace interp {
+static bool refersToUnion(const Expr *E) {
+ for (;;) {
+ if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ FD && FD->getParent()->isUnion())
+ return true;
+ E = ME->getBase();
+ continue;
+ }
+
+ if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+ E = ASE->getBase()->IgnoreImplicit();
+ continue;
+ }
+
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
+ ICE && (ICE->getCastKind() == CK_NoOp ||
+ ICE->getCastKind() == CK_DerivedToBase ||
+ ICE->getCastKind() == CK_UncheckedDerivedToBase)) {
+ E = ICE->getSubExpr();
+ continue;
+ }
+
+ break;
+ }
+ return false;
+}
+
static std::optional<bool> getBoolValue(const Expr *E) {
if (const auto *CE = dyn_cast_if_present<ConstantExpr>(E);
CE && CE->hasAPValueResult() &&
@@ -880,22 +908,11 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
return this->VisitPointerArithBinOp(BO);
}
- // Assignments require us to evalute the RHS first.
- if (BO->getOpcode() == BO_Assign) {
-
- if (!visit(RHS) || !visit(LHS))
- return false;
-
- // We don't support assignments in C.
- if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(BO))
- return false;
+ if (BO->getOpcode() == BO_Assign)
+ return this->visitAssignment(LHS, RHS, BO);
- if (!this->emitFlip(*LT, *RT, BO))
- return false;
- } else {
- if (!visit(LHS) || !visit(RHS))
- return false;
- }
+ if (!visit(LHS) || !visit(RHS))
+ return false;
// For languages such as C, cast the result of one
// of our comparision opcodes to T (which is usually int).
@@ -946,22 +963,6 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
if (BO->getType()->isFloatingType())
return Discard(this->emitDivf(getFPOptions(BO), BO));
return Discard(this->emitDiv(*T, BO));
- case BO_Assign:
- if (DiscardResult)
- return LHS->refersToBitField() ? this->emitStoreBitFieldPop(*T, BO)
- : this->emitStorePop(*T, BO);
- if (LHS->refersToBitField()) {
- if (!this->emitStoreBitField(*T, BO))
- return false;
- } else {
- if (!this->emitStore(*T, BO))
- return false;
- }
- // Assignments aren't necessarily lvalues in C.
- // Load from them in that case.
- if (!BO->isLValue())
- return this->emitLoadPop(*T, BO);
- return true;
case BO_And:
return Discard(this->emitBitAnd(*T, BO));
case BO_Or:
@@ -1790,19 +1791,26 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
return this->delegate(Inits[0]);
auto initPrimitiveField = [=](const Record::Field *FieldToInit,
- const Expr *Init, PrimType T) -> bool {
+ const Expr *Init, PrimType T,
+ bool Activate = false) -> bool {
InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init));
InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset));
if (!this->visit(Init))
return false;
- if (FieldToInit->isBitField())
+ bool BitField = FieldToInit->isBitField();
+ if (BitField && Activate)
+ return this->emitInitBitFieldActivate(T, FieldToInit, E);
+ if (BitField)
return this->emitInitBitField(T, FieldToInit, E);
+ if (Activate)
+ return this->emitInitFieldActivate(T, FieldToInit->Offset, E);
return this->emitInitField(T, FieldToInit->Offset, E);
};
auto initCompositeField = [=](const Record::Field *FieldToInit,
- const Expr *Init) -> bool {
+ const Expr *Init,
+ bool Activate = false) -> bool {
InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init));
InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset));
@@ -1810,6 +1818,10 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
// on the stack and recurse into visitInitializer().
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
return false;
+
+ if (Activate && !this->emitActivate(E))
+ return false;
+
if (!this->visitInitializer(Init))
return false;
return this->emitPopPtr(E);
@@ -1829,10 +1841,10 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
const Record::Field *FieldToInit = R->getField(FToInit);
if (std::optional<PrimType> T = classify(Init)) {
- if (!initPrimitiveField(FieldToInit, Init, *T))
+ if (!initPrimitiveField(FieldToInit, Init, *T, /*Activate=*/true))
return false;
} else {
- if (!initCompositeField(FieldToInit, Init))
+ if (!initCompositeField(FieldToInit, Init, /*Activate=*/true))
return false;
}
}
@@ -2023,7 +2035,8 @@ bool Compiler<Emitter>::visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
template <class Emitter>
bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
- const FunctionDecl *FuncDecl) {
+ const FunctionDecl *FuncDecl,
+ bool Activate) {
assert(VarScope->getKind() == ScopeKind::Call);
llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args);
@@ -2046,6 +2059,11 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
return false;
}
+ if (ArgIndex == 1 && Activate) {
+ if (!this->emitActivate(Arg))
+ return false;
+ }
+
if (FuncDecl && NonNullArgs[ArgIndex]) {
PrimType ArgT = classify(Arg).value_or(PT_Ptr);
if (ArgT == PT_Ptr) {
@@ -4227,10 +4245,13 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
PrimType T = classifyPrim(D->getType());
if (!this->visitZeroInitializer(T, QT, E))
return false;
+ if (R->isUnion()) {
+ if (!this->emitInitFieldActivate(T, Field.Offset, E))
+ return false;
+ break;
+ }
if (!this->emitInitField(T, Field.Offset, E))
return false;
- if (R->isUnion())
- break;
continue;
}
@@ -4256,13 +4277,15 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
} else
return false;
- if (!this->emitFinishInitPop(E))
- return false;
-
// C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the
// object's first non-static named data member is zero-initialized
- if (R->isUnion())
+ if (R->isUnion()) {
+ if (!this->emitFinishInitActivatePop(E))
+ return false;
break;
+ }
+ if (!this->emitFinishInitPop(E))
+ return false;
}
for (const Record::Base &B : R->bases()) {
@@ -4326,6 +4349,59 @@ bool Compiler<Emitter>::visitZeroArrayInitializer(QualType T, const Expr *E) {
}
template <class Emitter>
+bool Compiler<Emitter>::visitAssignment(const Expr *LHS, const Expr *RHS,
+ const Expr *E) {
+ if (!classify(E->getType()))
+ return false;
+
+ if (!this->visit(RHS))
+ return false;
+ if (!this->visit(LHS))
+ return false;
+
+ // We don't support assignments in C.
+ if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(E))
+ return false;
+
+ PrimType RHT = classifyPrim(RHS);
+ bool Activates = refersToUnion(LHS);
+ bool BitField = LHS->refersToBitField();
+
+ if (!this->emitFlip(PT_Ptr, RHT, E))
+ return false;
+
+ if (DiscardResult) {
+ if (BitField && Activates)
+ return this->emitStoreBitFieldActivatePop(RHT, E);
+ if (BitField)
+ return this->emitStoreBitFieldPop(RHT, E);
+ if (Activates)
+ return this->emitStoreActivatePop(RHT, E);
+ // Otherwise, regular non-activating store.
+ return this->emitStorePop(RHT, E);
+ }
+
+ auto maybeLoad = [&](bool Result) -> bool {
+ if (!Result)
+ return false;
+ // Assignments aren't necessarily lvalues in C.
+ // Load from them in that case.
+ if (!E->isLValue())
+ return this->emitLoadPop(RHT, E);
+ return true;
+ };
+
+ if (BitField && Activates)
+ return maybeLoad(this->emitStoreBitFieldActivate(RHT, E));
+ if (BitField)
+ return maybeLoad(this->emitStoreBitField(RHT, E));
+ if (Activates)
+ return maybeLoad(this->emitStoreActivate(RHT, E));
+ // Otherwise, regular non-activating store.
+ return maybeLoad(this->emitStore(RHT, E));
+}
+
+template <class Emitter>
template <typename T>
bool Compiler<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
switch (Ty) {
@@ -5067,7 +5143,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
return false;
}
- if (!this->visitCallArgs(Args, FuncDecl))
+ if (!this->visitCallArgs(Args, FuncDecl, IsAssignmentOperatorCall))
return false;
// Undo the argument reversal we did earlier.
@@ -5851,7 +5927,8 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
assert(!ReturnType);
auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
- const Expr *InitExpr) -> bool {
+ const Expr *InitExpr,
+ bool Activate = false) -> bool {
// We don't know what to do with these, so just return false.
if (InitExpr->getType().isNull())
return false;
@@ -5860,8 +5937,13 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (!this->visit(InitExpr))
return false;
- if (F->isBitField())
+ bool BitField = F->isBitField();
+ if (BitField && Activate)
+ return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr);
+ if (BitField)
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
+ if (Activate)
+ return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr);
return this->emitInitThisField(*T, FieldOffset, InitExpr);
}
// Non-primitive case. Get a pointer to the field-to-initialize
@@ -5870,6 +5952,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
return false;
+ if (Activate && !this->emitActivate(InitExpr))
+ return false;
+
if (!this->visitInitializer(InitExpr))
return false;
@@ -5880,8 +5965,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
const Record *R = this->getRecord(RD);
if (!R)
return false;
+ bool IsUnion = R->isUnion();
- if (R->isUnion() && Ctor->isCopyOrMoveConstructor()) {
+ if (IsUnion && Ctor->isCopyOrMoveConstructor()) {
if (R->getNumFields() == 0)
return this->emitRetVoid(Ctor);
// union copy and move ctors are special.
@@ -5908,7 +5994,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (const FieldDecl *Member = Init->getMember()) {
const Record::Field *F = R->getField(Member);
- if (!emitFieldInitializer(F, F->Offset, InitExpr))
+ if (!emitFieldInitializer(F, F->Offset, InitExpr, IsUnion))
return false;
} else if (const Type *Base = Init->getBaseClass()) {
const auto *BaseDecl = Base->getAsCXXRecordDecl();
@@ -5928,11 +6014,15 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
return false;
}
+ if (IsUnion && !this->emitActivate(InitExpr))
+ return false;
+
if (!this->visitInitializer(InitExpr))
return false;
if (!this->emitFinishInitPop(InitExpr))
return false;
} else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) {
+
assert(IFD->getChainingSize() >= 2);
unsigned NestedFieldOffset = 0;
@@ -5944,12 +6034,14 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
NestedField = FieldRecord->getField(FD);
assert(NestedField);
+ IsUnion = IsUnion || FieldRecord->isUnion();
NestedFieldOffset += NestedField->Offset;
}
assert(NestedField);
- if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
+ if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr,
+ IsUnion))
return false;
// Mark all chain links as initialized.