[clang] 8d4aa1f - [clang][Interp] Update global temporaries at the end of a declaration

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 29 12:10:38 PDT 2024


Author: Timm Bäder
Date: 2024-06-29T21:10:24+02:00
New Revision: 8d4aa1f22ea08b12a7958b681e8a265f78cb349f

URL: https://github.com/llvm/llvm-project/commit/8d4aa1f22ea08b12a7958b681e8a265f78cb349f
DIFF: https://github.com/llvm/llvm-project/commit/8d4aa1f22ea08b12a7958b681e8a265f78cb349f.diff

LOG: [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.

Added: 
    clang/test/AST/Interp/const-temporaries.cpp

Modified: 
    clang/lib/AST/Interp/EvalEmitter.cpp
    clang/lib/AST/Interp/EvalEmitter.h
    clang/lib/AST/Interp/Interp.h
    clang/lib/AST/Interp/InterpState.h

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index a13cd94b0ba1c..6748b305d5c8e 100644
--- a/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -71,6 +71,7 @@ EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
     EvalResult.setInvalid();
 
   S.EvaluatingDecl = nullptr;
+  updateGlobalTemporaries();
   return std::move(this->EvalResult);
 }
 
@@ -237,6 +238,28 @@ bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
   return true;
 }
 
+/// Global temporaries (LifetimeExtendedTemporary) carry their value
+/// around as an APValue, which codegen accesses.
+/// We set their value once when creating them, but we don't update it
+/// afterwards when code changes it later.
+/// This is what we do here.
+void EvalEmitter::updateGlobalTemporaries() {
+  for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
+    if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
+      const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
+      APValue *Cached = Temp->getOrCreateValue(true);
+
+      if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
+        TYPE_SWITCH(*T, { *Cached = Ptr.deref<T>().toAPValue(); });
+      } else {
+        if (std::optional<APValue> APV = Ptr.toRValue(Ctx))
+          *Cached = *APV;
+      }
+    }
+  }
+  S.SeenGlobalTemporaries.clear();
+}
+
 //===----------------------------------------------------------------------===//
 // Opcode evaluators
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h
index 109e6e0bc0d76..d1e125cae9594 100644
--- a/clang/lib/AST/Interp/EvalEmitter.h
+++ b/clang/lib/AST/Interp/EvalEmitter.h
@@ -109,6 +109,8 @@ class EvalEmitter : public SourceMapper {
     return reinterpret_cast<Block *>(It->second.get());
   }
 
+  void updateGlobalTemporaries();
+
   // The emitter always tracks the current instruction and sets OpPC to a token
   // value which is mapped to the location of the opcode being evaluated.
   CodePtr OpPC;

diff  --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 6c9246f692204..75a8f66cc5d50 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1283,16 +1283,20 @@ bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
                     const LifetimeExtendedTemporaryDecl *Temp) {
-  assert(Temp);
+  const Pointer &Ptr = S.P.getGlobal(I);
+
   const T Value = S.Stk.peek<T>();
   APValue APV = Value.toAPValue();
   APValue *Cached = Temp->getOrCreateValue(true);
   *Cached = APV;
 
-  const Pointer &P = S.P.getGlobal(I);
-  P.deref<T>() = S.Stk.pop<T>();
-  P.initialize();
+  assert(Ptr.getDeclDesc()->asExpr());
+
+  S.SeenGlobalTemporaries.push_back(
+      std::make_pair(Ptr.getDeclDesc()->asExpr(), Temp));
 
+  Ptr.deref<T>() = S.Stk.pop<T>();
+  Ptr.initialize();
   return true;
 }
 
@@ -1305,6 +1309,9 @@ inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC,
   const Pointer &P = S.Stk.peek<Pointer>();
   APValue *Cached = Temp->getOrCreateValue(true);
 
+  S.SeenGlobalTemporaries.push_back(
+      std::make_pair(P.getDeclDesc()->asExpr(), Temp));
+
   if (std::optional<APValue> APV = P.toRValue(S.getCtx())) {
     *Cached = *APV;
     return true;

diff  --git a/clang/lib/AST/Interp/InterpState.h b/clang/lib/AST/Interp/InterpState.h
index 925395b6eba0c..138e1d7ac95d5 100644
--- a/clang/lib/AST/Interp/InterpState.h
+++ b/clang/lib/AST/Interp/InterpState.h
@@ -123,6 +123,10 @@ class InterpState final : public State, public SourceMapper {
   SourceLocation EvalLocation;
   /// Declaration we're initializing/evaluting, if any.
   const VarDecl *EvaluatingDecl = nullptr;
+
+  llvm::SmallVector<
+      std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
+      SeenGlobalTemporaries;
 };
 
 } // namespace interp

