Skip to content

Commit 480df19

Browse files
committed
local activate
1 parent 2a3e745 commit 480df19

File tree

10 files changed

+148
-19
lines changed

10 files changed

+148
-19
lines changed

clang/lib/AST/ByteCode/ByteCodeEmitter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ enum Opcode : uint32_t;
2525
/// An emitter which links the program to bytecode for later use.
2626
class ByteCodeEmitter {
2727
protected:
28-
using LabelTy = uint32_t;
2928
using AddrTy = uintptr_t;
3029
using Local = Scope::Local;
3130

3231
public:
32+
using LabelTy = uint32_t;
3333
/// Compiles the function into the module.
3434
void compileFunc(const FunctionDecl *FuncDecl, Function *Func = nullptr);
3535

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "PrimType.h"
1717
#include "Program.h"
1818
#include "clang/AST/Attr.h"
19+
#include "llvm/Support/SaveAndRestore.h"
1920

2021
using namespace clang;
2122
using namespace clang::interp;
@@ -2500,17 +2501,18 @@ bool Compiler<Emitter>::VisitAbstractConditionalOperator(
25002501
const Expr *TrueExpr = E->getTrueExpr();
25012502
const Expr *FalseExpr = E->getFalseExpr();
25022503

2503-
auto visitChildExpr = [&](const Expr *E) -> bool {
2504-
LocalScope<Emitter> S(this);
2505-
if (!this->delegate(E))
2506-
return false;
2507-
return S.destroyLocals();
2508-
};
2504+
// The TrueExpr and FalseExpr of a conditional operator do _not_ create a
2505+
// scope, which means the local variables created within them unconditionally
2506+
// always exist. However, we need to later differentiate which branch was
2507+
// taken and only destroy the varibles of the active branch. This is what the
2508+
// "enabled" flags on local variables are used for.
2509+
llvm::SaveAndRestore LAAA(this->VarScope->LocalsAlwaysEnabled,
2510+
/*NewValue=*/false);
25092511

25102512
if (std::optional<bool> BoolValue = getBoolValue(Condition)) {
25112513
if (*BoolValue)
2512-
return visitChildExpr(TrueExpr);
2513-
return visitChildExpr(FalseExpr);
2514+
return this->delegate(TrueExpr);
2515+
return this->delegate(FalseExpr);
25142516
}
25152517

25162518
bool IsBcpCall = false;
@@ -2542,13 +2544,15 @@ bool Compiler<Emitter>::VisitAbstractConditionalOperator(
25422544

25432545
if (!this->jumpFalse(LabelFalse))
25442546
return false;
2545-
if (!visitChildExpr(TrueExpr))
2547+
if (!this->delegate(TrueExpr))
25462548
return false;
2549+
25472550
if (!this->jump(LabelEnd))
25482551
return false;
25492552
this->emitLabel(LabelFalse);
2550-
if (!visitChildExpr(FalseExpr))
2553+
if (!this->delegate(FalseExpr))
25512554
return false;
2555+
25522556
this->fallthrough(LabelEnd);
25532557
this->emitLabel(LabelEnd);
25542558

@@ -2955,10 +2959,15 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
29552959
bool IsVolatile = SubExpr->getType().isVolatileQualified();
29562960
unsigned LocalIndex = allocateLocalPrimitive(
29572961
E, *SubExprT, IsConst, IsVolatile, E->getExtendingDecl());
2962+
if (!this->VarScope->LocalsAlwaysEnabled &&
2963+
!this->emitEnableLocal(LocalIndex, E))
2964+
return false;
2965+
29582966
if (!this->visit(SubExpr))
29592967
return false;
29602968
if (!this->emitSetLocal(*SubExprT, LocalIndex, E))
29612969
return false;
2970+
29622971
return this->emitGetPtrLocal(LocalIndex, E);
29632972
}
29642973

@@ -2968,6 +2977,11 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
29682977
if (UnsignedOrNone LocalIndex =
29692978
allocateLocal(E, Inner->getType(), E->getExtendingDecl())) {
29702979
InitLinkScope<Emitter> ILS(this, InitLink::Temp(*LocalIndex));
2980+
2981+
if (!this->VarScope->LocalsAlwaysEnabled &&
2982+
!this->emitEnableLocal(*LocalIndex, E))
2983+
return false;
2984+
29712985
if (!this->emitGetPtrLocal(*LocalIndex, E))
29722986
return false;
29732987
return this->visitInitializer(SubExpr) && this->emitFinishInit(E);

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -477,12 +477,14 @@ template <class Emitter> class VariableScope {
477477
VariableScope(Compiler<Emitter> *Ctx, const ValueDecl *VD,
478478
ScopeKind Kind = ScopeKind::Block)
479479
: Ctx(Ctx), Parent(Ctx->VarScope), ValDecl(VD), Kind(Kind) {
480+
if (Parent)
481+
this->LocalsAlwaysEnabled = Parent->LocalsAlwaysEnabled;
480482
Ctx->VarScope = this;
481483
}
482484

483485
virtual ~VariableScope() { Ctx->VarScope = this->Parent; }
484486

485-
virtual void addLocal(const Scope::Local &Local) {
487+
virtual void addLocal(Scope::Local Local) {
486488
llvm_unreachable("Shouldn't be called");
487489
}
488490

@@ -519,7 +521,6 @@ template <class Emitter> class VariableScope {
519521
if (!P)
520522
break;
521523
}
522-
523524
// Add to this scope.
524525
this->addLocal(Local);
525526
}
@@ -530,6 +531,11 @@ template <class Emitter> class VariableScope {
530531
VariableScope *getParent() const { return Parent; }
531532
ScopeKind getKind() const { return Kind; }
532533

534+
/// Whether locals added to this scope are enabled by default.
535+
/// This is almost always true, except for the two branches
536+
/// of a conditional operator.
537+
bool LocalsAlwaysEnabled = true;
538+
533539
protected:
534540
/// Compiler instance.
535541
Compiler<Emitter> *Ctx;
@@ -576,29 +582,48 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
576582
return Success;
577583
}
578584

579-
void addLocal(const Scope::Local &Local) override {
585+
void addLocal(Scope::Local Local) override {
580586
if (!Idx) {
581587
Idx = static_cast<unsigned>(this->Ctx->Descriptors.size());
582588
this->Ctx->Descriptors.emplace_back();
583589
this->Ctx->emitInitScope(*Idx, {});
584590
}
585591

592+
Local.EnabledByDefault = this->LocalsAlwaysEnabled;
586593
this->Ctx->Descriptors[*Idx].emplace_back(Local);
587594
}
588595

589596
bool emitDestructors(const Expr *E = nullptr) override {
590597
if (!Idx)
591598
return true;
599+
assert(!this->Ctx->Descriptors[*Idx].empty());
600+
592601
// Emit destructor calls for local variables of record
593602
// type with a destructor.
594603
for (Scope::Local &Local : llvm::reverse(this->Ctx->Descriptors[*Idx])) {
595604
if (Local.Desc->hasTrivialDtor())
596605
continue;
597-
if (!this->Ctx->emitGetPtrLocal(Local.Offset, E))
598-
return false;
599606

600-
if (!this->Ctx->emitDestructionPop(Local.Desc, Local.Desc->getLoc()))
601-
return false;
607+
if (!Local.EnabledByDefault) {
608+
typename Emitter::LabelTy EndLabel = this->Ctx->getLabel();
609+
if (!this->Ctx->emitGetLocalEnabled(Local.Offset, E))
610+
return false;
611+
if (!this->Ctx->jumpFalse(EndLabel))
612+
return false;
613+
614+
if (!this->Ctx->emitGetPtrLocal(Local.Offset, E))
615+
return false;
616+
617+
if (!this->Ctx->emitDestructionPop(Local.Desc, Local.Desc->getLoc()))
618+
return false;
619+
620+
this->Ctx->emitLabel(EndLabel);
621+
} else {
622+
if (!this->Ctx->emitGetPtrLocal(Local.Offset, E))
623+
return false;
624+
if (!this->Ctx->emitDestructionPop(Local.Desc, Local.Desc->getLoc()))
625+
return false;
626+
}
602627

603628
removeIfStoredOpaqueValue(Local);
604629
}

clang/lib/AST/ByteCode/EvalEmitter.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Scope::Local EvalEmitter::createLocal(Descriptor *D) {
113113
InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
114114
Desc.Desc = D;
115115
Desc.Offset = sizeof(InlineDescriptor);
116-
Desc.IsActive = true;
116+
Desc.IsActive = false;
117117
Desc.IsBase = false;
118118
Desc.IsFieldMutable = false;
119119
Desc.IsConst = false;
@@ -322,6 +322,33 @@ bool EvalEmitter::emitDestroy(uint32_t I, SourceInfo Info) {
322322
return true;
323323
}
324324

325+
bool EvalEmitter::emitGetLocalEnabled(uint32_t I, SourceInfo Info) {
326+
if (!isActive())
327+
return true;
328+
329+
Block *B = getLocal(I);
330+
const InlineDescriptor &Desc =
331+
*reinterpret_cast<InlineDescriptor *>(B->rawData());
332+
333+
S.Stk.push<bool>(Desc.IsActive);
334+
return true;
335+
}
336+
337+
bool EvalEmitter::emitEnableLocal(uint32_t I, SourceInfo Info) {
338+
if (!isActive())
339+
return true;
340+
341+
// FIXME: This is a little dirty, but to avoid adding a flag to
342+
// InlineDescriptor that's only ever useful on the toplevel of local
343+
// variables, we reuse the IsActive flag for the enabled state. We should
344+
// probably use a different struct than InlineDescriptor for the block-level
345+
// inline descriptor of local varaibles.
346+
Block *B = getLocal(I);
347+
InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
348+
Desc.IsActive = true;
349+
return true;
350+
}
351+
325352
/// Global temporaries (LifetimeExtendedTemporary) carry their value
326353
/// around as an APValue, which codegen accesses.
327354
/// We set their value once when creating them, but we don't update it

clang/lib/AST/ByteCode/Function.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class Scope final {
4141
unsigned Offset;
4242
/// Descriptor of the local.
4343
Descriptor *Desc;
44+
/// If the cleanup for this local should be emitted.
45+
bool EnabledByDefault = true;
4446
};
4547

4648
using LocalVectorTy = llvm::SmallVector<Local, 8>;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,18 @@ inline bool InitScope(InterpState &S, CodePtr OpPC, uint32_t I) {
24742474
return true;
24752475
}
24762476

2477+
inline bool EnableLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
2478+
assert(!S.Current->isLocalEnabled(I));
2479+
S.Current->enableLocal(I);
2480+
return true;
2481+
}
2482+
2483+
inline bool GetLocalEnabled(InterpState &S, CodePtr OpPC, uint32_t I) {
2484+
assert(S.Current);
2485+
S.Stk.push<bool>(S.Current->isLocalEnabled(I));
2486+
return true;
2487+
}
2488+
24772489
//===----------------------------------------------------------------------===//
24782490
// Cast, CastFP
24792491
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/InterpFrame.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,23 @@ void InterpFrame::destroyScopes() {
8989
void InterpFrame::initScope(unsigned Idx) {
9090
if (!Func)
9191
return;
92+
9293
for (auto &Local : Func->getScope(Idx).locals()) {
9394
localBlock(Local.Offset)->invokeCtor();
9495
}
9596
}
9697

98+
void InterpFrame::enableLocal(unsigned Idx) {
99+
assert(Func);
100+
101+
// FIXME: This is a little dirty, but to avoid adding a flag to
102+
// InlineDescriptor that's only ever useful on the toplevel of local
103+
// variables, we reuse the IsActive flag for the enabled state. We should
104+
// probably use a different struct than InlineDescriptor for the block-level
105+
// inline descriptor of local varaibles.
106+
localInlineDesc(Idx)->IsActive = true;
107+
}
108+
97109
void InterpFrame::destroy(unsigned Idx) {
98110
for (auto &Local : Func->getScope(Idx).locals_reverse()) {
99111
S.deallocate(localBlock(Local.Offset));

clang/lib/AST/ByteCode/InterpFrame.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class InterpFrame final : public Frame {
5555
void destroy(unsigned Idx);
5656
void initScope(unsigned Idx);
5757
void destroyScopes();
58+
void enableLocal(unsigned Idx);
59+
bool isLocalEnabled(unsigned Idx) const {
60+
return localInlineDesc(Idx)->IsActive;
61+
}
5862

5963
/// Describes the frame with arguments for diagnostic purposes.
6064
void describe(llvm::raw_ostream &OS) const override;

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,16 @@ def InitScope : Opcode {
251251
let Args = [ArgUint32];
252252
}
253253

254+
def GetLocalEnabled : Opcode {
255+
let Args = [ArgUint32];
256+
let HasCustomEval = 1;
257+
}
258+
259+
def EnableLocal : Opcode {
260+
let Args = [ArgUint32];
261+
let HasCustomEval = 1;
262+
}
263+
254264
//===----------------------------------------------------------------------===//
255265
// Constants
256266
//===----------------------------------------------------------------------===//

clang/test/AST/ByteCode/cxx23.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,26 @@ namespace AIEWithIndex0Narrows {
473473
}
474474
static_assert(test());
475475
}
476+
477+
#if __cplusplus >= 202302L
478+
namespace InactiveLocalsInConditionalOp {
479+
struct A { constexpr A(){}; ~A(); constexpr int get() { return 10; } }; // all-note 2{{declared here}}
480+
constexpr int get(bool b) {
481+
return b ? A().get() : 1; // all-note {{non-constexpr function '~A' cannot be used in a constant expression}}
482+
}
483+
static_assert(get(false) == 1, "");
484+
static_assert(get(true) == 10, ""); // all-error {{not an integral constant expression}} \
485+
// all-note {{in call to}}
486+
487+
static_assert( (false ? A().get() : 1) == 1);
488+
static_assert( (true ? A().get() : 1) == 1); // all-error {{not an integral constant expression}} \
489+
// all-note {{non-constexpr function '~A' cannot be used in a constant expression}}
490+
491+
constexpr bool test2(bool b) {
492+
unsigned long __ms = b ? (const unsigned long &)0 : __ms;
493+
return true;
494+
}
495+
static_assert(test2(true));
496+
497+
}
498+
#endif

0 commit comments

Comments
 (0)