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
