Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 14 additions & 48 deletions llvm/lib/CodeGen/StackColoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,37 +370,6 @@ STATISTIC(EscapedAllocas, "Number of allocas that escaped the lifetime region");
// If in RPO ordering chosen to walk the CFG we happen to visit the b[k]
// before visiting the memcpy block (which will contain the lifetime start
// for "b" then it will appear that 'b' has a degenerate lifetime.
//
// Handle Windows Exception with LifetimeStartOnFirstUse:
// -----------------
//
// There was a bug for using LifetimeStartOnFirstUse in win32.
// class Type1 {
// ...
// ~Type1(){ write memory;}
// }
// ...
// try{
// Type1 V
// ...
// } catch (Type2 X){
// ...
// }
// For variable X in catch(X), we put point pX=&(&X) into ConservativeSlots
// to prevent using LifetimeStartOnFirstUse. Because pX may merged with
// object V which may call destructor after implicitly writing pX. All these
// are done in C++ EH runtime libs (through CxxThrowException), and can't
// obviously check it in IR level.
//
// The loader of pX, without obvious writing IR, is usually the first LOAD MI
// in EHPad, Some like:
// bb.x.catch.i (landing-pad, ehfunclet-entry):
// ; predecessors: %bb...
// successors: %bb...
// %n:gr32 = MOV32rm %stack.pX ...
// ...
// The Type2** %stack.pX will only be written in EH runtime libs, so we
// check the StoreSlots to screen it out.

namespace {

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

/// Record the FI slots referenced by a 'may write to memory'.
BitVector StoreSlots;

/// Number of iterations taken during data flow analysis.
unsigned NumIterations;

Expand Down Expand Up @@ -660,13 +626,10 @@ unsigned StackColoring::collectMarkers(unsigned NumSlot) {
InterestingSlots.resize(NumSlot);
ConservativeSlots.clear();
ConservativeSlots.resize(NumSlot);
StoreSlots.clear();
StoreSlots.resize(NumSlot);

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

// Step 1: collect markers and populate the "InterestingSlots"
// and "ConservativeSlots" sets.
Expand Down Expand Up @@ -722,13 +685,6 @@ unsigned StackColoring::collectMarkers(unsigned NumSlot) {
if (! BetweenStartEnd.test(Slot)) {
ConservativeSlots.set(Slot);
}
// Here we check the StoreSlots to screen catch point out. For more
// information, please refer "Handle Windows Exception with
// LifetimeStartOnFirstUse" at the head of this file.
if (MI.mayStore())
StoreSlots.set(Slot);
if (MF->getWinEHFuncInfo() && MBB->isEHPad() && MI.mayLoad())
NumLoadInCatchPad[Slot] += 1;
}
}
}
Expand All @@ -739,14 +695,24 @@ unsigned StackColoring::collectMarkers(unsigned NumSlot) {
return 0;
}

// 1) PR27903: slots with multiple start or end lifetime ops are not
// PR27903: slots with multiple start or end lifetime ops are not
// safe to enable for "lifetime-start-on-first-use".
// 2) And also not safe for variable X in catch(X) in windows.
for (unsigned slot = 0; slot < NumSlot; ++slot) {
if (NumStartLifetimes[slot] > 1 || NumEndLifetimes[slot] > 1 ||
(NumLoadInCatchPad[slot] > 1 && !StoreSlots.test(slot)))
if (NumStartLifetimes[slot] > 1 || NumEndLifetimes[slot] > 1)
ConservativeSlots.set(slot);
}

// The write to the catch object by the personality function is not propely
// modeled in IR: It happens before any cleanuppads are executed, even if the
// first mention of the catch object is in a catchpad. As such, mark catch
// object slots as conservative, so they are excluded from first-use analysis.
if (WinEHFuncInfo *EHInfo = MF->getWinEHFuncInfo())
for (WinEHTryBlockMapEntry &TBME : EHInfo->TryBlockMap)
for (WinEHHandlerType &H : TBME.HandlerArray)
if (H.CatchObj.FrameIndex != std::numeric_limits<int>::max() &&
H.CatchObj.FrameIndex >= 0)
ConservativeSlots.set(H.CatchObj.FrameIndex);

LLVM_DEBUG(dumpBV("Conservative slots", ConservativeSlots));

// Step 2: compute begin/end sets for each block
Expand Down
190 changes: 190 additions & 0 deletions llvm/test/CodeGen/X86/stack-coloring-wineh.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 3
; RUN: llc -mtriple=i686-windows-msvc < %s | FileCheck %s --check-prefix=I686
; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s --check-prefix=X86_64

@type_info = external global ptr

; Make sure %a1 and %a2 don't share the same stack offset.
define void @pr66984(ptr %arg) personality ptr @__CxxFrameHandler3 {
; I686-LABEL: pr66984:
; I686: # %bb.0: # %bb
; I686-NEXT: pushl %ebp
; I686-NEXT: movl %esp, %ebp
; I686-NEXT: pushl %ebx
; I686-NEXT: pushl %edi
; I686-NEXT: pushl %esi
; I686-NEXT: subl $24, %esp
; I686-NEXT: movl %esp, -28(%ebp)
; I686-NEXT: movl $-1, -16(%ebp)
; I686-NEXT: leal -24(%ebp), %eax
; I686-NEXT: movl $___ehhandler$pr66984, -20(%ebp)
; I686-NEXT: movl %fs:0, %ecx
; I686-NEXT: movl %ecx, -24(%ebp)
; I686-NEXT: movl %eax, %fs:0
; I686-NEXT: movl $1, -16(%ebp)
; I686-NEXT: calll _throw
; I686-NEXT: # %bb.1: # %bb14
; I686-NEXT: LBB0_3: # Block address taken
; I686-NEXT: # %bb17
; I686-NEXT: addl $12, %ebp
; I686-NEXT: jmp LBB0_4
; I686-NEXT: LBB0_4: # %exit
; I686-NEXT: $ehgcr_0_4:
; I686-NEXT: movl -24(%ebp), %eax
; I686-NEXT: movl %eax, %fs:0
; I686-NEXT: addl $24, %esp
; I686-NEXT: popl %esi
; I686-NEXT: popl %edi
; I686-NEXT: popl %ebx
; I686-NEXT: popl %ebp
; I686-NEXT: retl
; I686-NEXT: .def "?catch$2@?0?pr66984@4HA";
; I686-NEXT: .scl 3;
; I686-NEXT: .type 32;
; I686-NEXT: .endef
; I686-NEXT: .p2align 4, 0x90
; I686-NEXT: "?catch$2@?0?pr66984@4HA":
; I686-NEXT: LBB0_2: # %bb17
; I686-NEXT: pushl %ebp
; I686-NEXT: addl $12, %ebp
; I686-NEXT: movl %esp, -28(%ebp)
; I686-NEXT: movl -36(%ebp), %ecx
; I686-NEXT: movl $2, -16(%ebp)
; I686-NEXT: calll _cleanup
; I686-NEXT: movl $LBB0_3, %eax
; I686-NEXT: popl %ebp
; I686-NEXT: retl # CATCHRET
; I686-NEXT: .def "?dtor$5@?0?pr66984@4HA";
; I686-NEXT: .scl 3;
; I686-NEXT: .type 32;
; I686-NEXT: .endef
; I686-NEXT: .p2align 4, 0x90
; I686-NEXT: "?dtor$5@?0?pr66984@4HA":
; I686-NEXT: LBB0_5: # %bb8
; I686-NEXT: pushl %ebp
; I686-NEXT: addl $12, %ebp
; I686-NEXT: movl 8(%ebp), %eax
; I686-NEXT: movl %eax, -32(%ebp)
; I686-NEXT: leal -32(%ebp), %ecx
; I686-NEXT: calll _foo
; I686-NEXT: popl %ebp
; I686-NEXT: retl # CLEANUPRET
; I686-NEXT: Lfunc_end0:
;
; X86_64-LABEL: pr66984:
; X86_64: # %bb.0: # %bb
; X86_64-NEXT: pushq %rbp
; X86_64-NEXT: .seh_pushreg %rbp
; X86_64-NEXT: subq $64, %rsp
; X86_64-NEXT: .seh_stackalloc 64
; X86_64-NEXT: leaq {{[0-9]+}}(%rsp), %rbp
; X86_64-NEXT: .seh_setframe %rbp, 64
; X86_64-NEXT: .seh_endprologue
; X86_64-NEXT: movq $-2, -16(%rbp)
; X86_64-NEXT: movq %rcx, {{[-0-9]+}}(%r{{[sb]}}p) # 8-byte Spill
; X86_64-NEXT: .Ltmp0:
; X86_64-NEXT: callq throw
; X86_64-NEXT: .Ltmp1:
; X86_64-NEXT: # %bb.1: # %bb14
; X86_64-NEXT: .LBB0_3: # Block address taken
; X86_64-NEXT: # %exit
; X86_64-NEXT: $ehgcr_0_3:
; X86_64-NEXT: nop
; X86_64-NEXT: addq $64, %rsp
; X86_64-NEXT: popq %rbp
; X86_64-NEXT: retq
; X86_64-NEXT: .seh_handlerdata
; X86_64-NEXT: .long ($cppxdata$pr66984)@IMGREL
; X86_64-NEXT: .text
; X86_64-NEXT: .seh_endproc
; X86_64-NEXT: .def "?catch$2@?0?pr66984@4HA";
; X86_64-NEXT: .scl 3;
; X86_64-NEXT: .type 32;
; X86_64-NEXT: .endef
; X86_64-NEXT: .p2align 4, 0x90
; X86_64-NEXT: "?catch$2@?0?pr66984@4HA":
; X86_64-NEXT: .seh_proc "?catch$2@?0?pr66984@4HA"
; X86_64-NEXT: .seh_handler __CxxFrameHandler3, @unwind, @except
; X86_64-NEXT: .LBB0_2: # %bb17
; X86_64-NEXT: movq %rdx, {{[0-9]+}}(%rsp)
; X86_64-NEXT: pushq %rbp
; X86_64-NEXT: .seh_pushreg %rbp
; X86_64-NEXT: subq $32, %rsp
; X86_64-NEXT: .seh_stackalloc 32
; X86_64-NEXT: leaq 64(%rdx), %rbp
; X86_64-NEXT: .seh_endprologue
; X86_64-NEXT: movq -8(%rbp), %rcx
; X86_64-NEXT: callq cleanup
; X86_64-NEXT: leaq .LBB0_3(%rip), %rax
; X86_64-NEXT: addq $32, %rsp
; X86_64-NEXT: popq %rbp
; X86_64-NEXT: retq # CATCHRET
; X86_64-NEXT: .seh_handlerdata
; X86_64-NEXT: .long ($cppxdata$pr66984)@IMGREL
; X86_64-NEXT: .text
; X86_64-NEXT: .seh_endproc
; X86_64-NEXT: .def "?dtor$4@?0?pr66984@4HA";
; X86_64-NEXT: .scl 3;
; X86_64-NEXT: .type 32;
; X86_64-NEXT: .endef
; X86_64-NEXT: .p2align 4, 0x90
; X86_64-NEXT: "?dtor$4@?0?pr66984@4HA":
; X86_64-NEXT: .seh_proc "?dtor$4@?0?pr66984@4HA"
; X86_64-NEXT: .LBB0_4: # %bb8
; X86_64-NEXT: movq %rdx, {{[0-9]+}}(%rsp)
; X86_64-NEXT: pushq %rbp
; X86_64-NEXT: .seh_pushreg %rbp
; X86_64-NEXT: subq $32, %rsp
; X86_64-NEXT: .seh_stackalloc 32
; X86_64-NEXT: leaq 64(%rdx), %rbp
; X86_64-NEXT: .seh_endprologue
; X86_64-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rax # 8-byte Reload
; X86_64-NEXT: movq %rax, -32(%rbp)
; X86_64-NEXT: leaq -32(%rbp), %rcx
; X86_64-NEXT: callq foo
; X86_64-NEXT: nop
; X86_64-NEXT: addq $32, %rsp
; X86_64-NEXT: popq %rbp
; X86_64-NEXT: retq # CLEANUPRET
; X86_64-NEXT: .Lfunc_end0:
; X86_64-NEXT: .seh_handlerdata
; X86_64-NEXT: .text
; X86_64-NEXT: .seh_endproc
bb:
%a1 = alloca ptr, align 4
%a2 = alloca ptr, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %a2)
invoke void @throw()
to label %bb14 unwind label %bb8

bb8: ; preds = %bb7
%i9 = cleanuppad within none []
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %a1)
store ptr %arg, ptr %a1, align 4
call fastcc void @foo(ptr %a1) [ "funclet"(token %i9) ]
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %a1)
cleanupret from %i9 unwind label %bb15

bb14: ; preds = %bb7
unreachable

bb15: ; preds = %bb13, %bb5
%cs = catchswitch within none [label %bb17] unwind to caller

bb17: ; preds = %bb15
%cp = catchpad within %cs [ptr @type_info, i32 8, ptr %a2]
%p = load ptr, ptr %a2, align 4
call fastcc void @cleanup(ptr %p) [ "funclet"(token %cp) ]
catchret from %cp to label %exit

exit:
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %a2)
ret void
}

declare i32 @__CxxFrameHandler3(...)
declare void @throw()
declare void @cleanup(ptr)
declare void @foo(ptr)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)