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/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/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: