Skip to content

Commit 7b65971

Browse files
authored
InstCombine: sink loads with invariant.load metadata (#112692)
1 parent 5f7502b commit 7b65971

File tree

2 files changed

+117
-5
lines changed

2 files changed

+117
-5
lines changed

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4822,7 +4822,8 @@ bool InstCombinerImpl::tryToSinkInstruction(Instruction *I,
48224822

48234823
// We can only sink load instructions if there is nothing between the load and
48244824
// the end of block that could change the value.
4825-
if (I->mayReadFromMemory()) {
4825+
if (I->mayReadFromMemory() &&
4826+
!I->hasMetadata(LLVMContext::MD_invariant_load)) {
48264827
// We don't want to do any sophisticated alias analysis, so we only check
48274828
// the instructions after I in I's parent block if we try to sink to its
48284829
// successor block.

llvm/test/Transforms/InstCombine/sink_instruction.ll

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ define i32 @test3(ptr nocapture readonly %P, i32 %i) {
8686
; CHECK-LABEL: @test3(
8787
; CHECK-NEXT: entry:
8888
; CHECK-NEXT: switch i32 [[I:%.*]], label [[SW_EPILOG:%.*]] [
89-
; CHECK-NEXT: i32 5, label [[SW_BB:%.*]]
90-
; CHECK-NEXT: i32 2, label [[SW_BB]]
89+
; CHECK-NEXT: i32 5, label [[SW_BB:%.*]]
90+
; CHECK-NEXT: i32 2, label [[SW_BB]]
9191
; CHECK-NEXT: ]
9292
; CHECK: sw.bb:
9393
; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64
@@ -190,8 +190,8 @@ define i32 @test6(ptr nocapture readonly %P, i32 %i, i1 %cond) {
190190
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 [[IDXPROM]]
191191
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
192192
; CHECK-NEXT: switch i32 [[I]], label [[SW_BB:%.*]] [
193-
; CHECK-NEXT: i32 5, label [[SW_EPILOG:%.*]]
194-
; CHECK-NEXT: i32 2, label [[SW_EPILOG]]
193+
; CHECK-NEXT: i32 5, label [[SW_EPILOG:%.*]]
194+
; CHECK-NEXT: i32 2, label [[SW_EPILOG]]
195195
; CHECK-NEXT: ]
196196
; CHECK: sw.bb:
197197
; CHECK-NEXT: br label [[SW_EPILOG]]
@@ -272,3 +272,114 @@ abort:
272272
call void @abort()
273273
unreachable
274274
}
275+
276+
; Loads marked invariant can be sunk past potential memory writes.
277+
278+
define i32 @invariant_load_metadata(ptr %p, i1 %cond) {
279+
; CHECK-LABEL: @invariant_load_metadata(
280+
; CHECK-NEXT: entry:
281+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[BLOCK:%.*]], label [[END:%.*]]
282+
; CHECK: block:
283+
; CHECK-NEXT: call void @fn()
284+
; CHECK-NEXT: br label [[END]]
285+
; CHECK: end:
286+
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4, !invariant.load [[META0:![0-9]+]]
287+
; CHECK-NEXT: ret i32 [[V]]
288+
;
289+
entry:
290+
%v = load i32, ptr %p, !invariant.load !0
291+
br i1 %cond, label %block, label %end
292+
block:
293+
call void @fn()
294+
br label %end
295+
end:
296+
ret i32 %v
297+
}
298+
299+
; Loads not marked invariant cannot be sunk past potential memory writes.
300+
301+
define i32 @invariant_load_neg(ptr %p, i1 %cond) {
302+
; CHECK-LABEL: @invariant_load_neg(
303+
; CHECK-NEXT: entry:
304+
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
305+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[BLOCK:%.*]], label [[END:%.*]]
306+
; CHECK: block:
307+
; CHECK-NEXT: call void @fn()
308+
; CHECK-NEXT: br label [[END]]
309+
; CHECK: end:
310+
; CHECK-NEXT: ret i32 [[V]]
311+
;
312+
entry:
313+
%v = load i32, ptr %p
314+
br i1 %cond, label %block, label %end
315+
block:
316+
call void @fn()
317+
br label %end
318+
end:
319+
ret i32 %v
320+
}
321+
322+
; Loads that aren't marked invariant but used in one branch
323+
; can be sunk to that branch.
324+
325+
define void @invariant_load_use_in_br(ptr %p, i1 %cond) {
326+
; CHECK-LABEL: @invariant_load_use_in_br(
327+
; CHECK-NEXT: entry:
328+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[TRUE_BR:%.*]], label [[FALSE_BR:%.*]]
329+
; CHECK: true.br:
330+
; CHECK-NEXT: call void @fn()
331+
; CHECK-NEXT: br label [[EXIT:%.*]]
332+
; CHECK: false.br:
333+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[P:%.*]], align 4
334+
; CHECK-NEXT: call void @fn(i32 [[VAL]])
335+
; CHECK-NEXT: br label [[EXIT]]
336+
; CHECK: exit:
337+
; CHECK-NEXT: ret void
338+
;
339+
entry:
340+
%val = load i32, ptr %p
341+
br i1 %cond, label %true.br, label %false.br
342+
true.br:
343+
call void @fn()
344+
br label %exit
345+
false.br:
346+
call void @fn(i32 %val)
347+
br label %exit
348+
exit:
349+
ret void
350+
}
351+
352+
; Invariant loads marked with metadata can be sunk past calls.
353+
354+
define void @invariant_load_metadata_call(ptr %p, i1 %cond) {
355+
; CHECK-LABEL: @invariant_load_metadata_call(
356+
; CHECK-NEXT: entry:
357+
; CHECK-NEXT: call void @fn()
358+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[TRUE_BR:%.*]], label [[FALSE_BR:%.*]]
359+
; CHECK: true.br:
360+
; CHECK-NEXT: call void @fn()
361+
; CHECK-NEXT: br label [[EXIT:%.*]]
362+
; CHECK: false.br:
363+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[P:%.*]], align 4, !invariant.load [[META0]]
364+
; CHECK-NEXT: call void @fn(i32 [[VAL]])
365+
; CHECK-NEXT: br label [[EXIT]]
366+
; CHECK: exit:
367+
; CHECK-NEXT: ret void
368+
;
369+
entry:
370+
%val = load i32, ptr %p, !invariant.load !0
371+
call void @fn()
372+
br i1 %cond, label %true.br, label %false.br
373+
true.br:
374+
call void @fn()
375+
br label %exit
376+
false.br:
377+
call void @fn(i32 %val)
378+
br label %exit
379+
exit:
380+
ret void
381+
}
382+
383+
declare void @fn()
384+
385+
!0 = !{}

0 commit comments

Comments
 (0)