Skip to content

Commit 8d4aa1f

Browse files
committed
[clang][Interp] Update global temporaries at the end of a declaration
When we create a global temporary variable via a LifetimeExtendedTemporaryDecl, we have an expression to initialize it with, so we evaluate that expression, convert it to an APValue and set it on the LETD. However, when the value is updated after the initialization, we don't propagate the new value to the LETD, which means we will see an outdated value set on the LETD. Fix this by keeping a list of seen LETDs and update them from the global variable at the end of evaluation a declaration.
1 parent ddaa93b commit 8d4aa1f

File tree

5 files changed

+126
-4
lines changed

5 files changed

+126
-4
lines changed

clang/lib/AST/Interp/EvalEmitter.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
7171
EvalResult.setInvalid();
7272

7373
S.EvaluatingDecl = nullptr;
74+
updateGlobalTemporaries();
7475
return std::move(this->EvalResult);
7576
}
7677

@@ -237,6 +238,28 @@ bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
237238
return true;
238239
}
239240

241+
/// Global temporaries (LifetimeExtendedTemporary) carry their value
242+
/// around as an APValue, which codegen accesses.
243+
/// We set their value once when creating them, but we don't update it
244+
/// afterwards when code changes it later.
245+
/// This is what we do here.
246+
void EvalEmitter::updateGlobalTemporaries() {
247+
for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
248+
if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
249+
const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
250+
APValue *Cached = Temp->getOrCreateValue(true);
251+
252+
if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
253+
TYPE_SWITCH(*T, { *Cached = Ptr.deref<T>().toAPValue(); });
254+
} else {
255+
if (std::optional<APValue> APV = Ptr.toRValue(Ctx))
256+
*Cached = *APV;
257+
}
258+
}
259+
}
260+
S.SeenGlobalTemporaries.clear();
261+
}
262+
240263
//===----------------------------------------------------------------------===//
241264
// Opcode evaluators
242265
//===----------------------------------------------------------------------===//

clang/lib/AST/Interp/EvalEmitter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class EvalEmitter : public SourceMapper {
109109
return reinterpret_cast<Block *>(It->second.get());
110110
}
111111

112+
void updateGlobalTemporaries();
113+
112114
// The emitter always tracks the current instruction and sets OpPC to a token
113115
// value which is mapped to the location of the opcode being evaluated.
114116
CodePtr OpPC;

clang/lib/AST/Interp/Interp.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,16 +1283,20 @@ bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
12831283
template <PrimType Name, class T = typename PrimConv<Name>::T>
12841284
bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
12851285
const LifetimeExtendedTemporaryDecl *Temp) {
1286-
assert(Temp);
1286+
const Pointer &Ptr = S.P.getGlobal(I);
1287+
12871288
const T Value = S.Stk.peek<T>();
12881289
APValue APV = Value.toAPValue();
12891290
APValue *Cached = Temp->getOrCreateValue(true);
12901291
*Cached = APV;
12911292

1292-
const Pointer &P = S.P.getGlobal(I);
1293-
P.deref<T>() = S.Stk.pop<T>();
1294-
P.initialize();
1293+
assert(Ptr.getDeclDesc()->asExpr());
1294+
1295+
S.SeenGlobalTemporaries.push_back(
1296+
std::make_pair(Ptr.getDeclDesc()->asExpr(), Temp));
12951297

1298+
Ptr.deref<T>() = S.Stk.pop<T>();
1299+
Ptr.initialize();
12961300
return true;
12971301
}
12981302

@@ -1305,6 +1309,9 @@ inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC,
13051309
const Pointer &P = S.Stk.peek<Pointer>();
13061310
APValue *Cached = Temp->getOrCreateValue(true);
13071311

