[llvm] r248959 - [WinEH] Emit int3 after noreturn calls on Win64

Reid Kleckner via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 16:09:23 PDT 2015


Author: rnk
Date: Wed Sep 30 18:09:23 2015
New Revision: 248959

URL: http://llvm.org/viewvc/llvm-project?rev=248959&view=rev
Log:
[WinEH] Emit int3 after noreturn calls on Win64

The Win64 unwinder disassembles forwards from each PC to try to
determine if this PC is in an epilogue. If so, it skips calling the EH
personality function for that frame. Typically, this means you cannot
catch an exception in the same frame that you threw it, because 'throw'
calls a noreturn runtime function.

Previously we avoided this problem with the TrapUnreachable
TargetOption, but that's a much bigger hammer than we need. All we need
is a 1 byte non-epilogue instruction right after the call.  Instead,
what we got was an unconditional branch to a shared block containing the
ud2, potentially 7 bytes instead of 1. So, this reverts r206684, which
added TrapUnreachable, and replaces it with something better.

The new code pattern matches for invoke/call followed by unreachable and
inserts an int3 into the DAG. To be 100% watertight, we would need to
insert SEH_Epilogue instructions into all basic blocks ending in a call
with no terminators or successors, but in practice this is unlikely to
come up.

Added:
    llvm/trunk/test/CodeGen/X86/win-catchpad-rethrow.ll
Modified:
    llvm/trunk/include/llvm/Target/TargetOptions.h
    llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp
    llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
    llvm/trunk/lib/Target/X86/X86ISelLowering.cpp
    llvm/trunk/lib/Target/X86/X86TargetMachine.cpp
    llvm/trunk/test/CodeGen/X86/br-fold.ll
    llvm/trunk/test/CodeGen/X86/win64_call_epi.ll

Modified: llvm/trunk/include/llvm/Target/TargetOptions.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Target/TargetOptions.h?rev=248959&r1=248958&r2=248959&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Target/TargetOptions.h (original)
+++ llvm/trunk/include/llvm/Target/TargetOptions.h Wed Sep 30 18:09:23 2015
@@ -71,7 +71,7 @@ namespace llvm {
           EnableFastISel(false), PositionIndependentExecutable(false),
           UseInitArray(false), DisableIntegratedAS(false),
           CompressDebugSections(false), FunctionSections(false),
-          DataSections(false), UniqueSectionNames(true), TrapUnreachable(false),
+          DataSections(false), UniqueSectionNames(true),
           EmulatedTLS(false), FloatABIType(FloatABI::Default),
           AllowFPOpFusion(FPOpFusion::Standard), Reciprocals(TargetRecip()),
           JTType(JumpTable::Single),
@@ -169,9 +169,6 @@ namespace llvm {
 
     unsigned UniqueSectionNames : 1;
 
-    /// Emit target-specific trap instruction for 'unreachable' IR instructions.
-    unsigned TrapUnreachable : 1;
-
     /// EmulatedTLS - This flag enables emulated TLS model, using emutls
     /// function in the runtime library..
     unsigned EmulatedTLS : 1;
@@ -234,7 +231,6 @@ inline bool operator==(const TargetOptio
     ARE_EQUAL(EnableFastISel) &&
     ARE_EQUAL(PositionIndependentExecutable) &&
     ARE_EQUAL(UseInitArray) &&
-    ARE_EQUAL(TrapUnreachable) &&
     ARE_EQUAL(EmulatedTLS) &&
     ARE_EQUAL(FloatABIType) &&
     ARE_EQUAL(AllowFPOpFusion) &&

Modified: llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp?rev=248959&r1=248958&r2=248959&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp (original)
+++ llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp Wed Sep 30 18:09:23 2015
@@ -1569,10 +1569,8 @@ bool FastISel::selectOperator(const User
   }
 
   case Instruction::Unreachable:
-    if (TM.Options.TrapUnreachable)
-      return fastEmit_(MVT::Other, MVT::Other, ISD::TRAP) != 0;
-    else
-      return true;
+    // Nothing to emit.
+    return true;
 
   case Instruction::Alloca:
     // FunctionLowering has the static-sized case covered.

Modified: llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp?rev=248959&r1=248958&r2=248959&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (original)
+++ llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp Wed Sep 30 18:09:23 2015
@@ -2205,10 +2205,7 @@ void SelectionDAGBuilder::visitIndirectB
                           getValue(I.getAddress())));
 }
 
