From a8af9f679231a55b8a0f5707d8727679a98f4b06 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Fri, 6 Jan 2023 13:09:17 -0800 Subject: [PATCH 1/7] Bump version to 15.0.7 --- libcxx/include/__config | 2 +- llvm/CMakeLists.txt | 2 +- llvm/utils/gn/secondary/llvm/version.gni | 2 +- llvm/utils/lit/lit/__init__.py | 2 +- utils/bazel/llvm-project-overlay/clang/BUILD.bazel | 6 +++--- .../clang/include/clang/Config/config.h | 2 +- utils/bazel/llvm-project-overlay/lld/BUILD.bazel | 2 +- .../llvm/include/llvm/Config/llvm-config.h | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libcxx/include/__config b/libcxx/include/__config index 5f62b974170f..c97cbae96fba 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -36,7 +36,7 @@ #ifdef __cplusplus -# define _LIBCPP_VERSION 15006 +# define _LIBCPP_VERSION 15007 # define _LIBCPP_CONCAT_IMPL(_X, _Y) _X##_Y # define _LIBCPP_CONCAT(_X, _Y) _LIBCPP_CONCAT_IMPL(_X, _Y) diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 28e28cfe7b6a..db207e3328be 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -22,7 +22,7 @@ if(NOT DEFINED LLVM_VERSION_MINOR) set(LLVM_VERSION_MINOR 0) endif() if(NOT DEFINED LLVM_VERSION_PATCH) - set(LLVM_VERSION_PATCH 6) + set(LLVM_VERSION_PATCH 7) endif() if(NOT DEFINED LLVM_VERSION_SUFFIX) set(LLVM_VERSION_SUFFIX) diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni index 00e1dc6f1411..d485a133f921 100644 --- a/llvm/utils/gn/secondary/llvm/version.gni +++ b/llvm/utils/gn/secondary/llvm/version.gni @@ -1,4 +1,4 @@ llvm_version_major = 15 llvm_version_minor = 0 -llvm_version_patch = 6 +llvm_version_patch = 7 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 b34d17b9dc47..928d6db8d3cd 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__ = (15, 0, 6) +__versioninfo__ = (15, 0, 7) __version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev' __all__ = [] diff --git a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel index 5ee87d516519..59932954c949 100644 --- a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel @@ -358,11 +358,11 @@ genrule( name = "basic_version_gen", outs = ["include/clang/Basic/Version.inc"], cmd = ( - "echo '#define CLANG_VERSION 15.0.6' >> $@\n" + + "echo '#define CLANG_VERSION 15.0.7' >> $@\n" + "echo '#define CLANG_VERSION_MAJOR 15' >> $@\n" + "echo '#define CLANG_VERSION_MINOR 0' >> $@\n" + - "echo '#define CLANG_VERSION_PATCHLEVEL 6' >> $@\n" + - "echo '#define CLANG_VERSION_STRING \"15.0.6\"' >> $@\n" + "echo '#define CLANG_VERSION_PATCHLEVEL 7' >> $@\n" + + "echo '#define CLANG_VERSION_STRING \"15.0.7\"' >> $@\n" ), ) diff --git a/utils/bazel/llvm-project-overlay/clang/include/clang/Config/config.h b/utils/bazel/llvm-project-overlay/clang/include/clang/Config/config.h index ff4feb2b4af9..5ee35630d35a 100644 --- a/utils/bazel/llvm-project-overlay/clang/include/clang/Config/config.h +++ b/utils/bazel/llvm-project-overlay/clang/include/clang/Config/config.h @@ -93,7 +93,7 @@ /* CLANG_HAVE_RLIMITS defined conditionally below */ /* The LLVM product name and version */ -#define BACKEND_PACKAGE_STRING "LLVM 15.0.6" +#define BACKEND_PACKAGE_STRING "LLVM 15.0.7" /* Linker version detected at compile time. */ /* #undef HOST_LINK_VERSION */ diff --git a/utils/bazel/llvm-project-overlay/lld/BUILD.bazel b/utils/bazel/llvm-project-overlay/lld/BUILD.bazel index 6ba06c794413..06b57f39e333 100644 --- a/utils/bazel/llvm-project-overlay/lld/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/lld/BUILD.bazel @@ -13,7 +13,7 @@ package( genrule( name = "config_version_gen", outs = ["include/lld/Common/Version.inc"], - cmd = "echo '#define LLD_VERSION_STRING \"15.0.6\"' > $@", + cmd = "echo '#define LLD_VERSION_STRING \"15.0.7\"' > $@", ) genrule( diff --git a/utils/bazel/llvm-project-overlay/llvm/include/llvm/Config/llvm-config.h b/utils/bazel/llvm-project-overlay/llvm/include/llvm/Config/llvm-config.h index a44e9f60c0e1..1d5c3a4e879b 100644 --- a/utils/bazel/llvm-project-overlay/llvm/include/llvm/Config/llvm-config.h +++ b/utils/bazel/llvm-project-overlay/llvm/include/llvm/Config/llvm-config.h @@ -80,10 +80,10 @@ #define LLVM_VERSION_MINOR 0 /* Patch version of the LLVM API */ -#define LLVM_VERSION_PATCH 6 +#define LLVM_VERSION_PATCH 7 /* LLVM version string */ -#define LLVM_VERSION_STRING "15.0.6" +#define LLVM_VERSION_STRING "15.0.7" /* Whether LLVM records statistics for use with GetStatistics(), * PrintStatistics() or PrintStatisticsJSON() From 74d3ba1af5c09b85331c90105c461484762ee3e4 Mon Sep 17 00:00:00 2001 From: Bill Wendling Date: Tue, 13 Dec 2022 15:06:29 -0800 Subject: [PATCH 2/7] [X86] Don't zero out %eax if both %al and %ah are used The iterator over super and sub registers doesn't include both 8-bit registers in its list. So if both registers are used and only one of them is live on return, then we need to make sure that the other 8-bit register is also marked as live and not zeroed out. Reviewed By: nickdesaulniers Differential Revision: https://reviews.llvm.org/D139679 (cherry picked from commit 14d4cddc5506fb0fd3c4ac556b4edd970aa151eb) --- llvm/lib/CodeGen/PrologEpilogInserter.cpp | 8 +- .../CodeGen/X86/zero-call-used-regs-i386.ll | 112 ++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 llvm/test/CodeGen/X86/zero-call-used-regs-i386.ll diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index 85d051cfdbe7..a8d40edd88d3 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -1237,7 +1237,13 @@ void PEI::insertZeroCallUsedRegs(MachineFunction &MF) { if (!MO.isReg()) continue; - for (MCPhysReg SReg : TRI.sub_and_superregs_inclusive(MO.getReg())) + MCRegister Reg = MO.getReg(); + + // This picks up sibling registers (e.q. %al -> %ah). + for (MCRegUnitIterator Unit(Reg, &TRI); Unit.isValid(); ++Unit) + RegsToZero.reset(*Unit); + + for (MCPhysReg SReg : TRI.sub_and_superregs_inclusive(Reg)) RegsToZero.reset(SReg); } } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-i386.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-i386.ll new file mode 100644 index 000000000000..33e501ca8503 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-i386.ll @@ -0,0 +1,112 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=i386-unknown-linux-gnu -opaque-pointers | FileCheck %s --check-prefix=I386 +; +; Make sure we don't zero out %eax when both %ah and %al are used. +; +; PR1766: https://github.com/ClangBuiltLinux/linux/issues/1766 + +%struct.maple_subtree_state = type { ptr } + +@mas_data_end_type = dso_local local_unnamed_addr global i32 0, align 4 +@ma_meta_end_mn_0_0_0_0_0_0 = dso_local local_unnamed_addr global i8 0, align 1 +@mt_pivots_0 = dso_local local_unnamed_addr global i8 0, align 1 +@mas_data_end___trans_tmp_2 = dso_local local_unnamed_addr global ptr null, align 4 +@mt_slots_0 = dso_local local_unnamed_addr global i8 0, align 1 + +define dso_local zeroext i1 @test1(ptr nocapture noundef readonly %0) local_unnamed_addr "zero-call-used-regs"="used-gpr" nounwind { +; I386-LABEL: test1: +; I386: # %bb.0: +; I386-NEXT: pushl %ebx +; I386-NEXT: subl $24, %esp +; I386-NEXT: movl {{[0-9]+}}(%esp), %eax +; I386-NEXT: movl (%eax), %eax +; I386-NEXT: movzbl (%eax), %ebx +; I386-NEXT: calll bar +; I386-NEXT: testb %al, %al +; I386-NEXT: # implicit-def: $al +; I386-NEXT: # kill: killed $al +; I386-NEXT: je .LBB0_6 +; I386-NEXT: # %bb.1: +; I386-NEXT: cmpl $0, mas_data_end_type +; I386-NEXT: je .LBB0_3 +; I386-NEXT: # %bb.2: +; I386-NEXT: movzbl ma_meta_end_mn_0_0_0_0_0_0, %eax +; I386-NEXT: movb %al, {{[-0-9]+}}(%e{{[sb]}}p) # 1-byte Spill +; I386-NEXT: jmp .LBB0_6 +; I386-NEXT: .LBB0_3: +; I386-NEXT: movb mt_pivots_0, %ah +; I386-NEXT: movb %ah, %al +; I386-NEXT: decb %al +; I386-NEXT: movl mas_data_end___trans_tmp_2, %ecx +; I386-NEXT: movsbl %al, %edx +; I386-NEXT: cmpl $0, (%ecx,%edx,4) +; I386-NEXT: je .LBB0_5 +; I386-NEXT: # %bb.4: +; I386-NEXT: movb %al, %ah +; I386-NEXT: .LBB0_5: +; I386-NEXT: movb %ah, {{[-0-9]+}}(%e{{[sb]}}p) # 1-byte Spill +; I386-NEXT: .LBB0_6: +; I386-NEXT: movb mt_slots_0, %bh +; I386-NEXT: leal {{[0-9]+}}(%esp), %eax +; I386-NEXT: movl %eax, (%esp) +; I386-NEXT: calll baz +; I386-NEXT: subl $4, %esp +; I386-NEXT: cmpb %bh, %bl +; I386-NEXT: jae .LBB0_8 +; I386-NEXT: # %bb.7: +; I386-NEXT: movsbl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 1-byte Folded Reload +; I386-NEXT: movl %eax, (%esp) +; I386-NEXT: calll gaz +; I386-NEXT: .LBB0_8: +; I386-NEXT: movb $1, %al +; I386-NEXT: addl $24, %esp +; I386-NEXT: popl %ebx +; I386-NEXT: xorl %ecx, %ecx +; I386-NEXT: xorl %edx, %edx +; I386-NEXT: retl + %2 = alloca %struct.maple_subtree_state, align 4 + %3 = load ptr, ptr %0, align 4 + %4 = load i8, ptr %3, align 1 + %5 = tail call zeroext i1 @bar() + br i1 %5, label %6, label %20 + +6: ; preds = %1 + %7 = load i32, ptr @mas_data_end_type, align 4 + %8 = icmp eq i32 %7, 0 + br i1 %8, label %11, label %9 + +9: ; preds = %6 + %10 = load i8, ptr @ma_meta_end_mn_0_0_0_0_0_0, align 1 + br label %20 + +11: ; preds = %6 + %12 = load i8, ptr @mt_pivots_0, align 1 + %13 = add i8 %12, -1 + %14 = load ptr, ptr @mas_data_end___trans_tmp_2, align 4 + %15 = sext i8 %13 to i32 + %16 = getelementptr inbounds [1 x i32], ptr %14, i32 0, i32 %15 + %17 = load i32, ptr %16, align 4 + %18 = icmp eq i32 %17, 0 + %19 = select i1 %18, i8 %12, i8 %13 + br label %20 + +20: ; preds = %11, %9, %1 + %21 = phi i8 [ undef, %1 ], [ %10, %9 ], [ %19, %11 ] + %22 = load i8, ptr @mt_slots_0, align 1 + call void @baz(ptr nonnull sret(%struct.maple_subtree_state) align 4 %2) + %23 = icmp ult i8 %4, %22 + br i1 %23, label %24, label %25 + +24: ; preds = %20 + call void @gaz(i8 noundef signext %21) + br label %25 + +25: ; preds = %20, %24 + ret i1 true +} + +declare dso_local zeroext i1 @bar(...) local_unnamed_addr + +declare dso_local void @baz(ptr sret(%struct.maple_subtree_state) align 4, ...) local_unnamed_addr + +declare dso_local void @gaz(i8 noundef signext) local_unnamed_addr From 67fd0d2af4bf9e939ebcccb2b66552a31789af94 Mon Sep 17 00:00:00 2001 From: "chenglin.bi" Date: Tue, 3 Jan 2023 18:12:15 +0800 Subject: [PATCH 3/7] [TypePromotion] Add truncate in ConvertTruncs when the original truncate type is not extend type If the src type is not extend type, after convert the truncate to and we need to truncate the and also to make sure the all user is legal. The old fix D137613 doesn't work when the truncate convert to and have the other users. So this time I try to add the truncate after and to avoid all these potential issues. Fix: #59554 Reviewed By: samparker Differential Revision: https://reviews.llvm.org/D140869 (cherry picked from commit a0b470c9848c638e86f97eca53063642e07cea67) --- llvm/lib/CodeGen/TypePromotion.cpp | 8 ++--- .../TypePromotion/AArch64/pr58843.ll | 20 ----------- .../TypePromotion/AArch64/trunc-zext-chain.ll | 36 +++++++++++++++++++ 3 files changed, 38 insertions(+), 26 deletions(-) delete mode 100644 llvm/test/Transforms/TypePromotion/AArch64/pr58843.ll diff --git a/llvm/lib/CodeGen/TypePromotion.cpp b/llvm/lib/CodeGen/TypePromotion.cpp index a63118067139..36e3c1245f1c 100644 --- a/llvm/lib/CodeGen/TypePromotion.cpp +++ b/llvm/lib/CodeGen/TypePromotion.cpp @@ -570,7 +570,6 @@ void IRPromoter::Cleanup() { LLVM_DEBUG(dbgs() << "IR Promotion: Cleanup..\n"); // Some zexts will now have become redundant, along with their trunc // operands, so remove them. - // Some zexts need to be replaced with truncate if src bitwidth is larger. for (auto *V : Visited) { if (!isa(V)) continue; @@ -585,11 +584,6 @@ void IRPromoter::Cleanup() { << "\n"); ReplaceAllUsersOfWith(ZExt, Src); continue; - } else if (ZExt->getSrcTy()->getScalarSizeInBits() > PromotedWidth) { - IRBuilder<> Builder{ZExt}; - Value *Trunc = Builder.CreateTrunc(Src, ZExt->getDestTy()); - ReplaceAllUsersOfWith(ZExt, Trunc); - continue; } // We've inserted a trunc for a zext sink, but we already know that the @@ -626,6 +620,8 @@ void IRPromoter::ConvertTruncs() { ConstantInt *Mask = ConstantInt::get(SrcTy, APInt::getMaxValue(NumBits).getZExtValue()); Value *Masked = Builder.CreateAnd(Trunc->getOperand(0), Mask); + if (SrcTy != ExtTy) + Masked = Builder.CreateTrunc(Masked, ExtTy); if (auto *I = dyn_cast(Masked)) NewInsts.insert(I); diff --git a/llvm/test/Transforms/TypePromotion/AArch64/pr58843.ll b/llvm/test/Transforms/TypePromotion/AArch64/pr58843.ll deleted file mode 100644 index 983a32029c65..000000000000 --- a/llvm/test/Transforms/TypePromotion/AArch64/pr58843.ll +++ /dev/null @@ -1,20 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -mtriple=aarch64 -type-promotion -verify -S %s -o - | FileCheck %s -target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" - -; Check the case don't crash due to zext source type bitwidth -; larger than dest type bitwidth. -define i1 @test(i8 %arg) { -; CHECK-LABEL: @test( -; CHECK-NEXT: [[EXT1:%.*]] = zext i8 [[ARG:%.*]] to i64 -; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[EXT1]], 7 -; CHECK-NEXT: [[TMP2:%.*]] = trunc i64 [[TMP1]] to i32 -; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP2]], 0 -; CHECK-NEXT: ret i1 [[CMP]] -; - %ext1 = zext i8 %arg to i64 - %trunc = trunc i64 %ext1 to i3 - %ext2 = zext i3 %trunc to i8 - %cmp = icmp ne i8 %ext2, 0 - ret i1 %cmp -} diff --git a/llvm/test/Transforms/TypePromotion/AArch64/trunc-zext-chain.ll b/llvm/test/Transforms/TypePromotion/AArch64/trunc-zext-chain.ll index 0a846ba115ec..bcf8dfdd7cb0 100644 --- a/llvm/test/Transforms/TypePromotion/AArch64/trunc-zext-chain.ll +++ b/llvm/test/Transforms/TypePromotion/AArch64/trunc-zext-chain.ll @@ -177,3 +177,39 @@ latch: ; preds = %bb14, %bb9 exit: ret i64 %var30 } + +; Check the case don't crash due to zext source type bitwidth +; larger than dest type bitwidth. +define i1 @pr58843(i8 %arg) { +; CHECK-LABEL: @pr58843( +; CHECK-NEXT: [[EXT1:%.*]] = zext i8 [[ARG:%.*]] to i64 +; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[EXT1]], 7 +; CHECK-NEXT: [[TMP2:%.*]] = trunc i64 [[TMP1]] to i32 +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP2]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %ext1 = zext i8 %arg to i64 + %trunc = trunc i64 %ext1 to i3 + %ext2 = zext i3 %trunc to i8 + %cmp = icmp ne i8 %ext2, 0 + ret i1 %cmp +} + +; Check the case don't crash due to xor two op have different +; types +define i1 @pr59554(i8 %arg) { +; CHECK-LABEL: @pr59554( +; CHECK-NEXT: [[ARG_EXT:%.*]] = zext i8 [[ARG:%.*]] to i64 +; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[ARG_EXT]], 7 +; CHECK-NEXT: [[TMP2:%.*]] = trunc i64 [[TMP1]] to i32 +; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = xor i32 [[TMP2]], 1 +; CHECK-NEXT: [[SWITCH_LOBIT:%.*]] = icmp ne i32 [[TMP2]], 0 +; CHECK-NEXT: ret i1 [[SWITCH_LOBIT]] +; + %arg.ext = zext i8 %arg to i64 + %trunc = trunc i64 %arg.ext to i3 + %switch.tableidx = xor i3 %trunc, 1 + %switch.maskindex = zext i3 %trunc to i8 + %switch.lobit = icmp ne i8 %switch.maskindex, 0 + ret i1 %switch.lobit +} From 1095870e8ceddc5371f446f4e7c3473f89a461cd Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 17 Oct 2022 13:36:19 -0700 Subject: [PATCH 4/7] [wasm-ld] Define a `__heap_end` symbol marking the end of allocated memory. Define a `__heap_end` symbol that marks the end of the memory region that starts at `__heap_base`. This will allow malloc implementations to know how much memory they can use at `__heap_base` even if someone has done a `memory.grow` before they can initialize their state. Differential Revision: https://reviews.llvm.org/D136110 --- lld/test/wasm/export-all.s | 7 +++++-- lld/test/wasm/mutable-global-exports.s | 7 +++++-- lld/wasm/Driver.cpp | 1 + lld/wasm/Symbols.cpp | 1 + lld/wasm/Symbols.h | 11 +++++++---- lld/wasm/Writer.cpp | 16 +++++++++++++--- 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/lld/test/wasm/export-all.s b/lld/test/wasm/export-all.s index 009da9f6a381..5af835ce485e 100644 --- a/lld/test/wasm/export-all.s +++ b/lld/test/wasm/export-all.s @@ -40,9 +40,12 @@ foo: # CHECK-NEXT: - Name: __heap_base # CHECK-NEXT: Kind: GLOBAL # CHECK-NEXT: Index: 4 -# CHECK-NEXT: - Name: __memory_base +# CHECK-NEXT: - Name: __heap_end # CHECK-NEXT: Kind: GLOBAL # CHECK-NEXT: Index: 5 -# CHECK-NEXT: - Name: __table_base +# CHECK-NEXT: - Name: __memory_base # CHECK-NEXT: Kind: GLOBAL # CHECK-NEXT: Index: 6 +# CHECK-NEXT: - Name: __table_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: Index: 7 diff --git a/lld/test/wasm/mutable-global-exports.s b/lld/test/wasm/mutable-global-exports.s index e2e45ff93a4b..98009610ac55 100644 --- a/lld/test/wasm/mutable-global-exports.s +++ b/lld/test/wasm/mutable-global-exports.s @@ -79,10 +79,13 @@ _start: # CHECK-ALL-NEXT: - Name: __heap_base # CHECK-ALL-NEXT: Kind: GLOBAL # CHECK-ALL-NEXT: Index: 5 -# CHECK-ALL-NEXT: - Name: __memory_base +# CHECK-ALL-NEXT: - Name: __heap_end # CHECK-ALL-NEXT: Kind: GLOBAL # CHECK-ALL-NEXT: Index: 6 -# CHECK-ALL-NEXT: - Name: __table_base +# CHECK-ALL-NEXT: - Name: __memory_base # CHECK-ALL-NEXT: Kind: GLOBAL # CHECK-ALL-NEXT: Index: 7 +# CHECK-ALL-NEXT: - Name: __table_base +# CHECK-ALL-NEXT: Kind: GLOBAL +# CHECK-ALL-NEXT: Index: 8 # CHECK-ALL-NEXT: - Type: CODE diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 0a0f0c8a05bd..4afbfe24110f 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -681,6 +681,7 @@ static void createOptionalSymbols() { if (!config->isPic) { WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base"); WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base"); + WasmSym::heapEnd = symtab->addOptionalDataSymbol("__heap_end"); WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base"); WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base"); if (config->is64.value_or(false)) diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index e0670cea6425..a79c5bec3b3b 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -83,6 +83,7 @@ DefinedData *WasmSym::dsoHandle; DefinedData *WasmSym::dataEnd; DefinedData *WasmSym::globalBase; DefinedData *WasmSym::heapBase; +DefinedData *WasmSym::heapEnd; DefinedData *WasmSym::initMemoryFlag; GlobalSymbol *WasmSym::stackPointer; GlobalSymbol *WasmSym::tlsBase; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index c17b720a90fa..32e75a69c5f8 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -538,11 +538,14 @@ struct WasmSym { // Symbol marking the end of the data and bss. static DefinedData *dataEnd; - // __heap_base - // Symbol marking the end of the data, bss and explicit stack. Any linear - // memory following this address is not used by the linked code and can - // therefore be used as a backing store for brk()/malloc() implementations. + // __heap_base/__heap_end + // Symbols marking the beginning and end of the "heap". It starts at the end + // of the data, bss and explicit stack, and extends to the end of the linear + // memory allocated by wasm-ld. This region of memory is not used by the + // linked code, so it may be used as a backing store for `sbrk` or `malloc` + // implementations. static DefinedData *heapBase; + static DefinedData *heapEnd; // __wasm_init_memory_flag // Symbol whose contents are nonzero iff memory has already been initialized. diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index f98c95526c9e..f6bbaa02b571 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -340,10 +340,20 @@ void Writer::layoutMemory() { Twine(maxMemorySetting)); memoryPtr = config->initialMemory; } - out.memorySec->numMemoryPages = - alignTo(memoryPtr, WasmPageSize) / WasmPageSize; + + memoryPtr = alignTo(memoryPtr, WasmPageSize); + + out.memorySec->numMemoryPages = memoryPtr / WasmPageSize; log("mem: total pages = " + Twine(out.memorySec->numMemoryPages)); + if (WasmSym::heapEnd) { + // Set `__heap_end` to follow the end of the statically allocated linear + // memory. The fact that this comes last means that a malloc/brk + // implementation can grow the heap at runtime. + log("mem: heap end = " + Twine(memoryPtr)); + WasmSym::heapEnd->setVA(memoryPtr); + } + if (config->maxMemory != 0) { if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize)) error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned"); @@ -363,7 +373,7 @@ void Writer::layoutMemory() { if (config->isPic) max = maxMemorySetting; else - max = alignTo(memoryPtr, WasmPageSize); + max = memoryPtr; } out.memorySec->maxMemoryPages = max / WasmPageSize; log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages)); From 948cadd6d4247f7a5bd8fd6b1386062653d54c84 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 3 Jan 2023 16:04:45 -0800 Subject: [PATCH 5/7] [RegAllocFast] Handle new debug values for spills These new debug values get inserted after the place where the spill happens, which means they won't be reached by the reverse traversal of basic block instructions. This would crash or fail assertions if they contained any virtual registers to be replaced. We can manually handle the new debug values right away to resolve this. Fixes https://github.com/llvm/llvm-project/issues/59172 Reviewed By: StephenTozer Differential Revision: https://reviews.llvm.org/D139590 (cherry picked from commit 87f57f459e7acbb00a6ca4ee6dec6014c5a97e07) --- llvm/lib/CodeGen/RegAllocFast.cpp | 3 + .../PowerPC/regalloc-fast-debug-spill.ll | 250 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 llvm/test/CodeGen/PowerPC/regalloc-fast-debug-spill.ll diff --git a/llvm/lib/CodeGen/RegAllocFast.cpp b/llvm/lib/CodeGen/RegAllocFast.cpp index 9e4e26f1392e..cb552f212fbb 100644 --- a/llvm/lib/CodeGen/RegAllocFast.cpp +++ b/llvm/lib/CodeGen/RegAllocFast.cpp @@ -443,6 +443,9 @@ void RegAllocFast::spill(MachineBasicBlock::iterator Before, Register VirtReg, SpilledOperandsMap[MO->getParent()].push_back(MO); for (auto MISpilledOperands : SpilledOperandsMap) { MachineInstr &DBG = *MISpilledOperands.first; + // We don't have enough support for tracking operands of DBG_VALUE_LISTs. + if (DBG.isDebugValueList()) + continue; MachineInstr *NewDV = buildDbgValueForSpill( *MBB, Before, *MISpilledOperands.first, FI, MISpilledOperands.second); assert(NewDV->getParent() == MBB && "dangling parent pointer"); diff --git a/llvm/test/CodeGen/PowerPC/regalloc-fast-debug-spill.ll b/llvm/test/CodeGen/PowerPC/regalloc-fast-debug-spill.ll new file mode 100644 index 000000000000..cae3cb3bbdf8 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/regalloc-fast-debug-spill.ll @@ -0,0 +1,250 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -O0 < %s | FileCheck %s + +; This test would previously crash in RegisterScavenging, or with assertions +; enabled it would fail when RegAllocFast calls clearVirtRegs. This was due to +; unhandled virt regs in cloned DBG_VALUE_LIST for spills, which are now skipped. +; https://github.com/llvm/llvm-project/issues/59172 + +target datalayout = "e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512" +target triple = "powerpc64le-unknown-linux-gnu" + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 + +; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +define void @read_to_end(i1 %0) personality ptr null { +; CHECK-LABEL: read_to_end: +; CHECK: # %bb.0: +; CHECK-NEXT: mflr 0 +; CHECK-NEXT: std 0, 16(1) +; CHECK-NEXT: stdu 1, -80(1) +; CHECK-NEXT: .cfi_def_cfa_offset 80 +; CHECK-NEXT: .cfi_offset lr, 16 +; CHECK-NEXT: andi. 3, 3, 1 +; CHECK-NEXT: mfocrf 3, 128 +; CHECK-NEXT: rlwinm 3, 3, 1, 0, 0 +; CHECK-NEXT: stw 3, 60(1) +; CHECK-NEXT: ld 3, 0(0) +; CHECK-NEXT: std 3, 64(1) # 8-byte Folded Spill +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: std 3, 72(1) # 8-byte Folded Spill +; CHECK-NEXT: #DEBUG_VALUE: spec_extend:iterator <- [DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_constu 1, DW_OP_mul, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment 64 64] undef, $x3 +; CHECK-NEXT: creqv 20, 20, 20 +; CHECK-NEXT: crxor 20, 1, 20 +; CHECK-NEXT: bc 12, 20, .LBB0_2 +; CHECK-NEXT: b .LBB0_1 +; CHECK-NEXT: .LBB0_1: +; CHECK-NEXT: addi 1, 1, 80 +; CHECK-NEXT: ld 0, 16(1) +; CHECK-NEXT: mtlr 0 +; CHECK-NEXT: blr +; CHECK-NEXT: .LBB0_2: +; CHECK-NEXT: ld 5, 72(1) # 8-byte Folded Reload +; CHECK-NEXT: ld 4, 64(1) # 8-byte Folded Reload +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: bl memcpy +; CHECK-NEXT: nop +; CHECK-NEXT: lwz 4, 60(1) +; CHECK-NEXT: # implicit-def: $cr5lt +; CHECK-NEXT: mfocrf 3, 4 +; CHECK-NEXT: rlwimi 3, 4, 12, 20, 20 +; CHECK-NEXT: mtocrf 4, 3 +; CHECK-NEXT: bc 12, 20, .LBB0_4 +; CHECK-NEXT: b .LBB0_3 +; CHECK-NEXT: .LBB0_3: +; CHECK-NEXT: b .LBB0_4 +; CHECK-NEXT: .LBB0_4: +; CHECK-NEXT: addi 1, 1, 80 +; CHECK-NEXT: ld 0, 16(1) +; CHECK-NEXT: mtlr 0 +; CHECK-NEXT: blr + %2 = load ptr, ptr null, align 8 + %3 = sub i64 0, 0 + call void @llvm.dbg.value(metadata !DIArgList(ptr %2, i64 %3), metadata !129, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_constu, 1, DW_OP_mul, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 64, 64)), !dbg !140 + br i1 %0, label %4, label %5 + +4: ; preds = %1 + ret void + +5: ; preds = %1 + tail call void @llvm.memcpy.p0.p0.i64(ptr null, ptr %2, i64 %3, i1 false) + br i1 %0, label %7, label %6 + +6: ; preds = %5 + br label %7 + +7: ; preds = %6, %5 + ret void +} + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn } + +!llvm.module.flags = !{!0} +!llvm.dbg.cu = !{!1} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +!1 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !2, producer: "clang LLVM (rustc version 1.67.0-dev)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !3, globals: !4) +!2 = !DIFile(filename: "library/std/src/lib.rs/@/std.ff910444-cgu.11", directory: "/home/jistone/rust") +!3 = !{} +!4 = !{!5, !12, !18, !23, !28, !34, !43, !49, !52, !58, !64, !68, !77, !81, !86, !90, !98, !102, !106, !111, !117, !124} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !8, isLocal: true, isDefinition: true) +!7 = !DIFile(filename: "", directory: "") +!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 384, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !9, templateParams: !3, identifier: "1ec913b2a90798f33a12cdc627a17d3d") +!9 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "String", scope: !10, file: !7, size: 192, align: 64, elements: !3, templateParams: !3, identifier: "b616ccc9e18737e903266aae12eea82") +!10 = !DINamespace(name: "string", scope: !11) +!11 = !DINamespace(name: "alloc", scope: null) +!12 = !DIGlobalVariableExpression(var: !13, expr: !DIExpression()) +!13 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !14, isLocal: true, isDefinition: true) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !15, templateParams: !3, identifier: "7f09904511177108b2e94c43effbe403") +!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "BorrowMutError", scope: !16, file: !7, align: 8, elements: !3, identifier: "acf9edd4524e0ff9a9398905d3ba31a6") +!16 = !DINamespace(name: "cell", scope: !17) +!17 = !DINamespace(name: "core", scope: null) +!18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) +!19 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !20, isLocal: true, isDefinition: true) +!20 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !21, templateParams: !3, identifier: "84cb7e6d80fc4c532d8f45aaa75a7ae3") +!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Error", scope: !22, file: !7, align: 8, elements: !3, identifier: "abcb9fb1fe4fda8598a8687b517935b") +!22 = !DINamespace(name: "fmt", scope: !17) +!23 = !DIGlobalVariableExpression(var: !24, expr: !DIExpression()) +!24 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !25, isLocal: true, isDefinition: true) +!25 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !26, templateParams: !3, identifier: "bafa31943f8233dbf8d2de6a615f899") +!26 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TryFromSliceError", scope: !27, file: !7, align: 8, elements: !3, templateParams: !3, identifier: "2dd7cf8d77337f63be7c7f5feb370b37") +!27 = !DINamespace(name: "array", scope: !17) +!28 = !DIGlobalVariableExpression(var: !29, expr: !DIExpression()) +!29 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !30, isLocal: true, isDefinition: true) +!30 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !31, templateParams: !3, identifier: "3292395ea0f5a7e3e88f36db52eb440c") +!31 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TryFromIntError", scope: !32, file: !7, align: 8, elements: !3, templateParams: !3, identifier: "5b131d57d001578fbf4fb83f2028eb12") +!32 = !DINamespace(name: "error", scope: !33) +!33 = !DINamespace(name: "num", scope: !17) +!34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) +!35 = distinct !DIGlobalVariable(name: "OUTPUT_CAPTURE_USED", linkageName: "_ZN3std2io5stdio19OUTPUT_CAPTURE_USED17h6bb564f9f9e20f1bE", scope: !36, file: !39, line: 38, type: !40, isLocal: true, isDefinition: true, align: 8) +!36 = !DINamespace(name: "stdio", scope: !37) +!37 = !DINamespace(name: "io", scope: !38) +!38 = !DINamespace(name: "std", scope: null) +!39 = !DIFile(filename: "library/std/src/io/stdio.rs", directory: "/home/jistone/rust", checksumkind: CSK_MD5, checksum: "6e6a519ce8370e29f07d850a34a413c1") +!40 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "AtomicBool", scope: !41, file: !7, size: 8, align: 8, elements: !3, templateParams: !3, identifier: "c1cddf0305d4e6a98a8ddd4b6fdb5b91") +!41 = !DINamespace(name: "atomic", scope: !42) +!42 = !DINamespace(name: "sync", scope: !17) +!43 = !DIGlobalVariableExpression(var: !44, expr: !DIExpression()) +!44 = distinct !DIGlobalVariable(name: "INSTANCE", linkageName: "_ZN3std2io5stdio5stdin8INSTANCE17h225ddf7c6608f4aaE", scope: !45, file: !39, line: 320, type: !46, isLocal: true, isDefinition: true, align: 64) +!45 = !DINamespace(name: "stdin", scope: !36) +!46 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "OnceLock>>", scope: !47, file: !7, size: 448, align: 64, elements: !3, templateParams: !3, identifier: "2236c08b0846b8b2f33c235183822718") +!47 = !DINamespace(name: "once_lock", scope: !48) +!48 = !DINamespace(name: "sync", scope: !38) +!49 = !DIGlobalVariableExpression(var: !50, expr: !DIExpression()) +!50 = distinct !DIGlobalVariable(name: "STDOUT", linkageName: "_ZN3std2io5stdio6STDOUT17hd8472b9eb112f94aE", scope: !36, file: !39, line: 554, type: !51, isLocal: true, isDefinition: true, align: 64) +!51 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "OnceLock>>>", scope: !47, file: !7, size: 512, align: 64, elements: !3, templateParams: !3, identifier: "31535135376bcf9dc80438cb0beaa95") +!52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) +!53 = distinct !DIGlobalVariable(name: "INSTANCE", linkageName: "_ZN3std2io5stdio6stderr8INSTANCE17he81b75fda1609dccE", scope: !54, file: !39, line: 844, type: !55, isLocal: true, isDefinition: true, align: 64) +!54 = !DINamespace(name: "stderr", scope: !36) +!55 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "ReentrantMutex>", scope: !56, file: !7, size: 192, align: 64, elements: !3, templateParams: !3, identifier: "5c48123cbde8afbfd832b70e8bb014b") +!56 = !DINamespace(name: "remutex", scope: !57) +!57 = !DINamespace(name: "sys_common", scope: !38) +!58 = !DIGlobalVariableExpression(var: !59, expr: !DIExpression()) +!59 = distinct !DIGlobalVariable(name: " as core::fmt::Write>::{vtable}", scope: null, file: !7, type: !60, isLocal: true, isDefinition: true) +!60 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: " as core::fmt::Write>::{vtable_type}", file: !7, size: 384, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !61, templateParams: !3, identifier: "43681bec3eba7b0defb75fd847230ef3") +!61 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Adapter", scope: !62, file: !7, size: 128, align: 64, elements: !3, templateParams: !3, identifier: "eda3cd8f60f00feb7019a3d90d2413dd") +!62 = !DINamespace(name: "write_fmt", scope: !63) +!63 = !DINamespace(name: "Write", scope: !37) +!64 = !DIGlobalVariableExpression(var: !65, expr: !DIExpression()) +!65 = distinct !DIGlobalVariable(name: " as core::fmt::Write>::{vtable}", scope: null, file: !7, type: !66, isLocal: true, isDefinition: true) +!66 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: " as core::fmt::Write>::{vtable_type}", file: !7, size: 384, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !67, templateParams: !3, identifier: "2235adf22355a080446df25ada963d8f") +!67 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Adapter", scope: !62, file: !7, size: 128, align: 64, elements: !3, templateParams: !3, identifier: "50f418463bae1beffe989359452b170a") +!68 = !DIGlobalVariableExpression(var: !69, expr: !DIExpression()) +!69 = distinct !DIGlobalVariable(name: "__KEY", linkageName: "_ZN3std2io5stdio14OUTPUT_CAPTURE7__getit5__KEY17h82ea5b0c4e81236dE", scope: !70, file: !72, line: 331, type: !73, isLocal: true, isDefinition: true, align: 64) +!70 = !DINamespace(name: "__getit", scope: !71) +!71 = !DINamespace(name: "OUTPUT_CAPTURE", scope: !36) +!72 = !DIFile(filename: "library/std/src/thread/local.rs", directory: "/home/jistone/rust", checksumkind: CSK_MD5, checksum: "e3766fd5751a888dc2040f63031e944e") +!73 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Key>>>>>", scope: !74, file: !7, size: 192, align: 64, elements: !3, templateParams: !3, identifier: "4d1514f685cbd010d7b86d2eef080b6c") +!74 = !DINamespace(name: "fast", scope: !75) +!75 = !DINamespace(name: "local", scope: !76) +!76 = !DINamespace(name: "thread", scope: !38) +!77 = !DIGlobalVariableExpression(var: !78, expr: !DIExpression()) +!78 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !79, isLocal: true, isDefinition: true) +!79 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !80, templateParams: !3, identifier: "1a7c0806435616633a284f48de1194c5") +!80 = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) +!81 = !DIGlobalVariableExpression(var: !82, expr: !DIExpression()) +!82 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !83, isLocal: true, isDefinition: true) +!83 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !84, templateParams: !3, identifier: "8e82e7dafb168f2995a6711175975a8") +!84 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "PathBuf", scope: !85, file: !7, size: 192, align: 64, elements: !3, templateParams: !3, identifier: "85bd755ad2187534379df2cc01ef53a0") +!85 = !DINamespace(name: "path", scope: !38) +!86 = !DIGlobalVariableExpression(var: !87, expr: !DIExpression()) +!87 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !88, isLocal: true, isDefinition: true) +!88 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !89, templateParams: !3, identifier: "a8f7c32dd1df279746df60c6d46ce35e") +!89 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean) +!90 = !DIGlobalVariableExpression(var: !91, expr: !DIExpression()) +!91 = distinct !DIGlobalVariable(name: "STATX_STATE", linkageName: "_ZN3std3sys4unix2fs9try_statx11STATX_STATE17h465ade0d62262837E", scope: !92, file: !96, line: 157, type: !97, isLocal: true, isDefinition: true, align: 8) +!92 = !DINamespace(name: "try_statx", scope: !93) +!93 = !DINamespace(name: "fs", scope: !94) +!94 = !DINamespace(name: "unix", scope: !95) +!95 = !DINamespace(name: "sys", scope: !38) +!96 = !DIFile(filename: "library/std/src/sys/unix/fs.rs", directory: "/home/jistone/rust", checksumkind: CSK_MD5, checksum: "594559328c68ee77afe955cd571273ee") +!97 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "AtomicU8", scope: !41, file: !7, size: 8, align: 8, elements: !3, templateParams: !3, identifier: "dda3b691bea8e1b5292414dd97926af2") +!98 = !DIGlobalVariableExpression(var: !99, expr: !DIExpression()) +!99 = distinct !DIGlobalVariable(name: "<&bool as core::fmt::Debug>::{vtable}", scope: null, file: !7, type: !100, isLocal: true, isDefinition: true) +!100 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "<&bool as core::fmt::Debug>::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !101, templateParams: !3, identifier: "5e8d2c48c9cc79c318e2bd28b03e141a") +!101 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&bool", baseType: !89, size: 64, align: 64, dwarfAddressSpace: 0) +!102 = !DIGlobalVariableExpression(var: !103, expr: !DIExpression()) +!103 = distinct !DIGlobalVariable(name: "<&i32 as core::fmt::Debug>::{vtable}", scope: null, file: !7, type: !104, isLocal: true, isDefinition: true) +!104 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "<&i32 as core::fmt::Debug>::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !105, templateParams: !3, identifier: "d4029746615b6a868ffbc67515d99878") +!105 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&i32", baseType: !80, size: 64, align: 64, dwarfAddressSpace: 0) +!106 = !DIGlobalVariableExpression(var: !107, expr: !DIExpression()) +!107 = distinct !DIGlobalVariable(name: "<&u32 as core::fmt::Debug>::{vtable}", scope: null, file: !7, type: !108, isLocal: true, isDefinition: true) +!108 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "<&u32 as core::fmt::Debug>::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !109, templateParams: !3, identifier: "178e0e76b9d9178d686381b2d05a7777") +!109 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&u32", baseType: !110, size: 64, align: 64, dwarfAddressSpace: 0) +!110 = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) +!111 = !DIGlobalVariableExpression(var: !112, expr: !DIExpression()) +!112 = distinct !DIGlobalVariable(name: "<&core::option::Option as core::fmt::Debug>::{vtable}", scope: null, file: !7, type: !113, isLocal: true, isDefinition: true) +!113 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "<&core::option::Option as core::fmt::Debug>::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !114, templateParams: !3, identifier: "7ca8386b4d420d719587fa3255329a7a") +!114 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&core::option::Option", baseType: !115, size: 64, align: 64, dwarfAddressSpace: 0) +!115 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Option", scope: !116, file: !7, size: 128, align: 64, elements: !3, templateParams: !3, identifier: "ad8474e495013fa1e3af4a6b53a05f4b") +!116 = !DINamespace(name: "option", scope: !17) +!117 = !DIGlobalVariableExpression(var: !118, expr: !DIExpression()) +!118 = distinct !DIGlobalVariable(name: "HAS_CLONE3", linkageName: "_ZN3std3sys4unix7process13process_inner66_$LT$impl$u20$std..sys..unix..process..process_common..Command$GT$7do_fork10HAS_CLONE317h7d23eb353ae1c9a8E", scope: !119, file: !123, line: 148, type: !40, isLocal: true, isDefinition: true, align: 8) +!119 = !DINamespace(name: "do_fork", scope: !120) +!120 = !DINamespace(name: "{impl#0}", scope: !121) +!121 = !DINamespace(name: "process_inner", scope: !122) +!122 = !DINamespace(name: "process", scope: !94) +!123 = !DIFile(filename: "library/std/src/sys/unix/process/process_unix.rs", directory: "/home/jistone/rust", checksumkind: CSK_MD5, checksum: "91761d638041a5dd66c0a64d968debe6") +!124 = !DIGlobalVariableExpression(var: !125, expr: !DIExpression()) +!125 = distinct !DIGlobalVariable(name: "::{vtable}", scope: null, file: !7, type: !126, isLocal: true, isDefinition: true) +!126 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", file: !7, size: 256, align: 64, flags: DIFlagArtificial, elements: !3, vtableHolder: !127, templateParams: !3, identifier: "13903f30d26ee5869ef7a3fc63a2e03d") +!127 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "NonZeroI32", scope: !128, file: !7, size: 32, align: 32, elements: !3, templateParams: !3, identifier: "e292f11a32f1ce5cf3b26864e4a0f5e5") +!128 = !DINamespace(name: "nonzero", scope: !33) +!129 = !DILocalVariable(name: "iterator", arg: 2, scope: !130, file: !131, line: 83, type: !137) +!130 = distinct !DISubprogram(name: "spec_extend", linkageName: "_ZN132_$LT$alloc..vec..Vec$LT$T$C$A$GT$$u20$as$u20$alloc..vec..spec_extend..SpecExtend$LT$$RF$T$C$core..slice..iter..Iter$LT$T$GT$$GT$$GT$11spec_extend17hb56b69f474ec1e6dE", scope: !132, file: !131, line: 83, type: !135, scopeLine: 83, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !1, templateParams: !3, retainedNodes: !3) +!131 = !DIFile(filename: "library/alloc/src/vec/spec_extend.rs", directory: "/home/jistone/rust", checksumkind: CSK_MD5, checksum: "0614d5dabe9e343254af1b3fa1ec7315") +!132 = !DINamespace(name: "{impl#4}", scope: !133) +!133 = !DINamespace(name: "spec_extend", scope: !134) +!134 = !DINamespace(name: "vec", scope: !11) +!135 = distinct !DISubroutineType(types: !136) +!136 = !{null} +!137 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Iter", scope: !138, file: !7, size: 128, align: 64, elements: !3, templateParams: !3, identifier: "c31ab6f02ccece1f1a6e93425acabaa1") +!138 = !DINamespace(name: "iter", scope: !139) +!139 = !DINamespace(name: "slice", scope: !17) +!140 = !DILocation(line: 0, scope: !130, inlinedAt: !141) +!141 = distinct !DILocation(line: 2392, column: 9, scope: !142, inlinedAt: !146) +!142 = distinct !DISubprogram(name: "extend_from_slice", linkageName: "_ZN5alloc3vec16Vec$LT$T$C$A$GT$17extend_from_slice17hbc8d29f2694fd768E", scope: !144, file: !143, line: 2391, type: !145, scopeLine: 2391, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !1, templateParams: !3, retainedNodes: !3) +!143 = !DIFile(filename: "library/alloc/src/vec/mod.rs", directory: "/home/jistone/rust", checksumkind: CSK_MD5, checksum: "0d69d0c0c11b3e47364cf6be0d07c829") +!144 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Vec", scope: !134, file: !7, size: 192, align: 64, elements: !3, templateParams: !3, identifier: "f970dea4d30c1daf847db520fef9390d") +!145 = distinct !DISubroutineType(types: !136) +!146 = distinct !DILocation(line: 330, column: 9, scope: !147, inlinedAt: !154) +!147 = distinct !DILexicalBlock(scope: !149, file: !148, line: 329, column: 9) +!148 = !DIFile(filename: "library/std/src/io/buffered/bufreader.rs", directory: "/home/jistone/rust", checksumkind: CSK_MD5, checksum: "5375e06de487f85ee2f6d21c8a84ce7d") +!149 = distinct !DISubprogram(name: "read_to_end", linkageName: "_ZN82_$LT$std..io..buffered..bufreader..BufReader$LT$R$GT$$u20$as$u20$std..io..Read$GT$11read_to_end17h9f09720ee76e6db9E", scope: !150, file: !148, line: 328, type: !153, scopeLine: 328, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !1, templateParams: !3, retainedNodes: !3) +!150 = !DINamespace(name: "{impl#3}", scope: !151) +!151 = !DINamespace(name: "bufreader", scope: !152) +!152 = !DINamespace(name: "buffered", scope: !37) +!153 = distinct !DISubroutineType(types: !3) +!154 = distinct !DILocation(line: 464, column: 9, scope: !155, inlinedAt: !158) +!155 = distinct !DISubprogram(name: "read_to_end", linkageName: "_ZN59_$LT$std..io..stdio..StdinLock$u20$as$u20$std..io..Read$GT$11read_to_end17h38999a681cc6c5b5E", scope: !156, file: !39, line: 463, type: !157, scopeLine: 463, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !1, templateParams: !3, retainedNodes: !3) +!156 = !DINamespace(name: "{impl#7}", scope: !36) +!157 = distinct !DISubroutineType(types: !3) +!158 = distinct !DILocation(line: 430, column: 9, scope: !159) +!159 = distinct !DISubprogram(name: "read_to_end", linkageName: "_ZN55_$LT$std..io..stdio..Stdin$u20$as$u20$std..io..Read$GT$11read_to_end17haba70a09681d41d3E", scope: !160, file: !39, line: 429, type: !161, scopeLine: 429, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !1, templateParams: !3, retainedNodes: !3) +!160 = !DINamespace(name: "{impl#5}", scope: !36) +!161 = !DISubroutineType(types: !3) From 939f5a33711370697f9ad1de4267cfc7399dfe86 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Jan 2023 00:32:42 +0100 Subject: [PATCH 6/7] libc++: bring back the unsigned in the return type in wcstoull_l got remove here: https://github.com/llvm/llvm-project/commit/67b0b02ec9f2bbc57bf8f0550828d97f460ac11f#diff-e41832b8aa26da45585a57c5111531f2e1d07e91a67c4f8bf1cd6d566ae45a2bR42 Differential Revision: https://reviews.llvm.org/D141208 (cherry picked from commit fc87452916c0d8759625aad65e9335778ce9cc68) --- libcxx/include/__support/musl/xlocale.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/include/__support/musl/xlocale.h b/libcxx/include/__support/musl/xlocale.h index f564c87885ac..e674a41fa622 100644 --- a/libcxx/include/__support/musl/xlocale.h +++ b/libcxx/include/__support/musl/xlocale.h @@ -39,7 +39,7 @@ wcstoll_l(const wchar_t *__nptr, wchar_t **__endptr, int __base, locale_t) { return ::wcstoll(__nptr, __endptr, __base); } -inline _LIBCPP_HIDE_FROM_ABI long long +inline _LIBCPP_HIDE_FROM_ABI unsigned long long wcstoull_l(const wchar_t *__nptr, wchar_t **__endptr, int __base, locale_t) { return ::wcstoull(__nptr, __endptr, __base); } From 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Thu, 17 Nov 2022 21:34:29 +0100 Subject: [PATCH 7/7] [libc++] Fix memory leaks when throwing inside std::vector constructors Fixes #58392 Reviewed By: ldionne, #libc Spies: alexfh, hans, joanahalili, dblaikie, libcxx-commits Differential Revision: https://reviews.llvm.org/D138601 --- libcxx/include/vector | 119 ++++++--- .../vector.bool/ctor_exceptions.pass.cpp | 141 +++++++++++ .../vector/vector.cons/exceptions.pass.cpp | 229 ++++++++++++++++++ 3 files changed, 453 insertions(+), 36 deletions(-) create mode 100644 libcxx/test/std/containers/sequences/vector.bool/ctor_exceptions.pass.cpp create mode 100644 libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp diff --git a/libcxx/include/vector b/libcxx/include/vector index 252a0f051ff5..63759407ce94 100644 --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -297,6 +297,7 @@ erase_if(vector& c, Predicate pred); // C++20 #include <__utility/forward.h> #include <__utility/move.h> #include <__utility/swap.h> +#include <__utility/transaction.h> #include #include #include @@ -425,18 +426,27 @@ public: value_type, typename iterator_traits<_ForwardIterator>::reference>::value>::type* = 0); - _LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY - ~vector() - { - __annotate_delete(); - std::__debug_db_erase_c(this); +private: + class __destroy_vector { + public: + _LIBCPP_CONSTEXPR __destroy_vector(vector& __vec) : __vec_(__vec) {} - if (this->__begin_ != nullptr) - { - __clear(); - __alloc_traits::deallocate(__alloc(), this->__begin_, capacity()); + _LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_HIDE_FROM_ABI void operator()() { + __vec_.__annotate_delete(); + std::__debug_db_erase_c(std::addressof(__vec_)); + + if (__vec_.__begin_ != nullptr) { + __vec_.__clear(); + __alloc_traits::deallocate(__vec_.__alloc(), __vec_.__begin_, __vec_.capacity()); + } } - } + + private: + vector& __vec_; + }; + +public: + _LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_HIDE_FROM_ABI ~vector() { __destroy_vector(*this)(); } _LIBCPP_CONSTEXPR_AFTER_CXX17 vector(const vector& __x); _LIBCPP_CONSTEXPR_AFTER_CXX17 vector(const vector& __x, const __type_identity_t& __a); @@ -1075,12 +1085,14 @@ template _LIBCPP_CONSTEXPR_AFTER_CXX17 vector<_Tp, _Allocator>::vector(size_type __n) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); if (__n > 0) { __vallocate(__n); __construct_at_end(__n); } + __guard.__complete(); } #if _LIBCPP_STD_VER > 11 @@ -1089,12 +1101,14 @@ _LIBCPP_CONSTEXPR_AFTER_CXX17 vector<_Tp, _Allocator>::vector(size_type __n, const allocator_type& __a) : __end_cap_(nullptr, __a) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); if (__n > 0) { __vallocate(__n); __construct_at_end(__n); } + __guard.__complete(); } #endif @@ -1102,12 +1116,14 @@ template _LIBCPP_CONSTEXPR_AFTER_CXX17 vector<_Tp, _Allocator>::vector(size_type __n, const value_type& __x) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); if (__n > 0) { __vallocate(__n); __construct_at_end(__n, __x); } + __guard.__complete(); } template @@ -1120,9 +1136,11 @@ vector<_Tp, _Allocator>::vector(_InputIterator __first, typename iterator_traits<_InputIterator>::reference>::value, _InputIterator>::type __last) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); for (; __first != __last; ++__first) emplace_back(*__first); + __guard.__complete(); } template @@ -1135,9 +1153,11 @@ vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last, c typename iterator_traits<_InputIterator>::reference>::value>::type*) : __end_cap_(nullptr, __a) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); for (; __first != __last; ++__first) emplace_back(*__first); + __guard.__complete(); } template @@ -1150,13 +1170,15 @@ vector<_Tp, _Allocator>::vector(_ForwardIterator __first, typename iterator_traits<_ForwardIterator>::reference>::value, _ForwardIterator>::type __last) { - _VSTD::__debug_db_insert_c(this); - size_type __n = static_cast(_VSTD::distance(__first, __last)); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); + size_type __n = static_cast(std::distance(__first, __last)); if (__n > 0) { __vallocate(__n); __construct_at_end(__first, __last, __n); } + __guard.__complete(); } template @@ -1169,13 +1191,15 @@ vector<_Tp, _Allocator>::vector(_ForwardIterator __first, _ForwardIterator __las typename iterator_traits<_ForwardIterator>::reference>::value>::type*) : __end_cap_(nullptr, __a) { - _VSTD::__debug_db_insert_c(this); - size_type __n = static_cast(_VSTD::distance(__first, __last)); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); + size_type __n = static_cast(std::distance(__first, __last)); if (__n > 0) { __vallocate(__n); __construct_at_end(__first, __last, __n); } + __guard.__complete(); } template @@ -1183,13 +1207,15 @@ _LIBCPP_CONSTEXPR_AFTER_CXX17 vector<_Tp, _Allocator>::vector(const vector& __x) : __end_cap_(nullptr, __alloc_traits::select_on_container_copy_construction(__x.__alloc())) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); size_type __n = __x.size(); if (__n > 0) { __vallocate(__n); __construct_at_end(__x.__begin_, __x.__end_, __n); } + __guard.__complete(); } template @@ -1197,13 +1223,15 @@ _LIBCPP_CONSTEXPR_AFTER_CXX17 vector<_Tp, _Allocator>::vector(const vector& __x, const __type_identity_t& __a) : __end_cap_(nullptr, __a) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); size_type __n = __x.size(); if (__n > 0) { __vallocate(__n); __construct_at_end(__x.__begin_, __x.__end_, __n); } + __guard.__complete(); } template @@ -1243,7 +1271,9 @@ vector<_Tp, _Allocator>::vector(vector&& __x, const __type_identity_t _Ip; + auto __guard = std::__make_transaction(__destroy_vector(*this)); assign(_Ip(__x.begin()), _Ip(__x.end())); + __guard.__complete(); } } @@ -1254,12 +1284,14 @@ _LIBCPP_CONSTEXPR_AFTER_CXX17 inline _LIBCPP_INLINE_VISIBILITY vector<_Tp, _Allocator>::vector(initializer_list __il) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); if (__il.size() > 0) { __vallocate(__il.size()); __construct_at_end(__il.begin(), __il.end(), __il.size()); } + __guard.__complete(); } template @@ -1268,12 +1300,14 @@ inline _LIBCPP_INLINE_VISIBILITY vector<_Tp, _Allocator>::vector(initializer_list __il, const allocator_type& __a) : __end_cap_(nullptr, __a) { - _VSTD::__debug_db_insert_c(this); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + std::__debug_db_insert_c(this); if (__il.size() > 0) { __vallocate(__il.size()); __construct_at_end(__il.begin(), __il.end(), __il.size()); } + __guard.__complete(); } #endif // _LIBCPP_CXX03_LANG @@ -2111,8 +2145,26 @@ public: #else _NOEXCEPT; #endif - _LIBCPP_CONSTEXPR_AFTER_CXX17 ~vector(); - _LIBCPP_CONSTEXPR_AFTER_CXX17 explicit vector(size_type __n); + +private: + class __destroy_vector { + public: + _LIBCPP_CONSTEXPR __destroy_vector(vector& __vec) : __vec_(__vec) {} + + _LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_HIDE_FROM_ABI void operator()() { + if (__vec_.__begin_ != nullptr) + __storage_traits::deallocate(__vec_.__alloc(), __vec_.__begin_, __vec_.__cap()); + std::__debug_db_invalidate_all(this); + } + + private: + vector& __vec_; + }; + +public: + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 ~vector() { __destroy_vector(*this)(); } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 explicit vector(size_type __n); #if _LIBCPP_STD_VER > 11 _LIBCPP_CONSTEXPR_AFTER_CXX17 explicit vector(size_type __n, const allocator_type& __a); #endif @@ -2647,12 +2699,14 @@ vector::vector(_ForwardIterator __first, _ForwardIterator __la __size_(0), __cap_alloc_(0, __default_init_tag()) { - size_type __n = static_cast(_VSTD::distance(__first, __last)); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + size_type __n = static_cast(std::distance(__first, __last)); if (__n > 0) { __vallocate(__n); __construct_at_end(__first, __last); } + __guard.__complete(); } template @@ -2664,12 +2718,14 @@ vector::vector(_ForwardIterator __first, _ForwardIterator __la __size_(0), __cap_alloc_(0, static_cast<__storage_allocator>(__a)) { - size_type __n = static_cast(_VSTD::distance(__first, __last)); + auto __guard = std::__make_transaction(__destroy_vector(*this)); + size_type __n = static_cast(std::distance(__first, __last)); if (__n > 0) { __vallocate(__n); __construct_at_end(__first, __last); } + __guard.__complete(); } #ifndef _LIBCPP_CXX03_LANG @@ -2706,15 +2762,6 @@ vector::vector(initializer_list __il, const alloca #endif // _LIBCPP_CXX03_LANG -template -_LIBCPP_CONSTEXPR_AFTER_CXX17 -vector::~vector() -{ - if (__begin_ != nullptr) - __storage_traits::deallocate(__alloc(), __begin_, __cap()); - std::__debug_db_invalidate_all(this); -} - template _LIBCPP_CONSTEXPR_AFTER_CXX17 vector::vector(const vector& __v) diff --git a/libcxx/test/std/containers/sequences/vector.bool/ctor_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/ctor_exceptions.pass.cpp new file mode 100644 index 000000000000..592d733de42d --- /dev/null +++ b/libcxx/test/std/containers/sequences/vector.bool/ctor_exceptions.pass.cpp @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-exceptions + +// (bug report: https://llvm.org/PR58392) +// Check that vector constructors don't leak memory when an operation inside the constructor throws an exception + +#include +#include + +#include "count_new.h" +#include "test_iterators.h" + +template +struct Allocator { + using value_type = T; + using is_always_equal = std::false_type; + + template + Allocator(const Allocator&) {} + + Allocator(bool should_throw = true) { + if (should_throw) + throw 0; + } + + T* allocate(int n) { return std::allocator().allocate(n); } + void deallocate(T* ptr, int n) { std::allocator().deallocate(ptr, n); } + + friend bool operator==(const Allocator&, const Allocator&) { return false; } +}; + +template +struct Iterator { + using iterator_category = IterCat; + using difference_type = std::ptrdiff_t; + using value_type = bool; + using reference = bool&; + using pointer = bool*; + + int i_; + bool b_ = true; + Iterator(int i = 0) : i_(i) {} + bool& operator*() { + if (i_ == 1) + throw 1; + return b_; + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; } + + friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; } + + Iterator& operator++() { + ++i_; + return *this; + } + + Iterator operator++(int) { + auto tmp = *this; + ++i_; + return tmp; + } +}; + +void check_new_delete_called() { + assert(globalMemCounter.new_called == globalMemCounter.delete_called); + assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called); + assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called); + assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called); +} + +int main(int, char**) { + using AllocVec = std::vector >; + +#if TEST_STD_VER >= 14 + try { // Throw in vector(size_type, const allocator_type&) from allocator + Allocator alloc(false); + AllocVec get_alloc(0, alloc); + } catch (int) { + } + check_new_delete_called(); +#endif // TEST_STD_VER >= 14 + + try { // Throw in vector(InputIterator, InputIterator) from input iterator + std::vector vec((Iterator()), Iterator(2)); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator) from forward iterator + std::vector vec((Iterator()), Iterator(2)); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator) from allocator + int a[] = {1, 2}; + AllocVec vec(cpp17_input_iterator(a), cpp17_input_iterator(a + 2)); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator + std::allocator alloc; + std::vector vec(Iterator(), Iterator(2), alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator + std::allocator alloc; + std::vector vec(Iterator(), Iterator(2), alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator + bool a[] = {true, false}; + Allocator alloc(false); + AllocVec vec(cpp17_input_iterator(a), cpp17_input_iterator(a + 2), alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator + bool a[] = {true, false}; + Allocator alloc(false); + AllocVec vec(forward_iterator(a), forward_iterator(a + 2), alloc); + } catch (int) { + } + check_new_delete_called(); + + return 0; +} diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp new file mode 100644 index 000000000000..26ad7b4fd05a --- /dev/null +++ b/libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp @@ -0,0 +1,229 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: no-exceptions + +// (bug report: https://llvm.org/PR58392) +// Check that vector constructors don't leak memory when an operation inside the constructor throws an exception + +#include +#include + +#include "count_new.h" +#include "test_iterators.h" + +template +struct Allocator { + using value_type = T; + using is_always_equal = std::false_type; + + Allocator(bool should_throw = true) { + if (should_throw) + throw 0; + } + + T* allocate(int n) { return std::allocator().allocate(n); } + void deallocate(T* ptr, int n) { std::allocator().deallocate(ptr, n); } + + friend bool operator==(const Allocator&, const Allocator&) { return false; } +}; + +struct ThrowingT { + int* throw_after_n_ = nullptr; + ThrowingT() { throw 0; } + + ThrowingT(int& throw_after_n) : throw_after_n_(&throw_after_n) { + if (throw_after_n == 0) + throw 0; + --throw_after_n; + } + + ThrowingT(const ThrowingT&) { + if (throw_after_n_ == nullptr || *throw_after_n_ == 0) + throw 1; + --*throw_after_n_; + } + + ThrowingT& operator=(const ThrowingT&) { + if (throw_after_n_ == nullptr || *throw_after_n_ == 0) + throw 1; + --*throw_after_n_; + return *this; + } +}; + +template +struct Iterator { + using iterator_category = IterCat; + using difference_type = std::ptrdiff_t; + using value_type = int; + using reference = int&; + using pointer = int*; + + int i_; + Iterator(int i = 0) : i_(i) {} + int& operator*() { + if (i_ == 1) + throw 1; + return i_; + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; } + + friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; } + + Iterator& operator++() { + ++i_; + return *this; + } + + Iterator operator++(int) { + auto tmp = *this; + ++i_; + return tmp; + } +}; + +void check_new_delete_called() { + assert(globalMemCounter.new_called == globalMemCounter.delete_called); + assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called); + assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called); + assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called); +} + +int main(int, char**) { + using AllocVec = std::vector >; + try { // vector() + AllocVec vec; + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(size_type) from type + std::vector get_alloc(1); + } catch (int) { + } + check_new_delete_called(); + +#if TEST_STD_VER >= 14 + try { // Throw in vector(size_type, value_type) from type + int throw_after = 1; + ThrowingT v(throw_after); + std::vector get_alloc(1, v); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(size_type, const allocator_type&) from allocator + Allocator alloc(false); + AllocVec get_alloc(0, alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(size_type, const allocator_type&) from the type + std::vector vec(1, std::allocator()); + } catch (int) { + } + check_new_delete_called(); +#endif // TEST_STD_VER >= 14 + + try { // Throw in vector(InputIterator, InputIterator) from input iterator + std::vector vec((Iterator()), Iterator(2)); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator) from forward iterator + std::vector vec((Iterator()), Iterator(2)); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator) from allocator + int a[] = {1, 2}; + AllocVec vec(cpp17_input_iterator(a), cpp17_input_iterator(a + 2)); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator + std::allocator alloc; + std::vector vec(Iterator(), Iterator(2), alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator + std::allocator alloc; + std::vector vec(Iterator(), Iterator(2), alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator + int a[] = {1, 2}; + Allocator alloc(false); + AllocVec vec(cpp17_input_iterator(a), cpp17_input_iterator(a + 2), alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator + int a[] = {1, 2}; + Allocator alloc(false); + AllocVec vec(forward_iterator(a), forward_iterator(a + 2), alloc); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(const vector&) from type + std::vector vec; + int throw_after = 0; + vec.emplace_back(throw_after); + auto vec2 = vec; + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(const vector&, const allocator_type&) from type + std::vector vec; + int throw_after = 1; + vec.emplace_back(throw_after); + std::vector vec2(vec, std::allocator()); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(vector&&, const allocator_type&) from type + std::vector > vec(Allocator(false)); + int throw_after = 1; + vec.emplace_back(throw_after); + std::vector > vec2(std::move(vec), Allocator(false)); + } catch (int) { + } + check_new_delete_called(); + +#if TEST_STD_VER >= 11 + try { // Throw in vector(initializer_list) from type + int throw_after = 1; + std::vector vec({ThrowingT(throw_after)}); + } catch (int) { + } + check_new_delete_called(); + + try { // Throw in vector(initializer_list, const allocator_type&) constructor from type + int throw_after = 1; + std::vector vec({ThrowingT(throw_after)}, std::allocator()); + } catch (int) { + } + check_new_delete_called(); +#endif // TEST_STD_VER >= 11 + + return 0; +}