Skip to content

Commit 49e9ee1

Browse files
nikictru
authored andcommitted
[StackColoring] Handle SEH catch object stack slots conservatively
The write to the SEH catch object happens before cleanuppads are executed, while the first reference to the object will typically be in a catchpad. If we make use of first-use analysis, we may end up allocating an alloca used inside the cleanuppad and the catch object at the same stack offset, which would be incorrect. https://reviews.llvm.org/D86673 was a previous attempt to fix it. It used the heuristic "a slot loaded in a WinEH pad and never written" to detect catch objects. However, because it checks for more than one load (while probably more than zero was intended), the fix does not actually work. The general approach also seems dubious to me, so this patch reverts that change entirely, and instead marks all catch object slots as conservative (i.e. excluded from first-use analysis) based on the WinEHFuncInfo. As far as I can tell we don't need any heuristics here, we know exactly which slots are affected. Fixes llvm#66984. (cherry picked from commit b3cb4f0)
1 parent 17123a6 commit 49e9ee1

File tree

2 files changed

+17
-52
lines changed

2 files changed

+17
-52
lines changed

llvm/lib/CodeGen/StackColoring.cpp

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -370,37 +370,6 @@ STATISTIC(EscapedAllocas, "Number of allocas that escaped the lifetime region");
370370
// If in RPO ordering chosen to walk the CFG we happen to visit the b[k]
371371
// before visiting the memcpy block (which will contain the lifetime start
372372
// for "b" then it will appear that 'b' has a degenerate lifetime.
373-
//
374-
// Handle Windows Exception with LifetimeStartOnFirstUse:
375-
// -----------------
376-
//
377-
// There was a bug for using LifetimeStartOnFirstUse in win32.
378-
// class Type1 {
379-
// ...
380-
// ~Type1(){ write memory;}
381-
// }
382-
// ...
383-
// try{
384-
// Type1 V
385-
// ...
386-
// } catch (Type2 X){
387-
// ...
388-
// }
389-
// For variable X in catch(X), we put point pX=&(&X) into ConservativeSlots
390-
// to prevent using LifetimeStartOnFirstUse. Because pX may merged with
391-
// object V which may call destructor after implicitly writing pX. All these
392-
// are done in C++ EH runtime libs (through CxxThrowException), and can't
393-
// obviously check it in IR level.
394-
//
395-
// The loader of pX, without obvious writing IR, is usually the first LOAD MI
396-
// in EHPad, Some like:
397-
// bb.x.catch.i (landing-pad, ehfunclet-entry):
398-
// ; predecessors: %bb...
399-
// successors: %bb...
400-
// %n:gr32 = MOV32rm %stack.pX ...
401-
// ...
402-
// The Type2** %stack.pX will only be written in EH runtime libs, so we
403-
// check the StoreSlots to screen it out.
404373

