[WinEH] Iterate state changes instead of invokes
authorJoseph Tremoulet <jotrem@microsoft.com>
Tue, 13 Oct 2015 16:44:30 +0000 (16:44 +0000)
committerJoseph Tremoulet <jotrem@microsoft.com>
Tue, 13 Oct 2015 16:44:30 +0000 (16:44 +0000)
Summary:
Add an iterator that can walk across blocks and which visits the state
transitions rather than state ranges, with explicit transitions to -1
indicating the presence of top-level calls that may throw and cause the
current function to unwind to caller.  This will simplify code that needs
to identify nested try regions.

Refactor SEH and C++EH table generation to use the new
InvokeStateChangeIterator, and remove the InvokeLabelIterator they were
using.

Reviewers: majnemer, andrew.w.kaylor, rnk

Subscribers: llvm-commits

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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@250179 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/AsmPrinter/WinException.cpp
lib/CodeGen/AsmPrinter/WinException.h

index 86e6881..bd348f6 100644 (file)
@@ -279,7 +279,7 @@ const MCExpr *WinException::create32bitRef(const Value *V) {
   return create32bitRef(MMI->getAddrLabelSymbol(cast<BasicBlock>(V)));
 }
 
-const MCExpr *WinException::getLabelPlusOne(MCSymbol *Label) {
+const MCExpr *WinException::getLabelPlusOne(const MCSymbol *Label) {
   return MCBinaryExpr::createAdd(create32bitRef(Label),
                                  MCConstantExpr::create(1, Asm->OutContext),
                                  Asm->OutContext);
@@ -294,97 +294,182 @@ int WinException::getFrameIndexOffset(int FrameIndex) {
 }
 
 namespace {
-/// Information describing an invoke range.
-struct InvokeRange {
-  MCSymbol *BeginLabel = nullptr;
-  MCSymbol *EndLabel = nullptr;
-  int State = -1;
-
-  /// If we saw a potentially throwing call between this range and the last
-  /// range.
-  bool SawPotentiallyThrowing = false;
+
+/// Top-level state used to represent unwind to caller
+const int NullState = -1;
+
+struct InvokeStateChange {
+  /// EH Label immediately after the last invoke in the previous state, or
+  /// nullptr if the previous state was the null state.
+  const MCSymbol *PreviousEndLabel;
+
+  /// EH label immediately before the first invoke in the new state, or nullptr
+  /// if the new state is the null state.
+  const MCSymbol *NewStartLabel;
+
+  /// State of the invoke following NewStartLabel, or NullState to indicate
+  /// the presence of calls which may unwind to caller.
+  int NewState;
 };
 
-/// Iterator over the begin/end label pairs of invokes within a basic block.
-class InvokeLabelIterator {
-public:
-  InvokeLabelIterator(WinEHFuncInfo &EHInfo,
-                      MachineBasicBlock::const_iterator MBBI,
-                      MachineBasicBlock::const_iterator MBBIEnd)
-      : EHInfo(EHInfo), MBBI(MBBI), MBBIEnd(MBBIEnd) {
+/// Iterator that reports all the invoke state changes in a range of machine
+/// basic blocks.  Changes to the null state are reported whenever a call that
+/// may unwind to caller is encountered.  The MBB range is expected to be an
+/// entire function or funclet, and the start and end of the range are treated
+/// as being in the NullState even if there's not an unwind-to-caller call
+/// before the first invoke or after the last one (i.e., the first state change
+/// reported is the first change to something other than NullState, and a
+/// change back to NullState is always reported at the end of iteration).
+class InvokeStateChangeIterator {
+  InvokeStateChangeIterator(WinEHFuncInfo &EHInfo,
+                            MachineFunction::const_iterator MFI,
+                            MachineFunction::const_iterator MFE,
+                            MachineBasicBlock::const_iterator MBBI)
+      : EHInfo(EHInfo), MFI(MFI), MFE(MFE), MBBI(MBBI) {
+    LastStateChange.PreviousEndLabel = nullptr;
+    LastStateChange.NewStartLabel = nullptr;
+    LastStateChange.NewState = NullState;
     scan();
   }
 
+public:
+  static iterator_range<InvokeStateChangeIterator>
+  range(WinEHFuncInfo &EHInfo, const MachineFunction &MF) {
+    // Reject empty MFs to simplify bookkeeping by ensuring that we can get the
+    // end of the last block.
+    assert(!MF.empty());
+    auto FuncBegin = MF.begin();
+    auto FuncEnd = MF.end();
+    auto BlockBegin = FuncBegin->begin();
+    auto BlockEnd = MF.back().end();
+    return make_range(
+        InvokeStateChangeIterator(EHInfo, FuncBegin, FuncEnd, BlockBegin),
+        InvokeStateChangeIterator(EHInfo, FuncEnd, FuncEnd, BlockEnd));
+  }
+  static iterator_range<InvokeStateChangeIterator>
+  range(WinEHFuncInfo &EHInfo, MachineFunction::const_iterator Begin,
+        MachineFunction::const_iterator End) {
+    // Reject empty ranges to simplify bookkeeping by ensuring that we can get
+    // the end of the last block.
+    assert(Begin != End);
+    auto BlockBegin = Begin->begin();
+    auto BlockEnd = std::prev(End)->end();
+    return make_range(InvokeStateChangeIterator(EHInfo, Begin, End, BlockBegin),
+                      InvokeStateChangeIterator(EHInfo, End, End, BlockEnd));
+  }
+
   // Iterator methods.
-  bool operator==(const InvokeLabelIterator &o) const { return MBBI == o.MBBI; }
-  bool operator!=(const InvokeLabelIterator &o) const { return MBBI != o.MBBI; }
-  InvokeRange &operator*() { return CurRange; }
-  InvokeRange *operator->() { return &CurRange; }
-  InvokeLabelIterator &operator++() { return scan(); }
+  bool operator==(const InvokeStateChangeIterator &O) const {
+    // Must be visiting same block.
+    if (MFI != O.MFI)
+      return false;
+    // Must be visiting same isntr.
+    if (MBBI != O.MBBI)
+      return false;
+    // At end of block/instr iteration, we can still have two distinct states:
+    // one to report the final EndLabel, and another indicating the end of the
+    // state change iteration.  Check for CurrentEndLabel equality to
+    // distinguish these.
+    return CurrentEndLabel == O.CurrentEndLabel;
+  }
+
+  bool operator!=(const InvokeStateChangeIterator &O) const {
+    return !operator==(O);
+  }
+  InvokeStateChange &operator*() { return LastStateChange; }
+  InvokeStateChange *operator->() { return &LastStateChange; }
+  InvokeStateChangeIterator &operator++() { return scan(); }
 
 private:
-  // Scan forward to find the next invoke range, or hit the end iterator.
-  InvokeLabelIterator &scan();
+  InvokeStateChangeIterator &scan();
 
   WinEHFuncInfo &EHInfo;
+  const MCSymbol *CurrentEndLabel = nullptr;
+  MachineFunction::const_iterator MFI;
+  MachineFunction::const_iterator MFE;
   MachineBasicBlock::const_iterator MBBI;
-  MachineBasicBlock::const_iterator MBBIEnd;
-  InvokeRange CurRange;
+  InvokeStateChange LastStateChange;
+  bool VisitingInvoke = false;
 };
+
 } // end anonymous namespace
 
-/// Invoke label range iteration logic. Increment MBBI until we find the next
-/// EH_LABEL pair, and then update MBBI to point after the end label.
-InvokeLabelIterator &InvokeLabelIterator::scan() {
-  // Reset our state.
-  CurRange = InvokeRange{};
-
-  for (const MachineInstr &MI : make_range(MBBI, MBBIEnd)) {
-    // Remember if we had to cross a potentially throwing call instruction that
-    // must unwind to caller.
-    if (MI.isCall()) {
-      CurRange.SawPotentiallyThrowing |=
-          !EHStreamer::callToNoUnwindFunction(&MI);
-      continue;
+InvokeStateChangeIterator &InvokeStateChangeIterator::scan() {
+  bool IsNewBlock = false;
+  for (; MFI != MFE; ++MFI, IsNewBlock = true) {
+    if (IsNewBlock)
+      MBBI = MFI->begin();
+    for (auto MBBE = MFI->end(); MBBI != MBBE; ++MBBI) {
+      const MachineInstr &MI = *MBBI;
+      if (!VisitingInvoke && LastStateChange.NewState != NullState &&
+          MI.isCall() && !EHStreamer::callToNoUnwindFunction(&MI)) {
+        // Indicate a change of state to the null state.  We don't have
+        // start/end EH labels handy but the caller won't expect them for
+        // null state regions.
+        LastStateChange.PreviousEndLabel = CurrentEndLabel;
+        LastStateChange.NewStartLabel = nullptr;
+        LastStateChange.NewState = NullState;
+        CurrentEndLabel = nullptr;
+        // Don't re-visit this instr on the next scan
+        ++MBBI;
+        return *this;
+      }
+
+      // All other state changes are at EH labels before/after invokes.
+      if (!MI.isEHLabel())
+        continue;
+      MCSymbol *Label = MI.getOperand(0).getMCSymbol();
+      if (Label == CurrentEndLabel) {
+        VisitingInvoke = false;
+        continue;
+      }
+      auto InvokeMapIter = EHInfo.InvokeToStateMap.find(Label);
+      // Ignore EH labels that aren't the ones inserted before an invoke
+      if (InvokeMapIter == EHInfo.InvokeToStateMap.end())
+        continue;
+      auto &StateAndEnd = InvokeMapIter->second;
+      int NewState = StateAndEnd.first;
+      // Ignore EH labels explicitly annotated with the null state (which
+      // can happen for invokes that unwind to a chain of endpads the last
+      // of which unwinds to caller).  We'll see the subsequent invoke and
+      // report a transition to the null state same as we do for calls.
+      if (NewState == NullState)
+        continue;
+      // Keep track of the fact that we're between EH start/end labels so
+      // we know not to treat the inoke we'll see as unwinding to caller.
+      VisitingInvoke = true;
+      if (NewState == LastStateChange.NewState) {
+        // The state isn't actually changing here.  Record the new end and
+        // keep going.
+        CurrentEndLabel = StateAndEnd.second;
+        continue;
+      }
+      // Found a state change to report
+      LastStateChange.PreviousEndLabel = CurrentEndLabel;
+      LastStateChange.NewStartLabel = Label;
+      LastStateChange.NewState = NewState;
+      // Start keeping track of the new current end
+      CurrentEndLabel = StateAndEnd.second;
+      // Don't re-visit this instr on the next scan
+      ++MBBI;
+      return *this;
     }
-    // Find the next EH_LABEL instruction.
-    if (!MI.isEHLabel())
-      continue;
-
-    // If this is a begin label, break out with the state and end label.
-    // Otherwise this is probably a CFI EH_LABEL that we should continue past.
-    MCSymbol *Label = MI.getOperand(0).getMCSymbol();
-    auto StateAndEnd = EHInfo.InvokeToStateMap.find(Label);
-    if (StateAndEnd == EHInfo.InvokeToStateMap.end())
-      continue;
-    MBBI = MachineBasicBlock::const_iterator(&MI);
-    CurRange.BeginLabel = Label;
-    CurRange.EndLabel = StateAndEnd->second.second;
-    CurRange.State = StateAndEnd->second.first;
-    break;
   }
-
-  // If we didn't find a begin label, we are done, return the end iterator.
-  if (!CurRange.BeginLabel) {
-    MBBI = MBBIEnd;
+  // Iteration hit the end of the block range.
+  if (LastStateChange.NewState != NullState) {
+    // Report the end of the last new state
+    LastStateChange.PreviousEndLabel = CurrentEndLabel;
+    LastStateChange.NewStartLabel = nullptr;
+    LastStateChange.NewState = NullState;
+    // Leave CurrentEndLabel non-null to distinguish this state from end.
+    assert(CurrentEndLabel != nullptr);
     return *this;
   }
-
-  // If this is a begin label, update MBBI to point past the end label.
-  for (; MBBI != MBBIEnd; ++MBBI)
-    if (MBBI->isEHLabel() &&
-        MBBI->getOperand(0).getMCSymbol() == CurRange.EndLabel)
-      break;
+  // We've reported all state changes and hit the end state.
+  CurrentEndLabel = nullptr;
   return *this;
 }
 
-/// Utility for making a range for all the invoke ranges.
-static iterator_range<InvokeLabelIterator>
-invoke_ranges(WinEHFuncInfo &EHInfo, const MachineBasicBlock &MBB) {
-  return make_range(InvokeLabelIterator(EHInfo, MBB.begin(), MBB.end()),
-                    InvokeLabelIterator(EHInfo, MBB.end(), MBB.end()));
-}
-
 /// Emit the language-specific data that __C_specific_handler expects.  This
 /// handler lives in the x64 Microsoft C runtime and allows catching or cleaning
 /// up after faults with __try, __except, and __finally.  The typeinfo values
@@ -418,14 +503,6 @@ void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) {
   MCContext &Ctx = Asm->OutContext;
 
   WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(MF->getFunction());
-
-  // Remember what state we were in the last time we found a begin try label.
-  // This allows us to coalesce many nearby invokes with the same state into
-  // one entry.
-  int LastEHState = -1;
-  MCSymbol *LastBeginLabel = nullptr;
-  MCSymbol *LastEndLabel = nullptr;
-
   // Use the assembler to compute the number of table entries through label
   // difference and division.
   MCSymbol *TableBegin =
@@ -448,44 +525,31 @@ void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) {
   // each range of invokes in the same state, we emit table entries for all
   // the actions that would be taken in that state. This means our tables are
   // slightly bigger, which is OK.
-  for (const auto &MBB : *MF) {
-    // Break out before we enter into a finally funclet.
-    // FIXME: We need to emit separate EH tables for cleanups.
-    if (MBB.isEHFuncletEntry() && &MBB != MF->begin())
-      break;
-
-    for (InvokeRange &I : invoke_ranges(FuncInfo, MBB)) {
-      // If this invoke is in the same state as the last invoke and there were
-      // no non-throwing calls between it, extend the range to include both
-      // and continue.
-      if (!I.SawPotentiallyThrowing && I.State == LastEHState) {
-        LastEndLabel = I.EndLabel;
-        continue;
-      }
-
-      // If this invoke ends a previous one, emit all the actions for this
-      // state.
-      if (LastEHState != -1)
-        emitSEHActionsForRange(FuncInfo, LastBeginLabel, LastEndLabel,
-                               LastEHState);
-
-      LastBeginLabel = I.BeginLabel;
-      LastEndLabel = I.EndLabel;
-      LastEHState = I.State;
-    }
+  const MCSymbol *LastStartLabel = nullptr;
+  int LastEHState = -1;
+  // Break out before we enter into a finally funclet.
+  // FIXME: We need to emit separate EH tables for cleanups.
+  MachineFunction::const_iterator End = MF->end();
+  MachineFunction::const_iterator Stop = std::next(MF->begin());
+  while (Stop != End && !Stop->isEHFuncletEntry())
+    ++Stop;
+  for (const auto &StateChange :
+       InvokeStateChangeIterator::range(FuncInfo, MF->begin(), Stop)) {
+    // Emit all the actions for the state we just transitioned out of
+    // if it was not the null state
+    if (LastEHState != -1)
+      emitSEHActionsForRange(FuncInfo, LastStartLabel,
+                             StateChange.PreviousEndLabel, LastEHState);
+    LastStartLabel = StateChange.NewStartLabel;
+    LastEHState = StateChange.NewState;
   }
 
-  // Hitting the end of the function causes us to emit the range for the
-  // previous invoke.
-  if (LastEndLabel)
-    emitSEHActionsForRange(FuncInfo, LastBeginLabel, LastEndLabel, LastEHState);
-
   OS.EmitLabel(TableEnd);
 }
 
 void WinException::emitSEHActionsForRange(WinEHFuncInfo &FuncInfo,
-                                          MCSymbol *BeginLabel,
-                                          MCSymbol *EndLabel, int State) {
+                                          const MCSymbol *BeginLabel,
+                                          const MCSymbol *EndLabel, int State) {
   auto &OS = *Asm->OutStreamer;
   MCContext &Ctx = Asm->OutContext;
 
@@ -698,46 +762,26 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) {
 void WinException::computeIP2StateTable(
     const MachineFunction *MF, WinEHFuncInfo &FuncInfo,
     SmallVectorImpl<std::pair<const MCExpr *, int>> &IPToStateTable) {
-  // Remember what state we were in the last time we found a begin try label.
-  // This allows us to coalesce many nearby invokes with the same state into one
-  // entry.
-  int LastEHState = -1;
-  MCSymbol *LastEndLabel = Asm->getFunctionBegin();
-  assert(LastEndLabel && "need local function start label");
-
   // Indicate that all calls from the prologue to the first invoke unwind to
   // caller. We handle this as a special case since other ranges starting at end
   // labels need to use LtmpN+1.
-  IPToStateTable.push_back(std::make_pair(create32bitRef(LastEndLabel), -1));
-
-  for (const auto &MBB : *MF) {
-    // FIXME: Do we need to emit entries for funclet base states?
-
-    for (InvokeRange &I : invoke_ranges(FuncInfo, MBB)) {
-      assert(I.BeginLabel && I.EndLabel);
-      // If there was a potentially throwing call between this begin label and
-      // the last end label, we need an extra base state entry to indicate that
-      // those calls unwind directly to the caller.
-      if (I.SawPotentiallyThrowing && LastEHState != -1) {
-        IPToStateTable.push_back(
-            std::make_pair(getLabelPlusOne(LastEndLabel), -1));
-        LastEHState = -1;
-      }
-
-      // Emit an entry indicating that PCs after 'Label' have this EH state.
-      if (I.State != LastEHState)
-        IPToStateTable.push_back(
-            std::make_pair(getLabelPlusOne(I.BeginLabel), I.State));
-      LastEHState = I.State;
-      LastEndLabel = I.EndLabel;
-    }
-  }
-
-  if (LastEndLabel != Asm->getFunctionBegin()) {
-    // Indicate that all calls from the last invoke until the epilogue unwind to
-    // caller. This also ensures that we have at least one ip2state entry, if
-    // somehow all invokes were deleted during CodeGen.
-    IPToStateTable.push_back(std::make_pair(getLabelPlusOne(LastEndLabel), -1));
+  MCSymbol *StartLabel = Asm->getFunctionBegin();
+  assert(StartLabel && "need local function start label");
+  IPToStateTable.push_back(std::make_pair(create32bitRef(StartLabel), -1));
+
+  // FIXME: Do we need to emit entries for funclet base states?
+  for (const auto &StateChange :
+       InvokeStateChangeIterator::range(FuncInfo, *MF)) {
+    // Compute the label to report as the start of this entry; use the EH start
+    // label for the invoke if we have one, otherwise (this is a call which may
+    // unwind to our caller and does not have an EH start label, so) use the
+    // previous end label.
+    const MCSymbol *ChangeLabel = StateChange.NewStartLabel;
+    if (!ChangeLabel)
+      ChangeLabel = StateChange.PreviousEndLabel;
+    // Emit an entry indicating that PCs after 'Label' have this EH state.
+    IPToStateTable.push_back(
+        std::make_pair(getLabelPlusOne(ChangeLabel), StateChange.NewState));
   }
 }
 
index ded00d5..06a5fb4 100644 (file)
@@ -41,8 +41,9 @@ class LLVM_LIBRARY_VISIBILITY WinException : public EHStreamer {
 
   void emitCSpecificHandlerTable(const MachineFunction *MF);
 
-  void emitSEHActionsForRange(WinEHFuncInfo &FuncInfo, MCSymbol *BeginLabel,
-                              MCSymbol *EndLabel, int State);
+  void emitSEHActionsForRange(WinEHFuncInfo &FuncInfo,
+                              const MCSymbol *BeginLabel,
+                              const MCSymbol *EndLabel, int State);
 
   /// Emit the EH table data for 32-bit and 64-bit functions using
   /// the __CxxFrameHandler3 personality.
@@ -64,7 +65,7 @@ class LLVM_LIBRARY_VISIBILITY WinException : public EHStreamer {
 
   const MCExpr *create32bitRef(const MCSymbol *Value);
   const MCExpr *create32bitRef(const Value *V);
-  const MCExpr *getLabelPlusOne(MCSymbol *Label);
+  const MCExpr *getLabelPlusOne(const MCSymbol *Label);
 
   /// 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