Index: llvm/trunk/lib/Target/X86/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Target/X86/CMakeLists.txt +++ llvm/trunk/lib/Target/X86/CMakeLists.txt @@ -23,6 +23,7 @@ set(sources X86AsmPrinter.cpp + X86AvoidTrailingCall.cpp X86CallFrameOptimization.cpp X86CallingConv.cpp X86CallLowering.cpp Index: llvm/trunk/lib/Target/X86/X86.h =================================================================== --- llvm/trunk/lib/Target/X86/X86.h +++ llvm/trunk/lib/Target/X86/X86.h @@ -81,6 +81,12 @@ /// 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(); Index: llvm/trunk/lib/Target/X86/X86AvoidTrailingCall.cpp =================================================================== --- llvm/trunk/lib/Target/X86/X86AvoidTrailingCall.cpp +++ llvm/trunk/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; +} Index: llvm/trunk/lib/Target/X86/X86TargetMachine.cpp =================================================================== --- llvm/trunk/lib/Target/X86/X86TargetMachine.cpp +++ llvm/trunk/lib/Target/X86/X86TargetMachine.cpp @@ -219,17 +219,9 @@ 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::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)) Index: llvm/trunk/test/CodeGen/WinEH/wineh-noret-cleanup.ll =================================================================== --- llvm/trunk/test/CodeGen/WinEH/wineh-noret-cleanup.ll +++ llvm/trunk/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 @@ ; 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: Index: llvm/trunk/test/CodeGen/X86/br-fold.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/br-fold.ll +++ llvm/trunk/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 Index: llvm/trunk/test/CodeGen/X86/catchpad-lifetime.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/catchpad-lifetime.ll +++ llvm/trunk/test/CodeGen/X86/catchpad-lifetime.ll @@ -7,6 +7,8 @@ declare i32 @__CxxFrameHandler3(...) +declare void @llvm.trap() + define void @test1() personality i32 (...)* @__CxxFrameHandler3 { entry: %alloca2 = alloca i8*, align 4 @@ -30,6 +32,7 @@ %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 @@ %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" Index: llvm/trunk/test/CodeGen/X86/catchpad-regmask.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/catchpad-regmask.ll +++ llvm/trunk/test/CodeGen/X86/catchpad-regmask.ll @@ -75,7 +75,7 @@ ; 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 @@ ; 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]]) Index: llvm/trunk/test/CodeGen/X86/catchret-regmask.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/catchret-regmask.ll +++ llvm/trunk/test/CodeGen/X86/catchret-regmask.ll @@ -6,6 +6,7 @@ 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 @@ ; 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 @@ ; 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 @@ catchret from %cp to label %return unreachable: + call void @llvm.trap() unreachable catch.dispatch: @@ -65,7 +68,7 @@ ; 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 Index: llvm/trunk/test/CodeGen/X86/empty-function.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/empty-function.ll +++ llvm/trunk/test/CodeGen/X86/empty-function.ll @@ -15,7 +15,7 @@ ; CHECK-LABEL: f: ; WIN32: nop -; WIN64: ud2 +; WIN64: nop ; LINUX-NOT: nop ; LINUX-NOT: ud2 Index: llvm/trunk/test/CodeGen/X86/funclet-layout.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/funclet-layout.ll +++ llvm/trunk/test/CodeGen/X86/funclet-layout.ll @@ -9,6 +9,8 @@ @"\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 @@ ret void unreachable: + call void @llvm.trap() unreachable } @@ -76,6 +79,7 @@ ret i32 0 unreachable: ; preds = %catch, %entry + call void @llvm.trap() unreachable } @@ -125,11 +129,13 @@ 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 @@ ; 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 @@ ; 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*) Index: llvm/trunk/test/CodeGen/X86/noreturn-call-win64.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/noreturn-call-win64.ll +++ llvm/trunk/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 Index: llvm/trunk/test/CodeGen/X86/pr24374.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/pr24374.ll +++ llvm/trunk/test/CodeGen/X86/pr24374.ll @@ -31,6 +31,6 @@ unreachable } ; CHECK-LABEL: g: -; CHECK: ud2 +; CHECK: nop attributes #0 = { nounwind } Index: llvm/trunk/test/CodeGen/X86/trap.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/trap.ll +++ llvm/trunk/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 @@ ; 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( ) Index: llvm/trunk/test/CodeGen/X86/unreachable-trap.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/unreachable-trap.ll +++ llvm/trunk/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 @@ ; 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 } Index: llvm/trunk/test/CodeGen/X86/win64-eh-empty-block.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/win64-eh-empty-block.ll +++ llvm/trunk/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*) Index: llvm/trunk/test/CodeGen/X86/win64_call_epi.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/win64_call_epi.ll +++ llvm/trunk/test/CodeGen/X86/win64_call_epi.ll @@ -24,10 +24,9 @@ ; 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. Index: llvm/trunk/test/CodeGen/X86/win64_eh.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/win64_eh.ll +++ llvm/trunk/test/CodeGen/X86/win64_eh.ll @@ -125,11 +125,11 @@ ; 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 Index: llvm/trunk/test/CodeGen/X86/wineh-coreclr.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/wineh-coreclr.ll +++ llvm/trunk/test/CodeGen/X86/wineh-coreclr.ll @@ -320,6 +320,7 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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.*]]: } Index: llvm/trunk/test/DebugInfo/COFF/local-variable-gap.ll =================================================================== --- llvm/trunk/test/DebugInfo/COFF/local-variable-gap.ll +++ llvm/trunk/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