https://github.com/Takashiidobe updated 
https://github.com/llvm/llvm-project/pull/186594

>From bccd1fed6e25b57bcbb9990c6c4a6305d3af3288 Mon Sep 17 00:00:00 2001
From: Takashiidobe <[email protected]>
Date: Sat, 14 Mar 2026 09:43:44 -0400
Subject: [PATCH 1/4] allow C++26 constexpr structured pack bindings to be
 emitted as constants

---
 clang/lib/CodeGen/CGExpr.cpp | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 069846b854a87..b2ce2a4f7e24e 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1942,6 +1942,17 @@ CodeGenFunction::tryEmitAsConstant(const DeclRefExpr 
*RefExpr) {
     CEK = checkVarTypeForConstantEmission(var->getType());
   } else if (isa<EnumConstantDecl>(Value)) {
     CEK = CEK_AsValueOnly;
+  } else if (const auto *BD = dyn_cast<BindingDecl>(Value)) {
+    // For structured binding elements from tuple-like decompositions, the
+    // binding is backed by a hidden holding variable (the "reference
+    // temporary"). Use the holding variable's type to decide whether we
+    // can constant-emit. Without this, static constexpr pack bindings used
+    // as array indices always materialise as loads from their reference-
+    // temporary globals, blocking constant folding and vectorisation.
+    if (VarDecl *HV = BD->getHoldingVar())
+      CEK = checkVarTypeForConstantEmission(HV->getType());
+    else
+      CEK = CEK_None;
   } else {
     CEK = CEK_None;
   }
@@ -2003,6 +2014,16 @@ CodeGenFunction::tryEmitAsConstant(const DeclRefExpr 
*RefExpr) {
   if (isa<VarDecl>(Value)) {
     if (!getContext().DeclMustBeEmitted(cast<VarDecl>(Value)))
       EmitDeclRefExprDbgValue(RefExpr, result.Val);
+  } else if (const auto *BD = dyn_cast<BindingDecl>(Value)) {
+    // For tuple-like structured binding elements, the holding variable is
+    // always emitted (static storage), so only emit a debug reference if
+    // it is not otherwise required to be emitted.
+    if (VarDecl *HV = BD->getHoldingVar()) {
+      if (!getContext().DeclMustBeEmitted(HV))
+        EmitDeclRefExprDbgValue(RefExpr, result.Val);
+    } else {
+      EmitDeclRefExprDbgValue(RefExpr, result.Val);
+    }
   } else {
     assert(isa<EnumConstantDecl>(Value));
     EmitDeclRefExprDbgValue(RefExpr, result.Val);

>From eb2c2c1e517cd3fd421680eda83d697a6185613a Mon Sep 17 00:00:00 2001
From: Takashiidobe <[email protected]>
Date: Sat, 14 Mar 2026 09:43:54 -0400
Subject: [PATCH 2/4] add test from issue

---
 ...cpp26-constexpr-binding-pack-subscript.cpp | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 
clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp

diff --git a/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp 
b/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
new file mode 100644
index 0000000000000..3f7a02a78717e
--- /dev/null
+++ b/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang -std=c++26 -O3 -emit-llvm -S -target x86_64-unknown-linux-gnu 
-o - %s | FileCheck %s
+// RUN: %clang -std=c++26 -emit-llvm -S -target x86_64-unknown-linux-gnu -o - 
%s | FileCheck %s --check-prefix=IR
+// UNSUPPORTED: system-windows
+
+// static constexpr structured binding pack elements used as array
+// subscript indices must be constant-folded at the Clang codegen level.
+// Without the fix, each element is emitted as a load from a "reference
+// temporary" static variable, preventing vectorisation at -O3.
+
+#include <array>
+#include <cstdint>
+
+using u8 = std::uint8_t;
+
+template <u8 N>
+struct Range {
+  template <std::size_t I>
+  consteval friend u8 get(Range) noexcept { return I; }
+};
+namespace std {
+       template <u8 N>
+       struct tuple_size<Range<N>> { static constexpr std::size_t value = N; };
+       template <std::size_t I, u8 N>
+       struct tuple_element<I, Range<N>> { using type = u8; };
+} // namespace std
+
+template <std::size_t L, std::size_t R>
+__attribute__((always_inline)) inline constexpr std::array<u8, L + R>
+concat(const std::array<u8, L> &l, const std::array<u8, R> &r) {
+  static constexpr auto [...I] = Range<L>{};
+  static constexpr auto [...J] = Range<R>{};
+  return {l[I]..., r[J]...};
+}
+
+auto test(const std::array<u8, 16> &l, const std::array<u8, 16> &r) {
+  return concat(l, r);
+}
+
+// At -O3 the two 16-byte arrays should be copied with a pair of vector
+// loads/stores; no scalar byte loop and no reference-temporary indirection.
+// CHECK-LABEL: define {{.*}} @{{.*test.*}}
+// CHECK-NOT:   reference temporary
+// CHECK:       load <16 x i8>
+// CHECK:       store <16 x i8>
+// CHECK:       load <16 x i8>
+// CHECK:       store <16 x i8>
+// CHECK:       ret void
+
+// At any optimisation level the binding-pack indices must not be materialised
+// as "reference temporary" static variables.
+// IR-LABEL: define {{.*}} @{{.*test.*}}
+// IR-NOT:   reference temporary

>From a4547b4ec4fe3369e21aa5696a119e786650a126 Mon Sep 17 00:00:00 2001
From: Takashiidobe <[email protected]>
Date: Mon, 16 Mar 2026 21:45:52 -0400
Subject: [PATCH 3/4] code review feedback

---
 clang/lib/CodeGen/CGExpr.cpp                  | 24 ++++-----
 ...cpp26-constexpr-binding-pack-subscript.cpp | 52 +++++++++----------
 2 files changed, 35 insertions(+), 41 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index b2ce2a4f7e24e..bd94e0ea3ec5f 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1935,11 +1935,15 @@ CodeGenFunction::tryEmitAsConstant(const DeclRefExpr 
*RefExpr) {
   const ValueDecl *Value = RefExpr->getDecl();
 
   // The value needs to be an enum constant or a constant variable.
+  // For BindingDecls backed by a holding variable, UnderlyingVar points to
+  // that holding variable and is used below for debug-value emission.
   ConstantEmissionKind CEK;
+  const VarDecl *UnderlyingVar = nullptr;
   if (isa<ParmVarDecl>(Value)) {
     CEK = CEK_None;
   } else if (const auto *var = dyn_cast<VarDecl>(Value)) {
     CEK = checkVarTypeForConstantEmission(var->getType());
+    UnderlyingVar = var;
   } else if (isa<EnumConstantDecl>(Value)) {
     CEK = CEK_AsValueOnly;
   } else if (const auto *BD = dyn_cast<BindingDecl>(Value)) {
@@ -1949,10 +1953,12 @@ CodeGenFunction::tryEmitAsConstant(const DeclRefExpr 
*RefExpr) {
     // can constant-emit. Without this, static constexpr pack bindings used
     // as array indices always materialise as loads from their reference-
     // temporary globals, blocking constant folding and vectorisation.
-    if (VarDecl *HV = BD->getHoldingVar())
+    if (VarDecl *HV = BD->getHoldingVar()) {
       CEK = checkVarTypeForConstantEmission(HV->getType());
-    else
+      UnderlyingVar = HV;
+    } else {
       CEK = CEK_None;
+    }
   } else {
     CEK = CEK_None;
   }
@@ -2011,19 +2017,9 @@ CodeGenFunction::tryEmitAsConstant(const DeclRefExpr 
*RefExpr) {
 
   // Make sure we emit a debug reference to the global variable.
   // This should probably fire even for
-  if (isa<VarDecl>(Value)) {
-    if (!getContext().DeclMustBeEmitted(cast<VarDecl>(Value)))
+  if (UnderlyingVar) {
+    if (!getContext().DeclMustBeEmitted(UnderlyingVar))
       EmitDeclRefExprDbgValue(RefExpr, result.Val);
-  } else if (const auto *BD = dyn_cast<BindingDecl>(Value)) {
-    // For tuple-like structured binding elements, the holding variable is
-    // always emitted (static storage), so only emit a debug reference if
-    // it is not otherwise required to be emitted.
-    if (VarDecl *HV = BD->getHoldingVar()) {
-      if (!getContext().DeclMustBeEmitted(HV))
-        EmitDeclRefExprDbgValue(RefExpr, result.Val);
-    } else {
-      EmitDeclRefExprDbgValue(RefExpr, result.Val);
-    }
   } else {
     assert(isa<EnumConstantDecl>(Value));
     EmitDeclRefExprDbgValue(RefExpr, result.Val);
diff --git a/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp 
b/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
index 3f7a02a78717e..cd47a03dc3483 100644
--- a/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
+++ b/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
@@ -1,52 +1,50 @@
-// RUN: %clang -std=c++26 -O3 -emit-llvm -S -target x86_64-unknown-linux-gnu 
-o - %s | FileCheck %s
-// RUN: %clang -std=c++26 -emit-llvm -S -target x86_64-unknown-linux-gnu -o - 
%s | FileCheck %s --check-prefix=IR
-// UNSUPPORTED: system-windows
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -o - %s | 
FileCheck %s
 
 // static constexpr structured binding pack elements used as array
 // subscript indices must be constant-folded at the Clang codegen level.
 // Without the fix, each element is emitted as a load from a "reference
-// temporary" static variable, preventing vectorisation at -O3.
+// temporary" static variable.
 
-#include <array>
-#include <cstdint>
+namespace std {
+  using size_t = decltype(sizeof(0));
+  template <typename T> struct tuple_size;
+  template <size_t I, typename T> struct tuple_element;
+} // namespace std
 
-using u8 = std::uint8_t;
+using u8 = unsigned char;
 
 template <u8 N>
 struct Range {
   template <std::size_t I>
   consteval friend u8 get(Range) noexcept { return I; }
 };
+
 namespace std {
-       template <u8 N>
-       struct tuple_size<Range<N>> { static constexpr std::size_t value = N; };
-       template <std::size_t I, u8 N>
-       struct tuple_element<I, Range<N>> { using type = u8; };
+  template <u8 N>
+  struct tuple_size<Range<N>> { static constexpr std::size_t value = N; };
+  template <std::size_t I, u8 N>
+  struct tuple_element<I, Range<N>> { using type = u8; };
 } // namespace std
 
+template <std::size_t N>
+struct Array {
+  u8 data[N];
+  constexpr const u8 &operator[](std::size_t i) const { return data[i]; }
+};
+
 template <std::size_t L, std::size_t R>
-__attribute__((always_inline)) inline constexpr std::array<u8, L + R>
-concat(const std::array<u8, L> &l, const std::array<u8, R> &r) {
+__attribute__((always_inline)) inline constexpr Array<L + R>
+concat(const Array<L> &l, const Array<R> &r) {
   static constexpr auto [...I] = Range<L>{};
   static constexpr auto [...J] = Range<R>{};
-  return {l[I]..., r[J]...};
+  return {{l[I]..., r[J]...}};
 }
 
-auto test(const std::array<u8, 16> &l, const std::array<u8, 16> &r) {
+Array<32> test(const Array<16> &l, const Array<16> &r) {
   return concat(l, r);
 }
 
-// At -O3 the two 16-byte arrays should be copied with a pair of vector
-// loads/stores; no scalar byte loop and no reference-temporary indirection.
+// The binding-pack indices must not be materialised as "reference temporary"
+// static variables at any optimisation level.
 // CHECK-LABEL: define {{.*}} @{{.*test.*}}
 // CHECK-NOT:   reference temporary
-// CHECK:       load <16 x i8>
-// CHECK:       store <16 x i8>
-// CHECK:       load <16 x i8>
-// CHECK:       store <16 x i8>
-// CHECK:       ret void
-
-// At any optimisation level the binding-pack indices must not be materialised
-// as "reference temporary" static variables.
-// IR-LABEL: define {{.*}} @{{.*test.*}}
-// IR-NOT:   reference temporary

>From 976d8886fc642e143eb658c4b98dd6b687940865 Mon Sep 17 00:00:00 2001
From: Takashiidobe <[email protected]>
Date: Tue, 17 Mar 2026 22:04:09 -0400
Subject: [PATCH 4/4] fix test by adding cv-qualified tuple protocol forwarding
 to trigger constexpr pack decomposition

---
 ...cpp26-constexpr-binding-pack-subscript.cpp | 51 ++++++++++---------
 1 file changed, 28 insertions(+), 23 deletions(-)

diff --git a/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp 
b/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
index cd47a03dc3483..931469d207c49 100644
--- a/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
+++ b/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp
@@ -1,50 +1,55 @@
 // RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -o - %s | 
FileCheck %s
 
-// static constexpr structured binding pack elements used as array
-// subscript indices must be constant-folded at the Clang codegen level.
-// Without the fix, each element is emitted as a load from a "reference
-// temporary" static variable.
+// static constexpr structured binding pack elements used as array subscript 
+// indices must be constant-folded at the Clang codegen level. Without the 
fix, 
+// each element is emitted as a load from a reference temporary static 
variable.
 
 namespace std {
-  using size_t = decltype(sizeof(0));
-  template <typename T> struct tuple_size;
-  template <size_t I, typename T> struct tuple_element;
+       using size_t = decltype(sizeof(0));
+       template <typename T> struct tuple_size;
+       template <size_t I, typename T> struct tuple_element;
+
+       template <typename T> struct tuple_size<const T> : tuple_size<T> {};
+
+       template <size_t I, typename T>
+               struct tuple_element<I, const T> {
+                       using type = const typename tuple_element<I, T>::type;
+               };
 } // namespace std
 
 using u8 = unsigned char;
 
 template <u8 N>
 struct Range {
-  template <std::size_t I>
-  consteval friend u8 get(Range) noexcept { return I; }
+       template <std::size_t I>
+               consteval friend u8 get(Range) noexcept { return I; }
 };
 
 namespace std {
-  template <u8 N>
-  struct tuple_size<Range<N>> { static constexpr std::size_t value = N; };
-  template <std::size_t I, u8 N>
-  struct tuple_element<I, Range<N>> { using type = u8; };
+       template <u8 N>
+               struct tuple_size<Range<N>> { static constexpr std::size_t 
value = N; };
+       template <std::size_t I, u8 N>
+               struct tuple_element<I, Range<N>> { using type = u8; };
 } // namespace std
 
 template <std::size_t N>
 struct Array {
-  u8 data[N];
-  constexpr const u8 &operator[](std::size_t i) const { return data[i]; }
+       u8 data[N];
+       const u8 &operator[](std::size_t i) const { return data[i]; }
 };
 
 template <std::size_t L, std::size_t R>
-__attribute__((always_inline)) inline constexpr Array<L + R>
-concat(const Array<L> &l, const Array<R> &r) {
-  static constexpr auto [...I] = Range<L>{};
-  static constexpr auto [...J] = Range<R>{};
-  return {{l[I]..., r[J]...}};
+Array<L + R> concat(const Array<L> &l, const Array<R> &r) {
+       static constexpr auto [...I] = Range<L>{};
+       static constexpr auto [...J] = Range<R>{};
+       return {{l[I]..., r[J]...}};
 }
 
 Array<32> test(const Array<16> &l, const Array<16> &r) {
-  return concat(l, r);
+       return concat(l, r);
 }
 
-// The binding-pack indices must not be materialised as "reference temporary"
+// The binding-pack indices must not be materialised as reference-temporary
 // static variables at any optimisation level.
 // CHECK-LABEL: define {{.*}} @{{.*test.*}}
-// CHECK-NOT:   reference temporary
+// CHECK-NOT:   @_ZGR

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to