Skip to content

Commit 0a4e40b

Browse files
[LICM] Improve LICM when calls only change Inaccessible memory
Extend `MemorySSA`’s clobber query to better distinguish calls that access inaccessible memory improving code motion opportunities in loops. If both calls dont clobber Inaccessible Memory Location it can return from instructionClobbersQuery without setting ModeRefInfo. Otherwise it relies in the default behaviour to set ModRefInfo to Read and Write. This enables LICM to hoist calls that modify inaccessible memory, improving code motion opportunities in loops.
1 parent 5d0bfd1 commit 0a4e40b

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

llvm/lib/Analysis/MemorySSA.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,17 @@ static bool areLoadsReorderable(const LoadInst *Use,
277277
return !(SeqCstUse || MayClobberIsAcquire);
278278
}
279279

280+
bool hasInaccessibleMemoryClobber(const CallBase *CallFirst,
281+
const CallBase *CallSecond) {
282+
283+
MemoryEffects ME1 = CallFirst->getMemoryEffects();
284+
MemoryEffects ME2 = CallSecond->getMemoryEffects();
285+
if (CallFirst->onlyAccessesInaccessibleMemory() ||
286+
CallSecond->onlyAccessesInaccessibleMemory())
287+
return !(ME1 & ME2 & MemoryEffects::writeOnly()).onlyReadsMemory();
288+
return true;
289+
}
290+
280291
template <typename AliasAnalysisType>
281292
static bool
282293
instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
@@ -311,6 +322,9 @@ instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
311322
}
312323

313324
if (auto *CB = dyn_cast_or_null<CallBase>(UseInst)) {
325+
if (auto *CU = dyn_cast_or_null<CallBase>(DefInst))
326+
if (!hasInaccessibleMemoryClobber(CB, CU))
327+
return false;
314328
ModRefInfo I = AA.getModRefInfo(DefInst, CB);
315329
return isModOrRefSet(I);
316330
}

llvm/lib/IR/Instructions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@ void CallBase::setOnlyAccessesArgMemory() {
693693
bool CallBase::onlyAccessesInaccessibleMemory() const {
694694
return getMemoryEffects().onlyAccessesInaccessibleMem();
695695
}
696+
696697
void CallBase::setOnlyAccessesInaccessibleMemory() {
697698
setMemoryEffects(getMemoryEffects() & MemoryEffects::inaccessibleMemOnly());
698699
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
2+
; RUN: opt -aa-pipeline=basic-aa -passes='require<aa>,require<target-ir>,loop-mssa(licm)' < %s -S | FileCheck %s
3+
4+
define void @inaccessible_hoist(ptr noalias %loc, ptr noalias %loc2){
5+
; CHECK-LABEL: define void @inaccessible_hoist(
6+
; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
7+
; CHECK-NEXT: [[ENTRY:.*:]]
8+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
9+
; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
10+
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
11+
; CHECK-NEXT: call void @fn_read_inaccessible_mem()
12+
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
13+
; CHECK: [[FOR_COND_CLEANUP:.*:]]
14+
; CHECK-NEXT: ret void
15+
; CHECK: [[FOR_BODY]]:
16+
; CHECK-NEXT: br label %[[FOR_BODY]]
17+
;
18+
entry:
19+
br label %for.body
20+
for.cond.cleanup: ; preds = %for.body
21+
ret void
22+
for.body:
23+
%val = load i32, ptr %loc2
24+
store i32 %val, ptr %loc
25+
call void @fn_write_inaccessible_mem()
26+
call void @fn_read_inaccessible_mem()
27+
br label %for.body
28+
}
29+
30+
31+
define void @neg_inaccessible_hoist(ptr noalias %loc, ptr noalias %loc2){
32+
; CHECK-LABEL: define void @neg_inaccessible_hoist(
33+
; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
34+
; CHECK-NEXT: [[ENTRY:.*:]]
35+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
36+
; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
37+
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
38+
; CHECK: [[FOR_BODY]]:
39+
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
40+
; CHECK-NEXT: call void @fn_read_inaccessible_mem()
41+
; CHECK-NEXT: call void @fn_readwrite_inaccessible_mem()
42+
; CHECK-NEXT: br label %[[FOR_BODY]]
43+
;
44+
entry:
45+
br label %for.body
46+
for.body:
47+
%val = load i32, ptr %loc2
48+
store i32 %val, ptr %loc
49+
call void @fn_write_inaccessible_mem()
50+
call void @fn_read_inaccessible_mem()
51+
call void @fn_readwrite_inaccessible_mem()
52+
br label %for.body
53+
}
54+
55+
56+
; Nothing should be hoisted from the loop because volatile
57+
; sets inaccessible memory to read write
58+
define void @neg_volatile(ptr %loc, ptr %loc2) {
59+
; CHECK-LABEL: define void @neg_volatile(
60+
; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) {
61+
; CHECK-NEXT: [[ENTRY:.*:]]
62+
; CHECK-NEXT: br label %[[LOOP:.*]]
63+
; CHECK: [[LOOP]]:
64+
; CHECK-NEXT: store volatile i32 0, ptr [[LOC]], align 4
65+
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
66+
; CHECK-NEXT: call void @fn_read_inaccessible_mem()
67+
; CHECK-NEXT: br label %[[LOOP]]
68+
;
69+
entry:
70+
br label %loop
71+
72+
loop:
73+
%val = load i32, ptr %loc2
74+
store volatile i32 0, ptr %loc
75+
call void @fn_write_inaccessible_mem()
76+
call void @fn_read_inaccessible_mem()
77+
br label %loop
78+
}
79+
80+
declare void @fn_write_inaccessible_mem()#0
81+
memory(inaccessiblemem: write)
82+
83+
declare void @fn_read_inaccessible_mem()#0
84+
memory(inaccessiblemem: read)
85+
86+
declare void @fn_readwrite_inaccessible_mem()#0
87+
memory(inaccessiblemem: readwrite)
88+
89+
; Needs to set nounwind because of doesNotThrow
90+
attributes #0 = { mustprogress nofree norecurse nosync nounwind}

0 commit comments

Comments
 (0)