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/2] 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/2] 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

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

Reply via email to