From b4fe3880d09bf3d396bde45cd481449da31e5e17 Mon Sep 17 00:00:00 2001 From: Cullen Rhodes Date: Tue, 2 Jun 2026 06:58:49 +0000 Subject: [PATCH 01/11] Bump version to 22.1.8 --- cmake/Modules/LLVMVersion.cmake | 2 +- libcxx/include/__config | 2 +- llvm/utils/gn/secondary/llvm/version.gni | 2 +- llvm/utils/lit/lit/__init__.py | 2 +- llvm/utils/mlgo-utils/mlgo/__init__.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake index feb48f124e6ee..087b24ae6edb9 100644 --- a/cmake/Modules/LLVMVersion.cmake +++ b/cmake/Modules/LLVMVersion.cmake @@ -7,7 +7,7 @@ if(NOT DEFINED LLVM_VERSION_MINOR) set(LLVM_VERSION_MINOR 1) endif() if(NOT DEFINED LLVM_VERSION_PATCH) - set(LLVM_VERSION_PATCH 7) + set(LLVM_VERSION_PATCH 8) endif() if(NOT DEFINED LLVM_VERSION_SUFFIX) set(LLVM_VERSION_SUFFIX) diff --git a/libcxx/include/__config b/libcxx/include/__config index 3a34eea8edc1e..f72f091d28c76 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -30,7 +30,7 @@ // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM. // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is // defined to XXYYZZ. -# define _LIBCPP_VERSION 220107 +# define _LIBCPP_VERSION 220108 # define _LIBCPP_CONCAT_IMPL(_X, _Y) _X##_Y # define _LIBCPP_CONCAT(_X, _Y) _LIBCPP_CONCAT_IMPL(_X, _Y) diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni index 22820f1de88aa..1b7c6951cc695 100644 --- a/llvm/utils/gn/secondary/llvm/version.gni +++ b/llvm/utils/gn/secondary/llvm/version.gni @@ -1,4 +1,4 @@ llvm_version_major = 22 llvm_version_minor = 1 -llvm_version_patch = 7 +llvm_version_patch = 8 llvm_version = "$llvm_version_major.$llvm_version_minor.$llvm_version_patch" diff --git a/llvm/utils/lit/lit/__init__.py b/llvm/utils/lit/lit/__init__.py index 5d26e351fd2e8..7dcd3b34547dc 100644 --- a/llvm/utils/lit/lit/__init__.py +++ b/llvm/utils/lit/lit/__init__.py @@ -2,7 +2,7 @@ __author__ = "Daniel Dunbar" __email__ = "daniel@minormatter.com" -__versioninfo__ = (22, 1, 7) +__versioninfo__ = (22, 1, 8) __version__ = ".".join(str(v) for v in __versioninfo__) + "dev" __all__ = [] diff --git a/llvm/utils/mlgo-utils/mlgo/__init__.py b/llvm/utils/mlgo-utils/mlgo/__init__.py index 3b4b668e5bdd1..198ab79a7e158 100644 --- a/llvm/utils/mlgo-utils/mlgo/__init__.py +++ b/llvm/utils/mlgo-utils/mlgo/__init__.py @@ -4,7 +4,7 @@ from datetime import timezone, datetime -__versioninfo__ = (22, 1, 7) +__versioninfo__ = (22, 1, 8) __version__ = ( ".".join(str(v) for v in __versioninfo__) + "dev" From b65aa9b5ea8d15276cb4f02214039708af045cbd Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Mon, 1 Jun 2026 20:36:12 +0800 Subject: [PATCH 02/11] [Clang] Profile the NNS of UnresolvedUsingType and CXXThisType correctly in concept hashing (#199617) They were sometimes incorrect because the written type doesn't contain an NNS which contains template parameters we're interested in. No release note because the bug broke MS STL and I want to backport it to the last 22.x release Fixes https://github.com/llvm/llvm-project/issues/198663 (cherry picked from commit 06ffb655904657e847854eee1c7c76ccdc75b254) --- clang/lib/Sema/SemaConcept.cpp | 14 +++ .../test/SemaTemplate/concepts-using-decl.cpp | 98 +++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index fdaad31233b29..9ffecaca04a18 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -381,6 +381,10 @@ class HashParameterMapping : public RecursiveASTVisitor { return true; } + bool TraverseCXXThisExpr(CXXThisExpr *E) { + return inherited::TraverseType(E->getType()); + } + bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) { // We don't care about TypeLocs. So traverse Types instead. return TraverseType(TL.getType().getCanonicalType(), TraverseQualifier); @@ -394,6 +398,16 @@ class HashParameterMapping : public RecursiveASTVisitor { return true; } + bool TraverseUnresolvedUsingType(UnresolvedUsingType *T, + bool TraverseQualifier) { + // Sometimes the written type doesn't contain a qualifier which contains + // necessary template arguments, whereas the declaration does. + if (NestedNameSpecifier NNS = T->getDecl()->getQualifier(); + TraverseQualifier && NNS) + return inherited::TraverseNestedNameSpecifier(NNS); + return inherited::TraverseUnresolvedUsingType(T, TraverseQualifier); + } + bool TraverseInjectedClassNameType(InjectedClassNameType *T, bool TraverseQualifier) { return TraverseTemplateArguments(T->getTemplateArgs(SemaRef.Context)); diff --git a/clang/test/SemaTemplate/concepts-using-decl.cpp b/clang/test/SemaTemplate/concepts-using-decl.cpp index 41f7b6d2f8faa..5ccec3cee9402 100644 --- a/clang/test/SemaTemplate/concepts-using-decl.cpp +++ b/clang/test/SemaTemplate/concepts-using-decl.cpp @@ -197,3 +197,101 @@ struct child : base { }; } + +namespace GH198663 { + +template +concept HasIsTransparent = requires { typename T::is_transparent; }; + +template +struct FlatMapBase { + using key_compare = Compare; +}; + +template +struct FlatMap : FlatMapBase { + using Base = FlatMapBase; + + using typename Base::key_compare; + + void at(const K&) {} + void at(const K&) const {} + template + void at(const Other&) + requires HasIsTransparent + {} + template + void at(const Other&) const + requires HasIsTransparent + {} +}; + +template +struct Transparent { + T t; +}; + +struct TransparentComparator { + using is_transparent = void; + + template + bool operator()(const T&, const Transparent&) const; + + template + bool operator()(const Transparent&, const T& t) const; + + template + bool operator()(const T&, const T&) const; +}; + +struct NonTransparentComparator { + template + bool operator()(const T&, const Transparent&) const; + + template + bool operator()(const Transparent&, const T&) const; + + template + bool operator()(const T&, const T&) const; +}; + +template +concept CanAt = requires(M m, Transparent k) { m.at(k); }; + +using TransparentMap = FlatMap; +using NonTransparentMap = FlatMap; + +static_assert(CanAt); + +static_assert(!CanAt); + +} + +namespace GH198663_2 { + +template +auto mv(T& t) -> T&&; + +template +concept does_foo = requires(S s) { + s.template foo(); +}; + +template +struct type { + S member; + template + auto foo() -> T requires does_foo; +}; + +struct returns_int { + template + auto foo() -> T; +}; + +struct nothing {}; + +static_assert(does_foo&, int>); +static_assert(not does_foo&, int>); + +} From 60c18c6aeaedc70e0c1c95be2e9397e788e3b142 Mon Sep 17 00:00:00 2001 From: Cullen Rhodes Date: Tue, 2 Jun 2026 06:45:33 +0100 Subject: [PATCH 03/11] [CMake][Release] Use llvm-bitcode-strip on Darwin for build with -ffat-lto-objects (#200764) Building with --fat-lto-objects was added in #140381 (cff9ae7a15a5). On macOS the tests are failing when building release binaries with many "The file was not recognized as a valid object file" errors, e.g.: 2026-01-16T12:54:26.0928880Z /Users/runner/work/llvm-project/llvm-project/build/tools/clang/stage2-instrumented-bins/tools/clang/stage2-bins/_CPack_Packages/Darwin/TXZ/LLVM-22.1.0-rc1-macOS-ARM64/bin/llvm-strip: error: '/Users/runner/work/llvm-project/llvm-project/build/tools/clang/stage2-instrumented-bins/tools/clang/stage2-bins/_CPack_Packages/Darwin/TXZ/LLVM-22.1.0-rc1-macOS-ARM64/lib/libLLVMAArch64AsmParser.a(AArch64AsmParser.cpp.o)': The file was not recognized as a valid object file It's assuming bitcode is embedded in section .llvm.lto (llvm/lib/Object/ObjectFile.cpp:80) but on MachO it's in __LLVM,__bitcode (llvm/lib/Object/MachOObjectFile.cpp:2184) llvm-bitcode-strip is a driver for the MachO bitcode_strip utility which handles this. Use this instead. Fixes #176398. Assisted-by: codex (cherry picked from commit b8e768f6ae95343ac542b195265572ecd063fc2b) --- .../caches/release_cpack_pre_build_strip_lto.cmake | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/clang/cmake/caches/release_cpack_pre_build_strip_lto.cmake b/clang/cmake/caches/release_cpack_pre_build_strip_lto.cmake index 743b64fe00f58..e7d8830515439 100644 --- a/clang/cmake/caches/release_cpack_pre_build_strip_lto.cmake +++ b/clang/cmake/caches/release_cpack_pre_build_strip_lto.cmake @@ -1,5 +1,14 @@ file(GLOB files ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/lib/*.a) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(strip_command + ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/bin/llvm-bitcode-strip) + set(strip_args -r) +else() + set(strip_command ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/bin/llvm-strip) + set(strip_args --no-strip-all -R .llvm.lto) +endif() + foreach(file ${files}) - execute_process(COMMAND ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/bin/llvm-strip --no-strip-all -R .llvm.lto ${file}) + execute_process(COMMAND ${strip_command} ${strip_args} ${file} -o ${file}) endforeach() From 724301d98c6ef981111a077489358de10ec14127 Mon Sep 17 00:00:00 2001 From: Abinaya Saravanan Date: Thu, 4 Jun 2026 23:24:11 +0530 Subject: [PATCH 04/11] Fix "Cannot select" crash on bitcast between f64 and int vector types (#201509) All of i64, f64, v2i32, v4i16, v8i8 are assigned to the DoubleRegs register class (64-bit register pairs). A bitcast between any two of these types is a machine-level no-op (ie. the same physical register is reinterpreted with a different type). HexagonPatterns.td had NopCast_pat entries for all int-to-int bitcasts within DoubleRegs, and explicit patterns for f64 <-> i64, but was missing patterns for f64 <-> v2i32, f64 <-> v4i16, and f64 <-> v8i8. The same gap existed in IntRegs for f32 <-> v2i16 and f32 <-> v4i8. Without a tableGen pattern for "f64 = bitcast v2i32" node, the instruction selector crashed with: LLVM ERROR: Cannot select: t26: f64 = bitcast t6 t6: v2i32,ch = CopyFromReg t0, Register:v2i32 %2 Fix by adding the five missing NopCast_pat entries. Fixes: https://github.com/llvm/llvm-project/issues/195495 (cherry picked from commit c005f737e764fab61a02ca5587cf43a16716b5fc) --- llvm/lib/Target/Hexagon/HexagonPatterns.td | 9 ++- .../CodeGen/Hexagon/bitcast-f64-vector.ll | 63 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/Hexagon/bitcast-f64-vector.ll diff --git a/llvm/lib/Target/Hexagon/HexagonPatterns.td b/llvm/lib/Target/Hexagon/HexagonPatterns.td index f6d51acce562e..7d5e3feec6bbe 100644 --- a/llvm/lib/Target/Hexagon/HexagonPatterns.td +++ b/llvm/lib/Target/Hexagon/HexagonPatterns.td @@ -530,17 +530,22 @@ def: Pat<(i64 (bitconvert F64:$v)), (I64:$v)>; def: Pat<(f64 (bitconvert I64:$v)), (F64:$v)>; // Bit convert 32- and 64-bit types. -// All of these are bitcastable to one another: i32, v2i16, v4i8. +// All of these are bitcastable to one another: i32, f32, v2i16, v4i8. defm: NopCast_pat; defm: NopCast_pat; defm: NopCast_pat; -// All of these are bitcastable to one another: i64, v2i32, v4i16, v8i8. +defm: NopCast_pat; +defm: NopCast_pat; +// All of these are bitcastable to one another: i64, f64, v2i32, v4i16, v8i8. defm: NopCast_pat; defm: NopCast_pat; defm: NopCast_pat; defm: NopCast_pat; defm: NopCast_pat; defm: NopCast_pat; +defm: NopCast_pat; +defm: NopCast_pat; +defm: NopCast_pat; // --(3) Extend/truncate/saturate ---------------------------------------- diff --git a/llvm/test/CodeGen/Hexagon/bitcast-f64-vector.ll b/llvm/test/CodeGen/Hexagon/bitcast-f64-vector.ll new file mode 100644 index 0000000000000..172a3ab32e616 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/bitcast-f64-vector.ll @@ -0,0 +1,63 @@ +; RUN: llc -mtriple=hexagon -mcpu=hexagonv68 -O3 < %s | FileCheck %s + +; Verify that bitcasts between f64 and the integer vector types that share the +; DoubleRegs register class (v2i32, v4i16, v8i8) are treated as no-ops by the +; instruction selector and do not cause a "Cannot select" crash. +; +; All of i64, f64, v2i32, v4i16, v8i8 live in DoubleRegs (64-bit register +; pairs). A bitcast between any two of them is a pure reinterpretation of the +; same 64 bits. Therefore no instruction is emitted. +; +; Regression test for: llvm.org/PR195495 +; llc -mtriple=hexagon -mcpu=hexagonv68 -O3 crashed with +; "Cannot select: f64 = bitcast v2i32" when compiling Eigen's packetmath. + +; CHECK-LABEL: test_v2i32_to_f64: +; CHECK: dfcmp +; CHECK: jumpr r31 +define i1 @test_v2i32_to_f64(<2 x i32> %a) { + %bc = bitcast <2 x i32> %a to double + %cmp = fcmp une double %bc, 0.0 + ret i1 %cmp +} + +; f64->v2i32 is a no-op: the argument is already in a DoubleReg pair. +; CHECK-LABEL: test_f64_to_v2i32: +; CHECK-NOT: combine +; CHECK: jumpr r31 +define <2 x i32> @test_f64_to_v2i32(double %a) { + %bc = bitcast double %a to <2 x i32> + ret <2 x i32> %bc +} + +; CHECK-LABEL: test_v4i16_to_f64: +; CHECK: dfcmp +; CHECK: jumpr r31 +define i1 @test_v4i16_to_f64(<4 x i16> %a) { + %bc = bitcast <4 x i16> %a to double + %cmp = fcmp une double %bc, 0.0 + ret i1 %cmp +} + +; CHECK-LABEL: test_v8i8_to_f64: +; CHECK: dfcmp +; CHECK: jumpr r31 +define i1 @test_v8i8_to_f64(<8 x i8> %a) { + %bc = bitcast <8 x i8> %a to double + %cmp = fcmp une double %bc, 0.0 + ret i1 %cmp +} + +; Regression test: the original crash. +; <4 x i32> is passed in two v2i32 DoubleReg pairs; after type-legalizing +; <2 x f64> setcc into two scalar f64 setcc ops, each f64 operand is produced +; by a "f64 = bitcast v2i32" node that previously had no matching pattern. +; CHECK-LABEL: test_packetmath_reduced: +; CHECK: dfcmp +; CHECK: jumpr r31 +define <2 x i1> @test_packetmath_reduced(<4 x i32> %arg) { +entry: + %bc = bitcast <4 x i32> %arg to <2 x double> + %cmp = fcmp une <2 x double> %bc, zeroinitializer + ret <2 x i1> %cmp +} From 761b9134dd9beda6f133fdc19a42b148834cb06b Mon Sep 17 00:00:00 2001 From: Bidhan Date: Wed, 20 May 2026 00:39:54 +0545 Subject: [PATCH 05/11] [BPF] treat compiler fence as codegen no-op (#196734) The BPF backend has no instruction-selection pattern for `ISD::ATOMIC_FENCE`, so LLVM IR containing a fence instruction crashes with `Cannot select: AtomicFence...`. **Rust code snippet** ``` #[unsafe(no_mangle)] pub fn entrypoint(_input: *mut u8) -> u64 { core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); 0 } ``` **LLVM IR** ``` ; ModuleID = 'linked_module' source_filename = "linked_module" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpfel" ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn define dso_local noundef i64 @entrypoint(ptr noundef readnone captures(none) %0) unnamed_addr #0 { fence syncscope("singlethread") seq_cst ret i64 0 } attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn "target-cpu"="generic" } !llvm.ident = !{!0} !0 = !{!"rustc version 1.97.0-nightly (f53b654a8 2026-04-30)"} ``` **Error** ``` LLVM ERROR: Cannot select: 0x7fcf7701a1c0: ch = AtomicFence 0x7fcf75718df8, TargetConstant:i64<7>, TargetConstant:i64<0> In function: entrypoint Stack dump: 0. Running pass 'Function Pass Manager' on module 'linked_module'. 1. Running pass 'BPF DAG->DAG Pattern Instruction Selection' on function '@entrypoint' ``` ----- **Fix** This patch lowers single-thread (compiler) fences to `ISD::MEMBARRIER` (no-op) and produces an error for cross-thread (runtime) fences. --- llvm/lib/Target/BPF/BPFISelLowering.cpp | 17 +++++++++++++++++ llvm/lib/Target/BPF/BPFISelLowering.h | 1 + llvm/test/CodeGen/BPF/fence-singlethread.ll | 14 ++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 llvm/test/CodeGen/BPF/fence-singlethread.ll diff --git a/llvm/lib/Target/BPF/BPFISelLowering.cpp b/llvm/lib/Target/BPF/BPFISelLowering.cpp index 5c03776dd6653..faf3af0918d54 100644 --- a/llvm/lib/Target/BPF/BPFISelLowering.cpp +++ b/llvm/lib/Target/BPF/BPFISelLowering.cpp @@ -110,6 +110,8 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM, setOperationAction(ISD::ATOMIC_STORE, VT, Custom); } + setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom); + for (auto VT : { MVT::i32, MVT::i64 }) { if (VT == MVT::i32 && !STI.getHasAlu32()) continue; @@ -368,6 +370,8 @@ SDValue BPFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { case ISD::ATOMIC_LOAD: case ISD::ATOMIC_STORE: return LowerATOMIC_LOAD_STORE(Op, DAG); + case ISD::ATOMIC_FENCE: + return LowerATOMIC_FENCE(Op, DAG); case ISD::TRAP: return LowerTRAP(Op, DAG); } @@ -772,6 +776,19 @@ SDValue BPFTargetLowering::LowerATOMIC_LOAD_STORE(SDValue Op, return Op; } +SDValue BPFTargetLowering::LowerATOMIC_FENCE(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SyncScope::ID FenceSSID = + static_cast(Op.getConstantOperandVal(2)); + + if (FenceSSID == SyncScope::SingleThread) + // MEMBARRIER is a compiler barrier; it codegens to a no-op. + return DAG.getNode(ISD::MEMBARRIER, DL, MVT::Other, Op.getOperand(0)); + + report_fatal_error("Runtime fence is not supported at the moment"); +} + static Function *createBPFUnreachable(Module *M) { if (auto *Fn = M->getFunction(BPF_TRAP)) return Fn; diff --git a/llvm/lib/Target/BPF/BPFISelLowering.h b/llvm/lib/Target/BPF/BPFISelLowering.h index 8607e4f8c9e69..df1e2d8114ae1 100644 --- a/llvm/lib/Target/BPF/BPFISelLowering.h +++ b/llvm/lib/Target/BPF/BPFISelLowering.h @@ -73,6 +73,7 @@ class BPFTargetLowering : public TargetLowering { SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerATOMIC_LOAD_STORE(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerATOMIC_FENCE(SDValue Op, SelectionDAG &DAG) const; SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerTRAP(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/test/CodeGen/BPF/fence-singlethread.ll b/llvm/test/CodeGen/BPF/fence-singlethread.ll new file mode 100644 index 0000000000000..91ea38db8b8c8 --- /dev/null +++ b/llvm/test/CodeGen/BPF/fence-singlethread.ll @@ -0,0 +1,14 @@ +; RUN: llc < %s -mtriple=bpfel | FileCheck %s +; RUN: llc < %s -mtriple=bpfeb | FileCheck %s + +; CHECK-LABEL: fence_singlethread: +; CHECK-COUNT-4: #MEMBARRIER +; CHECK-NEXT: exit +define void @fence_singlethread() nounwind { +entry: + fence syncscope("singlethread") acquire + fence syncscope("singlethread") release + fence syncscope("singlethread") acq_rel + fence syncscope("singlethread") seq_cst + ret void +} From 070e505ce21d5bb0aae18704ec3a2660f59b7e3c Mon Sep 17 00:00:00 2001 From: Andrew Ng Date: Tue, 9 Jun 2026 11:55:05 +0100 Subject: [PATCH 06/11] [LLD][ELF] Add missing initialization of Symbol `used` member. On the "main" branch, Symbol no longer has the `used` member which was moved in PR #190117 (commit 6a874161). Therefore, when the cherry pick from PR #198129 (commit 905a88b9) was merged to the "release/22.x" branch, the initialization of `used` was lost. --- lld/ELF/Symbols.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 23994ccea1ef0..26d28825558c9 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -243,11 +243,11 @@ class Symbol { uint8_t stOther, uint8_t type) : file(file), nameData(name.data()), nameSize(name.size()), type(type), binding(binding), stOther(stOther), symbolKind(k), isPreemptible(false), - isUsedInRegularObj(false), isExported(false), ltoCanOmit(false), - traced(false), hasVersionSuffix(false), isInIplt(false), - gotInIgot(false), folded(false), archSpecificBit(false), - scriptDefined(false), dsoDefined(false), dsoProtected(false), - versionScriptAssigned(false), thunkAccessed(false), + isUsedInRegularObj(false), used(false), isExported(false), + ltoCanOmit(false), traced(false), hasVersionSuffix(false), + isInIplt(false), gotInIgot(false), folded(false), + archSpecificBit(false), scriptDefined(false), dsoDefined(false), + dsoProtected(false), versionScriptAssigned(false), thunkAccessed(false), inDynamicList(false), referenced(false), referencedAfterWrap(false) {} void overwrite(Symbol &sym, Kind k) const { From 1f5aee55a3fc26c87fc689011053d8069dd3d849 Mon Sep 17 00:00:00 2001 From: Feng Zou Date: Sun, 7 Jun 2026 10:14:56 +0200 Subject: [PATCH 07/11] [DAG] Narrow vselect mask to vXi1 in foldToMaskedStore (#201609) foldToMaskedStore (added in https://github.com/llvm/llvm-project/commit/1c0ac80d4a9ef6c21914f2317003979952c2a2c3) rewrites store(vselect(cond, x, load(ptr)), ptr) -> masked_store(x, ptr, cond) passing the vselect condition straight through as the store mask. A masked store follows the IR convention of a vXi1 mask, but the condition can be a wider boolean vector. On AVX512F targets without VLX, a maxnum/minnum store-back lowers the NaN test with a legacy packed (CMPP) comparison whose result is a vXi32/vXi64 vector, so the masked store is created with a wide mask and LowerMSTORE asserts: Assertion `Mask.getSimpleValueType().getScalarType() == MVT::i1 && "Unexpected mask type"' failed. When the matching vXi1 type is legal, narrow the mask to it before building the masked store. Targets where vXi1 is illegal (e.g. AVX/AVX2) keep the wide mask and continue to lower it as a blend/vmaskmov, and targets whose vselect condition is already vXi1 (e.g. AArch64 SVE, RISC-V RVV) are unaffected. This fixes the crash at the source and lets the X86 LowerMSTORE keep its invariant of only ever seeing a vXi1 mask (no target-specific workaround). Co-Authored-By: Claude Opus 4.8 (1M context) (cherry picked from commit e6bd7887070e92bba3615de04d3fdefde4beb2de) --- llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 13 ++ .../X86/avx512-maxnum-minnum-masked-store.ll | 151 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 llvm/test/CodeGen/X86/avx512-maxnum-minnum-masked-store.ll diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index 3fdb9bf7e5171..ee9238753735a 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -23117,6 +23117,19 @@ static SDValue foldToMaskedStore(StoreSDNode *Store, SelectionDAG &DAG, if (LoadPos == 1) Mask = DAG.getNOT(Dl, Mask, Mask.getValueType()); + // A masked store follows the IR convention of a vXi1 mask (one bit per + // element). A vselect condition may instead be a wider boolean vector, e.g. + // a vXi32/vXi64 comparison result produced on AVX512 targets without VLX. + // When the matching vXi1 type is legal, narrow the mask to it so that targets + // expecting a vXi1 mask lower it correctly. Targets where vXi1 is illegal + // (e.g. AVX/AVX2) keep the wide mask and lower it as a blend/vmaskmov. + EVT MaskVT = Mask.getValueType(); + if (MaskVT.getVectorElementType() != MVT::i1) { + EVT BoolVT = MaskVT.changeVectorElementType(*DAG.getContext(), MVT::i1); + if (TLI.isTypeLegal(BoolVT)) + Mask = DAG.getNode(ISD::TRUNCATE, Dl, BoolVT, Mask); + } + return DAG.getMaskedStore(Store->getChain(), Dl, OtherVec, StorePtr, StoreOffset, Mask, VT, Store->getMemOperand(), Store->getAddressingMode()); diff --git a/llvm/test/CodeGen/X86/avx512-maxnum-minnum-masked-store.ll b/llvm/test/CodeGen/X86/avx512-maxnum-minnum-masked-store.ll new file mode 100644 index 0000000000000..f6ca1490b558c --- /dev/null +++ b/llvm/test/CodeGen/X86/avx512-maxnum-minnum-masked-store.ll @@ -0,0 +1,151 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux -mattr=+avx | FileCheck %s --check-prefix=AVX +; RUN: llc < %s -mtriple=x86_64-unknown-linux -mattr=+avx2 | FileCheck %s --check-prefix=AVX +; RUN: llc < %s -mtriple=x86_64-unknown-linux -mattr=+avx512f | FileCheck %s --check-prefix=AVX512F +; RUN: llc < %s -mtriple=x86_64-unknown-linux -mattr=+avx512f,+avx512vl | FileCheck %s --check-prefix=AVX512VL + +; On AVX512F targets without VLX (e.g. KNL), a maxnum/minnum store-back fuses a +; legacy CMPP vector comparison into a masked store via foldToMaskedStore, so +; the vselect mask is a wide vNi32/vNi64 vector rather than vNi1. The combine +; must narrow the mask to vNi1 (legal here) before building the masked store, so +; the widening custom lowering only ever sees an i1 mask and does not assert. +; +; On AVX/AVX2 the matching vNi1 type is illegal, so the combine keeps the wide +; mask and the masked store lowers to vmaskmov (the isTypeLegal guard leaves +; this path unchanged). + +define void @maxnum_v4f32_masked_store(<4 x float> %a, ptr %ptr) { +; AVX-LABEL: maxnum_v4f32_masked_store: +; AVX: # %bb.0: +; AVX-NEXT: vmovups (%rdi), %xmm1 +; AVX-NEXT: vmaxps %xmm0, %xmm1, %xmm1 +; AVX-NEXT: vcmpordps %xmm0, %xmm0, %xmm0 +; AVX-NEXT: vmaskmovps %xmm1, %xmm0, (%rdi) +; AVX-NEXT: retq +; +; AVX512F-LABEL: maxnum_v4f32_masked_store: +; AVX512F: # %bb.0: +; AVX512F-NEXT: vmovups (%rdi), %xmm1 +; AVX512F-NEXT: vmaxps %xmm0, %xmm1, %xmm1 +; AVX512F-NEXT: vcmpunordps %xmm0, %xmm0, %xmm0 +; AVX512F-NEXT: vptestnmd %zmm0, %zmm0, %k0 +; AVX512F-NEXT: kshiftlw $12, %k0, %k0 +; AVX512F-NEXT: kshiftrw $12, %k0, %k1 +; AVX512F-NEXT: vmovups %zmm1, (%rdi) {%k1} +; AVX512F-NEXT: vzeroupper +; AVX512F-NEXT: retq +; +; AVX512VL-LABEL: maxnum_v4f32_masked_store: +; AVX512VL: # %bb.0: +; AVX512VL-NEXT: vmovups (%rdi), %xmm1 +; AVX512VL-NEXT: vmaxps %xmm0, %xmm1, %xmm1 +; AVX512VL-NEXT: vcmpordps %xmm0, %xmm0, %k1 +; AVX512VL-NEXT: vmovups %xmm1, (%rdi) {%k1} +; AVX512VL-NEXT: retq + %b = load <4 x float>, ptr %ptr, align 4 + %m = call <4 x float> @llvm.maxnum.v4f32(<4 x float> %a, <4 x float> %b) + store <4 x float> %m, ptr %ptr, align 4 + ret void +} + +define void @maxnum_v2f64_masked_store(<2 x double> %a, ptr %ptr) { +; AVX-LABEL: maxnum_v2f64_masked_store: +; AVX: # %bb.0: +; AVX-NEXT: vmovupd (%rdi), %xmm1 +; AVX-NEXT: vmaxpd %xmm0, %xmm1, %xmm1 +; AVX-NEXT: vcmpordpd %xmm0, %xmm0, %xmm0 +; AVX-NEXT: vmaskmovpd %xmm1, %xmm0, (%rdi) +; AVX-NEXT: retq +; +; AVX512F-LABEL: maxnum_v2f64_masked_store: +; AVX512F: # %bb.0: +; AVX512F-NEXT: vmovupd (%rdi), %xmm1 +; AVX512F-NEXT: vmaxpd %xmm0, %xmm1, %xmm1 +; AVX512F-NEXT: vcmpunordpd %xmm0, %xmm0, %xmm0 +; AVX512F-NEXT: vptestnmq %zmm0, %zmm0, %k0 +; AVX512F-NEXT: kshiftlw $14, %k0, %k0 +; AVX512F-NEXT: kshiftrw $14, %k0, %k1 +; AVX512F-NEXT: vmovupd %zmm1, (%rdi) {%k1} +; AVX512F-NEXT: vzeroupper +; AVX512F-NEXT: retq +; +; AVX512VL-LABEL: maxnum_v2f64_masked_store: +; AVX512VL: # %bb.0: +; AVX512VL-NEXT: vmovupd (%rdi), %xmm1 +; AVX512VL-NEXT: vmaxpd %xmm0, %xmm1, %xmm1 +; AVX512VL-NEXT: vcmpordpd %xmm0, %xmm0, %k1 +; AVX512VL-NEXT: vmovupd %xmm1, (%rdi) {%k1} +; AVX512VL-NEXT: retq + %b = load <2 x double>, ptr %ptr, align 8 + %m = call <2 x double> @llvm.maxnum.v2f64(<2 x double> %a, <2 x double> %b) + store <2 x double> %m, ptr %ptr, align 8 + ret void +} + +define void @minnum_v4f32_masked_store(<4 x float> %a, ptr %ptr) { +; AVX-LABEL: minnum_v4f32_masked_store: +; AVX: # %bb.0: +; AVX-NEXT: vmovups (%rdi), %xmm1 +; AVX-NEXT: vminps %xmm0, %xmm1, %xmm1 +; AVX-NEXT: vcmpordps %xmm0, %xmm0, %xmm0 +; AVX-NEXT: vmaskmovps %xmm1, %xmm0, (%rdi) +; AVX-NEXT: retq +; +; AVX512F-LABEL: minnum_v4f32_masked_store: +; AVX512F: # %bb.0: +; AVX512F-NEXT: vmovups (%rdi), %xmm1 +; AVX512F-NEXT: vminps %xmm0, %xmm1, %xmm1 +; AVX512F-NEXT: vcmpunordps %xmm0, %xmm0, %xmm0 +; AVX512F-NEXT: vptestnmd %zmm0, %zmm0, %k0 +; AVX512F-NEXT: kshiftlw $12, %k0, %k0 +; AVX512F-NEXT: kshiftrw $12, %k0, %k1 +; AVX512F-NEXT: vmovups %zmm1, (%rdi) {%k1} +; AVX512F-NEXT: vzeroupper +; AVX512F-NEXT: retq +; +; AVX512VL-LABEL: minnum_v4f32_masked_store: +; AVX512VL: # %bb.0: +; AVX512VL-NEXT: vmovups (%rdi), %xmm1 +; AVX512VL-NEXT: vminps %xmm0, %xmm1, %xmm1 +; AVX512VL-NEXT: vcmpordps %xmm0, %xmm0, %k1 +; AVX512VL-NEXT: vmovups %xmm1, (%rdi) {%k1} +; AVX512VL-NEXT: retq + %b = load <4 x float>, ptr %ptr, align 4 + %m = call <4 x float> @llvm.minnum.v4f32(<4 x float> %a, <4 x float> %b) + store <4 x float> %m, ptr %ptr, align 4 + ret void +} + +define void @minnum_v2f64_masked_store(<2 x double> %a, ptr %ptr) { +; AVX-LABEL: minnum_v2f64_masked_store: +; AVX: # %bb.0: +; AVX-NEXT: vmovupd (%rdi), %xmm1 +; AVX-NEXT: vminpd %xmm0, %xmm1, %xmm1 +; AVX-NEXT: vcmpordpd %xmm0, %xmm0, %xmm0 +; AVX-NEXT: vmaskmovpd %xmm1, %xmm0, (%rdi) +; AVX-NEXT: retq +; +; AVX512F-LABEL: minnum_v2f64_masked_store: +; AVX512F: # %bb.0: +; AVX512F-NEXT: vmovupd (%rdi), %xmm1 +; AVX512F-NEXT: vminpd %xmm0, %xmm1, %xmm1 +; AVX512F-NEXT: vcmpunordpd %xmm0, %xmm0, %xmm0 +; AVX512F-NEXT: vptestnmq %zmm0, %zmm0, %k0 +; AVX512F-NEXT: kshiftlw $14, %k0, %k0 +; AVX512F-NEXT: kshiftrw $14, %k0, %k1 +; AVX512F-NEXT: vmovupd %zmm1, (%rdi) {%k1} +; AVX512F-NEXT: vzeroupper +; AVX512F-NEXT: retq +; +; AVX512VL-LABEL: minnum_v2f64_masked_store: +; AVX512VL: # %bb.0: +; AVX512VL-NEXT: vmovupd (%rdi), %xmm1 +; AVX512VL-NEXT: vminpd %xmm0, %xmm1, %xmm1 +; AVX512VL-NEXT: vcmpordpd %xmm0, %xmm0, %k1 +; AVX512VL-NEXT: vmovupd %xmm1, (%rdi) {%k1} +; AVX512VL-NEXT: retq + %b = load <2 x double>, ptr %ptr, align 8 + %m = call <2 x double> @llvm.minnum.v2f64(<2 x double> %a, <2 x double> %b) + store <2 x double> %m, ptr %ptr, align 8 + ret void +} From 3397c37d5d31e4d4cf1226ba1c36824aa2287252 Mon Sep 17 00:00:00 2001 From: "Rong \"Mantle\" Bao" Date: Fri, 8 May 2026 08:14:18 +0800 Subject: [PATCH 08/11] Inline stack probes immediately after `allocateStack` in `eliminateCallFramePseudoInstr` (#195456) [ Upstream commit 589faedadf141e5e63f7a1e92a0327fc9bdc9b09 ] Revert `bltu` in probing loops to `blt` because commit f162be248636046a20e71209e139347e084b637a isn't applied on release/22.x yet. Link: https://github.com/llvm/llvm-project/pull/192485 ("[RISCV] Use unsigned comparison for stack clash probing loop") --- This PR adds a call to `inlineStackProbe` immediately after `allocateStack` in `eliminateCallFramePseudoInstr`. This allows code generation for stack probe pseudoinstructions in non-entry BBs. Fixes #195454. --- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 1 + .../RISCV/stack-probing-dynamic-nonentry.ll | 115 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 llvm/test/CodeGen/RISCV/stack-probing-dynamic-nonentry.ll diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp index 8246623e8e5aa..20b43538d69c4 100644 --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -1898,6 +1898,7 @@ MachineBasicBlock::iterator RISCVFrameLowering::eliminateCallFramePseudoInstr( needsDwarfCFI(MF) && !hasFP(MF), /*NeedProbe=*/true, ProbeSize, DynAllocation, MachineInstr::NoFlags); + inlineStackProbe(MF, MBB); } else { const RISCVRegisterInfo &RI = *STI.getRegisterInfo(); RI.adjustReg(MBB, MI, DL, SPReg, SPReg, StackOffset::getFixed(Amount), diff --git a/llvm/test/CodeGen/RISCV/stack-probing-dynamic-nonentry.ll b/llvm/test/CodeGen/RISCV/stack-probing-dynamic-nonentry.ll new file mode 100644 index 0000000000000..4c8bb653b4cff --- /dev/null +++ b/llvm/test/CodeGen/RISCV/stack-probing-dynamic-nonentry.ll @@ -0,0 +1,115 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; RUN: llc -mtriple=riscv64 -mattr=+m -O2 < %s | FileCheck %s -check-prefix=RV64 +; RUN: llc -mtriple=riscv32 -mattr=+m -O2 < %s | FileCheck %s -check-prefix=RV32 + +; Test that very large outgoing call frames in functions with variable-sized +; objects get proper stack probing. The outgoing args are large enough to force +; the PROBED_STACKALLOC path, which must be expanded in a non-entry block. + +define void @f(i64 %n) #0 { +; RV64-LABEL: f: +; RV64: # %bb.0: # %entry +; RV64-NEXT: addi sp, sp, -16 +; RV64-NEXT: .cfi_def_cfa_offset 16 +; RV64-NEXT: sd zero, 0(sp) +; RV64-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; RV64-NEXT: sd s0, 0(sp) # 8-byte Folded Spill +; RV64-NEXT: .cfi_offset ra, -8 +; RV64-NEXT: .cfi_offset s0, -16 +; RV64-NEXT: addi s0, sp, 16 +; RV64-NEXT: .cfi_def_cfa s0, 0 +; RV64-NEXT: slli a0, a0, 2 +; RV64-NEXT: addi a0, a0, 15 +; RV64-NEXT: andi a0, a0, -16 +; RV64-NEXT: sub a0, sp, a0 +; RV64-NEXT: lui a1, 1 +; RV64-NEXT: .LBB0_1: # %entry +; RV64-NEXT: # =>This Inner Loop Header: Depth=1 +; RV64-NEXT: sub sp, sp, a1 +; RV64-NEXT: sd zero, 0(sp) +; RV64-NEXT: blt a0, sp, .LBB0_1 +; RV64-NEXT: # %bb.2: # %entry +; RV64-NEXT: mv sp, a0 +; RV64-NEXT: lui a1, 5 +; RV64-NEXT: sub t1, sp, a1 +; RV64-NEXT: lui t2, 1 +; RV64-NEXT: .LBB0_3: # %entry +; RV64-NEXT: # =>This Inner Loop Header: Depth=1 +; RV64-NEXT: sub sp, sp, t2 +; RV64-NEXT: sd zero, 0(sp) +; RV64-NEXT: bne sp, t1, .LBB0_3 +; RV64-NEXT: # %bb.4: # %entry +; RV64-NEXT: addi sp, sp, -2048 +; RV64-NEXT: addi sp, sp, -1424 +; RV64-NEXT: sd zero, 0(sp) +; RV64-NEXT: call g +; RV64-NEXT: lui a0, 6 +; RV64-NEXT: addi a0, a0, -624 +; RV64-NEXT: add sp, sp, a0 +; RV64-NEXT: addi sp, s0, -16 +; RV64-NEXT: .cfi_def_cfa sp, 16 +; RV64-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; RV64-NEXT: ld s0, 0(sp) # 8-byte Folded Reload +; RV64-NEXT: .cfi_restore ra +; RV64-NEXT: .cfi_restore s0 +; RV64-NEXT: addi sp, sp, 16 +; RV64-NEXT: .cfi_def_cfa_offset 0 +; RV64-NEXT: ret +; +; RV32-LABEL: f: +; RV32: # %bb.0: # %entry +; RV32-NEXT: addi sp, sp, -16 +; RV32-NEXT: .cfi_def_cfa_offset 16 +; RV32-NEXT: sw zero, 0(sp) +; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill +; RV32-NEXT: sw s0, 8(sp) # 4-byte Folded Spill +; RV32-NEXT: .cfi_offset ra, -4 +; RV32-NEXT: .cfi_offset s0, -8 +; RV32-NEXT: addi s0, sp, 16 +; RV32-NEXT: .cfi_def_cfa s0, 0 +; RV32-NEXT: slli a0, a0, 2 +; RV32-NEXT: addi a0, a0, 15 +; RV32-NEXT: andi a0, a0, -16 +; RV32-NEXT: sub a0, sp, a0 +; RV32-NEXT: lui a1, 1 +; RV32-NEXT: .LBB0_1: # %entry +; RV32-NEXT: # =>This Inner Loop Header: Depth=1 +; RV32-NEXT: sub sp, sp, a1 +; RV32-NEXT: sw zero, 0(sp) +; RV32-NEXT: blt a0, sp, .LBB0_1 +; RV32-NEXT: # %bb.2: # %entry +; RV32-NEXT: mv sp, a0 +; RV32-NEXT: lui a1, 5 +; RV32-NEXT: sub t1, sp, a1 +; RV32-NEXT: lui t2, 1 +; RV32-NEXT: .LBB0_3: # %entry +; RV32-NEXT: # =>This Inner Loop Header: Depth=1 +; RV32-NEXT: sub sp, sp, t2 +; RV32-NEXT: sw zero, 0(sp) +; RV32-NEXT: bne sp, t1, .LBB0_3 +; RV32-NEXT: # %bb.4: # %entry +; RV32-NEXT: addi sp, sp, -2048 +; RV32-NEXT: addi sp, sp, -1456 +; RV32-NEXT: sw zero, 0(sp) +; RV32-NEXT: call g +; RV32-NEXT: lui a0, 6 +; RV32-NEXT: addi a0, a0, -592 +; RV32-NEXT: add sp, sp, a0 +; RV32-NEXT: addi sp, s0, -16 +; RV32-NEXT: .cfi_def_cfa sp, 16 +; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload +; RV32-NEXT: lw s0, 8(sp) # 4-byte Folded Reload +; RV32-NEXT: .cfi_restore ra +; RV32-NEXT: .cfi_restore s0 +; RV32-NEXT: addi sp, sp, 16 +; RV32-NEXT: .cfi_def_cfa_offset 0 +; RV32-NEXT: ret +entry: + %v = alloca i32, i64 %n + call void @g(ptr %v, [3000 x i64] poison) + ret void +} + +declare void @g(ptr, [3000 x i64]) + +attributes #0 = { "probe-stack"="inline-asm" } From e80beda6e2558f03f17351f6805e95c8687a82a9 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Thu, 12 Mar 2026 21:51:23 +0000 Subject: [PATCH 09/11] [VPlan] Account for any-of costs in legacy cost model Some VPlan transforms, like vectorizing fmin without fast-math, introduce AnyOfs, which have costs assigned in the VPlan-based cost model, but not the legacy cost model. Account for their cost like done for other similar VPInstrctions, like EVL. Fixes https://github.com/llvm/llvm-project/issues/185867. (cherry picked from commit 475cc4fe0b4065775db470bb512c9c9142242e55) --- .../Transforms/Vectorize/LoopVectorize.cpp | 1 + .../LoopVectorize/X86/cost-any-of.ll | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 llvm/test/Transforms/LoopVectorize/X86/cost-any-of.ll diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp index cdc6ecfa21bcb..77589e06d9c33 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -4282,6 +4282,7 @@ VectorizationFactor LoopVectorizationPlanner::selectVectorizationFactor() { break; } case VPInstruction::ExplicitVectorLength: + case VPInstruction::AnyOf: C += VPI->cost(VF, CostCtx); break; default: diff --git a/llvm/test/Transforms/LoopVectorize/X86/cost-any-of.ll b/llvm/test/Transforms/LoopVectorize/X86/cost-any-of.ll new file mode 100644 index 0000000000000..a03ae83a8d617 --- /dev/null +++ b/llvm/test/Transforms/LoopVectorize/X86/cost-any-of.ll @@ -0,0 +1,41 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 6 +; RUN: opt -passes=loop-vectorize -S %s | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" + +; Test case for https://github.com/llvm/llvm-project/issues/185867. +define void @fminnum_with_any_of_cost(ptr %p) #0 { +; CHECK-LABEL: define void @fminnum_with_any_of_cost( +; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[SCALAR_PH:.*]]: +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[SCALAR_PH]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] +; CHECK-NEXT: [[RED:%.*]] = phi float [ 0.000000e+00, %[[SCALAR_PH]] ], [ [[MIN:%.*]], %[[LOOP]] ] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds float, ptr [[P]], i64 [[IV]] +; CHECK-NEXT: [[LDV:%.*]] = load float, ptr [[GEP]], align 4 +; CHECK-NEXT: [[MIN]] = tail call float @llvm.minnum.f32(float [[RED]], float [[LDV]]) +; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], 140 +; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT:.*]], label %[[LOOP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: + %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ] + %red = phi float [ 0.000000e+00, %entry ], [ %min, %loop ] + %gep = getelementptr inbounds float, ptr %p, i64 %iv + %ldv = load float, ptr %gep, align 4 + %min = tail call float @llvm.minnum.f32(float %red, float %ldv) + %iv.next = add i64 %iv, 1 + %cmp = icmp eq i64 %iv.next, 140 + br i1 %cmp, label %exit, label %loop + +exit: + ret void +} + +attributes #0 = { "target-cpu"="znver4" } From 208ef916cf2062f833913a767e6aafc2c9b543fc Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 5 Jun 2026 20:15:22 +0200 Subject: [PATCH 10/11] [WebAssembly] narrow instructions use signed saturation (#201798) Fixes https://github.com/llvm/llvm-project/issues/201780 Per https://www.w3.org/TR/wasm-core-2/#-hrefop-narrowmathrmnarrowmathsfu_m-n-i the saturation is signed, the truncation is unsigned. (cherry picked from commit c19fa5be5f47d8747f523509382b12997f2bd25f) --- .../WebAssembly/WebAssemblyInstrSIMD.td | 20 ++++- .../CodeGen/WebAssembly/fpclamptosat_vec.ll | 38 ++++------ .../WebAssembly/saturating-truncation.ll | 76 +++++++++++++++++-- 3 files changed, 101 insertions(+), 33 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td index 72feb5492c67a..c69cd03f97b78 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -1504,12 +1504,26 @@ multiclass SignedSaturatingTruncate; defm : SignedSaturatingTruncate; +// NOTE: the saturating is actually signed, the truncation is unsigned, see +// https://www.w3.org/TR/wasm-core-2/#-hrefop-narrowmathrmnarrowmathsfu_m-n-i multiclass UnsignedSaturatingTruncate { + Instruction narrow, int maxval> { def : Pat< (output (wasm_narrow_u - (umin (input V128:$a), (splat_vector (i32 maxval))), - (umin (input V128:$b), (splat_vector (i32 maxval))) + (smin (smax (input V128:$a), (splat_vector (i32 0))), + (splat_vector (i32 maxval))), + (smin (smax (input V128:$b), (splat_vector (i32 0))), + (splat_vector (i32 maxval))) + )), + (narrow V128:$a, V128:$b) + >; + + def : Pat< + (output (wasm_narrow_u + (smax (smin (input V128:$a), (splat_vector (i32 maxval))), + (splat_vector (i32 0))), + (smax (smin (input V128:$b), (splat_vector (i32 maxval))), + (splat_vector (i32 0))) )), (narrow V128:$a, V128:$b) >; diff --git a/llvm/test/CodeGen/WebAssembly/fpclamptosat_vec.ll b/llvm/test/CodeGen/WebAssembly/fpclamptosat_vec.ll index 55409d5b2d8c3..f77443e2725c2 100644 --- a/llvm/test/CodeGen/WebAssembly/fpclamptosat_vec.ll +++ b/llvm/test/CodeGen/WebAssembly/fpclamptosat_vec.ll @@ -484,7 +484,7 @@ entry: define <8 x i16> @utest_f16i16(<8 x half> %x) { ; CHECK-LABEL: utest_f16i16: ; CHECK: .functype utest_f16i16 (i32, i32, i32, i32, i32, i32, i32, i32) -> (v128) -; CHECK-NEXT: .local f32, f32, f32, f32, f32 +; CHECK-NEXT: .local f32, f32, f32, f32, f32, v128 ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 5 ; CHECK-NEXT: call __extendhfsf2 @@ -516,6 +516,9 @@ define <8 x i16> @utest_f16i16(<8 x half> %x) { ; CHECK-NEXT: call __extendhfsf2 ; CHECK-NEXT: i32.trunc_sat_f32_u ; CHECK-NEXT: i32x4.replace_lane 3 +; CHECK-NEXT: v128.const 65535, 65535, 65535, 65535 +; CHECK-NEXT: local.tee 13 +; CHECK-NEXT: i32x4.min_u ; CHECK-NEXT: local.get 9 ; CHECK-NEXT: i32.trunc_sat_f32_u ; CHECK-NEXT: i32x4.splat @@ -528,6 +531,8 @@ define <8 x i16> @utest_f16i16(<8 x half> %x) { ; CHECK-NEXT: local.get 11 ; CHECK-NEXT: i32.trunc_sat_f32_u ; CHECK-NEXT: i32x4.replace_lane 3 +; CHECK-NEXT: local.get 13 +; CHECK-NEXT: i32x4.min_u ; CHECK-NEXT: i16x8.narrow_i32x4_u ; CHECK-NEXT: # fallthrough-return entry: @@ -541,7 +546,7 @@ entry: define <8 x i16> @ustest_f16i16(<8 x half> %x) { ; CHECK-LABEL: ustest_f16i16: ; CHECK: .functype ustest_f16i16 (i32, i32, i32, i32, i32, i32, i32, i32) -> (v128) -; CHECK-NEXT: .local f32, f32, f32, f32, f32, v128, v128 +; CHECK-NEXT: .local f32, f32, f32, f32, f32 ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 5 ; CHECK-NEXT: call __extendhfsf2 @@ -573,12 +578,6 @@ define <8 x i16> @ustest_f16i16(<8 x half> %x) { ; CHECK-NEXT: call __extendhfsf2 ; CHECK-NEXT: i32.trunc_sat_f32_s ; CHECK-NEXT: i32x4.replace_lane 3 -; CHECK-NEXT: v128.const 65535, 65535, 65535, 65535 -; CHECK-NEXT: local.tee 13 -; CHECK-NEXT: i32x4.min_s -; CHECK-NEXT: v128.const 0, 0, 0, 0 -; CHECK-NEXT: local.tee 14 -; CHECK-NEXT: i32x4.max_s ; CHECK-NEXT: local.get 9 ; CHECK-NEXT: i32.trunc_sat_f32_s ; CHECK-NEXT: i32x4.splat @@ -591,10 +590,6 @@ define <8 x i16> @ustest_f16i16(<8 x half> %x) { ; CHECK-NEXT: local.get 11 ; CHECK-NEXT: i32.trunc_sat_f32_s ; CHECK-NEXT: i32x4.replace_lane 3 -; CHECK-NEXT: local.get 13 -; CHECK-NEXT: i32x4.min_s -; CHECK-NEXT: local.get 14 -; CHECK-NEXT: i32x4.max_s ; CHECK-NEXT: i16x8.narrow_i32x4_u ; CHECK-NEXT: # fallthrough-return entry: @@ -1850,7 +1845,7 @@ entry: define <8 x i16> @utest_f16i16_mm(<8 x half> %x) { ; CHECK-LABEL: utest_f16i16_mm: ; CHECK: .functype utest_f16i16_mm (i32, i32, i32, i32, i32, i32, i32, i32) -> (v128) -; CHECK-NEXT: .local f32, f32, f32, f32, f32 +; CHECK-NEXT: .local f32, f32, f32, f32, f32, v128 ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 5 ; CHECK-NEXT: call __extendhfsf2 @@ -1882,6 +1877,9 @@ define <8 x i16> @utest_f16i16_mm(<8 x half> %x) { ; CHECK-NEXT: call __extendhfsf2 ; CHECK-NEXT: i32.trunc_sat_f32_u ; CHECK-NEXT: i32x4.replace_lane 3 +; CHECK-NEXT: v128.const 65535, 65535, 65535, 65535 +; CHECK-NEXT: local.tee 13 +; CHECK-NEXT: i32x4.min_u ; CHECK-NEXT: local.get 9 ; CHECK-NEXT: i32.trunc_sat_f32_u ; CHECK-NEXT: i32x4.splat @@ -1894,6 +1892,8 @@ define <8 x i16> @utest_f16i16_mm(<8 x half> %x) { ; CHECK-NEXT: local.get 11 ; CHECK-NEXT: i32.trunc_sat_f32_u ; CHECK-NEXT: i32x4.replace_lane 3 +; CHECK-NEXT: local.get 13 +; CHECK-NEXT: i32x4.min_u ; CHECK-NEXT: i16x8.narrow_i32x4_u ; CHECK-NEXT: # fallthrough-return entry: @@ -1906,7 +1906,7 @@ entry: define <8 x i16> @ustest_f16i16_mm(<8 x half> %x) { ; CHECK-LABEL: ustest_f16i16_mm: ; CHECK: .functype ustest_f16i16_mm (i32, i32, i32, i32, i32, i32, i32, i32) -> (v128) -; CHECK-NEXT: .local f32, f32, f32, f32, f32, v128, v128 +; CHECK-NEXT: .local f32, f32, f32, f32, f32 ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 5 ; CHECK-NEXT: call __extendhfsf2 @@ -1938,12 +1938,6 @@ define <8 x i16> @ustest_f16i16_mm(<8 x half> %x) { ; CHECK-NEXT: call __extendhfsf2 ; CHECK-NEXT: i32.trunc_sat_f32_s ; CHECK-NEXT: i32x4.replace_lane 3 -; CHECK-NEXT: v128.const 65535, 65535, 65535, 65535 -; CHECK-NEXT: local.tee 13 -; CHECK-NEXT: i32x4.min_s -; CHECK-NEXT: v128.const 0, 0, 0, 0 -; CHECK-NEXT: local.tee 14 -; CHECK-NEXT: i32x4.max_s ; CHECK-NEXT: local.get 9 ; CHECK-NEXT: i32.trunc_sat_f32_s ; CHECK-NEXT: i32x4.splat @@ -1956,10 +1950,6 @@ define <8 x i16> @ustest_f16i16_mm(<8 x half> %x) { ; CHECK-NEXT: local.get 11 ; CHECK-NEXT: i32.trunc_sat_f32_s ; CHECK-NEXT: i32x4.replace_lane 3 -; CHECK-NEXT: local.get 13 -; CHECK-NEXT: i32x4.min_s -; CHECK-NEXT: local.get 14 -; CHECK-NEXT: i32x4.max_s ; CHECK-NEXT: i16x8.narrow_i32x4_u ; CHECK-NEXT: # fallthrough-return entry: diff --git a/llvm/test/CodeGen/WebAssembly/saturating-truncation.ll b/llvm/test/CodeGen/WebAssembly/saturating-truncation.ll index f3f3ba9b268d7..6070dfa5bed76 100644 --- a/llvm/test/CodeGen/WebAssembly/saturating-truncation.ll +++ b/llvm/test/CodeGen/WebAssembly/saturating-truncation.ll @@ -56,12 +56,19 @@ bb2: ret <8 x i16> %3 } -define <16 x i8> @i16_unsigned(<8 x i16> %a, <8 x i16> %b) { -; CHECK-LABEL: i16_unsigned: -; CHECK: .functype i16_unsigned (v128, v128) -> (v128) +; NOTE: unsigned narrow uses *signed* saturation, the manual unsigned saturation cannot be optimized out. +define <16 x i8> @i16_unsigned_sat_unsigned_truncate(<8 x i16> %a, <8 x i16> %b) { +; CHECK-LABEL: i16_unsigned_sat_unsigned_truncate: +; CHECK: .functype i16_unsigned_sat_unsigned_truncate (v128, v128) -> (v128) +; CHECK-NEXT: .local v128 ; CHECK-NEXT: # %bb.0: # %bb2 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: v128.const 255, 255, 255, 255, 255, 255, 255, 255 +; CHECK-NEXT: local.tee 2 +; CHECK-NEXT: i16x8.min_u ; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: i16x8.min_u ; CHECK-NEXT: i8x16.narrow_i16x8_u ; CHECK-NEXT: # fallthrough-return bb2: @@ -71,12 +78,19 @@ bb2: ret <16 x i8> %2 } -define <8 x i16> @i32_unsigned(<4 x i32> %a, <4 x i32> %b) { -; CHECK-LABEL: i32_unsigned: -; CHECK: .functype i32_unsigned (v128, v128) -> (v128) +; NOTE: unsigned narrow uses *signed* saturation, the manual unsigned saturation cannot be optimized out. +define <8 x i16> @i32_unsigned_sat_unsigned_truncate(<4 x i32> %a, <4 x i32> %b) { +; CHECK-LABEL: i32_unsigned_sat_unsigned_truncate: +; CHECK: .functype i32_unsigned_sat_unsigned_truncate (v128, v128) -> (v128) +; CHECK-NEXT: .local v128 ; CHECK-NEXT: # %bb.0: # %bb2 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: v128.const 65535, 65535, 65535, 65535 +; CHECK-NEXT: local.tee 2 +; CHECK-NEXT: i32x4.min_u ; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: i32x4.min_u ; CHECK-NEXT: i16x8.narrow_i32x4_u ; CHECK-NEXT: # fallthrough-return bb2: @@ -85,3 +99,53 @@ bb2: %2 = trunc nsw <8 x i32> %1 to <8 x i16> ret <8 x i16> %2 } + +; NOTE: narrow_i16x8_u uses *signed* saturation, the manual unsigned saturation cannot be optimized out. +define <16 x i8> @narrow_with_manual_unsigned_sat(<8 x i16> %a) { +; CHECK-LABEL: narrow_with_manual_unsigned_sat: +; CHECK: .functype narrow_with_manual_unsigned_sat (v128) -> (v128) +; CHECK-NEXT: # %bb.0: # %start +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: v128.const 255, 255, 255, 255, 255, 255, 255, 255 +; CHECK-NEXT: i16x8.min_u +; CHECK-NEXT: local.tee 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i8x16.narrow_i16x8_u +; CHECK-NEXT: # fallthrough-return +start: + %0 = tail call <8 x i16> @llvm.umin.v8i16(<8 x i16> %a, <8 x i16> splat (i16 255)) + %_21 = tail call <16 x i8> @llvm.wasm.narrow.unsigned.v16i8.v8i16(<8 x i16> %0, <8 x i16> %0) + ret <16 x i8> %_21 +} + +define <16 x i8> @i16_signed_sat_unsigned_truncate(<8 x i16> %a, <8 x i16> %b) { +; CHECK-LABEL: i16_signed_sat_unsigned_truncate: +; CHECK: .functype i16_signed_sat_unsigned_truncate (v128, v128) -> (v128) +; CHECK-NEXT: # %bb.0: # %bb2 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i8x16.narrow_i16x8_u +; CHECK-NEXT: # fallthrough-return +bb2: + %0 = shufflevector <8 x i16> %a, <8 x i16> %b, <16 x i32> + %1 = tail call <16 x i16> @llvm.smax.v16i16(<16 x i16> %0, <16 x i16> zeroinitializer) + %2 = tail call <16 x i16> @llvm.smin.v16i16(<16 x i16> %1, <16 x i16> splat (i16 255)) + %3 = trunc nuw <16 x i16> %2 to <16 x i8> + ret <16 x i8> %3 +} + +define <8 x i16> @i32_signed_sat_unsigned_truncate(<4 x i32> %a, <4 x i32> %b) { +; CHECK-LABEL: i32_signed_sat_unsigned_truncate: +; CHECK: .functype i32_signed_sat_unsigned_truncate (v128, v128) -> (v128) +; CHECK-NEXT: # %bb.0: # %bb2 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i16x8.narrow_i32x4_u +; CHECK-NEXT: # fallthrough-return +bb2: + %0 = shufflevector <4 x i32> %a, <4 x i32> %b, <8 x i32> + %1 = tail call <8 x i32> @llvm.smin.v8i32(<8 x i32> %0, <8 x i32> splat (i32 65535)) + %2 = tail call <8 x i32> @llvm.smax.v8i32(<8 x i32> %1, <8 x i32> zeroinitializer) + %3 = trunc nuw <8 x i32> %2 to <8 x i16> + ret <8 x i16> %3 +} From ca7933e47d3a3451d81e72ac174dcb5aa28b59d1 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 29 Apr 2026 06:15:45 -0400 Subject: [PATCH 11/11] [libc++] Disable mistakenly enabled `optional` constructors for `optional` (#194446) Resolves #194415 - A constructor specifically meant for `optional` was left enabled for `optional` - Fix it, and add a test to check for regression. - This patch also corrects the constraints for `optional(optional&)` and `optional(const optional&)` , as they were incorrectly disallowing [valid conversions](https://godbolt.org/z/1r5Ea7z5M) - Also, correct the `noexcept` specification. - Add tests for both corrections. (cherry picked from commit 239189ca28847aa4797368827107c22c32080509) --- libcxx/include/optional | 26 +++++++------ .../const_optional_U.pass.cpp | 31 +++++++++++++++ .../optional.object.ctor/optional_U.pass.cpp | 35 +++++++++++++++++ .../ref_constructs_from_temporary.verify.cpp | 8 ++-- .../optional.object/optional_helper_types.h | 39 ++++++++++++++++++- 5 files changed, 122 insertions(+), 17 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 12fbcdfa5c5d6..4d85ac1e0ff5e 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -828,6 +828,10 @@ private: template constexpr static bool __libcpp_opt_ref_ctor_deleted = is_lvalue_reference_v<_Tp> && reference_constructs_from_temporary_v<_Tp, _Up>; + + template + constexpr static bool __ref_ctor_enabled = + is_lvalue_reference_v<_Tp> && !reference_constructs_from_temporary_v<_Tp, _Up>; # endif // LWG2756: conditionally explicit conversion from _Up @@ -935,7 +939,7 @@ public: template ::template __enable_implicit<_Up>(), int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) # if _LIBCPP_STD_VER >= 26 - noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up&>) + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, const _Up&>) # endif { this->__construct_from(__v); @@ -943,7 +947,7 @@ public: template ::template __enable_explicit<_Up>(), int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) # if _LIBCPP_STD_VER >= 26 - noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up&>) + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, const _Up&>) # endif { this->__construct_from(__v); @@ -981,25 +985,25 @@ public: // optional(optional& rhs) template - requires(!__libcpp_opt_ref_ctor_deleted<_Up>) && (!is_same_v, optional<_Up>>) && - (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up&> + requires __ref_ctor_enabled<_Up&> && (!is_same_v, optional<_Up>>) && (!is_same_v<_Tp&, _Up>) && + is_constructible_v<_Tp&, _Up&> _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up&, _Tp&>) optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) { this->__construct_from(__rhs); } template - requires __libcpp_opt_ref_ctor_deleted<_Up> && (!is_same_v, optional<_Up>>) && + requires __libcpp_opt_ref_ctor_deleted<_Up&> && (!is_same_v, optional<_Up>>) && (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up&> constexpr explicit optional(optional<_Up>& __rhs) noexcept = delete; // optional(const optional&) template ::template __enable_implicit<_Up>(), int> = 0> - requires __libcpp_opt_ref_ctor_deleted<_Up> + requires __libcpp_opt_ref_ctor_deleted optional(const optional<_Up>&) = delete; template ::template __enable_explicit<_Up>(), int> = 0> - requires __libcpp_opt_ref_ctor_deleted<_Up> + requires __libcpp_opt_ref_ctor_deleted explicit optional(const optional<_Up>&) = delete; // optional(optional&&) @@ -1013,16 +1017,16 @@ public: // optional(const optional&&) template - requires(!__libcpp_opt_ref_ctor_deleted<_Up>) && (!is_same_v, optional<_Up>>) && - (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up> + requires __ref_ctor_enabled && (!is_same_v, optional<_Up>>) && + (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, const _Up> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!is_convertible_v) optional(const optional<_Up>&& __v) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) { this->__construct_from(std::move(__v)); } template - requires __libcpp_opt_ref_ctor_deleted<_Up> && (!is_same_v, optional<_Up>>) && - (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up> + requires __libcpp_opt_ref_ctor_deleted && (!is_same_v, optional<_Up>>) && + (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, const _Up> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>&& __v) noexcept = delete; # endif diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp index 597110bcb54fe..844317c8570d9 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp @@ -151,6 +151,37 @@ constexpr bool test_ref() { assert(*o2 == 1); } + { + const std::optional o1{2}; + std::optional o2(o1); + assert(*o2 == 2); + assert(&(*o2) == &(*o1)); + } + + { + const std::optional> o1({1, 2}); + std::optional o2(o1); + assert(o2.has_value()); + assert(&(*o2) == &o1->lvalue); + assert(*o2 == 1); + } + + { + const std::optional> o1({1, 2}); + std::optional o2(std::move(o1)); + assert(o2.has_value()); + assert(&(*o2) == &o1->rvalue); + assert(*o2 == 2); + } + + { + const std::optional> o1({1, 2}); + ASSERT_NOT_NOEXCEPT(std::optional(o1)); + ASSERT_NOT_NOEXCEPT(std::optional(std::move(o1))); + } + + static_assert(!std::is_constructible_v, const std::optional>&&>); + return true; } #endif diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp index 39a80efa475f7..03082033cd4dc 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp @@ -144,6 +144,36 @@ constexpr bool test_ref() { assert(*o2 == 1); } + { + std::optional o1(1); + std::optional o2(o1); + assert(o2.has_value()); + assert(*o2 == 1); + assert(&(*o2) == &(*o1)); + } + + { + std::optional> o1({1, 2}); + std::optional o2(o1); + assert(o2.has_value()); + assert(*o2 == 1); + assert(&(*o2) == &o1->lvalue); + } + + { + std::optional> o1({1, 2}); + std::optional o2(std::move(o1)); + assert(o2.has_value()); + assert(*o2 == 2); + assert(&(*o2) == &o1->rvalue); + } + + { + std::optional> o1({1, 2}); + ASSERT_NOT_NOEXCEPT(std::optional(o1)); + ASSERT_NOT_NOEXCEPT(std::optional(std::move(o1))); + } + return true; } #endif @@ -164,6 +194,11 @@ int main(int, char**) { test(std::move(rhs), true); } +#if TEST_STD_VER >= 26 + // GH: #194415 + static_assert(!std::is_constructible_v, std::optional>&>); +#endif + static_assert(!(std::is_constructible, optional>::value), ""); #if TEST_STD_VER >= 26 diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp index 53ac77a2e5620..d4c95c302d817 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp @@ -29,12 +29,10 @@ void test() { const std::optional co(1); std::optional o0(1); - // expected-error-re@*:* 10 {{call to deleted constructor of 'std::optional<{{.*}}>'}} + // expected-error-re@*:* 8 {{call to deleted constructor of 'std::optional<{{.*}}>'}} std::optional o1{1}; // optional(U&&) - std::optional o2{o0}; // optional(optional&) - std::optional o3{co}; // optional(const optional&) - std::optional o4{std::move(o0)}; // optional(optional&&&) - std::optional o5{std::move(co)}; // optional(optional&&&) + std::optional o4{std::move(o0)}; // optional(optional&&) + std::optional o5{std::move(co)}; // optional(optional&&) std::optional o6{1}; // optional(U&&) std::optional o7{o0}; // optional(optional&) diff --git a/libcxx/test/std/utilities/optional/optional.object/optional_helper_types.h b/libcxx/test/std/utilities/optional/optional.object/optional_helper_types.h index 704d99acb15e9..d9b17a0b91f3b 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional_helper_types.h +++ b/libcxx/test/std/utilities/optional/optional.object/optional_helper_types.h @@ -21,8 +21,9 @@ struct ReferenceConversion { constexpr ReferenceConversion(T lval, T rval) : lvalue(lval), rvalue(rval) {} constexpr operator T&() & noexcept { return lvalue; } - + constexpr operator const T&() const& noexcept { return lvalue; } constexpr operator T&() && noexcept { return rvalue; } + constexpr operator const T&() const&& noexcept { return rvalue; } }; template @@ -42,6 +43,14 @@ struct ReferenceConversionThrows { return lvalue; } + constexpr operator const T&() const& { + if (throws) { + TEST_THROW(1); + } + + return lvalue; + } + constexpr operator T&() && { if (throws) { TEST_THROW(2); @@ -49,6 +58,34 @@ struct ReferenceConversionThrows { return rvalue; } + + constexpr operator const T&() const&& { + if (throws) { + TEST_THROW(2); + } + + return rvalue; + } +}; + +template +struct LValueOnly { + T val{}; + + constexpr operator T&() & noexcept { return val; } + constexpr operator T&() const& = delete; + constexpr operator T&() && = delete; + constexpr operator T&() const&& = delete; +}; + +template +struct ConstRValueOnly { + mutable T val{}; + + constexpr operator T&() & = delete; + constexpr operator T&() const& = delete; + constexpr operator T&() && = delete; + constexpr operator T&() const&& { return val; }; }; #endif