Skip to content

Commit 1c26e2b

Browse files
authored
[ArgPromotion] Perform alias analysis on actual arguments of Calls (#106216)
Teach Argument Promotion to perform alias analysis on actual arguments of Calls to a Function, to try to prove that all Calls to the Function do not modify the memory pointed to by an argument. This surfaces more opportunities to perform Argument Promotion in cases where simply looking at a Function's instructions is insufficient to prove that the pointer argument is not invalidated before all loads from it.
1 parent 6fe7234 commit 1c26e2b

File tree

2 files changed

+49
-22
lines changed

2 files changed

+49
-22
lines changed

llvm/lib/Transforms/IPO/ArgumentPromotion.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,36 @@ static bool allCallersPassValidPointerForArgument(
485485
});
486486
}
487487

488+
// Try to prove that all Calls to F do not modify the memory pointed to by Arg,
489+
// using alias analysis local to each caller of F.
490+
static bool isArgUnmodifiedByAllCalls(Argument *Arg,
491+
FunctionAnalysisManager &FAM) {
492+
for (User *U : Arg->getParent()->users()) {
493+
494+
// Bail if we find an unexpected (non CallInst) use of the function.
495+
auto *Call = dyn_cast<CallInst>(U);
496+
if (!Call)
497+
return false;
498+
499+
MemoryLocation Loc =
500+
MemoryLocation::getForArgument(Call, Arg->getArgNo(), nullptr);
501+
502+
AAResults &AAR = FAM.getResult<AAManager>(*Call->getFunction());
503+
// Bail as soon as we find a Call where Arg may be modified.
504+
if (isModSet(AAR.getModRefInfo(Call, Loc)))
505+
return false;
506+
}
507+
508+
// All Users are Calls which do not modify the Arg.
509+
return true;
510+
}
511+
488512
/// Determine that this argument is safe to promote, and find the argument
489513
/// parts it can be promoted into.
490514
static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
491515
unsigned MaxElements, bool IsRecursive,
492-
SmallVectorImpl<OffsetAndArgPart> &ArgPartsVec) {
516+
SmallVectorImpl<OffsetAndArgPart> &ArgPartsVec,
517+
FunctionAnalysisManager &FAM) {
493518
// Quick exit for unused arguments
494519
if (Arg->use_empty())
495520
return true;
@@ -716,10 +741,16 @@ static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
716741
return true;
717742

718743
// Okay, now we know that the argument is only used by load instructions, and
719-
// it is safe to unconditionally perform all of them. Use alias analysis to
720-
// check to see if the pointer is guaranteed to not be modified from entry of
721-
// the function to each of the load instructions.
744+
// it is safe to unconditionally perform all of them.
745+
746+
// If we can determine that no call to the Function modifies the memory region
747+
// accessed through Arg, through alias analysis using actual arguments in the
748+
// callers, we know that it is guaranteed to be safe to promote the argument.
749+
if (isArgUnmodifiedByAllCalls(Arg, FAM))
750+
return true;
722751

752+
// Otherwise, use alias analysis to check if the pointer is guaranteed to not
753+
// be modified from entry of the function to each of the load instructions.
723754
for (LoadInst *Load : Loads) {
724755
// Check to see if the load is invalidated from the start of the block to
725756
// the load itself.
@@ -846,7 +877,8 @@ static Function *promoteArguments(Function *F, FunctionAnalysisManager &FAM,
846877
// If we can promote the pointer to its value.
847878
SmallVector<OffsetAndArgPart, 4> ArgParts;
848879

849-
if (findArgParts(PtrArg, DL, AAR, MaxElements, IsRecursive, ArgParts)) {
880+
if (findArgParts(PtrArg, DL, AAR, MaxElements, IsRecursive, ArgParts,
881+
FAM)) {
850882
SmallVector<Type *, 4> Types;
851883
for (const auto &Pair : ArgParts)
852884
Types.push_back(Pair.second.Ty);

llvm/test/Transforms/ArgumentPromotion/actual-arguments.ll

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,14 @@ define internal i32 @test_cannot_promote_3(ptr %p, ptr nocapture readonly %test_
6868
ret i32 %sum
6969
}
7070

71-
; FIXME: We should perform ArgPromotion here!
72-
;
7371
; This is called only by @caller_safe_args_1, from which we can prove that
7472
; %test_c does not alias %p for any Call to the function, so we can promote it.
7573
;
7674
define internal i32 @test_can_promote_1(ptr %p, ptr nocapture readonly %test_c) {
7775
; CHECK-LABEL: define {{[^@]+}}@test_can_promote_1
78-
; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[TEST_C:%.*]]) {
79-
; CHECK-NEXT: [[TEST_C_VAL:%.*]] = load i32, ptr [[TEST_C]], align 4
80-
; CHECK-NEXT: [[RES:%.*]] = call i32 @callee(ptr [[P]], i32 [[TEST_C_VAL]])
81-
; CHECK-NEXT: [[LTEST_C:%.*]] = load i32, ptr [[TEST_C]], align 4
82-
; CHECK-NEXT: [[SUM:%.*]] = add i32 [[LTEST_C]], [[RES]]
76+
; CHECK-SAME: (ptr [[P:%.*]], i32 [[TEST_C_0_VAL:%.*]]) {
77+
; CHECK-NEXT: [[RES:%.*]] = call i32 @callee(ptr [[P]], i32 [[TEST_C_0_VAL]])
78+
; CHECK-NEXT: [[SUM:%.*]] = add i32 [[TEST_C_0_VAL]], [[RES]]
8379
; CHECK-NEXT: ret i32 [[SUM]]
8480
;
8581
%res = call i32 @callee(ptr %p, ptr %test_c)
@@ -91,19 +87,15 @@ define internal i32 @test_can_promote_1(ptr %p, ptr nocapture readonly %test_c)
9187
ret i32 %sum
9288
}
9389

94-
; FIXME: We should perform ArgPromotion here!
95-
;
9690
; This is called by multiple callers (@caller_safe_args_1, @caller_safe_args_2),
9791
; from which we can prove that %test_c does not alias %p for any Call to the
9892
; function, so we can promote it.
9993
;
10094
define internal i32 @test_can_promote_2(ptr %p, ptr nocapture readonly %test_c) {
10195
; CHECK-LABEL: define {{[^@]+}}@test_can_promote_2
102-
; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[TEST_C:%.*]]) {
103-
; CHECK-NEXT: [[TEST_C_VAL:%.*]] = load i32, ptr [[TEST_C]], align 4
104-
; CHECK-NEXT: [[RES:%.*]] = call i32 @callee(ptr [[P]], i32 [[TEST_C_VAL]])
105-
; CHECK-NEXT: [[LTEST_C:%.*]] = load i32, ptr [[TEST_C]], align 4
106-
; CHECK-NEXT: [[SUM:%.*]] = add i32 [[LTEST_C]], [[RES]]
96+
; CHECK-SAME: (ptr [[P:%.*]], i32 [[TEST_C_0_VAL:%.*]]) {
97+
; CHECK-NEXT: [[RES:%.*]] = call i32 @callee(ptr [[P]], i32 [[TEST_C_0_VAL]])
98+
; CHECK-NEXT: [[SUM:%.*]] = add i32 [[TEST_C_0_VAL]], [[RES]]
10799
; CHECK-NEXT: ret i32 [[SUM]]
108100
;
109101
%res = call i32 @callee(ptr %p, ptr %test_c)
@@ -186,8 +178,10 @@ define i32 @caller_safe_args_1(i64 %n) {
186178
; CHECK-NEXT: [[CALLER_C:%.*]] = alloca i32, align 4
187179
; CHECK-NEXT: store i32 5, ptr [[CALLER_C]], align 4
188180
; CHECK-NEXT: [[RES1:%.*]] = call i32 @test_cannot_promote_3(ptr [[P]], ptr [[CALLER_C]])
189-
; CHECK-NEXT: [[RES2:%.*]] = call i32 @test_can_promote_1(ptr [[P]], ptr [[CALLER_C]])
190-
; CHECK-NEXT: [[RES3:%.*]] = call i32 @test_can_promote_2(ptr [[P]], ptr [[CALLER_C]])
181+
; CHECK-NEXT: [[CALLER_C_VAL:%.*]] = load i32, ptr [[CALLER_C]], align 4
182+
; CHECK-NEXT: [[RES2:%.*]] = call i32 @test_can_promote_1(ptr [[P]], i32 [[CALLER_C_VAL]])
183+
; CHECK-NEXT: [[CALLER_C_VAL1:%.*]] = load i32, ptr [[CALLER_C]], align 4
184+
; CHECK-NEXT: [[RES3:%.*]] = call i32 @test_can_promote_2(ptr [[P]], i32 [[CALLER_C_VAL1]])
191185
; CHECK-NEXT: [[RES12:%.*]] = add i32 [[RES1]], [[RES2]]
192186
; CHECK-NEXT: [[RES:%.*]] = add i32 [[RES12]], [[RES3]]
193187
; CHECK-NEXT: ret i32 [[RES]]
@@ -215,7 +209,8 @@ define i32 @caller_safe_args_2(i64 %n, ptr %p) {
215209
; CHECK-NEXT: call void @memset(ptr [[P]], i64 0, i64 [[N]])
216210
; CHECK-NEXT: [[CALLER_C:%.*]] = alloca i32, align 4
217211
; CHECK-NEXT: store i32 5, ptr [[CALLER_C]], align 4
218-
; CHECK-NEXT: [[RES:%.*]] = call i32 @test_can_promote_2(ptr [[P]], ptr [[CALLER_C]])
212+
; CHECK-NEXT: [[CALLER_C_VAL:%.*]] = load i32, ptr [[CALLER_C]], align 4
213+
; CHECK-NEXT: [[RES:%.*]] = call i32 @test_can_promote_2(ptr [[P]], i32 [[CALLER_C_VAL]])
219214
; CHECK-NEXT: ret i32 [[RES]]
220215
;
221216
call void @memset(ptr %p, i64 0, i64 %n)

0 commit comments

Comments
 (0)