405374
namespace {
406375

@@ -462,9 +431,6 @@ class StackColoring : public MachineFunctionPass {
462431
/// slots lifetime-start-on-first-use is disabled).
463432
BitVector ConservativeSlots;
464433

465-
/// Record the FI slots referenced by a 'may write to memory'.
466-
BitVector StoreSlots;
467-
468434
/// Number of iterations taken during data flow analysis.
469435
unsigned NumIterations;
470436

@@ -660,13 +626,10 @@ unsigned StackColoring::collectMarkers(unsigned NumSlot) {
660626
InterestingSlots.resize(NumSlot);
661627
ConservativeSlots.clear();
662628
ConservativeSlots.resize(NumSlot);
663-
StoreSlots.clear();
664-
StoreSlots.resize(NumSlot);
665629

666630
// number of start and end lifetime ops for each slot
667631
SmallVector<int, 8> NumStartLifetimes(NumSlot, 0);
668632
SmallVector<int, 8> NumEndLifetimes(NumSlot, 0);
669-
SmallVector<int, 8> NumLoadInCatchPad(NumSlot, 0);
670633

671634
// Step 1: collect markers and populate the "InterestingSlots"
672635
// and "ConservativeSlots" sets.
@@ -722,13 +685,6 @@ unsigned StackColoring::collectMarkers(unsigned NumSlot) {
722685
if (! BetweenStartEnd.test(Slot)) {
723686
ConservativeSlots.set(Slot);
724687
}
725-
// Here we check the StoreSlots to screen catch point out. For more
726-
// information, please refer "Handle Windows Exception with
727-
// LifetimeStartOnFirstUse" at the head of this file.
728-
if (MI.mayStore())
729-
StoreSlots.set(Slot);
730-
if (MF->getWinEHFuncInfo() && MBB->isEHPad() && MI.mayLoad())
731-
NumLoadInCatchPad[Slot] += 1;
732688
}
733689
}
734690
}
@@ -739,14 +695,23 @@ unsigned StackColoring::collectMarkers(unsigned NumSlot) {
739695
return 0;
740696
}
741697

742-
// 1) PR27903: slots with multiple start or end lifetime ops are not
698+
// PR27903: slots with multiple start or end lifetime ops are not
743699
// safe to enable for "lifetime-start-on-first-use".
744-
// 2) And also not safe for variable X in catch(X) in windows.
745700
for (unsigned slot = 0; slot < NumSlot; ++slot) {
746-
if (NumStartLifetimes[slot] > 1 || NumEndLifetimes[slot] > 1 ||
747-
(NumLoadInCatchPad[slot] > 1 && !StoreSlots.test(slot)))
701+
if (NumStartLifetimes[slot] > 1 || NumEndLifetimes[slot] > 1)
748702
ConservativeSlots.set(slot);
749703
}
704+
705+
// The write to the catch object by the personality function is not propely
706+
// modeled in IR: It happens before any cleanuppads are executed, even if the
707+
// first mention of the catch object is in a catchpad. As such, mark catch
708+
// object slots as conservative, so they are excluded from first-use analysis.
709+
if (WinEHFuncInfo *EHInfo = MF->getWinEHFuncInfo())
710+
for (WinEHTryBlockMapEntry &TBME : EHInfo->TryBlockMap)
711+
for (WinEHHandlerType &H : TBME.HandlerArray)
712+
if (H.CatchObj.FrameIndex != std::numeric_limits<int>::max())
713+
ConservativeSlots.set(H.CatchObj.FrameIndex);
714+
750715
LLVM_DEBUG(dumpBV("Conservative slots", ConservativeSlots));
751716

752717
// Step 2: compute begin/end sets for each block

llvm/test/CodeGen/X86/stack-coloring-seh.ll renamed to llvm/test/CodeGen/X86/stack-coloring-wineh.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
@type_info = external global ptr
55

6-
; FIXME: This is a miscompile.
6+
; Make sure %a1 and %a2 don't share the same stack offset.
77
define void @pr66984(ptr %arg) personality ptr @__CxxFrameHandler3 {
88
; CHECK-LABEL: pr66984:
99
; CHECK: # %bb.0: # %bb
@@ -12,7 +12,7 @@ define void @pr66984(ptr %arg) personality ptr @__CxxFrameHandler3 {
1212
; CHECK-NEXT: pushl %ebx
1313
; CHECK-NEXT: pushl %edi
1414
; CHECK-NEXT: pushl %esi
15-
; CHECK-NEXT: subl $20, %esp
15+
; CHECK-NEXT: subl $24, %esp
1616
; CHECK-NEXT: movl %esp, -28(%ebp)
1717
; CHECK-NEXT: movl $-1, -16(%ebp)
1818
; CHECK-NEXT: leal -24(%ebp), %eax
@@ -31,7 +31,7 @@ define void @pr66984(ptr %arg) personality ptr @__CxxFrameHandler3 {
3131
; CHECK-NEXT: $ehgcr_0_4:
3232
; CHECK-NEXT: movl -24(%ebp), %eax
3333
; CHECK-NEXT: movl %eax, %fs:0
34-
; CHECK-NEXT: addl $20, %esp
34+
; CHECK-NEXT: addl $24, %esp
3535
; CHECK-NEXT: popl %esi
3636
; CHECK-NEXT: popl %edi
3737
; CHECK-NEXT: popl %ebx
@@ -47,7 +47,7 @@ define void @pr66984(ptr %arg) personality ptr @__CxxFrameHandler3 {
4747
; CHECK-NEXT: pushl %ebp
4848
; CHECK-NEXT: addl $12, %ebp
4949
; CHECK-NEXT: movl %esp, -28(%ebp)
50-
; CHECK-NEXT: movl -32(%ebp), %ecx
50+
; CHECK-NEXT: movl -36(%ebp), %ecx
5151
; CHECK-NEXT: movl $2, -16(%ebp)
5252
; CHECK-NEXT: calll _cleanup
5353
; CHECK-NEXT: movl $LBB0_3, %eax

0 commit comments

Comments
 (0)