Skip to content

Commit 917574d

Browse files
kalle-llvmmstorsjo
authored andcommitted
[MachineLICM][WinEH] Don't hoist register reloads out of funclets
This fixes llvm#60766 With MSVC style exception-handling (funclets), no registers are alive when entering the funclet so they must be reloaded from the stack. MachineLICM can sometimes hoist such reloads out of the funclet which is not correct, the register will have been clobbered when entering the funclet. This can happen in any loop that contains a try-catch. This has been tested on x86_64-pc-window-msvc. I'm not sure if funclets work the same on the other windows archs. Reviewed By: rnk, arsenm Differential Revision: https://reviews.llvm.org/D153337
1 parent 484c961 commit 917574d

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

llvm/lib/CodeGen/MachineLICM.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ void MachineLICMBase::HoistRegionPostRA() {
538538
PhysRegDefs.set(*AI);
539539
}
540540

541+
// Funclet entry blocks will clobber all registers
542+
if (const uint32_t *Mask = BB->getBeginClobberMask(TRI))
543+
PhysRegClobbers.setBitsNotInMask(Mask);
544+
541545
SpeculationState = SpeculateUnknown;
542546
for (MachineInstr &MI : *BB)
543547
ProcessMI(&MI, PhysRegDefs, PhysRegClobbers, StoredFIs, Candidates);
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# RUN: llc -o - %s -mtriple=x86_64-pc-windows-msvc -run-pass=machinelicm | FileCheck %s
2+
#
3+
# This test checks that MachineLICM doesn't hoist loads out of funclets.
4+
# Manually modified from the IR of the following C++ function by running
5+
# llc -stop-after=machine-cp.
6+
#
7+
# void may_throw();
8+
# void use(int);
9+
#
10+
# void test(int n, int arg)
11+
# {
12+
# for (int i = 0 ; i < n ; i++)
13+
# try {
14+
# may_throw();
15+
# }
16+
# catch (...) {
17+
# // Two uses to get 'arg' allocated to a register
18+
# use(arg);
19+
# use(arg);
20+
# }
21+
# }
22+
23+
--- |
24+
target triple = "x86_64-pc-windows-msvc"
25+
26+
define void @test(i32 %n, i32 %arg) personality ptr @__CxxFrameHandler3 {
27+
entry:
28+
%cmp3 = icmp sgt i32 %n, 0
29+
br i1 %cmp3, label %for.body.preheader, label %for.cond.cleanup
30+
31+
for.body.preheader: ; preds = %entry
32+
br label %for.body
33+
34+
for.cond.cleanup: ; preds = %for.inc, %entry
35+
ret void
36+
37+
for.body: ; preds = %for.body.preheader, %for.inc
38+
%lsr.iv = phi i32 [ %n, %for.body.preheader ], [ %lsr.iv.next, %for.inc ]
39+
invoke void @may_throw()
40+
to label %for.inc unwind label %catch.dispatch
41+
42+
catch.dispatch: ; preds = %for.body
43+
%0 = catchswitch within none [label %catch] unwind to caller
44+
45+
catch: ; preds = %catch.dispatch
46+
%1 = catchpad within %0 [ptr null, i32 64, ptr null]
47+
call void @use(i32 %arg) [ "funclet"(token %1) ]
48+
call void @use(i32 %arg) [ "funclet"(token %1) ]
49+
catchret from %1 to label %for.inc
50+
51+
for.inc: ; preds = %catch, %for.body
52+
%lsr.iv.next = add i32 %lsr.iv, -1
53+
%exitcond.not = icmp eq i32 %lsr.iv.next, 0
54+
br i1 %exitcond.not, label %for.cond.cleanup, label %for.body
55+
}
56+
57+
declare i32 @__CxxFrameHandler3(...)
58+
59+
declare void @may_throw()
60+
61+
declare void @use(i32)
62+
63+
...
64+
---
65+
name: test
66+
alignment: 16
67+
tracksRegLiveness: true
68+
hasEHCatchret: true
69+
hasEHScopes: true
70+
hasEHFunclets: true
71+
debugInstrRef: true
72+
tracksDebugUserValues: true
73+
liveins:
74+
- { reg: '$ecx' }
75+
- { reg: '$edx' }
76+
frameInfo:
77+
maxAlignment: 8
78+
hasCalls: true
79+
hasOpaqueSPAdjustment: true
80+
stack:
81+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
82+
- { id: 1, type: spill-slot, size: 4, alignment: 4 }
83+
machineFunctionInfo: {}
84+
body: |
85+
bb.0.entry:
86+
successors: %bb.1, %bb.2
87+
liveins: $ecx, $edx
88+
89+
MOV32mr %stack.1, 1, $noreg, 0, $noreg, $edx :: (store (s32) into %stack.1)
90+
TEST32rr renamable $ecx, renamable $ecx, implicit-def $eflags
91+
JCC_1 %bb.2, 14, implicit killed $eflags
92+
93+
bb.1:
94+
liveins: $ecx
95+
96+
JMP_1 %bb.3
97+
98+
bb.2.for.cond.cleanup:
99+
RET 0
100+
101+
bb.3.for.body:
102+
successors: %bb.5, %bb.4
103+
liveins: $ecx
104+
105+
EH_LABEL <mcsymbol .Leh1>
106+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $ecx :: (store (s32) into %stack.0)
107+
ADJCALLSTACKDOWN64 32, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
108+
CALL64pcrel32 @may_throw, csr_win64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp
109+
ADJCALLSTACKUP64 32, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
110+
EH_LABEL <mcsymbol .Leh2>
111+
JMP_1 %bb.5
112+
113+
bb.4.catch (landing-pad, ehfunclet-entry):
114+
successors: %bb.5
115+
116+
ADJCALLSTACKDOWN64 32, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
117+
renamable $esi = MOV32rm %stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %stack.1)
118+
$ecx = COPY renamable $esi
119+
CALL64pcrel32 @use, csr_win64, implicit $rsp, implicit $ssp, implicit $ecx, implicit-def $rsp, implicit-def $ssp
120+
ADJCALLSTACKUP64 32, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
121+
ADJCALLSTACKDOWN64 32, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
122+
$ecx = COPY killed renamable $esi
123+
CALL64pcrel32 @use, csr_win64, implicit $rsp, implicit $ssp, implicit $ecx, implicit-def $rsp, implicit-def $ssp
124+
ADJCALLSTACKUP64 32, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
125+
CATCHRET %bb.5, %bb.0
126+
127+
bb.5.for.inc:
128+
successors: %bb.2, %bb.3
129+
130+
renamable $ecx = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
131+
renamable $ecx = DEC32r killed renamable $ecx, implicit-def $eflags
132+
JCC_1 %bb.2, 4, implicit killed $eflags
133+
JMP_1 %bb.3
134+
135+
...
136+
#
137+
# CHECK: bb.4.catch
138+
# CHECK: ADJCALLSTACKDOWN64
139+
# CHECK-NEXT: renamable [[REG:\$[a-z0-9]+]] = MOV32rm %stack.1
140+
# CHECK-NEXT: $ecx = COPY renamable [[REG]]
141+
# CHECK-NEXT: CALL64pcrel32 @use

0 commit comments

Comments
 (0)