diff  --git a/clang/test/AST/Interp/const-temporaries.cpp b/clang/test/AST/Interp/const-temporaries.cpp
new file mode 100644
index 0000000000000..1f48786691c1d
--- /dev/null
+++ b/clang/test/AST/Interp/const-temporaries.cpp
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++1y -fexperimental-new-constant-interpreter | FileCheck %s
+//
+// expected-no-diagnostics
+
+struct A {
+  constexpr A() : n(1) {}
+  ~A();
+  int n;
+};
+struct B : A {
+  A a[3];
+  constexpr B() {
+    ++a[0].n;
+    a[1].n += 2;
+    a[2].n = n + a[1].n;
+  }
+};
+B b;
+
+// CHECK: @b ={{.*}} global {{.*}} i32 1, {{.*}} { i32 2 }, {{.*}} { i32 3 }, {{.*}} { i32 4 }
+// CHECK-NOT: _ZN1BC
+
+namespace ModifyStaticTemporary {
+  struct A { int &&temporary; int x; };
+  constexpr int f(int &r) { r *= 9; return r - 12; }
+  A a = { 6, f(a.temporary) };
+  // CHECK: @_ZGRN21ModifyStaticTemporary1aE_ = internal global i32 54
+  // CHECK: @_ZN21ModifyStaticTemporary1aE ={{.*}} global {{.*}} ptr @_ZGRN21ModifyStaticTemporary1aE_, i32 42
+
+  A b = { 7, ++b.temporary };
+  // CHECK: @_ZGRN21ModifyStaticTemporary1bE_ = internal global i32 8
+  // CHECK: @_ZN21ModifyStaticTemporary1bE ={{.*}} global {{.*}} ptr @_ZGRN21ModifyStaticTemporary1bE_, i32 8
+
+  // Can't emit all of 'c' as a constant here, so emit the initial value of
+  // 'c.temporary', not the value as modified by the partial evaluation within
+  // the initialization of 'c.x'.
+  A c = { 10, (++c.temporary, b.x) };
+  // CHECK: @_ZN21ModifyStaticTemporary1cE ={{.*}} global {{.*}} zeroinitializer
+  // CHECK: @_ZGRN21ModifyStaticTemporary1cE_ = internal global i32 10
+}
+
+// CHECK: @_ZGRN28VariableTemplateWithConstRef1iIvEE_ = linkonce_odr constant i32 5, align 4
+// CHECK: @_ZN28VariableTemplateWithConstRef3useE ={{.*}} constant ptr @_ZGRN28VariableTemplateWithConstRef1iIvEE_
+namespace VariableTemplateWithConstRef {
+  template <typename T>
+  const int &i = 5;
+  const int &use = i<void>;
+}
+
+// CHECK: @_ZGRN34HiddenVariableTemplateWithConstRef1iIvEE_ = linkonce_odr hidden constant i32 5, align 4
+// CHECK: @_ZN34HiddenVariableTemplateWithConstRef3useE ={{.*}} constant ptr @_ZGRN34HiddenVariableTemplateWithConstRef1iIvEE_
+namespace HiddenVariableTemplateWithConstRef {
+  template <typename T>
+  __attribute__((visibility("hidden"))) const int &i = 5;
+  const int &use = i<void>;
+}
+
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE1_ = linkonce_odr constant i32 1
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE0_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE1_ }
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE3_ = linkonce_odr constant i32 2
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE2_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE3_ }
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE5_ = linkonce_odr constant i32 3
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE4_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE5_ }
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE7_ = linkonce_odr constant i32 4
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE6_ = linkonce_odr global {{.*}} { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE7_ }
+// CHECK: @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE_ = linkonce_odr global %"struct.VariableTemplateWithPack::S" { ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE0_, ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE2_, ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE4_, ptr @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE6_ }
+// CHECK: @_ZN24VariableTemplateWithPack1pE ={{.*}} global {{.*}} @_ZGRN24VariableTemplateWithPack1sIJLi1ELi2ELi3ELi4EEEE_
+namespace VariableTemplateWithPack {
+  struct A {
+    const int &r;
+  };
+  struct S {
+    A &&a, &&b, &&c, &&d;
+  };
+  template <int... N>
+  S &&s = {A{N}...};
+  S *p = &s<1, 2, 3, 4>;
+}
+
+
+// CHECK: @_ZGR1z_ ={{.*}} global [2 x i32] [i32 10, i32 2]
+// CHECK: @z = global { ptr, i32 } { ptr @_ZGR1z_, i32 10 }
+typedef int v[2];
+struct Z { int &&x, y; };
+Z z = { v{1,2}[0], z.x = 10 };
+


        


More information about the cfe-commits mailing list