Add an option that allows one to "decode" the LSDA.
[oota-llvm.git] / lib / MC / MCAsmStreamer.cpp
index e8b09fcaced82f2386f45c73605b6af2b5dd90d4..01de450d9717eb2719ff22138252c8b56e67e345 100644 (file)
@@ -19,6 +19,7 @@
 #include "llvm/MC/MCSymbol.h"
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MathExtras.h"
@@ -33,8 +34,10 @@ using namespace llvm;
 namespace {
 
 class MCAsmStreamer : public MCStreamer {
+protected:
   formatted_raw_ostream &OS;
   const MCAsmInfo &MAI;
+private:
   OwningPtr<MCInstPrinter> InstPrinter;
   OwningPtr<MCCodeEmitter> Emitter;
   OwningPtr<TargetAsmBackend> AsmBackend;
@@ -1242,12 +1245,371 @@ void MCAsmStreamer::Finish() {
     EmitFrames(false);
 }
 
+//===----------------------------------------------------------------------===//
+/// MCLSDADecoderAsmStreamer - This is identical to the MCAsmStreamer, but
+/// outputs a description of the LSDA in a human readable format.
+///
+namespace {
+
+class MCLSDADecoderAsmStreamer : public MCAsmStreamer {
+  const MCSymbol *PersonalitySymbol;
+  const MCSymbol *LSDASymbol;
+  bool InLSDA;
+  bool ReadingULEB128;
+
+  uint64_t BytesRead;
+  uint64_t ActionTableBytes;
+  uint64_t LSDASize;
+
+  SmallVector<char, 2> ULEB128Value;
+  std::vector<int64_t> LSDAEncoding;
+  std::vector<const MCExpr*> Assignments;
+
+  /// GetULEB128Value - A helper function to convert the value in the
+  /// ULEB128Value vector into a uint64_t.
+  uint64_t GetULEB128Value(SmallVectorImpl<char> &ULEB128Value) {
+    uint64_t Val = 0;
+    for (unsigned i = 0, e = ULEB128Value.size(); i != e; ++i)
+      Val |= (ULEB128Value[i] & 0x7F) << (7 * i);
+    return Val;
+  }
+
+  /// ResetState - Reset the state variables.
+  void ResetState() {
+    PersonalitySymbol = 0;
+    LSDASymbol = 0;
+    LSDASize = 0;
+    BytesRead = 0;
+    ActionTableBytes = 0;
+    InLSDA = false;
+    ReadingULEB128 = false;
+    ULEB128Value.clear();
+    LSDAEncoding.clear();
+    Assignments.clear();
+  }
+
+  void EmitEHTableDescription();
+
+  const char *DecodeDWARFEncoding(unsigned Encoding) {
+    switch (Encoding) {
+    case dwarf::DW_EH_PE_absptr: return "absptr";
+    case dwarf::DW_EH_PE_omit:   return "omit";
+    case dwarf::DW_EH_PE_pcrel:  return "pcrel";
+    case dwarf::DW_EH_PE_udata4: return "udata4";
+    case dwarf::DW_EH_PE_udata8: return "udata8";
+    case dwarf::DW_EH_PE_sdata4: return "sdata4";
+    case dwarf::DW_EH_PE_sdata8: return "sdata8";
+    case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata4: return "pcrel udata4";
+    case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata4: return "pcrel sdata4";
+    case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata8: return "pcrel udata8";
+    case dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata8: return "pcrel sdata8";
+    case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata4:
+      return "indirect pcrel udata4";
+    case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata4:
+      return "indirect pcrel sdata4";
+    case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_udata8:
+      return "indirect pcrel udata8";
+    case dwarf::DW_EH_PE_indirect|dwarf::DW_EH_PE_pcrel|dwarf::DW_EH_PE_sdata8:
+      return "indirect pcrel sdata8";
+    }
+  
+    return "<unknown encoding>";
+  }
+public:
+  MCLSDADecoderAsmStreamer(MCContext &Context, formatted_raw_ostream &os,
+                           bool isVerboseAsm, bool useLoc, bool useCFI,
+                           MCInstPrinter *printer, MCCodeEmitter *emitter,
+                           TargetAsmBackend *asmbackend,
+                           bool showInst)
+    : MCAsmStreamer(Context, os, isVerboseAsm, useLoc, useCFI,
+                    printer, emitter, asmbackend, showInst) {
+    ResetState();
+  }
+  ~MCLSDADecoderAsmStreamer() {}
+
+  virtual void Finish() {
+    ResetState();
+    MCAsmStreamer::Finish();
+  }
+
+  virtual void EmitLabel(MCSymbol *Symbol) {
+    if (Symbol == LSDASymbol)
+      InLSDA = true;
+    MCAsmStreamer::EmitLabel(Symbol);
+  }
+  virtual void EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) {
+    if (InLSDA)
+      Assignments.push_back(Value);
+    MCAsmStreamer::EmitAssignment(Symbol, Value);
+  }
+  virtual void EmitIntValue(uint64_t Value, unsigned Size,
+                            unsigned AddrSpace = 0);
+  virtual void EmitValueImpl(const MCExpr *Value, unsigned Size,
+                             unsigned AddrSpace);
+  virtual void EmitFill(uint64_t NumBytes, uint8_t FillValue,
+                        unsigned AddrSpace);
+  virtual void EmitCFIPersonality(const MCSymbol *Sym, unsigned Encoding) {
+    PersonalitySymbol = Sym;
+    MCAsmStreamer::EmitCFIPersonality(Sym, Encoding);
+  }
+  virtual void EmitCFILsda(const MCSymbol *Sym, unsigned Encoding) {
+    LSDASymbol = Sym;
+    MCAsmStreamer::EmitCFILsda(Sym, Encoding);
+  }
+};
+
+} // end anonymous namespace
+
+void MCLSDADecoderAsmStreamer::EmitIntValue(uint64_t Value, unsigned Size,
+                                            unsigned AddrSpace) {
+  if (!InLSDA)
+    return MCAsmStreamer::EmitIntValue(Value, Size, AddrSpace);
+
+  BytesRead += Size;
+
+  // We place the LSDA into the LSDAEncoding vector for later analysis. If we
+  // have a ULEB128, we read that in separate iterations through here and then
+  // get its value.
+  if (!ReadingULEB128) {
+    LSDAEncoding.push_back(Value);
+    int EncodingSize = LSDAEncoding.size();
+
+    if (EncodingSize == 1 || EncodingSize == 3) {
+      // The LPStart and TType encodings.
+      if (Value != dwarf::DW_EH_PE_omit) {
+        // The encoding is next and is a ULEB128 value.
+        ReadingULEB128 = true;
+        ULEB128Value.clear();
+      } else {
+        // The encoding was omitted. Put a 0 here as a placeholder.
+        LSDAEncoding.push_back(0);
+      }
+    } else if (EncodingSize == 5) {
+      // The next value is a ULEB128 value that tells us how long the call site
+      // table is -- where the start of the action tab
+      ReadingULEB128 = true;
+      ULEB128Value.clear();
+    }
+
+    InLSDA = (LSDASize == 0 || BytesRead < LSDASize);
+  } else {
+    // We're reading a ULEB128. Make it so!
+    assert(Size == 1 && "Non-byte representation of a ULEB128?");
+    ULEB128Value.push_back(Value);
+
+    if ((Value & 0x80) == 0) {
+      uint64_t Val = GetULEB128Value(ULEB128Value);
+      LSDAEncoding.push_back(Val);
+      ULEB128Value.clear();
+      ReadingULEB128 = false;
+
+      if (LSDAEncoding.size() == 4)
+        // The fourth value tells us where the bottom of the type table is.
+        LSDASize = BytesRead + LSDAEncoding[3];
+      else if (LSDAEncoding.size() == 6)
+        // The sixth value tells us where the start of the action table is.
+        ActionTableBytes = BytesRead;
+    }
+  }
+
+  MCAsmStreamer::EmitValueImpl(MCConstantExpr::Create(Value, getContext()),
+                               Size, AddrSpace);
+
+  if (LSDASize != 0 && !InLSDA)
+    EmitEHTableDescription();
+}
+
+void MCLSDADecoderAsmStreamer::EmitValueImpl(const MCExpr *Value,
+                                             unsigned Size,
+                                             unsigned AddrSpace) {
+  if (InLSDA && LSDASize != 0) {
+    assert(BytesRead + Size <= LSDASize  && "EH table too small!");
+
+    if (BytesRead > uint64_t(LSDAEncoding[5]) + ActionTableBytes)
+      // Insert the type values.
+      Assignments.push_back(Value);
+
+    LSDAEncoding.push_back(Assignments.size());
+    BytesRead += Size;
+    InLSDA = (LSDASize == 0 || BytesRead < LSDASize);
+  }
+
+  MCAsmStreamer::EmitValueImpl(Value, Size, AddrSpace);
+
+  if (LSDASize != 0 && !InLSDA)
+    EmitEHTableDescription();
+}
+
+void MCLSDADecoderAsmStreamer::EmitFill(uint64_t NumBytes, uint8_t FillValue,
+                                        unsigned AddrSpace) {
+  if (InLSDA && ReadingULEB128) {
+    for (uint64_t I = NumBytes; I != 0; --I)
+      ULEB128Value.push_back(FillValue);
+
+    BytesRead += NumBytes;
+
+    if ((FillValue & 0x80) == 0) {
+      uint64_t Val = GetULEB128Value(ULEB128Value);
+      LSDAEncoding.push_back(Val);
+      ULEB128Value.clear();
+      ReadingULEB128 = false;
+
+      if (LSDAEncoding.size() == 4)
+        // The fourth value tells us where the bottom of the type table is.
+        LSDASize = BytesRead + LSDAEncoding[3];
+      else if (LSDAEncoding.size() == 6)
+        // The sixth value tells us where the start of the action table is.
+        ActionTableBytes = BytesRead;
+    }
+  }
+
+  MCAsmStreamer::EmitFill(NumBytes, FillValue, AddrSpace);
+}
+
+/// EmitEHTableDescription - Emit a human readable version of the LSDA.
+void MCLSDADecoderAsmStreamer::EmitEHTableDescription() {
+  assert(LSDAEncoding.size() > 6 && "Invalid LSDA!");
+
+  // Emit header information.
+  StringRef C = MAI.getCommentString();
+#define CMT OS << C << ' '
+  CMT << "Exception Handling Table: " << LSDASymbol->getName() << "\n";
+  CMT << " @LPStart Encoding: " << DecodeDWARFEncoding(LSDAEncoding[0]) << "\n";
+  if (LSDAEncoding[1])
+    CMT << "@LPStart: 0x" << LSDAEncoding[1] << "\n";
+  CMT << "   @TType Encoding: " << DecodeDWARFEncoding(LSDAEncoding[2]) << "\n";
+  CMT << "       @TType Base: " << LSDAEncoding[3] << " bytes\n";
+  CMT << "@CallSite Encoding: " << DecodeDWARFEncoding(LSDAEncoding[4]) << "\n";
+  CMT << "@Action Table Size: " << LSDAEncoding[5] << " bytes\n\n";
+
+  bool isSjLjEH = (MAI.getExceptionHandlingType() == ExceptionHandling::ARM);
+
+  int64_t CallSiteTableSize = LSDAEncoding[5];
+  unsigned CallSiteEntrySize;
+  if (!isSjLjEH)
+    CallSiteEntrySize = 4 + // Region start.
+                        4 + // Region end.
+                        4 + // Landing pad.
+                        1;  // TType index.
+  else
+    CallSiteEntrySize = 1 + // Call index.
+                        1 + // Landing pad.
+                        1;  // TType index.
+
+  unsigned NumEntries = CallSiteTableSize / CallSiteEntrySize;
+  assert(CallSiteTableSize % CallSiteEntrySize == 0 &&
+         "The action table size is not a multiple of what it should be!");
+  unsigned TTypeIdx = 5 +              // Action table size index.
+     (isSjLjEH ? 3 : 4) * NumEntries + // Action table entries.
+                      1;               // Just because.
+
+  // Emit the action table.
+  unsigned Action = 1;
+  for (unsigned I = 6; I < TTypeIdx; ) {
+    CMT << "Action " << Action++ << ":\n";
+
+    // The beginning of the throwing region.
+    uint64_t Idx = LSDAEncoding[I++];
+
+    if (!isSjLjEH) {
+      CMT << "  A throw between "
+          << *cast<MCBinaryExpr>(Assignments[Idx - 1])->getLHS() << " and ";
+
+      // The end of the throwing region.
+      Idx = LSDAEncoding[I++];
+      OS << *cast<MCBinaryExpr>(Assignments[Idx - 1])->getLHS();
+    } else {
+      CMT << "  A throw from call " << *Assignments[Idx - 1];
+    }
+
+    // The landing pad.
+    Idx = LSDAEncoding[I++];
+    if (Idx) {
+      OS << " jumps to "
+         << *cast<MCBinaryExpr>(Assignments[Idx - 1])->getLHS()
+         << " on an exception.\n";
+    } else {
+      OS << " does not have a landing pad.\n";
+      ++I;
+      continue;
+    }
+
+    // The index into the action table.
+    Idx = LSDAEncoding[I++];
+    if (!Idx) {
+      CMT << "    :cleanup:\n";
+      continue;
+    }
+
+    // A semi-graphical representation of what the different indexes are in the
+    // loop below.
+    //
+    //    Idx    - Index into the action table.
+    //    Action - Index into the type table from the type table base.
+    //    Next   - Offset from Idx to the next action type.
+    //
+    //                               Idx---.
+    //                                     |
+    //                                     v
+    //            [call site table] _1 _2 _3
+    // TTypeIdx--> .........................
+    //            [action 1] _1 _2
+    //            [action 2] _1 _2
+    //              ...
+    //            [action n] _1 _2
+    //            [type m]      ^
+    //              ...         |
+    //            [type 1]      `---Next
+    //
+
+    int Action = LSDAEncoding[TTypeIdx + Idx - 1];
+    if ((Action & 0x40) != 0)
+      // Ignore exception specifications.
+      continue;
+
+    // Emit the types that are caught by this exception.
+    CMT << "    For type(s): ";
+    for (;;) {
+      if ((Action & 0x40) != 0)
+        // Ignore exception specifications.
+        break;
+
+      if (uint64_t Ty = LSDAEncoding[LSDAEncoding.size() - Action]) {
+        OS << " " << *Assignments[Ty - 1];
+
+        // Types can be chained together. Typically, it's a negative offset from
+        // the current type to a different one in the type table.
+        int Next = LSDAEncoding[TTypeIdx + Idx];
+        if (Next == 0)
+          break;
+        if ((Next & 0x40) != 0)
+          Next = (int)(signed char)(Next | 0x80);
+        Idx += Next + 1;
+        Action = LSDAEncoding[TTypeIdx + Idx - 1];
+        continue;
+      } else {
+        OS << " :catchall:";
+      }
+      break;
+    }
+
+    OS << "\n";
+  }
+
+  OS << "\n";
+  ResetState();
+}
+
 MCStreamer *llvm::createAsmStreamer(MCContext &Context,
                                     formatted_raw_ostream &OS,
                                     bool isVerboseAsm, bool useLoc,
-                                    bool useCFI,
-                                    MCInstPrinter *IP, MCCodeEmitter *CE,
-                                    TargetAsmBackend *TAB, bool ShowInst) {
+                                    bool useCFI, MCInstPrinter *IP,
+                                    MCCodeEmitter *CE, TargetAsmBackend *TAB,
+                                    bool ShowInst, bool DecodeLSDA) {
+  if (DecodeLSDA)
+    return new MCLSDADecoderAsmStreamer(Context, OS, isVerboseAsm, useLoc,
+                                        useCFI, IP, CE, TAB, ShowInst);
+
   return new MCAsmStreamer(Context, OS, isVerboseAsm, useLoc, useCFI,
                            IP, CE, TAB, ShowInst);
 }