1312+
S.SeenGlobalTemporaries.push_back(
1313+
std::make_pair(P.getDeclDesc()->asExpr(), Temp));
1314+
13081315
if (std::optional<APValue> APV = P.toRValue(S.getCtx())) {
13091316
*Cached = *APV;
13101317
return true;

clang/lib/AST/Interp/InterpState.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class InterpState final : public State, public SourceMapper {
123123
SourceLocation EvalLocation;
124124
/// Declaration we're initializing/evaluting, if any.
125125
const VarDecl *EvaluatingDecl = nullptr;
126+
127+
llvm::SmallVector<
128+
std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
129+
SeenGlobalTemporaries;
126130
};
127131

128132
} // namespace interp
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++1y -fexperimental-new-constant-interpreter | FileCheck %s
2+
//
3+
// expected-no-diagnostics
4+
5+
struct A {
6+
constexpr A() : n(1) {}
7+
~A();
8+
int n;
9+
};
10+
struct B : A {
11+
A a[3];
12+
constexpr B() {
13+
++a[0].n;
14+
a[1].n += 2;
15+
a[2].n = n + a[1].n;
16+
}
17+
};
18+
B b;
19+
20+
// CHECK: @b ={{.*}} global {{.*}} i32 1, {{.*}} { i32 2 }, {{.*}} { i32 3 }, {{.*}} { i32 4 }
21+
// CHECK-NOT: _ZN1BC
22+
23+
namespace ModifyStaticTemporary {
24+
struct A { int &&temporary; int x; };
25+
constexpr int f(int &r) { r *= 9; return r - 12; }
26+
A a = { 6, f(a.temporary) };
27+
// CHECK: @_ZGRN21ModifyStaticTemporary1aE_ = internal global i32 54
28+
// CHECK: @_ZN21ModifyStaticTemporary1aE ={{.*}} global {{.*}} ptr @_ZGRN21ModifyStaticTemporary1aE_, i32 42
29+
30+
A b = { 7, ++b.temporary };
31+
// CHECK: @_ZGRN21ModifyStaticTemporary1bE_ = internal global i32 8
32+
// CHECK: @_ZN21ModifyStaticTemporary1bE ={{.*}} global {{.*}} ptr @_ZGRN21ModifyStaticTemporary1bE_, i32 8
33+
34+
// Can't emit all of 'c' as a constant here, so emit the initial value of
35+
// 'c.temporary', not the value as modified by the partial evaluation within
36+
// the initialization of 'c.x'.
37+
A c = { 10, (++c.temporary, b.x) };
38+
// CHECK: @_ZN21ModifyStaticTemporary1cE ={{.*}} global {{.*}} zeroinitializer
39+
// CHECK: @_ZGRN21ModifyStaticTemporary1cE_ = internal global i32 10
40+
}
41+
42+
// CHECK: @_ZGRN28VariableTemplateWithConstRef1iIvEE_ = linkonce_odr constant i32 5, align 4
43+
// CHECK: @_ZN28VariableTemplateWithConstRef3useE ={{.*}} constant ptr @_ZGRN28VariableTemplateWithConstRef1iIvEE_
44+
namespace VariableTemplateWithConstRef {
45+
template <typename T>
46+
const int &i = 5;
47+
const int &use = i<void>;
48+
}
49+
50+
// CHECK: @_ZGRN34HiddenVariableTemplateWithConstRef1iIvEE_ = linkonce_odr hidden constant i32 5, align 4
51+
// CHECK: @_ZN34HiddenVariableTemplateWithConstRef3useE ={{.*}} constant ptr @_ZGRN34HiddenVariableTemplateWithConstRef1iIvEE_
52+
namespace HiddenVariableTemplateWithConstRef {
53+
template <typename T>
54+
__attribute__((visibility("hidden"))) const int &i = 5;
55+
const int &use = i<void>;
56+
}
57+
58+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE1_ = linkonce_odr constant i32 1
59+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE0_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE1_ }
60+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE3_ = linkonce_odr constant i32 2
61+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE2_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE3_ }
62+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE5_ = linkonce_odr constant i32 3
63+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE4_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE5_ }
64+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE7_ = linkonce_odr constant i32 4
65+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE6_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE7_ }
66+
// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE_ = linkonce_odr global %"struct.VariableTemplateWithPack::S" { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE0_, ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE2_, ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE4_, ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE6_ }
67+
// CHECK: @_ZN24VariableTemplateWithPack1pE ={{.*}} global {{.*}} @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE_
68+
namespace VariableTemplateWithPack {
69+
struct A {
70+
const int &r;
71+
};
72+
struct S {
73+
A &&a, &&b, &&c, &&d;
74+
};
75+
template <int... N>
76+
S &&s = {A{N}...};
77+
S *p = &s<1, 2, 3, 4>;
78+
}
79+
80+
81+
// CHECK: @_ZGR1z_ ={{.*}} global [2 x i32] [i32 10, i32 2]
82+
// CHECK: @z = global { ptr, i32 } { ptr @_ZGR1z_, i32 10 }
83+
typedef int v[2];
84+
struct Z { int &&x, y; };
85+
Z z = { v{1,2}[0], z.x = 10 };
86+

0 commit comments

Comments
 (0)