-void SelectionDAGBuilder::visitUnreachable(const UnreachableInst &I) {
-  if (DAG.getTarget().Options.TrapUnreachable)
-    DAG.setRoot(DAG.getNode(ISD::TRAP, getCurSDLoc(), MVT::Other, DAG.getRoot()));
-}
+void SelectionDAGBuilder::visitUnreachable(const UnreachableInst &I) {}
 
 void SelectionDAGBuilder::visitFSub(const User &I) {
   // -0.0 - X --> fneg

Modified: llvm/trunk/lib/Target/X86/X86ISelLowering.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86ISelLowering.cpp?rev=248959&r1=248958&r2=248959&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86ISelLowering.cpp (original)
+++ llvm/trunk/lib/Target/X86/X86ISelLowering.cpp Wed Sep 30 18:09:23 2015
@@ -2945,6 +2945,20 @@ static SDValue getMOVL(SelectionDAG &DAG
   return DAG.getVectorShuffle(VT, dl, V1, V2, &Mask[0]);
 }
 
+/// Check if the fall through instruction after a call site is unreachable.
+/// FIXME: This will fail if there are interesting non-code generating IR
+/// instructions between the call and the unreachable (lifetime.end). In
+/// practice, this should be rare because optimizations like to delete non-call
+/// code before unreachable.
+static bool isCallFollowedByUnreachable(ImmutableCallSite CS) {
+  const Instruction *NextInst;
+  if (auto *II = dyn_cast<InvokeInst>(CS.getInstruction()))
+    NextInst = II->getNormalDest()->getFirstNonPHIOrDbg();
+  else
+    NextInst = CS.getInstruction()->getNextNode();
+  return isa<UnreachableInst>(NextInst);
+}
+
 SDValue
 X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
                              SmallVectorImpl<SDValue> &InVals) const {
@@ -3450,6 +3464,15 @@ X86TargetLowering::LowerCall(TargetLower
     InFlag = Chain.getValue(1);
   }
 
+  if (Subtarget->isTargetWin64() && CLI.CS) {
+    // Look for a call followed by unreachable. On Win64, we need to ensure that
+    // the call does not accidentally fall through to something that looks like
+    // an epilogue. We do this by inserting a DEBUGTRAP, which lowers to int3,
+    // which is what MSVC emits after noreturn calls.
+    if (isCallFollowedByUnreachable(*CLI.CS))
+      Chain = DAG.getNode(ISD::DEBUGTRAP, dl, MVT::Other, Chain);
+  }
+
   // Handle result values, copying them out of physregs into vregs that we
   // return.
   return LowerCallResult(Chain, InFlag, CallConv, isVarArg,

Modified: llvm/trunk/lib/Target/X86/X86TargetMachine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86TargetMachine.cpp?rev=248959&r1=248958&r2=248959&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86TargetMachine.cpp (original)
+++ llvm/trunk/lib/Target/X86/X86TargetMachine.cpp Wed Sep 30 18:09:23 2015
@@ -110,13 +110,6 @@ X86TargetMachine::X86TargetMachine(const
                         OL),
       TLOF(createTLOF(getTargetTriple())),
       Subtarget(TT, CPU, FS, *this, Options.StackAlignmentOverride) {
-  // 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)
-  if (Subtarget.isTargetWin64())
-    this->Options.TrapUnreachable = true;
-
   // By default (and when -ffast-math is on), enable estimate codegen for
   // everything except scalar division. By default, use 1 refinement step for
   // all operations. Defaults may be overridden by using command-line options.

Modified: llvm/trunk/test/CodeGen/X86/br-fold.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/X86/br-fold.ll?rev=248959&r1=248958&r2=248959&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/X86/br-fold.ll (original)
+++ llvm/trunk/test/CodeGen/X86/br-fold.ll Wed Sep 30 18:09:23 2015
@@ -10,10 +10,10 @@
 ; X64_LINUX-NEXT: %bb8.i329
 
 ; X64_WINDOWS: orq %rax, %rcx
-; X64_WINDOWS-NEXT: ud2
+; X64_WINDOWS-NEXT: %bb8.i329
 
 ; X64_WINDOWS_GNU: orq %rax, %rcx
-; X64_WINDOWS_GNU-NEXT: ud2
+; X64_WINDOWS_GNU-NEXT: %bb8.i329
 
 @_ZN11xercesc_2_513SchemaSymbols21fgURI_SCHEMAFORSCHEMAE = external constant [33 x i16], align 32 ; <[33 x i16]*> [#uses=1]
 @_ZN11xercesc_2_56XMLUni16fgNotationStringE = external constant [9 x i16], align 16 ; <[9 x i16]*> [#uses=1]

Added: llvm/trunk/test/CodeGen/X86/win-catchpad-rethrow.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/X86/win-catchpad-rethrow.ll?rev=248959&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/X86/win-catchpad-rethrow.ll (added)
+++ llvm/trunk/test/CodeGen/X86/win-catchpad-rethrow.ll Wed Sep 30 18:09:23 2015
@@ -0,0 +1,103 @@
+; RUN: llc -mtriple=x86_64-pc-windows-msvc < %s | FileCheck %s
+
+; C++ EH rethrows are interesting, because they are calls to noreturn
+; functions. There *must* be some code after the call instruction that doesn't
+; look like an epilogue. We use int3 to be consistent with MSVC.
+
+; Based on this C++ source:
+; int main() {
+;   try {
+;     throw 42;
+;   } catch (int) {
+;     try {
+;       throw;
+;     } catch (int) {
+;     }
+;   }
+;   return 0;
+; }
+
+; ModuleID = 't.cpp'
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] }
+%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 }
+%eh.CatchableTypeArray.1 = type { i32, [1 x i32] }
+%eh.ThrowInfo = type { i32, i32, i32, i32 }
+
+$"\01??_R0H at 8" = comdat any
+
+$"_CT??_R0H at 84" = comdat any
+
+$_CTA1H = comdat any
+
+$_TI1H = comdat any
+
+@"\01??_7type_info@@6B@" = external constant i8*
+@"\01??_R0H at 8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat
+ at __ImageBase = external constant i8
+@"_CT??_R0H at 84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H at 8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat
+ at _CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H at 84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat
+ at _TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat
+
+define i32 @main() #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
+entry:
+  %tmp = alloca i32, align 4
+  store i32 42, i32* %tmp, align 4
+  %0 = bitcast i32* %tmp to i8*
+  invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* nonnull @_TI1H) #1
+          to label %unreachable unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %entry
+  %1 = catchpad [%rtti.TypeDescriptor2* @"\01??_R0H at 8", i32 0, i8* null]
+          to label %catch unwind label %catchendblock
+
+catch:                                            ; preds = %catch.dispatch
+  invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #1
+          to label %unreachable unwind label %catch.dispatch.1
+
+catch.dispatch.1:                                 ; preds = %catch
+  %2 = catchpad [%rtti.TypeDescriptor2* @"\01??_R0H at 8", i32 0, i8* null]
+          to label %catch.3 unwind label %catchendblock.2
+
+catch.3:                                          ; preds = %catch.dispatch.1
+  catchret %2 to label %try.cont
+
+try.cont:                                         ; preds = %catch.3
+  catchret %1 to label %try.cont.5
+
+try.cont.5:                                       ; preds = %try.cont
+  ret i32 0
+
+catchendblock.2:                                  ; preds = %catch.dispatch.1
+  catchendpad unwind label %catchendblock
+
+catchendblock:                                    ; preds = %catchendblock.2, %catch.dispatch
+  catchendpad unwind to caller
+
+unreachable:                                      ; preds = %catch, %entry
+  unreachable
+}
+
+declare void @_CxxThrowException(i8*, %eh.ThrowInfo*)
+
+declare i32 @__CxxFrameHandler3(...)
+
+attributes #0 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { noreturn }
+
+; CHECK: main:
+; CHECK: .seh_proc main
+; CHECK: movl $42,
+; CHECK-DAG: leaq {{.*}}, %rcx
+; CHECK-DAG: leaq _TI1H(%rip), %rdx
+; CHECK: callq _CxxThrowException
+; CHECK-NEXT: int3
+
+; CHECK: "?catch$1@?0?main at 4HA":
+; CHECK: .seh_proc "?catch$1@?0?main at 4HA"
+; CHECK-DAG: xorl %ecx, %ecx
+; CHECK-DAG: xorl %edx, %edx
+; CHECK: callq _CxxThrowException
+; CHECK-NEXT: int3

Modified: llvm/trunk/test/CodeGen/X86/win64_call_epi.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/X86/win64_call_epi.ll?rev=248959&r1=248958&r2=248959&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/X86/win64_call_epi.ll (original)
+++ llvm/trunk/test/CodeGen/X86/win64_call_epi.ll Wed Sep 30 18:09:23 2015
@@ -24,9 +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-NEXT: int3
 ; WIN64: .seh_endproc
 
 




More information about the llvm-commits mailing list