From aa1e5aa9df2a6e3ceca10f03bef08962ff9c6606 Mon Sep 17 00:00:00 2001 From: Quentin Colombet Date: Wed, 4 Nov 2015 22:37:28 +0000 Subject: [PATCH] [x86] Teach the shrink-wrapping hooks to do the proper thing with Win64. Win64 has some strict requirements for the epilogue. As a result, we disable shrink-wrapping for Win64 unless the block that gets the epilogue is already an exit block. Fixes PR24193. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252088 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/X86/X86FrameLowering.cpp | 8 ++ test/CodeGen/X86/x86-win64-shrink-wrapping.ll | 122 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 test/CodeGen/X86/x86-win64-shrink-wrapping.ll diff --git a/lib/Target/X86/X86FrameLowering.cpp b/lib/Target/X86/X86FrameLowering.cpp index 7b7f0daf12b..ee866fa45c3 100644 --- a/lib/Target/X86/X86FrameLowering.cpp +++ b/lib/Target/X86/X86FrameLowering.cpp @@ -2184,6 +2184,14 @@ eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, bool X86FrameLowering::canUseAsEpilogue(const MachineBasicBlock &MBB) const { assert(MBB.getParent() && "Block is not attached to a function!"); + // Win64 has strict requirements in terms of epilogue and we are + // not taking a chance at messing with them. + // I.e., unless this block is already an exit block, we can't use + // it as an epilogue. + if (MBB.getParent()->getSubtarget().isTargetWin64() && + !MBB.succ_empty() && !MBB.isReturnBlock()) + return false; + if (canUseLEAForSPInEpilogue(*MBB.getParent())) return true; diff --git a/test/CodeGen/X86/x86-win64-shrink-wrapping.ll b/test/CodeGen/X86/x86-win64-shrink-wrapping.ll new file mode 100644 index 00000000000..5d9b2ba3267 --- /dev/null +++ b/test/CodeGen/X86/x86-win64-shrink-wrapping.ll @@ -0,0 +1,122 @@ +; RUN: llc %s -o - -enable-shrink-wrap=true | FileCheck %s --check-prefix=CHECK --check-prefix=ENABLE +; RUN: llc %s -o - -enable-shrink-wrap=false | FileCheck %s --check-prefix=CHECK --check-prefix=DISABLE + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "x86_64--windows-gnu" + +; The output of this function with or without shrink-wrapping +; shouldn't change. +; Indeed, the epilogue block would have been if.else, meaning +; after the pops, we will have additional instruction (jump, mov, +; etc.) prior to the return and this is forbidden for Win64. +; CHECK-LABEL: loopInfoSaveOutsideLoop: +; CHECK: push +; CHECK-NOT: popq +; CHECK: popq +; CHECK-NOT: popq +; CHECK-NEXT: retq +define i32 @loopInfoSaveOutsideLoop(i32 %cond, i32 %N) #0 { +entry: + %tobool = icmp eq i32 %cond, 0 + br i1 %tobool, label %if.else, label %for.preheader + +for.preheader: ; preds = %entry + tail call void asm "nop", ""() + br label %for.body + +for.body: ; preds = %for.body, %for.preheader + %i.05 = phi i32 [ %inc, %for.body ], [ 0, %for.preheader ] + %sum.04 = phi i32 [ %add, %for.body ], [ 0, %for.preheader ] + %call = tail call i32 asm "movl $$1, $0", "=r,~{ebx}"() + %add = add nsw i32 %call, %sum.04 + %inc = add nuw nsw i32 %i.05, 1 + %exitcond = icmp eq i32 %inc, 10 + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body + tail call void asm "nop", "~{ebx}"() + %shl = shl i32 %add, 3 + br label %if.end + +if.else: ; preds = %entry + %mul = shl nsw i32 %N, 1 + br label %if.end + +if.end: ; preds = %if.else, %for.end + %sum.1 = phi i32 [ %shl, %for.end ], [ %mul, %if.else ] + ret i32 %sum.1 +} + +; When we can sink the epilogue of the function into an existing exit block, +; this is Ok for shrink-wrapping to kicks in. +; CHECK-LABEL: loopInfoSaveOutsideLoop2: +; ENABLE: testl %ecx, %ecx +; ENABLE-NEXT: je [[ELSE_LABEL:.LBB[0-9_]+]] +; +; Prologue code. +; Make sure we save the CSR used in the inline asm: rbx. +; CHECK: pushq %rbx +; +; DISABLE: testl %ecx, %ecx +; DISABLE-NEXT: je [[ELSE_LABEL:.LBB[0-9_]+]] +; +; CHECK: nop +; CHECK: xorl [[SUM:%eax]], [[SUM]] +; CHECK-NEXT: movl $10, [[IV:%e[a-z]+]] +; +; CHECK: [[LOOP_LABEL:.LBB[0-9_]+]]: # %for.body +; CHECK: movl $1, [[TMP:%e[a-z]+]] +; CHECK: addl [[TMP]], [[SUM]] +; CHECK-NEXT: decl [[IV]] +; CHECK-NEXT: jne [[LOOP_LABEL]] +; Next BB. +; CHECK: nop +; CHECK: shll $3, [[SUM]] +; +; DISABLE: jmp [[EPILOG_BB:.LBB[0-9_]+]] +; +; ENABLE-NEXT: popq %rbx +; ENABLE-NEXT: retq +; +; CHECK: [[ELSE_LABEL]]: # %if.else +; Shift second argument by one and store into returned register. +; CHECK: addl %edx, %edx +; CHECK: movl %edx, %eax +; +; DISABLE: [[EPILOG_BB]]: # %if.end +; DISABLE-NEXT: popq %rbx +; +; CHECK: retq +; +define i32 @loopInfoSaveOutsideLoop2(i32 %cond, i32 %N) #0 { +entry: + %tobool = icmp eq i32 %cond, 0 + br i1 %tobool, label %if.else, label %for.preheader + +for.preheader: ; preds = %entry + tail call void asm "nop", ""() + br label %for.body + +for.body: ; preds = %for.body, %for.preheader + %i.05 = phi i32 [ %inc, %for.body ], [ 0, %for.preheader ] + %sum.04 = phi i32 [ %add, %for.body ], [ 0, %for.preheader ] + %call = tail call i32 asm "movl $$1, $0", "=r,~{ebx}"() + %add = add nsw i32 %call, %sum.04 + %inc = add nuw nsw i32 %i.05, 1 + %exitcond = icmp eq i32 %inc, 10 + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body + tail call void asm "nop", "~{ebx}"() + %shl = shl i32 %add, 3 + ret i32 %shl + +if.else: ; preds = %entry + %mul = shl nsw i32 %N, 1 + br label %if.end + +if.end: ; preds = %if.else, %for.end + ret i32 %mul +} + +attributes #0 = { uwtable } -- 2.34.1