llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Takashi Idobe (Takashiidobe)

<details>
<summary>Changes</summary>

Resolves: https://github.com/llvm/llvm-project/issues/164150

C++26 allows for constexpr packs in structured bindings. This is a new feature 
(the code doesn't compile on lower the -std=c++26) and so was previously 
unhandled in clang. 

This makes clang aware of packs and handle them as one constant unit instead of 
materializing them as separate mutable reference temporaries allowing llvm to 
optimize them.

This turns the example code from the issue into this as you would expect 
without compiling for zen 5 (the good codegen described).

```asm
  movq    %rdi, %rax
  movups  (%rsi), %xmm0
  movups  %xmm0, (%rdi)
  movups  (%rdx), %xmm0
  movups  %xmm0, 16(%rdi)
  retq
```

---
Full diff: https://github.com/llvm/llvm-project/pull/186594.diff


2 Files Affected:

- (modified) clang/lib/CodeGen/CGExpr.cpp (+21) 
- (added) clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp 
(+52) 


``````````diff
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);
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

``````````

</details>


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

Reply via email to