[llvm] r250219 - [WinEH] Add CoreCLR EH table emission

Joseph Tremoulet via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 13 13:18:27 PDT 2015


Author: josepht
Date: Tue Oct 13 15:18:27 2015
New Revision: 250219

URL: http://llvm.org/viewvc/llvm-project?rev=250219&view=rev
Log:
[WinEH] Add CoreCLR EH table emission

Summary:
Emit the handler and clause locations immediately after the standard
xdata.
Clauses are emitted in the same order and format used to communiate them
to the CLR Execution Engine.
Add a lit test to verify correct table generation on a small but
interesting example function.

Reviewers: majnemer, andrew.w.kaylor, rnk

Subscribers: pgavlin, AndyAyers, llvm-commits

Differential Revision: http://reviews.llvm.org/D13451

Added:
    llvm/trunk/test/CodeGen/WinEH/wineh-coreclr.ll
Modified:
    llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp
    llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h

Modified: llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp?rev=250219&r1=250218&r2=250219&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp (original)
+++ llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp Tue Oct 13 15:18:27 2015
@@ -139,6 +139,8 @@ void WinException::endFunction(const Mac
       emitExceptHandlerTable(MF);
     else if (Per == EHPersonality::MSVC_CXX)
       emitCXXFrameHandler3Table(MF);
+    else if (Per == EHPersonality::CoreCLR)
+      emitCLRExceptionTable(MF);
     else
       emitExceptionTable();
 
@@ -285,6 +287,20 @@ const MCExpr *WinException::getLabelPlus
                                  Asm->OutContext);
 }
 
+const MCExpr *WinException::getOffset(const MCSymbol *OffsetOf,
+                                      const MCSymbol *OffsetFrom) {
+  return MCBinaryExpr::createSub(
+      MCSymbolRefExpr::create(OffsetOf, Asm->OutContext),
+      MCSymbolRefExpr::create(OffsetFrom, Asm->OutContext), Asm->OutContext);
+}
+
+const MCExpr *WinException::getOffsetPlusOne(const MCSymbol *OffsetOf,
+                                             const MCSymbol *OffsetFrom) {
+  return MCBinaryExpr::createAdd(getOffset(OffsetOf, OffsetFrom),
+                                 MCConstantExpr::create(1, Asm->OutContext),
+                                 Asm->OutContext);
+}
+
 int WinException::getFrameIndexOffset(int FrameIndex) {
   const TargetFrameLowering &TFI = *Asm->MF->getSubtarget().getFrameLowering();
   unsigned UnusedReg;
@@ -509,9 +525,7 @@ void WinException::emitCSpecificHandlerT
       Ctx.createTempSymbol("lsda_begin", /*AlwaysAddSuffix=*/true);
   MCSymbol *TableEnd =
       Ctx.createTempSymbol("lsda_end", /*AlwaysAddSuffix=*/true);
-  const MCExpr *LabelDiff =
-      MCBinaryExpr::createSub(MCSymbolRefExpr::create(TableEnd, Ctx),
-                              MCSymbolRefExpr::create(TableBegin, Ctx), Ctx);
+  const MCExpr *LabelDiff = getOffset(TableEnd, TableBegin);
   const MCExpr *EntrySize = MCConstantExpr::create(16, Ctx);
   const MCExpr *EntryCount = MCBinaryExpr::createDiv(LabelDiff, EntrySize, Ctx);
   OS.EmitValue(EntryCount, 4);
@@ -856,3 +870,263 @@ void WinException::emitExceptHandlerTabl
     OS.EmitValue(create32bitRef(ExceptOrFinally), 4); // Except/Finally
   }
 }
+
+static int getRank(WinEHFuncInfo &FuncInfo, int State) {
+  int Rank = 0;
+  while (State != -1) {
+    ++Rank;
+    State = FuncInfo.ClrEHUnwindMap[State].Parent;
+  }
+  return Rank;
+}
+
+static int getAncestor(WinEHFuncInfo &FuncInfo, int Left, int Right) {
+  int LeftRank = getRank(FuncInfo, Left);
+  int RightRank = getRank(FuncInfo, Right);
+
+  while (LeftRank < RightRank) {
+    Right = FuncInfo.ClrEHUnwindMap[Right].Parent;
+    --RightRank;
+  }
+
+  while (RightRank < LeftRank) {
+    Left = FuncInfo.ClrEHUnwindMap[Left].Parent;
+    --LeftRank;
+  }
+
+  while (Left != Right) {
+    Left = FuncInfo.ClrEHUnwindMap[Left].Parent;
+    Right = FuncInfo.ClrEHUnwindMap[Right].Parent;
+  }
+
+  return Left;
+}
+
+void WinException::emitCLRExceptionTable(const MachineFunction *MF) {
+  // CLR EH "states" are really just IDs that identify handlers/funclets;
+  // states, handlers, and funclets all have 1:1 mappings between them, and a
+  // handler/funclet's "state" is its index in the ClrEHUnwindMap.
+  MCStreamer &OS = *Asm->OutStreamer;
+  const Function *F = MF->getFunction();
+  WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(F);
+  MCSymbol *FuncBeginSym = Asm->getFunctionBegin();
+  MCSymbol *FuncEndSym = Asm->getFunctionEnd();
+
+  // A ClrClause describes a protected region.
+  struct ClrClause {
+    const MCSymbol *StartLabel; // Start of protected region
+    const MCSymbol *EndLabel;   // End of protected region
+    int State;          // Index of handler protecting the protected region
+    int EnclosingState; // Index of funclet enclosing the protected region
+  };
+  SmallVector<ClrClause, 8> Clauses;
+
+  // Build a map from handler MBBs to their corresponding states (i.e. their
+  // indices in the ClrEHUnwindMap).
+  int NumStates = FuncInfo.ClrEHUnwindMap.size();
+  assert(NumStates > 0 && "Don't need exception table!");
+  DenseMap<const MachineBasicBlock *, int> HandlerStates;
+  for (int State = 0; State < NumStates; ++State) {
+    MachineBasicBlock *HandlerBlock =
+        FuncInfo.ClrEHUnwindMap[State].Handler.get<MachineBasicBlock *>();
+    HandlerStates[HandlerBlock] = State;
+    // Use this loop through all handlers to verify our assumption (used in
+    // the MinEnclosingState computation) that ancestors have lower state
+    // numbers than their descendants.
+    assert(FuncInfo.ClrEHUnwindMap[State].Parent < State &&
+           "ill-formed state numbering");
+  }
+  // Map the main function to the NullState.
+  HandlerStates[MF->begin()] = NullState;
+
+  // Write out a sentinel indicating the end of the standard (Windows) xdata
+  // and the start of the additional (CLR) info.
+  OS.EmitIntValue(0xffffffff, 4);
+  // Write out the number of funclets
+  OS.EmitIntValue(NumStates, 4);
+
+  // Walk the machine blocks/instrs, computing and emitting a few things:
+  // 1. Emit a list of the offsets to each handler entry, in lexical order.
+  // 2. Compute a map (EndSymbolMap) from each funclet to the symbol at its end.
+  // 3. Compute the list of ClrClauses, in the required order (inner before
+  //    outer, earlier before later; the order by which a forward scan with
+  //    early termination will find the innermost enclosing clause covering
+  //    a given address).
+  // 4. A map (MinClauseMap) from each handler index to the index of the
+  //    outermost funclet/function which contains a try clause targeting the
+  //    key handler.  This will be used to determine IsDuplicate-ness when
+  //    emitting ClrClauses.  The NullState value is used to indicate that the
+  //    top-level function contains a try clause targeting the key handler.
+  // HandlerStack is a stack of (PendingStartLabel, PendingState) pairs for
+  // try regions we entered before entering the PendingState try but which
+  // we haven't yet exited.
+  SmallVector<std::pair<const MCSymbol *, int>, 4> HandlerStack;
+  // EndSymbolMap and MinClauseMap are maps described above.
+  std::unique_ptr<MCSymbol *[]> EndSymbolMap(new MCSymbol *[NumStates]);
+  SmallVector<int, 4> MinClauseMap((size_t)NumStates, NumStates);
+
+  // Visit the root function and each funclet.
+
+  for (MachineFunction::const_iterator FuncletStart = MF->begin(),
+                                       FuncletEnd = MF->begin(),
+                                       End = MF->end();
+       FuncletStart != End; FuncletStart = FuncletEnd) {
+    int FuncletState = HandlerStates[FuncletStart];
+    // Find the end of the funclet
+    MCSymbol *EndSymbol = FuncEndSym;
+    while (++FuncletEnd != End) {
+      if (FuncletEnd->isEHFuncletEntry()) {
+        EndSymbol = getMCSymbolForMBB(Asm, FuncletEnd);
+        break;
+      }
+    }
+    // Emit the function/funclet end and, if this is a funclet (and not the
+    // root function), record it in the EndSymbolMap.
+    OS.EmitValue(getOffset(EndSymbol, FuncBeginSym), 4);
+    if (FuncletState != NullState) {
+      // Record the end of the handler.
+      EndSymbolMap[FuncletState] = EndSymbol;
+    }
+
+    // Walk the state changes in this function/funclet and compute its clauses.
+    // Funclets always start in the null state.
+    const MCSymbol *CurrentStartLabel = nullptr;
+    int CurrentState = NullState;
+    assert(HandlerStack.empty());
+    for (const auto &StateChange :
+         InvokeStateChangeIterator::range(FuncInfo, FuncletStart, FuncletEnd)) {
+      // Close any try regions we're not still under
+      int AncestorState =
+          getAncestor(FuncInfo, CurrentState, StateChange.NewState);
+      while (CurrentState != AncestorState) {
+        assert(CurrentState != NullState && "Failed to find ancestor!");
+        // Close the pending clause
+        Clauses.push_back({CurrentStartLabel, StateChange.PreviousEndLabel,
+                           CurrentState, FuncletState});
+        // Now the parent handler is current
+        CurrentState = FuncInfo.ClrEHUnwindMap[CurrentState].Parent;
+        // Pop the new start label from the handler stack if we've exited all
+        // descendants of the corresponding handler.
+        if (HandlerStack.back().second == CurrentState)
+          CurrentStartLabel = HandlerStack.pop_back_val().first;
+      }
+
+      if (StateChange.NewState != CurrentState) {
+        // For each clause we're starting, update the MinClauseMap so we can
+        // know which is the topmost funclet containing a clause targeting
+        // it.
+        for (int EnteredState = StateChange.NewState;
+             EnteredState != CurrentState;
+             EnteredState = FuncInfo.ClrEHUnwindMap[EnteredState].Parent) {
+          int &MinEnclosingState = MinClauseMap[EnteredState];
+          if (FuncletState < MinEnclosingState)
+            MinEnclosingState = FuncletState;
+        }
+        // Save the previous current start/label on the stack and update to
+        // the newly-current start/state.
+        HandlerStack.emplace_back(CurrentStartLabel, CurrentState);
+        CurrentStartLabel = StateChange.NewStartLabel;
+        CurrentState = StateChange.NewState;
+      }
+    }
+    assert(HandlerStack.empty());
+  }
+
+  // Now emit the clause info, starting with the number of clauses.
+  OS.EmitIntValue(Clauses.size(), 4);
+  for (ClrClause &Clause : Clauses) {
+    // Emit a CORINFO_EH_CLAUSE :
+    /*
+      struct CORINFO_EH_CLAUSE
+      {
+          CORINFO_EH_CLAUSE_FLAGS Flags;         // actually a CorExceptionFlag
+          DWORD                   TryOffset;
+          DWORD                   TryLength;     // actually TryEndOffset
+          DWORD                   HandlerOffset;
+          DWORD                   HandlerLength; // actually HandlerEndOffset
+          union
+          {
+              DWORD               ClassToken;   // use for catch clauses
+              DWORD               FilterOffset; // use for filter clauses
+          };
+      };
+
+      enum CORINFO_EH_CLAUSE_FLAGS
+      {
+          CORINFO_EH_CLAUSE_NONE    = 0,
+          CORINFO_EH_CLAUSE_FILTER  = 0x0001, // This clause is for a filter
+          CORINFO_EH_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause
+          CORINFO_EH_CLAUSE_FAULT   = 0x0004, // This clause is a fault clause
+      };
+      typedef enum CorExceptionFlag
+      {
+          COR_ILEXCEPTION_CLAUSE_NONE,
+          COR_ILEXCEPTION_CLAUSE_FILTER  = 0x0001, // This is a filter clause
+          COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002, // This is a finally clause
+          COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004,   // This is a fault clause
+          COR_ILEXCEPTION_CLAUSE_DUPLICATED = 0x0008, // duplicated clause. This
+                                                      // clause was duplicated
+                                                      // to a funclet which was
+                                                      // pulled out of line
+      } CorExceptionFlag;
+    */
+    // Add 1 to the start/end of the EH clause; the IP associated with a
+    // call when the runtime does its scan is the IP of the next instruction
+    // (the one to which control will return after the call), so we need
+    // to add 1 to the end of the clause to cover that offset.  We also add
+    // 1 to the start of the clause to make sure that the ranges reported
+    // for all clauses are disjoint.  Note that we'll need some additional
+    // logic when machine traps are supported, since in that case the IP
+    // that the runtime uses is the offset of the faulting instruction
+    // itself; if such an instruction immediately follows a call but the
+    // two belong to different clauses, we'll need to insert a nop between
+    // them so the runtime can distinguish the point to which the call will
+    // return from the point at which the fault occurs.
+
+    const MCExpr *ClauseBegin =
+        getOffsetPlusOne(Clause.StartLabel, FuncBeginSym);
+    const MCExpr *ClauseEnd = getOffsetPlusOne(Clause.EndLabel, FuncBeginSym);
+
+    ClrEHUnwindMapEntry &Entry = FuncInfo.ClrEHUnwindMap[Clause.State];
+    MachineBasicBlock *HandlerBlock = Entry.Handler.get<MachineBasicBlock *>();
+    MCSymbol *BeginSym = getMCSymbolForMBB(Asm, HandlerBlock);
+    const MCExpr *HandlerBegin = getOffset(BeginSym, FuncBeginSym);
+    MCSymbol *EndSym = EndSymbolMap[Clause.State];
+    const MCExpr *HandlerEnd = getOffset(EndSym, FuncBeginSym);
+
+    uint32_t Flags = 0;
+    switch (Entry.HandlerType) {
+    case ClrHandlerType::Catch:
+      // Leaving bits 0-2 clear indicates catch.
+      break;
+    case ClrHandlerType::Filter:
+      Flags |= 1;
+      break;
+    case ClrHandlerType::Finally:
+      Flags |= 2;
+      break;
+    case ClrHandlerType::Fault:
+      Flags |= 4;
+      break;
+    }
+    if (Clause.EnclosingState != MinClauseMap[Clause.State]) {
+      // This is a "duplicate" clause; the handler needs to be entered from a
+      // frame above the one holding the invoke.
+      assert(Clause.EnclosingState > MinClauseMap[Clause.State]);
+      Flags |= 8;
+    }
+    OS.EmitIntValue(Flags, 4);
+
+    // Write the clause start/end
+    OS.EmitValue(ClauseBegin, 4);
+    OS.EmitValue(ClauseEnd, 4);
+
+    // Write out the handler start/end
+    OS.EmitValue(HandlerBegin, 4);
+    OS.EmitValue(HandlerEnd, 4);
+
+    // Write out the type token or filter offset
+    assert(Entry.HandlerType != ClrHandlerType::Filter && "NYI: filters");
+    OS.EmitIntValue(Entry.TypeToken, 4);
+  }
+}

