diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -1267,6 +1267,14 @@ /// IdentifiedObjects. bool isIdentifiedFunctionLocal(const Value *V); +/// Return true if Object memory is not visible after an unwind, in the sense +/// that program semantics cannot depend on Object containing any particular +/// value on unwind. If the RequiresNoCaptureBeforeUnwind out parameter is set +/// to true, then the memory is only not visible if the object has not been +/// captured prior to the unwind. Otherwise it is not visible even if captured. +bool isNotVisibleOnUnwind(const Value *Object, + bool &RequiresNoCaptureBeforeUnwind); + /// A manager for alias analyses. /// /// This class can have analyses registered with it and when run, it will run diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -988,6 +988,29 @@ return isa(V) || isNoAliasCall(V) || isNoAliasOrByValArgument(V); } +bool llvm::isNotVisibleOnUnwind(const Value *Object, + bool &RequiresNoCaptureBeforeUnwind) { + RequiresNoCaptureBeforeUnwind = false; + + // Alloca goes out of scope on unwind. + if (isa(Object)) + return true; + + // Byval goes out of scope on unwind. + if (auto *A = dyn_cast(Object)) + return A->hasByValAttr(); + + // 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 + // memory either. + if (isNoAliasCall(Object)) { + RequiresNoCaptureBeforeUnwind = true; + return true; + } + + return false; +} + void llvm::getAAResultsAnalysisUsage(AnalysisUsage &AU) { // This function needs to be in sync with llvm::createLegacyPMAAResults -- if // more alias analyses are added to llvm::createLegacyPMAAResults, they need diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp --- a/llvm/lib/Transforms/Scalar/LICM.cpp +++ b/llvm/lib/Transforms/Scalar/LICM.cpp @@ -1925,21 +1925,14 @@ /// Return true if we can prove that a caller cannot inspect the object if an /// unwind occurs inside the loop. -bool isNotVisibleOnUnwind(Value *Object, const Loop *L, DominatorTree *DT) { - if (isa(Object)) - // Since the alloca goes out of scope, we know the caller can't retain a - // reference to it and be well defined. Thus, we don't need to check for - // capture. - return true; +bool isNotVisibleOnUnwindInLoop(const Value *Object, const Loop *L, + DominatorTree *DT) { + bool RequiresNoCaptureBeforeUnwind; + if (!isNotVisibleOnUnwind(Object, RequiresNoCaptureBeforeUnwind)) + return false; - // For all other objects we need to know that the caller can't possibly - // have gotten a reference to the object prior to an unwind in the loop. - // There are two components of that: - // 1) Object can't have been captured prior to the definition site. - // For this, we need to know the return value is noalias. - // 1) Object can't be captured before or inside the loop. This is what - // isNotCapturedBeforeOrInLoop() checks. - return isNoAliasCall(Object) && isNotCapturedBeforeOrInLoop(Object, L, DT); + return !RequiresNoCaptureBeforeUnwind || + isNotCapturedBeforeOrInLoop(Object, L, DT); } } // namespace @@ -2026,7 +2019,7 @@ // this by proving that the caller can't have a reference to the object // after return and thus can't possibly load from the object. Value *Object = getUnderlyingObject(SomePtr); - if (!isNotVisibleOnUnwind(Object, CurLoop, DT)) + if (!isNotVisibleOnUnwindInLoop(Object, CurLoop, DT)) return false; // Subtlety: Alloca's aren't visible to callers, but *are* potentially // visible to other threads if captured and used during their lifetimes. diff --git a/llvm/test/Transforms/LICM/scalar-promote-unwind.ll b/llvm/test/Transforms/LICM/scalar-promote-unwind.ll --- a/llvm/test/Transforms/LICM/scalar-promote-unwind.ll +++ b/llvm/test/Transforms/LICM/scalar-promote-unwind.ll @@ -100,16 +100,16 @@ ret void } -; TODO: byval memory cannot be accessed on unwind either. +; byval memory cannot be accessed on unwind either. define void @test_byval(i32* byval(i32) %a, i1 zeroext %y) uwtable { ; CHECK-LABEL: @test_byval( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_PROMOTED:%.*]] = load i32, i32* [[A:%.*]], align 4 ; CHECK-NEXT: br label [[FOR_BODY:%.*]] ; CHECK: for.body: -; CHECK-NEXT: [[I_03:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ] -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[A:%.*]], align 4 -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 -; CHECK-NEXT: store i32 [[ADD]], i32* [[A]], align 4 +; 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: br i1 [[Y:%.*]], label [[IF_THEN:%.*]], label [[FOR_INC]] ; CHECK: if.then: ; CHECK-NEXT: tail call void @f() @@ -119,6 +119,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]], i32* [[A]], align 4 ; CHECK-NEXT: ret void ; entry: