https://github.com/Takashiidobe updated https://github.com/llvm/llvm-project/pull/186594
>From 60e7856e41ec089ee744263575c64336f6b0c533 Mon Sep 17 00:00:00 2001 From: Takashiidobe <[email protected]> Date: Sat, 14 Mar 2026 09:43:44 -0400 Subject: [PATCH 1/5] 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 23802cdeb4811..aa9411cff654e 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 34ef1db8f571117678b697355b4c48e128f3894a Mon Sep 17 00:00:00 2001 From: Takashiidobe <[email protected]> Date: Sat, 14 Mar 2026 09:43:54 -0400 Subject: [PATCH 2/5] 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 dc87ac57f932d6350c648d01f77cf76f6dd20437 Mon Sep 17 00:00:00 2001 From: Takashiidobe <[email protected]> Date: Mon, 16 Mar 2026 21:45:52 -0400 Subject: [PATCH 3/5] 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 aa9411cff654e..a82b30f65cc32 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 70997ba1cdefdc4cc415f04eaff9c9a549dc3574 Mon Sep 17 00:00:00 2001 From: Takashiidobe <[email protected]> Date: Tue, 17 Mar 2026 22:04:09 -0400 Subject: [PATCH 4/5] fix test by adding cv-qualified tuple protocol forwarding to trigger constexpr pack decomposition --- ...cpp26-constexpr-binding-pack-subscript.cpp | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 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..fdc6e9fb36e93 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 +// RUN: %clang_cc1 -std=c++26 -O2 -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 >From 45b620964bb6025d20b49c83ed1e8f822486e63c Mon Sep 17 00:00:00 2001 From: Takashiidobe <[email protected]> Date: Wed, 18 Mar 2026 19:36:35 -0400 Subject: [PATCH 5/5] fix constexpr codegen problem in CodeGenModule instead. Keep the CV-qualifiers from the MaterializeTemporaryExpr which can be used to generate better code in a const context --- clang/lib/CodeGen/CGExpr.cpp | 21 +------ clang/lib/CodeGen/CodeGenModule.cpp | 11 ++-- ...egen-for-constexpr-structured-bindings.cpp | 40 ++++++++++++++ clang/test/CodeGenCXX/const-init-cxx11.cpp | 3 +- ...cpp26-constexpr-binding-pack-subscript.cpp | 55 ------------------- .../CodeGenCXX/reference-temporary-ms.cpp | 2 +- clang/test/CodeGenCXX/temporaries.cpp | 5 +- 7 files changed, 52 insertions(+), 85 deletions(-) create mode 100644 clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp delete mode 100644 clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index a82b30f65cc32..23802cdeb4811 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1935,30 +1935,13 @@ 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)) { - // 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()); - UnderlyingVar = HV; - } else { - CEK = CEK_None; - } } else { CEK = CEK_None; } @@ -2017,8 +2000,8 @@ CodeGenFunction::tryEmitAsConstant(const DeclRefExpr *RefExpr) { // Make sure we emit a debug reference to the global variable. // This should probably fire even for - if (UnderlyingVar) { - if (!getContext().DeclMustBeEmitted(UnderlyingVar)) + if (isa<VarDecl>(Value)) { + if (!getContext().DeclMustBeEmitted(cast<VarDecl>(Value))) EmitDeclRefExprDbgValue(RefExpr, result.Val); } else { assert(isa<EnumConstantDecl>(Value)); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index daaa846bf42bc..256db38233c0e 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7357,11 +7357,12 @@ ConstantAddress CodeGenModule::GetAddrOfGlobalTemporary( E->getStorageDuration() == SD_Thread) && "not a global temporary"); const auto *VD = cast<VarDecl>(E->getExtendingDecl()); - // If we're not materializing a subobject of the temporary, keep the - // cv-qualifiers from the type of the MaterializeTemporaryExpr. - QualType MaterializedType = Init->getType(); - if (Init == E->getSubExpr()) - MaterializedType = E->getType(); + // Keep cv-qualifiers from the MaterializeTemporaryExpr on the storage type. + // The initializer expression may have had rvalue subobject adjustments + // stripped, which can drop top-level qualifiers that are still part of the + // materialized temporary's type. + QualType MaterializedType = getContext().getQualifiedType( + Init->getType(), E->getType().getQualifiers()); CharUnits Align = getContext().getTypeAlignInChars(MaterializedType); diff --git a/clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp b/clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp new file mode 100644 index 0000000000000..ce81f7d8d026e --- /dev/null +++ b/clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s + +namespace std { +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> + constexpr 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 + +const u8 &f() { + static constexpr auto [I] = Range<1>(); + return I; +} + +// CHECK: @[[TMP:_ZGR.*]] = internal constant i8 0, align 1 +// CHECK-LABEL: define {{.*}} @_Z1fv( +// CHECK: ret ptr @[[TMP]] diff --git a/clang/test/CodeGenCXX/const-init-cxx11.cpp b/clang/test/CodeGenCXX/const-init-cxx11.cpp index 0795fb534af4b..5dfe3488ca7bb 100644 --- a/clang/test/CodeGenCXX/const-init-cxx11.cpp +++ b/clang/test/CodeGenCXX/const-init-cxx11.cpp @@ -226,8 +226,7 @@ namespace LiteralReference { int n; }; - // This creates a non-const temporary and binds a reference to it. - // CHECK: @[[TEMP:.*]] = internal global {{.*}} { i32 5 }, align 4 + // CHECK: @[[TEMP:.*]] = internal constant {{.*}} { i32 5 }, align 4 // CHECK: @_ZN16LiteralReference3litE ={{.*}} constant {{.*}} @[[TEMP]], align 8 const Lit &lit = Lit(); diff --git a/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp b/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp deleted file mode 100644 index fdc6e9fb36e93..0000000000000 --- a/clang/test/CodeGenCXX/cpp26-constexpr-binding-pack-subscript.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// RUN: %clang_cc1 -std=c++26 -O2 -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. - -namespace std { - 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; } -}; - -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 N> -struct Array { - u8 data[N]; - const u8 &operator[](std::size_t i) const { return data[i]; } -}; - -template <std::size_t L, std::size_t R> -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); -} - -// The binding-pack indices must not be materialised as reference-temporary -// static variables at any optimisation level. -// CHECK-LABEL: define {{.*}} @{{.*test.*}} -// CHECK-NOT: @_ZGR diff --git a/clang/test/CodeGenCXX/reference-temporary-ms.cpp b/clang/test/CodeGenCXX/reference-temporary-ms.cpp index 6c4101634920d..34145cdbd96a7 100644 --- a/clang/test/CodeGenCXX/reference-temporary-ms.cpp +++ b/clang/test/CodeGenCXX/reference-temporary-ms.cpp @@ -5,6 +5,6 @@ const int __declspec(dllexport) &Exported = 42; // The reference temporary shouldn't be dllexport, even if the reference is. // PRE17: @"?$RT1@Exported@@3ABHB" = internal constant i32 42 -// CXX17: @"?$RT1@Exported@@3ABHB" = internal global i32 42 +// CXX17: @"?$RT1@Exported@@3ABHB" = internal constant i32 42 // CHECK: @"?Exported@@3ABHB" = dso_local dllexport constant ptr @"?$RT1@Exported@@3ABHB" diff --git a/clang/test/CodeGenCXX/temporaries.cpp b/clang/test/CodeGenCXX/temporaries.cpp index 146ebc7dd090e..c3842776e0c5e 100644 --- a/clang/test/CodeGenCXX/temporaries.cpp +++ b/clang/test/CodeGenCXX/temporaries.cpp @@ -48,8 +48,7 @@ namespace BraceInit { typedef const int &CIR; CIR x = CIR{3}; // CHECK-CXX11: @_ZGRN9BraceInit1xE_ = internal constant i32 3 - // FIXME: This should still be emitted as 'constant' in C++17. - // CHECK-CXX17: @_ZGRN9BraceInit1xE_ = internal global i32 3 + // CHECK-CXX17: @_ZGRN9BraceInit1xE_ = internal constant i32 3 // CHECK: @_ZN9BraceInit1xE ={{.*}} constant ptr @_ZGRN9BraceInit1xE_ } @@ -59,7 +58,7 @@ namespace RefTempSubobject { int ints[3] = {1, 2, 3}; }; - // CHECK: @_ZGRN16RefTempSubobject2srE_ = internal global { ptr, [3 x i32] } { {{.*}} getelementptr {{.*}} @_ZGRN16RefTempSubobject2srE_, {{.*}}, [3 x i32] [i32 1, i32 2, i32 3] } + // CHECK: @_ZGRN16RefTempSubobject2srE_ = internal constant { ptr, [3 x i32] } { {{.*}} getelementptr {{.*}} @_ZGRN16RefTempSubobject2srE_, {{.*}}, [3 x i32] [i32 1, i32 2, i32 3] } // CHECK: @_ZN16RefTempSubobject2srE = constant {{.*}} @_ZGRN16RefTempSubobject2srE_ constexpr const SelfReferential &sr = SelfReferential(); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