Modified: llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h?rev=250219&r1=250218&r2=250219&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h (original)
+++ llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h Tue Oct 13 15:18:27 2015
@@ -54,6 +54,8 @@ class LLVM_LIBRARY_VISIBILITY WinExcepti
   /// tables.
   void emitExceptHandlerTable(const MachineFunction *MF);
 
+  void emitCLRExceptionTable(const MachineFunction *MF);
+
   void computeIP2StateTable(
       const MachineFunction *MF, WinEHFuncInfo &FuncInfo,
       SmallVectorImpl<std::pair<const MCExpr *, int>> &IPToStateTable);
@@ -66,6 +68,9 @@ class LLVM_LIBRARY_VISIBILITY WinExcepti
   const MCExpr *create32bitRef(const MCSymbol *Value);
   const MCExpr *create32bitRef(const Value *V);
   const MCExpr *getLabelPlusOne(const MCSymbol *Label);
+  const MCExpr *getOffset(const MCSymbol *OffsetOf, const MCSymbol *OffsetFrom);
+  const MCExpr *getOffsetPlusOne(const MCSymbol *OffsetOf,
+                                 const MCSymbol *OffsetFrom);
 
   /// Gets the offset that we should use in a table for a stack object with the
   /// given index. For targets using CFI (Win64, etc), this is relative to the

Added: llvm/trunk/test/CodeGen/WinEH/wineh-coreclr.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/wineh-coreclr.ll?rev=250219&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/wineh-coreclr.ll (added)
+++ llvm/trunk/test/CodeGen/WinEH/wineh-coreclr.ll Tue Oct 13 15:18:27 2015
@@ -0,0 +1,239 @@
+; RUN: llc -mtriple=x86_64-pc-windows-coreclr < %s | FileCheck %s
+
+declare void @ProcessCLRException()
+declare void @f(i32)
+
+; Simplified IR for pseudo-C# like the following:
+; void test1() {
+;   try {
+;     f(1);
+;     try {
+;       f(2);
+;     } catch (type1) {
+;       f(3);
+;     } catch (type2) [
+;       f(4);
+;       try {
+;         f(5);
+;       } fault {
+;         f(6);
+;       }
+;     }
+;   } finally {
+;     f(7);
+;   }
+;   f(8);
+; }
+
+; CHECK-LABEL: test1:     # @test1
+; CHECK-NEXT: [[L_begin:.*func_begin.*]]:
+define void @test1() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
+entry:
+; CHECK: # %entry
+; CHECK: .seh_endprologue
+; CHECK: [[L_before_f1:.+]]:
+; CHECK-NEXT: movl $1, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[L_after_f1:.+]]:
+  invoke void @f(i32 1)
+    to label %inner_try unwind label %finally.pad
+inner_try:
+; CHECK: # %inner_try
+; CHECK: [[L_before_f2:.+]]:
+; CHECK-NEXT: movl $2, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[L_after_f2:.+]]:
+  invoke void @f(i32 2)
+    to label %finally.clone unwind label %catch1.pad
+catch1.pad:
+; CHECK: .seh_proc [[L_catch1:[^ ]+]]
+  %catch1 = catchpad [i32 1]
+    to label %catch1.body unwind label %catch2.pad
+catch1.body:
+; CHECK: .seh_endprologue
+; CHECK: [[L_before_f3:.+]]:
+; CHECK-NEXT: movl $3, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[L_after_f3:.+]]:
+  invoke void @f(i32 3)
+    to label %catch1.ret unwind label %catch.end
+catch1.ret:
+  catchret %catch1 to label %finally.clone
+catch2.pad:
+; CHECK: .seh_proc [[L_catch2:[^ ]+]]
+  %catch2 = catchpad [i32 2]
+    to label %catch2.body unwind label %catch.end
+catch2.body:
+; CHECK: .seh_endprologue
+; CHECK: [[L_before_f4:.+]]:
+; CHECK-NEXT: movl $4, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[L_after_f4:.+]]:
+  invoke void @f(i32 4)
+    to label %try_in_catch unwind label %catch.end
+try_in_catch:
+; CHECK: # %try_in_catch
+; CHECK: [[L_before_f5:.+]]:
+; CHECK-NEXT: movl $5, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[L_after_f5:.+]]:
+  invoke void @f(i32 5)
+    to label %catch2.ret unwind label %fault.pad
+fault.pad:
+; CHECK: .seh_proc [[L_fault:[^ ]+]]
+  %fault = cleanuppad [i32 undef]
+; CHECK: .seh_endprologue
+; CHECK: [[L_before_f6:.+]]:
+; CHECK-NEXT: movl $6, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[L_after_f6:.+]]:
+  invoke void @f(i32 6)
+    to label %fault.ret unwind label %fault.end
+fault.ret:
+  cleanupret %fault unwind label %catch.end
+fault.end:
+  cleanupendpad %fault unwind label %catch.end
+catch2.ret:
+  catchret %catch2 to label %finally.clone
+catch.end:
+  catchendpad unwind label %finally.pad
+finally.clone:
+  call void @f(i32 7)
+  br label %tail
+finally.pad:
+; CHECK: .seh_proc [[L_finally:[^ ]+]]
+  %finally = cleanuppad []
+; CHECK: .seh_endprologue
+; CHECK: [[L_before_f7:.+]]:
+; CHECK-NEXT: movl $7, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[L_after_f7:.+]]:
+  invoke void @f(i32 7)
+    to label %finally.ret unwind label %finally.end
+finally.ret:
+  cleanupret %finally unwind to caller
+finally.end:
+   cleanupendpad %finally unwind to caller
+tail:
+  call void @f(i32 8)
+  ret void
+; CHECK: [[L_end:.*func_end.*]]:
+}
+
+; Now check for EH table in xdata (following standard xdata)
+; CHECK-LABEL: .section .xdata
+; standard xdata comes here
+; CHECK:      .long 4{{$}}
+;                   ^ number of funclets
+; CHECK-NEXT: .long [[L_catch1]]-[[L_begin]]
+;                   ^ offset from L_begin to start of 1st funclet
+; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
+;                   ^ offset from L_begin to start of 2nd funclet
+; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
+;                   ^ offset from L_begin to start of 3rd funclet
+; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
+;                   ^ offset from L_begin to start of 4th funclet
+; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
+;                   ^ offset from L_begin to end of last funclet
+; CHECK-NEXT: .long 7
+;                   ^ number of EH clauses
+; Clause 1: call f(2) is guarded by catch1
+; CHECK-NEXT: .long 0
+;                   ^ flags (0 => catch handler)
+; CHECK-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1
+;                   ^ offset of start of clause
+; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
+;                   ^ offset of end of clause
+; CHECK-NEXT: .long [[L_catch1]]-[[L_begin]]
+;                   ^ offset of start of handler
+; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
+;                   ^ offset of end of handler
+; CHECK-NEXT: .long 1
+;                   ^ type token of catch (from catchpad)
+; Clause 2: call f(2) is also guarded by catch2
+; CHECK-NEXT: .long 0
+;                   ^ flags (0 => catch handler)
+; CHECK-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1
+;                   ^ offset of start of clause
+; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
+;                   ^ offset of end of clause
+; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
+;                   ^ offset of start of handler
+; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
+;                   ^ offset of end of handler
+; CHECK-NEXT: .long 2
+;                   ^ type token of catch (from catchpad)
+; Clause 3: calls f(1) and f(2) are guarded by finally
+; CHECK-NEXT: .long 2
+;                   ^ flags (2 => finally handler)
+; CHECK-NEXT: .long ([[L_before_f1]]-[[L_begin]])+1
+;                   ^ offset of start of clause
+; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
+;                   ^ offset of end of clause
+; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
+;                   ^ offset of start of handler
+; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
+;                   ^ offset of end of handler
+; CHECK-NEXT: .long 0
+;                   ^ type token slot (null for finally)
+; Clause 4: call f(3) is guarded by finally
+;           This is a "duplicate" because the protected range (f(3))
+;           is in funclet catch1 but the finally's immediate parent
+;           is the main function, not that funclet.
+; CHECK-NEXT: .long 10
+;                   ^ flags (2 => finally handler | 8 => duplicate)
+; CHECK-NEXT: .long ([[L_before_f3]]-[[L_begin]])+1
+;                   ^ offset of start of clause
+; CHECK-NEXT: .long ([[L_after_f3]]-[[L_begin]])+1
+;                   ^ offset of end of clause
+; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
+;                   ^ offset of start of handler
+; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
+;                   ^ offset of end of handler
+; CHECK-NEXT: .long 0
+;                   ^ type token slot (null for finally)
+; Clause 5: call f(5) is guarded by fault
+; CHECK-NEXT: .long 4
+;                   ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[L_before_f5]]-[[L_begin]])+1
+;                   ^ offset of start of clause
+; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
+;                   ^ offset of end of clause
+; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
+;                   ^ offset of start of handler
+; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
+;                   ^ offset of end of handler
+; CHECK-NEXT: .long 0
+;                   ^ type token slot (null for fault)
+; Clause 6: calls f(4) and f(5) are guarded by finally
+;           This is a "duplicate" because the protected range (f(4)-f(5))
+;           is in funclet catch2 but the finally's immediate parent
+;           is the main function, not that funclet.
+; CHECK-NEXT: .long 10
+;                   ^ flags (2 => finally handler | 8 => duplicate)
+; CHECK-NEXT: .long ([[L_before_f4]]-[[L_begin]])+1
+;                   ^ offset of start of clause
+; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
+;                   ^ offset of end of clause
+; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
+;                   ^ offset of start of handler
+; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
+;                   ^ offset of end of handler
+; CHECK-NEXT: .long 0
+;                   ^ type token slot (null for finally)
+; Clause 7: call f(6) is guarded by finally
+;           This is a "duplicate" because the protected range (f(3))
+;           is in funclet catch1 but the finally's immediate parent
+;           is the main function, not that funclet.
+; CHECK-NEXT: .long 10
+;                   ^ flags (2 => finally handler | 8 => duplicate)
+; CHECK-NEXT: .long ([[L_before_f6]]-[[L_begin]])+1
+;                   ^ offset of start of clause
+; CHECK-NEXT: .long ([[L_after_f6]]-[[L_begin]])+1
+;                   ^ offset of end of clause
+; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
+;                   ^ offset of start of handler
+; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
+;                   ^ offset of end of handler
+; CHECK-NEXT: .long 0
+;                   ^ type token slot (null for finally)




More information about the llvm-commits mailing list