Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -1312,6 +1312,12 @@ This indicates that callee does not free the pointer argument. This is not a valid attribute for return values. +``noreadafterunwind`` + This indicates that the caller will not read the underlying object of a + pointer argument if the call unwinds. As such, stores to the object that + are only visible on unwind paths can be elided. This attribute cannot be + applied to return values. + .. _nest: ``nest`` Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -713,6 +713,7 @@ ATTR_KIND_SKIP_PROFILE = 85, ATTR_KIND_MEMORY = 86, ATTR_KIND_NOFPCLASS = 87, + ATTR_KIND_NOREADAFTERUNWIND = 88, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -151,6 +151,9 @@ /// Function does not deallocate memory. def NoFree : EnumAttr<"nofree", [FnAttr, ParamAttr]>; +/// Argument is not read if the call unwinds. +def NoReadAfterUnwind : EnumAttr<"noreadafterunwind", [ParamAttr]>; + /// Disable implicit floating point insts. def NoImplicitFloat : EnumAttr<"noimplicitfloat", [FnAttr]>; Index: llvm/lib/Analysis/AliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/AliasAnalysis.cpp +++ llvm/lib/Analysis/AliasAnalysis.cpp @@ -896,7 +896,7 @@ // Byval goes out of scope on unwind. if (auto *A = dyn_cast(Object)) - return A->hasByValAttr(); + return A->hasByValAttr() || A->hasAttribute(Attribute::NoReadAfterUnwind); // A noalias return is not accessible from any other code. If the pointer // does not escape prior to the unwind, then the caller cannot access the Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2060,6 +2060,8 @@ return Attribute::Hot; case bitc::ATTR_KIND_PRESPLIT_COROUTINE: return Attribute::PresplitCoroutine; + case bitc::ATTR_KIND_NOREADAFTERUNWIND: + return Attribute::NoReadAfterUnwind; } } Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -809,6 +809,8 @@ return bitc::ATTR_KIND_MUSTPROGRESS; case Attribute::PresplitCoroutine: return bitc::ATTR_KIND_PRESPLIT_COROUTINE; + case Attribute::NoReadAfterUnwind: + return bitc::ATTR_KIND_NOREADAFTERUNWIND; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -1961,7 +1961,8 @@ .addAttribute(Attribute::ReadNone) .addAttribute(Attribute::ReadOnly) .addAttribute(Attribute::Dereferenceable) - .addAttribute(Attribute::DereferenceableOrNull); + .addAttribute(Attribute::DereferenceableOrNull) + .addAttribute(Attribute::NoReadAfterUnwind); if (ASK & ASK_UNSAFE_TO_DROP) Incompatible.addAttribute(Attribute::Nest) .addAttribute(Attribute::SwiftError) Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -990,6 +990,7 @@ case Attribute::ImmArg: case Attribute::ByRef: case Attribute::WriteOnly: + case Attribute::NoReadAfterUnwind: // These are not really attributes. case Attribute::None: case Attribute::EndAttrKinds: Index: llvm/test/Bitcode/attributes.ll =================================================================== --- llvm/test/Bitcode/attributes.ll +++ llvm/test/Bitcode/attributes.ll @@ -511,6 +511,11 @@ ; CHECK: define void @f88() [[SKIPPROFILE:#[0-9]+]] define void @f88() skipprofile { ret void } +; CHECK: define void @f89(ptr noreadafterunwind %p) +define void @f89(ptr noreadafterunwind %p) { + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { memory(none) } Index: llvm/test/Transforms/DeadStoreElimination/simple.ll =================================================================== --- llvm/test/Transforms/DeadStoreElimination/simple.ll +++ llvm/test/Transforms/DeadStoreElimination/simple.ll @@ -515,12 +515,10 @@ ret void } ; Same as previous case, but with an sret argument. -; TODO: The first store could be eliminated if sret is not visible on unwind. -define void @test34_sret(ptr noalias sret(i32) %p) { -; CHECK-LABEL: @test34_sret( -; CHECK-NEXT: store i32 1, ptr [[P:%.*]], align 4 +define void @test34_noreadafterunwind(ptr noalias noreadafterunwind %p) { +; CHECK-LABEL: @test34_noreadafterunwind( ; CHECK-NEXT: call void @unknown_func() -; CHECK-NEXT: store i32 0, ptr [[P]], align 4 +; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4 ; CHECK-NEXT: ret void ; store i32 1, ptr %p Index: llvm/test/Transforms/LICM/scalar-promote-unwind.ll =================================================================== --- llvm/test/Transforms/LICM/scalar-promote-unwind.ll +++ llvm/test/Transforms/LICM/scalar-promote-unwind.ll @@ -147,9 +147,8 @@ ret void } -; TODO: sret could be specified to not be accessed on unwind either. -define void @test_sret(ptr noalias sret(i32) %a, i1 zeroext %y) uwtable { -; CHECK-LABEL: @test_sret( +define void @test_noreadafterunwind(ptr noalias noreadafterunwind %a, i1 zeroext %y) uwtable { +; CHECK-LABEL: @test_noreadafterunwind( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A_PROMOTED:%.*]] = load i32, ptr [[A:%.*]], align 4 ; CHECK-NEXT: br label [[FOR_BODY:%.*]] @@ -157,7 +156,6 @@ ; CHECK-NEXT: [[ADD1:%.*]] = phi i32 [ [[A_PROMOTED]], [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[FOR_INC:%.*]] ] ; CHECK-NEXT: [[I_03:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[INC:%.*]], [[FOR_INC]] ] ; CHECK-NEXT: [[ADD]] = add nsw i32 [[ADD1]], 1 -; CHECK-NEXT: store i32 [[ADD]], ptr [[A]], align 4 ; CHECK-NEXT: br i1 [[Y:%.*]], label [[IF_THEN:%.*]], label [[FOR_INC]] ; CHECK: if.then: ; CHECK-NEXT: tail call void @f() @@ -167,6 +165,8 @@ ; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[INC]], 10000 ; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]] ; CHECK: for.cond.cleanup: +; CHECK-NEXT: [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[FOR_INC]] ] +; CHECK-NEXT: store i32 [[ADD_LCSSA]], ptr [[A]], align 4 ; CHECK-NEXT: ret void ; entry: Index: llvm/test/Transforms/MemCpyOpt/callslot_throw.ll =================================================================== --- llvm/test/Transforms/MemCpyOpt/callslot_throw.ll +++ llvm/test/Transforms/MemCpyOpt/callslot_throw.ll @@ -56,14 +56,11 @@ ret void } -; TODO: With updated semantics, sret could also be invisible on unwind. -define void @test_sret(ptr nocapture noalias dereferenceable(4) sret(i32) %x) { -; CHECK-LABEL: @test_sret( +define void @test_noreadafterunwind(ptr nocapture noalias noreadafterunwind dereferenceable(4) %x) { +; CHECK-LABEL: @test_noreadafterunwind( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[T:%.*]] = alloca i32, align 4 -; CHECK-NEXT: call void @may_throw(ptr nonnull [[T]]) -; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[T]], align 4 -; CHECK-NEXT: store i32 [[LOAD]], ptr [[X:%.*]], align 4 +; CHECK-NEXT: call void @may_throw(ptr nonnull [[X:%.*]]) ; CHECK-NEXT: ret void ; entry: Index: llvm/test/Verifier/noreadafterunwind.ll =================================================================== --- /dev/null +++ llvm/test/Verifier/noreadafterunwind.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s + +; CHECK: Attribute 'noreadafterunwind' applied to incompatible type! +; CHECK-NEXT: ptr @not_pointer +define void @not_pointer(i32 noreadafterunwind %arg) { + ret void +}