diff --git a/llvm/lib/Target/X86/CMakeLists.txt b/llvm/lib/Target/X86/CMakeLists.txt index ed34a59df4ad8..58f2292dd4cd7 100644 --- a/llvm/lib/Target/X86/CMakeLists.txt +++ b/llvm/lib/Target/X86/CMakeLists.txt @@ -23,6 +23,7 @@ add_public_tablegen_target(X86CommonTableGen) set(sources X86AsmPrinter.cpp + X86AvoidTrailingCall.cpp X86CallFrameOptimization.cpp X86CallingConv.cpp X86CallLowering.cpp diff --git a/llvm/lib/Target/X86/X86.h b/llvm/lib/Target/X86/X86.h index 573888a57fe5a..6840fc12751da 100644 --- a/llvm/lib/Target/X86/X86.h +++ b/llvm/lib/Target/X86/X86.h @@ -81,6 +81,12 @@ FunctionPass *createX86FlagsCopyLoweringPass(); /// Return a pass that expands WinAlloca pseudo-instructions. FunctionPass *createX86WinAllocaExpander(); +/// Return a pass that inserts int3 at the end of the function if it ends with a +/// CALL instruction. The pass does the same for each funclet as well. This +/// ensures that the open interval of function start and end PCs contains all +/// return addresses for the benefit of the Windows x64 unwinder. +FunctionPass *createX86AvoidTrailingCallPass(); + /// Return a pass that optimizes the code-size of x86 call sequences. This is /// done by replacing esp-relative movs with pushes. FunctionPass *createX86CallFrameOptimization(); diff --git a/llvm/lib/Target/X86/X86AvoidTrailingCall.cpp b/llvm/lib/Target/X86/X86AvoidTrailingCall.cpp new file mode 100644 index 0000000000000..fb4f9e2901dc7 --- /dev/null +++ b/llvm/lib/Target/X86/X86AvoidTrailingCall.cpp @@ -0,0 +1,108 @@ +//===----- X86AvoidTrailingCall.cpp - Insert int3 after trailing calls ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The Windows x64 unwinder has trouble unwinding the stack when a return +// address points to the end of the function. This pass maintains the invariant +// that every return address is inside the bounds of its parent function or +// funclet by inserting int3 if the last instruction would otherwise be a call. +// +//===----------------------------------------------------------------------===// + +#include "X86.h" +#include "X86InstrInfo.h" +#include "X86Subtarget.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" + +#define DEBUG_TYPE "x86-avoid-trailing-call" + +using namespace llvm; + +namespace { + +class X86AvoidTrailingCallPass : public MachineFunctionPass { +public: + X86AvoidTrailingCallPass() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: + StringRef getPassName() const override { + return "X86 avoid trailing call pass"; + } + static char ID; +}; + +char X86AvoidTrailingCallPass::ID = 0; + +} // end anonymous namespace + +FunctionPass *llvm::createX86AvoidTrailingCallPass() { + return new X86AvoidTrailingCallPass(); +} + +// A real instruction is a non-meta, non-pseudo instruction. Some pseudos +// expand to nothing, and some expand to code. This logic conservatively assumes +// they might expand to nothing. +static bool isRealInstruction(MachineInstr &MI) { + return !MI.isPseudo() && !MI.isMetaInstruction(); +} + +// Return true if this is a call instruction, but not a tail call. +static bool isCallInstruction(const MachineInstr &MI) { + return MI.isCall() && !MI.isReturn(); +} + +bool X86AvoidTrailingCallPass::runOnMachineFunction(MachineFunction &MF) { + const X86Subtarget &STI = MF.getSubtarget(); + const X86InstrInfo &TII = *STI.getInstrInfo(); + assert(STI.isTargetWin64() && "pass only runs on Win64"); + + // FIXME: Perhaps this pass should also replace SEH_Epilogue by inserting nops + // before epilogues. + + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + // Look for basic blocks that precede funclet entries or are at the end of + // the function. + MachineBasicBlock *NextMBB = MBB.getNextNode(); + if (NextMBB && !NextMBB->isEHFuncletEntry()) + continue; + + // Find the last real instruction in this block, or previous blocks if this + // block is empty. + MachineBasicBlock::reverse_iterator LastRealInstr; + for (MachineBasicBlock &RMBB : + make_range(MBB.getReverseIterator(), MF.rend())) { + LastRealInstr = llvm::find_if(reverse(RMBB), isRealInstruction); + if (LastRealInstr != RMBB.rend()) + break; + } + + // Do nothing if this function or funclet has no instructions. + if (LastRealInstr == MF.begin()->rend()) + continue; + + // If this is a call instruction, insert int3 right after it with the same + // DebugLoc. Convert back to a forward iterator and advance the insertion + // position once. + if (isCallInstruction(*LastRealInstr)) { + LLVM_DEBUG({ + dbgs() << "inserting int3 after trailing call instruction:\n"; + LastRealInstr->dump(); + dbgs() << '\n'; + }); + + MachineBasicBlock::iterator MBBI = std::next(LastRealInstr.getReverse()); + BuildMI(*LastRealInstr->getParent(), MBBI, LastRealInstr->getDebugLoc(), + TII.get(X86::INT3)); + Changed = true; + } + } + + return Changed; +} diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp b/llvm/lib/Target/X86/X86TargetMachine.cpp index d33a7a0b96309..f6184a3c1f1a7 100644 --- a/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/llvm/lib/Target/X86/X86TargetMachine.cpp @@ -219,17 +219,9 @@ X86TargetMachine::X86TargetMachine(const Target &T, const Triple &TT, getEffectiveX86CodeModel(CM, JIT, TT.getArch() == Triple::x86_64), OL), TLOF(createTLOF(getTargetTriple())) { - // Windows stack unwinder gets confused when execution flow "falls through" - // after a call to 'noreturn' function. - // To prevent that, we emit a trap for 'unreachable' IR instructions. - // (which on X86, happens to be the 'ud2' instruction) // On PS4, the "return address" of a 'noreturn' call must still be within // the calling function, and TrapUnreachable is an easy way to get that. - // The check here for 64-bit windows is a bit icky, but as we're unlikely - // to ever want to mix 32 and 64-bit windows code in a single module - // this should be fine. - if ((TT.isOSWindows() && TT.getArch() == Triple::x86_64) || TT.isPS4() || - TT.isOSBinFormatMachO()) { + if (TT.isPS4() || TT.isOSBinFormatMachO()) { this->Options.TrapUnreachable = true; this->Options.NoTrapAfterNoreturn = TT.isOSBinFormatMachO(); } @@ -518,12 +510,19 @@ void X86PassConfig::addPreEmitPass() { } void X86PassConfig::addPreEmitPass2() { + const Triple &TT = TM->getTargetTriple(); + const MCAsmInfo *MAI = TM->getMCAsmInfo(); + addPass(createX86RetpolineThunksPass()); + + // Insert extra int3 instructions after trailing call instructions to avoid + // issues in the unwinder. + if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) + addPass(createX86AvoidTrailingCallPass()); + // Verify basic block incoming and outgoing cfa offset and register values and // correct CFA calculation rule where needed by inserting appropriate CFI // instructions. - const Triple &TT = TM->getTargetTriple(); - const MCAsmInfo *MAI = TM->getMCAsmInfo(); if (!TT.isOSDarwin() && (!TT.isOSWindows() || MAI->getExceptionHandlingType() == ExceptionHandling::DwarfCFI)) diff --git a/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll b/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll index 9f7c995e5e779..7d4d833aa9ba9 100644 --- a/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll +++ b/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll @@ -1,4 +1,3 @@ -; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: sed -e s/.Cxx:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefix=CXX ; RUN: sed -e s/.Seh:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefix=SEH @@ -69,13 +68,13 @@ catch.body.2: ; SEH-NEXT: .long .Ltmp0@IMGREL+1 ; SEH-NEXT: .long .Ltmp1@IMGREL+1 ; SEH-NEXT: .long dummy_filter@IMGREL -; SEH-NEXT: .long .LBB0_2@IMGREL +; SEH-NEXT: .long .LBB0_5@IMGREL ; SEH-NEXT: .long .Ltmp2@IMGREL+1 ; SEH-NEXT: .long .Ltmp3@IMGREL+1 -; SEH-NEXT: .long "?dtor$5@?0?test@4HA"@IMGREL +; SEH-NEXT: .long "?dtor$2@?0?test@4HA"@IMGREL ; SEH-NEXT: .long 0 ; SEH-NEXT: .long .Ltmp2@IMGREL+1 ; SEH-NEXT: .long .Ltmp3@IMGREL+1 ; SEH-NEXT: .long dummy_filter@IMGREL -; SEH-NEXT: .long .LBB0_2@IMGREL +; SEH-NEXT: .long .LBB0_5@IMGREL ; SEH-NEXT: .Llsda_end0: diff --git a/llvm/test/CodeGen/X86/br-fold.ll b/llvm/test/CodeGen/X86/br-fold.ll index bcd672eb51712..6cd9b78c1c7ed 100644 --- a/llvm/test/CodeGen/X86/br-fold.ll +++ b/llvm/test/CodeGen/X86/br-fold.ll @@ -5,18 +5,18 @@ ; RUN: llc -mtriple=x86_64-scei-ps4 < %s | FileCheck -check-prefix=PS4 %s ; X64_DARWIN: orq -; X64-DARWIN-NEXT: ud2 +; X64_DARWIN-NEXT: ud2 ; X64_LINUX: orq %rax, %rcx ; X64_LINUX-NEXT: jne ; X64_LINUX-NEXT: %bb8.i329 ; X64_WINDOWS: orq %rax, %rcx -; X64_WINDOWS-NEXT: ud2 +; X64_WINDOWS-NEXT: jne ; X64_WINDOWS_GNU: movq .refptr._ZN11xercesc_2_513SchemaSymbols21fgURI_SCHEMAFORSCHEMAE(%rip), %rax ; X64_WINDOWS_GNU: orq .refptr._ZN11xercesc_2_56XMLUni16fgNotationStringE(%rip), %rax -; X64_WINDOWS_GNU-NEXT: ud2 +; X64_WINDOWS_GNU-NEXT: jne ; PS4: orq %rax, %rcx ; PS4-NEXT: ud2 diff --git a/llvm/test/CodeGen/X86/catchpad-lifetime.ll b/llvm/test/CodeGen/X86/catchpad-lifetime.ll index d85adec360c8a..8a6d4cbd271de 100644 --- a/llvm/test/CodeGen/X86/catchpad-lifetime.ll +++ b/llvm/test/CodeGen/X86/catchpad-lifetime.ll @@ -7,6 +7,8 @@ declare void @throw() declare i32 @__CxxFrameHandler3(...) +declare void @llvm.trap() + define void @test1() personality i32 (...)* @__CxxFrameHandler3 { entry: %alloca2 = alloca i8*, align 4 @@ -30,6 +32,7 @@ catch.pad: ; preds = %catch.dispatch %bc2 = bitcast i8** %alloca2 to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %bc2) store volatile i8* null, i8** %alloca1 + call void @llvm.trap() unreachable ; CHECK-LABEL: "?catch$2@?0?test1@4HA" @@ -67,6 +70,7 @@ catch.pad: ; preds = %catch.dispatch %bc2 = bitcast i8** %alloca2 to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %bc2) store volatile i8* null, i8** %alloca1 + call void @llvm.trap() unreachable ; CHECK-LABEL: "?catch$2@?0?test2@4HA" diff --git a/llvm/test/CodeGen/X86/catchpad-regmask.ll b/llvm/test/CodeGen/X86/catchpad-regmask.ll index 0d436f6eb595d..0981f8e15f12f 100644 --- a/llvm/test/CodeGen/X86/catchpad-regmask.ll +++ b/llvm/test/CodeGen/X86/catchpad-regmask.ll @@ -75,7 +75,7 @@ unreachable: ; preds = %entry ; CHECK: popq %rbp ; CHECK: retq -; CHECK: "?catch$2@?0?global_array@4HA": +; CHECK: "?catch${{[0-9]+}}@?0?global_array@4HA": ; CHECK: pushq %rbp ; CHECK: movslq {{.*}}, %[[idx:[^ ]*]] ; CHECK: leaq array(%rip), %[[base:[^ ]*]] @@ -122,7 +122,7 @@ unreachable: ; preds = %entry ; CHECK: popq %rbp ; CHECK: retq -; CHECK: "?catch$2@?0?access_imported@4HA": +; CHECK: "?catch${{[0-9]+}}@?0?access_imported@4HA": ; CHECK: pushq %rbp ; CHECK: movq __imp_imported(%rip), %[[base:[^ ]*]] ; CHECK: movl $222, (%[[base]]) diff --git a/llvm/test/CodeGen/X86/catchret-regmask.ll b/llvm/test/CodeGen/X86/catchret-regmask.ll index 1231172a7e951..4af57e109f183 100644 --- a/llvm/test/CodeGen/X86/catchret-regmask.ll +++ b/llvm/test/CodeGen/X86/catchret-regmask.ll @@ -6,6 +6,7 @@ target triple = "x86_64-pc-windows-msvc" declare i32 @__CxxFrameHandler3(...) declare void @throw() noreturn uwtable declare i8* @getval() +declare void @llvm.trap() define i8* @reload_out_of_pad(i8* %arg) #0 personality i32 (...)* @__CxxFrameHandler3 { assertPassed: @@ -19,6 +20,7 @@ catch: ; This block *must* appear after the catchret to test the bug. ; FIXME: Make this an MIR test so we can control MBB layout. unreachable: + call void @llvm.trap() unreachable catch.dispatch: @@ -35,7 +37,7 @@ return: ; CHECK: movq -[[arg_slot]](%rbp), %rax # 8-byte Reload ; CHECK: retq -; CHECK: "?catch$3@?0?reload_out_of_pad@4HA": +; CHECK: "?catch${{[0-9]+}}@?0?reload_out_of_pad@4HA": ; CHECK-NOT: Reload ; CHECK: retq @@ -50,6 +52,7 @@ catch: catchret from %cp to label %return unreachable: + call void @llvm.trap() unreachable catch.dispatch: @@ -65,7 +68,7 @@ return: ; CHECK: movq -[[val_slot:[0-9]+]](%rbp), %rax # 8-byte Reload ; CHECK: retq -; CHECK: "?catch$3@?0?spill_in_pad@4HA": +; CHECK: "?catch${{[0-9]+}}@?0?spill_in_pad@4HA": ; CHECK: callq getval ; CHECK: movq %rax, -[[val_slot]](%rbp) # 8-byte Spill ; CHECK: retq diff --git a/llvm/test/CodeGen/X86/empty-function.ll b/llvm/test/CodeGen/X86/empty-function.ll index 92bebd0ab1a7c..7d908311ec8dc 100644 --- a/llvm/test/CodeGen/X86/empty-function.ll +++ b/llvm/test/CodeGen/X86/empty-function.ll @@ -15,7 +15,7 @@ entry: ; CHECK-LABEL: f: ; WIN32: nop -; WIN64: ud2 +; WIN64: nop ; LINUX-NOT: nop ; LINUX-NOT: ud2 diff --git a/llvm/test/CodeGen/X86/funclet-layout.ll b/llvm/test/CodeGen/X86/funclet-layout.ll index 0942645cf5a40..72f60ea72d43d 100644 --- a/llvm/test/CodeGen/X86/funclet-layout.ll +++ b/llvm/test/CodeGen/X86/funclet-layout.ll @@ -9,6 +9,8 @@ target triple = "x86_64-pc-windows-msvc" @"\01??_7type_info@@6B@" = external constant i8* @"\01??_R0H@8" = internal global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" } +declare void @llvm.trap() + define void @test1(i1 %B) personality i32 (...)* @__CxxFrameHandler3 { entry: invoke void @g() @@ -31,6 +33,7 @@ try.cont: ret void unreachable: + call void @llvm.trap() unreachable } @@ -76,6 +79,7 @@ try.cont.5: ; preds = %try.cont ret i32 0 unreachable: ; preds = %catch, %entry + call void @llvm.trap() unreachable } @@ -125,11 +129,13 @@ try.cont: ; preds = %entry br i1 %V, label %exit_one, label %exit_two exit_one: - tail call void @exit(i32 0) + tail call void @g() + call void @llvm.trap() unreachable exit_two: - tail call void @exit(i32 0) + tail call void @g() + call void @llvm.trap() unreachable } @@ -138,7 +144,7 @@ exit_two: ; The entry funclet contains %entry and %try.cont ; CHECK: # %entry ; CHECK: # %try.cont -; CHECK: callq exit +; CHECK: callq g ; CHECK-NOT: # exit_one ; CHECK-NOT: # exit_two ; CHECK: ud2 @@ -146,12 +152,12 @@ exit_two: ; The catch(...) funclet contains %catch.2 ; CHECK: # %catch.2{{$}} ; CHECK: callq exit -; CHECK: ud2 +; CHECK-NEXT: int3 ; The catch(int) funclet contains %catch ; CHECK: # %catch{{$}} ; CHECK: callq exit -; CHECK: ud2 +; CHECK-NEXT: int3 declare void @exit(i32) noreturn nounwind declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) diff --git a/llvm/test/CodeGen/X86/noreturn-call-win64.ll b/llvm/test/CodeGen/X86/noreturn-call-win64.ll new file mode 100644 index 0000000000000..6289eef6bb48f --- /dev/null +++ b/llvm/test/CodeGen/X86/noreturn-call-win64.ll @@ -0,0 +1,53 @@ +; RUN: llc < %s -mtriple=x86_64-windows-msvc | FileCheck %s + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @foo() { +entry: + %call = call i32 @cond() + %tobool = icmp ne i32 %call, 0 + br i1 %tobool, label %if.then, label %if.end + +if.then: ; preds = %entry + call void @abort1() + unreachable + +if.end: ; preds = %entry + %call1 = call i32 @cond() + %tobool2 = icmp ne i32 %call1, 0 + br i1 %tobool2, label %if.then3, label %if.end4 + +if.then3: ; preds = %if.end + call void @abort2() + unreachable + +if.end4: ; preds = %if.end + %call5 = call i32 @cond() + %tobool6 = icmp ne i32 %call5, 0 + br i1 %tobool6, label %if.then7, label %if.end8 + +if.then7: ; preds = %if.end4 + call void @abort3() + unreachable + +if.end8: ; preds = %if.end4 + ret i32 0 +} + +; CHECK-LABEL: foo: +; CHECK: callq cond +; CHECK: callq cond +; CHECK: callq cond +; We don't need int3's between these calls to abort, since they won't confuse +; the unwinder. +; CHECK: callq abort1 +; CHECK-NEXT: # %if.then3 +; CHECK: callq abort2 +; CHECK-NEXT: # %if.then7 +; CHECK: callq abort3 +; CHECK-NEXT: int3 + +declare dso_local i32 @cond() + +declare dso_local void @abort1() noreturn +declare dso_local void @abort2() noreturn +declare dso_local void @abort3() noreturn diff --git a/llvm/test/CodeGen/X86/pr24374.ll b/llvm/test/CodeGen/X86/pr24374.ll index dab3b3f41599a..7ad05e8b77a89 100644 --- a/llvm/test/CodeGen/X86/pr24374.ll +++ b/llvm/test/CodeGen/X86/pr24374.ll @@ -31,6 +31,6 @@ define void @g() { unreachable } ; CHECK-LABEL: g: -; CHECK: ud2 +; CHECK: nop attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/X86/trap.ll b/llvm/test/CodeGen/X86/trap.ll index ca33f9e6b4e1e..452be48a605e5 100644 --- a/llvm/test/CodeGen/X86/trap.ll +++ b/llvm/test/CodeGen/X86/trap.ll @@ -1,13 +1,19 @@ ; RUN: llc < %s -mtriple=i686-apple-darwin8 -mcpu=yonah | FileCheck %s -check-prefix=DARWIN ; RUN: llc < %s -mtriple=i686-unknown-linux -mcpu=yonah | FileCheck %s -check-prefix=LINUX ; RUN: llc < %s -mtriple=x86_64-scei-ps4 | FileCheck %s -check-prefix=PS4 +; RUN: llc < %s -mtriple=x86_64-windows-msvc | FileCheck %s -check-prefix=WIN64 ; DARWIN-LABEL: test0: ; DARWIN: ud2 ; LINUX-LABEL: test0: ; LINUX: ud2 +; FIXME: PS4 probably doesn't want two ud2s. ; PS4-LABEL: test0: ; PS4: ud2 +; PS4: ud2 +; WIN64-LABEL: test0: +; WIN64: ud2 +; WIN64-NOT: ud2 define i32 @test0() noreturn nounwind { entry: tail call void @llvm.trap( ) @@ -20,6 +26,9 @@ entry: ; LINUX: int3 ; PS4-LABEL: test1: ; PS4: int $65 +; WIN64-LABEL: test1: +; WIN64: int3 +; WIN64-NOT: ud2 define i32 @test1() noreturn nounwind { entry: tail call void @llvm.debugtrap( ) diff --git a/llvm/test/CodeGen/X86/unreachable-trap.ll b/llvm/test/CodeGen/X86/unreachable-trap.ll index 8de0510ed387b..ee1a11c767784 100644 --- a/llvm/test/CodeGen/X86/unreachable-trap.ll +++ b/llvm/test/CodeGen/X86/unreachable-trap.ll @@ -1,10 +1,13 @@ -; RUN: llc -o - %s -mtriple=x86_64-windows-msvc | FileCheck %s --check-prefixes=CHECK,TRAP_AFTER_NORETURN +; RUN: llc -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,NORMAL +; RUN: llc -o - %s -mtriple=x86_64-windows-msvc | FileCheck %s --check-prefixes=CHECK,NORMAL +; RUN: llc -o - %s -mtriple=x86_64-scei-ps4 | FileCheck %s --check-prefixes=CHECK,TRAP_AFTER_NORETURN ; RUN: llc -o - %s -mtriple=x86_64-apple-darwin | FileCheck %s --check-prefixes=CHECK,NO_TRAP_AFTER_NORETURN ; CHECK-LABEL: call_exit: ; CHECK: callq {{_?}}exit ; TRAP_AFTER_NORETURN: ud2 ; NO_TRAP_AFTER_NORETURN-NOT: ud2 +; NORMAL-NOT: ud2 define i32 @call_exit() noreturn nounwind { tail call void @exit(i32 0) unreachable @@ -14,13 +17,17 @@ define i32 @call_exit() noreturn nounwind { ; CHECK: ud2 ; TRAP_AFTER_NORETURN: ud2 ; NO_TRAP_AFTER_NORETURN-NOT: ud2 +; NORMAL-NOT: ud2 define i32 @trap() noreturn nounwind { tail call void @llvm.trap() unreachable } ; CHECK-LABEL: unreachable: -; CHECK: ud2 +; TRAP_AFTER_NORETURN: ud2 +; NO_TRAP_AFTER_NORETURN: ud2 +; NORMAL-NOT: ud2 +; NORMAL: # -- End function define i32 @unreachable() noreturn nounwind { unreachable } diff --git a/llvm/test/CodeGen/X86/win64-eh-empty-block.ll b/llvm/test/CodeGen/X86/win64-eh-empty-block.ll new file mode 100644 index 0000000000000..c93c53b6b68e0 --- /dev/null +++ b/llvm/test/CodeGen/X86/win64-eh-empty-block.ll @@ -0,0 +1,107 @@ +; RUN: llc -mtriple=x86_64-windows-gnu %s -o - | FileCheck %s + +; Based on this C++ code: +; struct as { +; as() { at = static_cast(operator new(sizeof(int))); } +; ~as() { operator delete(at); } +; int *at; +; }; +; void am(int) { +; static as au; +; as av; +; throw 0; +; } + +; optnone was added to ensure that branch folding and block layout are not +; disturbed. The key thing about this test is that it ends in an empty +; unreachable block, which forces us to scan back across blocks. + +; CHECK: _Z2ami: +; CHECK: callq __cxa_throw +; CHECK: # %eh.resume +; CHECK: callq _Unwind_Resume +; CHECK-NEXT: int3 +; CHECK-NEXT: # %unreachable +; CHECK-NEXT: .Lfunc_end0: + +%struct.as = type { i32* } + +@_ZZ2amiE2au = internal unnamed_addr global %struct.as zeroinitializer, align 8 +@_ZGVZ2amiE2au = internal global i64 0, align 8 +@_ZTIi = external constant i8* + +define dso_local void @_Z2ami(i32 %0) noinline optnone personality i8* bitcast (i32 (...)* @__gxx_personality_seh0 to i8*) { +entry: + %1 = load atomic i8, i8* bitcast (i64* @_ZGVZ2amiE2au to i8*) acquire, align 8 + %guard.uninitialized = icmp eq i8 %1, 0 + br i1 %guard.uninitialized, label %init.check, label %init.end + +init.check: ; preds = %entry + %2 = tail call i32 @__cxa_guard_acquire(i64* nonnull @_ZGVZ2amiE2au) + %tobool = icmp eq i32 %2, 0 + br i1 %tobool, label %init.end, label %init + +init: ; preds = %init.check + %call.i3 = invoke i8* @_Znwy(i64 4) + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %init + store i8* %call.i3, i8** bitcast (%struct.as* @_ZZ2amiE2au to i8**), align 8 + %3 = tail call i32 @atexit(void ()* nonnull @__dtor__ZZ2amiE2au) + tail call void @__cxa_guard_release(i64* nonnull @_ZGVZ2amiE2au) + br label %init.end + +init.end: ; preds = %init.check, %invoke.cont, %entry + %call.i = tail call i8* @_Znwy(i64 4) + %exception = tail call i8* @__cxa_allocate_exception(i64 4) + %4 = bitcast i8* %exception to i32* + store i32 0, i32* %4, align 16 + invoke void @__cxa_throw(i8* %exception, i8* bitcast (i8** @_ZTIi to i8*), i8* null) + to label %unreachable unwind label %lpad1 + +lpad: ; preds = %init + %5 = landingpad { i8*, i32 } + cleanup + %6 = extractvalue { i8*, i32 } %5, 0 + %7 = extractvalue { i8*, i32 } %5, 1 + tail call void @__cxa_guard_abort(i64* nonnull @_ZGVZ2amiE2au) + br label %eh.resume + +lpad1: ; preds = %init.end + %8 = landingpad { i8*, i32 } + cleanup + %9 = extractvalue { i8*, i32 } %8, 0 + %10 = extractvalue { i8*, i32 } %8, 1 + tail call void @_ZdlPv(i8* %call.i) + br label %eh.resume + +eh.resume: ; preds = %lpad1, %lpad + %exn.slot.0 = phi i8* [ %9, %lpad1 ], [ %6, %lpad ] + %ehselector.slot.0 = phi i32 [ %10, %lpad1 ], [ %7, %lpad ] + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn.slot.0, 0 + %lpad.val2 = insertvalue { i8*, i32 } %lpad.val, i32 %ehselector.slot.0, 1 + resume { i8*, i32 } %lpad.val2 + +unreachable: ; preds = %init.end + unreachable +} + +declare dso_local i32 @__cxa_guard_acquire(i64*) + +declare dso_local i32 @__gxx_personality_seh0(...) + +declare dso_local void @__dtor__ZZ2amiE2au() + +declare dso_local i32 @atexit(void ()*) + +declare dso_local void @__cxa_guard_abort(i64*) + +declare dso_local void @__cxa_guard_release(i64*) + +declare dso_local i8* @__cxa_allocate_exception(i64) + +declare dso_local void @__cxa_throw(i8*, i8*, i8*) + +declare dso_local noalias i8* @_Znwy(i64) + +declare dso_local void @_ZdlPv(i8*) diff --git a/llvm/test/CodeGen/X86/win64_call_epi.ll b/llvm/test/CodeGen/X86/win64_call_epi.ll index 096cbe41c5404..3e8f57e651459 100644 --- a/llvm/test/CodeGen/X86/win64_call_epi.ll +++ b/llvm/test/CodeGen/X86/win64_call_epi.ll @@ -24,10 +24,9 @@ catch: ; WIN64: nop ; WIN64: addq ${{[0-9]+}}, %rsp ; WIN64: retq -; Check for 'ud2' after noreturn call +; Check for 'int3' after noreturn call. ; WIN64: callq _Unwind_Resume -; WIN64-NEXT: ud2 -; WIN64: .seh_endproc +; WIN64-NEXT: int3 ; Check it still works when blocks are reordered. diff --git a/llvm/test/CodeGen/X86/win64_eh.ll b/llvm/test/CodeGen/X86/win64_eh.ll index 7f6a3629613ce..caadea4fe2e4b 100644 --- a/llvm/test/CodeGen/X86/win64_eh.ll +++ b/llvm/test/CodeGen/X86/win64_eh.ll @@ -125,11 +125,11 @@ endtryfinally: ; WIN64-LABEL: foo4: ; WIN64: .seh_proc foo4 ; WIN64: .seh_handler _d_eh_personality, @unwind, @except -; NORM: subq $56, %rsp -; ATOM: leaq -56(%rsp), %rsp -; WIN64: .seh_stackalloc 56 +; NORM: subq $40, %rsp +; ATOM: leaq -40(%rsp), %rsp +; WIN64: .seh_stackalloc 40 ; WIN64: .seh_endprologue -; WIN64: addq $56, %rsp +; WIN64: addq $40, %rsp ; WIN64: ret ; WIN64: .seh_handlerdata ; WIN64: .seh_endproc diff --git a/llvm/test/CodeGen/X86/wineh-coreclr.ll b/llvm/test/CodeGen/X86/wineh-coreclr.ll index af6bef57ec7bb..fd2569e4bfb03 100644 --- a/llvm/test/CodeGen/X86/wineh-coreclr.ll +++ b/llvm/test/CodeGen/X86/wineh-coreclr.ll @@ -320,6 +320,7 @@ unreachable: ; CHECK: [[test2_before_f2:.+]]: ; CHECK-NEXT: movl $2, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: int3 ; CHECK-NEXT: [[test2_after_f2:.+]]: ; CHECK: [[test2_end:.*func_end.*]]: @@ -511,6 +512,7 @@ unreachable: ; CHECK: [[test3_before_f4:.+]]: ; CHECK-NEXT: movl $4, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: int3 ; CHECK-NEXT: [[test3_after_f4:.+]]: ; CHECK: .seh_proc [[test3_fault2:[^ ]+]] ; CHECK: # %fault2 @@ -518,6 +520,7 @@ unreachable: ; CHECK: [[test3_before_f3:.+]]: ; CHECK-NEXT: movl $3, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: int3 ; CHECK-NEXT: [[test3_after_f3:.+]]: ; CHECK: .seh_proc [[test3_fault1:[^ ]+]] ; CHECK: # %fault1 @@ -525,6 +528,7 @@ unreachable: ; CHECK: [[test3_before_f2:.+]]: ; CHECK-NEXT: movl $2, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: int3 ; CHECK-NEXT: [[test3_after_f2:.+]]: ; CHECK: [[test3_end:.*func_end.*]]: } diff --git a/llvm/test/DebugInfo/COFF/local-variable-gap.ll b/llvm/test/DebugInfo/COFF/local-variable-gap.ll index db8de6327392a..5bbc058b5b3ce 100644 --- a/llvm/test/DebugInfo/COFF/local-variable-gap.ll +++ b/llvm/test/DebugInfo/COFF/local-variable-gap.ll @@ -54,7 +54,7 @@ ; ASM: [[p_b2:\.Ltmp[0-9]+]]: ; ASM: #DEBUG_VALUE: p <- $esi ; ASM: callq call_noreturn -; ASM: ud2 +; ASM: int3 ; ASM: .Lfunc_end0: ; ASM: .short {{.*}} # Record length