diff --git a/llvm/lib/CodeGen/StackColoring.cpp b/llvm/lib/CodeGen/StackColoring.cpp index 66b9086e1d88..3453b718b453 100644 --- a/llvm/lib/CodeGen/StackColoring.cpp +++ b/llvm/lib/CodeGen/StackColoring.cpp @@ -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 { @@ -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; @@ -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 NumStartLifetimes(NumSlot, 0); SmallVector NumEndLifetimes(NumSlot, 0); - SmallVector NumLoadInCatchPad(NumSlot, 0); // Step 1: collect markers and populate the "InterestingSlots" // and "ConservativeSlots" sets. @@ -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; } } } @@ -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::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 diff --git a/llvm/test/CodeGen/X86/stack-coloring-wineh.ll b/llvm/test/CodeGen/X86/stack-coloring-wineh.ll new file mode 100644 index 000000000000..79057e0ea78d --- /dev/null +++ b/llvm/test/CodeGen/X86/stack-coloring-wineh.ll @@ -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)