[LICM] Generalize unwinding check during scalar promotion

This extract a common isNotVisibleOnUnwind() helper into
AliasAnalysis, which handles allocas, byval arguments and noalias
calls. After D116998 this could also handle sret arguments. We
have similar logic in DSE and MemCpyOpt, which will be switched
to use this helper as well.

The noalias call case is a bit different from the others, because
it also requires that the object is not captured. The caller is
responsible for doing the appropriate check.

Differential Revision: https://reviews.llvm.org/D117000
This commit is contained in:
Nikita Popov 2022-01-11 11:02:26 +01:00
parent 7c02776567
commit 44cfc3a816
4 changed files with 46 additions and 20 deletions

View File

@ -1267,6 +1267,14 @@ bool isIdentifiedObject(const Value *V);
/// 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

View File

@ -988,6 +988,29 @@ bool llvm::isIdentifiedFunctionLocal(const Value *V) {
return isa<AllocaInst>(V) || isNoAliasCall(V) || isNoAliasOrByValArgument(V);
}
bool llvm::isNotVisibleOnUnwind(const Value *Object,
bool &RequiresNoCaptureBeforeUnwind) {
RequiresNoCaptureBeforeUnwind = false;
// Alloca goes out of scope on unwind.
if (isa<AllocaInst>(Object))
return true;
// Byval goes out of scope on unwind.
if (auto *A = dyn_cast<Argument>(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

View File

@ -1925,21 +1925,14 @@ bool isNotCapturedBeforeOrInLoop(const Value *V, const Loop *L,
/// 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<AllocaInst>(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 @@ bool llvm::promoteLoopAccessesToScalars(
// 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.

View File

@ -100,16 +100,16 @@ for.cond.cleanup:
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 @@ define void @test_byval(i32* byval(i32) %a, i1 zeroext %y) uwtable {
; 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: