Fix ARM unwind opcode assembler in several cases.
authorLogan Chien <tzuhsiang.chien@gmail.com>
Sun, 9 Jun 2013 12:22:30 +0000 (12:22 +0000)
committerLogan Chien <tzuhsiang.chien@gmail.com>
Sun, 9 Jun 2013 12:22:30 +0000 (12:22 +0000)
Changes to ARM unwind opcode assembler:

* Fix multiple .save or .vsave directives.  Besides, the
  order is preserved now.

* For the directives which will generate multiple opcodes,
  such as ".save {r0-r11}", the order of the unwind opcode
  is fixed now, i.e. the registers with less encoding value
  are popped first.

* Fix the $sp offset calculation.  Now, we can use the
  .setfp, .pad, .save, and .vsave directives at any order.

Changes to test cases:

* Add test cases to check the order of multiple opcodes
  for the .save directive.

* Fix the incorrect $sp offset in the test case.  The
  stack pointer offset specified in the test case was
  incorrect.  (Changed test cases: ehabi-mc-section.ll and
  ehabi-mc.ll)

* The opcode to restore $sp are slightly reordered.  The
  behavior are not changed, and the new output is same
  as the output of GNU as.  (Changed test cases:
  eh-directive-pad.s and eh-directive-setfp.s)

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

lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.cpp
lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h
test/CodeGen/ARM/ehabi-mc-section.ll
test/CodeGen/ARM/ehabi-mc.ll
test/MC/ARM/eh-directive-integrated-test.s [new file with mode: 0644]
test/MC/ARM/eh-directive-multiple-offsets.s [new file with mode: 0644]
test/MC/ARM/eh-directive-pad.s
test/MC/ARM/eh-directive-save.s
test/MC/ARM/eh-directive-setfp.s

index 82d296ffd7ed7b5bff87ebbfba2eb2161687c98d..d83567c8854989aca4d13fb215068c7982257584 100644 (file)
@@ -203,7 +203,7 @@ private:
   void Reset();
 
   void EmitPersonalityFixup(StringRef Name);
-  void CollectUnwindOpcodes();
+  void FlushPendingOffset();
   void FlushUnwindOpcodes(bool AllowCompactModel0);
 
   void SwitchToEHSection(const char *Prefix, unsigned Type, unsigned Flags,
@@ -221,13 +221,14 @@ private:
   MCSymbol *ExTab;
   MCSymbol *FnStart;
   const MCSymbol *Personality;
-  uint32_t VFPRegSave; // Register mask for {d31-d0}
-  uint32_t RegSave; // Register mask for {r15-r0}
-  int64_t SPOffset;
-  uint16_t FPReg;
-  int64_t FPOffset;
+  unsigned PersonalityIndex;
+  unsigned FPReg; // Frame pointer register
+  int64_t FPOffset; // Offset: (final frame pointer) - (initial $sp)
+  int64_t SPOffset; // Offset: (final $sp) - (initial $sp)
+  int64_t PendingOffset; // Offset: (final $sp) - (emitted $sp)
   bool UsedFP;
   bool CantUnwind;
+  SmallVector<uint8_t, 64> Opcodes;
   UnwindOpcodeAssembler UnwindOpAsm;
 };
 } // end anonymous namespace
@@ -280,19 +281,18 @@ inline void ARMELFStreamer::SwitchToExIdxSection(const MCSymbol &FnStart) {
 }
 
 void ARMELFStreamer::Reset() {
-  const MCRegisterInfo &MRI = getContext().getRegisterInfo();
-
   ExTab = NULL;
   FnStart = NULL;
   Personality = NULL;
-  VFPRegSave = 0;
-  RegSave = 0;
-  FPReg = MRI.getEncodingValue(ARM::SP);
+  PersonalityIndex = NUM_PERSONALITY_INDEX;
+  FPReg = ARM::SP;
   FPOffset = 0;
   SPOffset = 0;
+  PendingOffset = 0;
   UsedFP = false;
   CantUnwind = false;
 
+  Opcodes.clear();
   UnwindOpAsm.Reset();
 }
 
@@ -312,18 +312,6 @@ void ARMELFStreamer::EmitPersonalityFixup(StringRef Name) {
                     MCFixup::getKindForSize(4, false)));
 }
 
-void ARMELFStreamer::CollectUnwindOpcodes() {
-  if (UsedFP) {
-    UnwindOpAsm.EmitSetFP(FPReg);
-    UnwindOpAsm.EmitSPOffset(-FPOffset);
-  } else {
-    UnwindOpAsm.EmitSPOffset(SPOffset);
-  }
-  UnwindOpAsm.EmitVFPRegSave(VFPRegSave);
-  UnwindOpAsm.EmitRegSave(RegSave);
-  UnwindOpAsm.Finalize();
-}
-
 void ARMELFStreamer::EmitFnStart() {
   assert(FnStart == 0);
   FnStart = getContext().CreateTempSymbol();
@@ -340,7 +328,6 @@ void ARMELFStreamer::EmitFnEnd() {
   // Emit the exception index table entry
   SwitchToExIdxSection(*FnStart);
 
-  unsigned PersonalityIndex = UnwindOpAsm.getPersonalityIndex();
   if (PersonalityIndex < NUM_PERSONALITY_INDEX)
     EmitPersonalityFixup(GetAEABIUnwindPersonalityName(PersonalityIndex));
 
@@ -366,9 +353,10 @@ void ARMELFStreamer::EmitFnEnd() {
     // opcodes should always be 4 bytes.
     assert(PersonalityIndex == AEABI_UNWIND_CPP_PR0 &&
            "Compact model must use __aeabi_cpp_unwind_pr0 as personality");
-    assert(UnwindOpAsm.size() == 4u &&
+    assert(Opcodes.size() == 4u &&
            "Unwind opcode size for __aeabi_cpp_unwind_pr0 must be equal to 4");
-    EmitBytes(UnwindOpAsm.data(), 0);
+    EmitBytes(StringRef(reinterpret_cast<const char*>(Opcodes.data()),
+                        Opcodes.size()), 0);
   }
 
   // Switch to the section containing FnStart
@@ -382,15 +370,31 @@ void ARMELFStreamer::EmitCantUnwind() {
   CantUnwind = true;
 }
 
+void ARMELFStreamer::FlushPendingOffset() {
+  if (PendingOffset != 0) {
+    UnwindOpAsm.EmitSPOffset(-PendingOffset);
+    PendingOffset = 0;
+  }
+}
+
 void ARMELFStreamer::FlushUnwindOpcodes(bool AllowCompactModel0) {
-  // Collect and finalize the unwind opcodes
-  CollectUnwindOpcodes();
+  // Emit the unwind opcode to restore $sp.
+  if (UsedFP) {
+    const MCRegisterInfo &MRI = getContext().getRegisterInfo();
+    int64_t LastRegSaveSPOffset = SPOffset - PendingOffset;
+    UnwindOpAsm.EmitSPOffset(LastRegSaveSPOffset - FPOffset);
+    UnwindOpAsm.EmitSetSP(MRI.getEncodingValue(FPReg));
+  } else {
+    FlushPendingOffset();
+  }
+
+  // Finalize the unwind opcode sequence
+  UnwindOpAsm.Finalize(PersonalityIndex, Opcodes);
 
   // For compact model 0, we have to emit the unwind opcodes in the .ARM.exidx
   // section.  Thus, we don't have to create an entry in the .ARM.extab
   // section.
-  if (AllowCompactModel0 &&
-      UnwindOpAsm.getPersonalityIndex() == AEABI_UNWIND_CPP_PR0)
+  if (AllowCompactModel0 && PersonalityIndex == AEABI_UNWIND_CPP_PR0)
     return;
 
   // Switch to .ARM.extab section.
@@ -412,7 +416,8 @@ void ARMELFStreamer::FlushUnwindOpcodes(bool AllowCompactModel0) {
   }
 
   // Emit unwind opcodes
-  EmitBytes(UnwindOpAsm.data(), 0);
+  EmitBytes(StringRef(reinterpret_cast<const char *>(Opcodes.data()),
+                      Opcodes.size()), 0);
 }
 
 void ARMELFStreamer::EmitHandlerData() {
@@ -427,42 +432,55 @@ void ARMELFStreamer::EmitPersonality(const MCSymbol *Per) {
 void ARMELFStreamer::EmitSetFP(unsigned NewFPReg,
                                unsigned NewSPReg,
                                int64_t Offset) {
-  assert(SPOffset == 0 &&
-         "Current implementation assumes .setfp precedes .pad");
-
-  const MCRegisterInfo &MRI = getContext().getRegisterInfo();
-
-  uint16_t NewFPRegEncVal = MRI.getEncodingValue(NewFPReg);
-#ifndef NDEBUG
-  uint16_t NewSPRegEncVal = MRI.getEncodingValue(NewSPReg);
-#endif
-
-  assert((NewSPReg == ARM::SP || NewSPRegEncVal == FPReg) &&
+  assert((NewSPReg == ARM::SP || NewSPReg == FPReg) &&
          "the operand of .setfp directive should be either $sp or $fp");
 
   UsedFP = true;
-  FPReg = NewFPRegEncVal;
-  FPOffset = Offset;
+  FPReg = NewFPReg;
+
+  if (NewSPReg == ARM::SP)
+    FPOffset = SPOffset + Offset;
+  else
+    FPOffset += Offset;
 }
 
 void ARMELFStreamer::EmitPad(int64_t Offset) {
-  SPOffset += Offset;
+  // Track the change of the $sp offset
+  SPOffset -= Offset;
+
+  // To squash multiple .pad directives, we should delay the unwind opcode
+  // until the .save, .vsave, .handlerdata, or .fnend directives.
+  PendingOffset -= Offset;
 }
 
 void ARMELFStreamer::EmitRegSave(const SmallVectorImpl<unsigned> &RegList,
                                  bool IsVector) {
+  // Collect the registers in the register list
+  unsigned Count = 0;
+  uint32_t Mask = 0;
   const MCRegisterInfo &MRI = getContext().getRegisterInfo();
-
-#ifndef NDEBUG
-  unsigned Max = IsVector ? 32 : 16;
-#endif
-  uint32_t &RegMask = IsVector ? VFPRegSave : RegSave;
-
   for (size_t i = 0; i < RegList.size(); ++i) {
     unsigned Reg = MRI.getEncodingValue(RegList[i]);
-    assert(Reg < Max && "Register encoded value out of range");
-    RegMask |= 1u << Reg;
+    assert(Reg < (IsVector ? 32 : 16) && "Register out of range");
+    unsigned Bit = (1u << Reg);
+    if ((Mask & Bit) == 0) {
+      Mask |= Bit;
+      ++Count;
+    }
   }
+
+  // Track the change the $sp offset: For the .save directive, the
+  // corresponding push instruction will decrease the $sp by (4 * Count).
+  // For the .vsave directive, the corresponding vpush instruction will
+  // decrease $sp by (8 * Count).
+  SPOffset -= Count * (IsVector ? 8 : 4);
+
+  // Emit the opcode
+  FlushPendingOffset();
+  if (IsVector)
+    UnwindOpAsm.EmitVFPRegSave(Mask);
+  else
+    UnwindOpAsm.EmitRegSave(Mask);
 }
 
 namespace llvm {
index 191db69fbc2b59a519be08b55dc8aeb5e42d5d66..c943370818848bb8a69ce0af7927cc75b9f78068 100644 (file)
 
 using namespace llvm;
 
+namespace {
+  /// UnwindOpcodeStreamer - The simple wrapper over SmallVector to emit bytes
+  /// with MSB to LSB per uint32_t ordering.  For example, the first byte will
+  /// be placed in Vec[3], and the following bytes will be placed in 2, 1, 0,
+  /// 7, 6, 5, 4, 11, 10, 9, 8, and so on.
+  class UnwindOpcodeStreamer {
+  private:
+    SmallVectorImpl<uint8_t> &Vec;
+    size_t Pos;
+
+  public:
+    UnwindOpcodeStreamer(SmallVectorImpl<uint8_t> &V) : Vec(V), Pos(3) {
+    }
+
+    /// Emit the byte in MSB to LSB per uint32_t order.
+    inline void EmitByte(uint8_t elem) {
+      Vec[Pos] = elem;
+      Pos = (((Pos ^ 0x3u) + 1) ^ 0x3u);
+    }
+
+    /// Emit the size prefix.
+    inline void EmitSize(size_t Size) {
+      size_t SizeInWords = (Size + 3) / 4;
+      assert(SizeInWords <= 0x100u &&
+             "Only 256 additional words are allowed for unwind opcodes");
+      EmitByte(static_cast<uint8_t>(SizeInWords - 1));
+    }
+
+    /// Emit the personality index prefix.
+    inline void EmitPersonalityIndex(unsigned PI) {
+      assert(PI < NUM_PERSONALITY_INDEX && "Invalid personality prefix");
+      EmitByte(EHT_COMPACT | PI);
+    }
+
+    /// Fill the rest of bytes with FINISH opcode.
+    inline void FillFinishOpcode() {
+      while (Pos < Vec.size())
+        EmitByte(UNWIND_OPCODE_FINISH);
+    }
+  };
+}
+
 void UnwindOpcodeAssembler::EmitRegSave(uint32_t RegSave) {
   if (RegSave == 0u)
     return;
@@ -43,28 +85,22 @@ void UnwindOpcodeAssembler::EmitRegSave(uint32_t RegSave) {
     uint32_t UnmaskedReg = RegSave & 0xfff0u & (~Mask);
     if (UnmaskedReg == 0u) {
       // Pop r[4 : (4 + n)]
-      Ops.push_back(UNWIND_OPCODE_POP_REG_RANGE_R4 | Range);
+      EmitInt8(UNWIND_OPCODE_POP_REG_RANGE_R4 | Range);
       RegSave &= 0x000fu;
     } else if (UnmaskedReg == (1u << 14)) {
       // Pop r[14] + r[4 : (4 + n)]
-      Ops.push_back(UNWIND_OPCODE_POP_REG_RANGE_R4_R14 | Range);
+      EmitInt8(UNWIND_OPCODE_POP_REG_RANGE_R4_R14 | Range);
       RegSave &= 0x000fu;
     }
   }
 
   // Two bytes opcode to save register r15-r4
-  if ((RegSave & 0xfff0u) != 0) {
-    uint32_t Op = UNWIND_OPCODE_POP_REG_MASK_R4 | (RegSave >> 4);
-    Ops.push_back(static_cast<uint8_t>(Op >> 8));
-    Ops.push_back(static_cast<uint8_t>(Op & 0xff));
-  }
+  if ((RegSave & 0xfff0u) != 0)
+    EmitInt16(UNWIND_OPCODE_POP_REG_MASK_R4 | (RegSave >> 4));
 
   // Opcode to save register r3-r0
-  if ((RegSave & 0x000fu) != 0) {
-    uint32_t Op = UNWIND_OPCODE_POP_REG_MASK | (RegSave & 0x000fu);
-    Ops.push_back(static_cast<uint8_t>(Op >> 8));
-    Ops.push_back(static_cast<uint8_t>(Op & 0xff));
-  }
+  if ((RegSave & 0x000fu) != 0)
+    EmitInt16(UNWIND_OPCODE_POP_REG_MASK | (RegSave & 0x000fu));
 }
 
 /// Emit unwind opcodes for .vsave directives
@@ -89,10 +125,8 @@ void UnwindOpcodeAssembler::EmitVFPRegSave(uint32_t VFPRegSave) {
       Bit >>= 1;
     }
 
-    uint32_t Op =
-        UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD_D16 | ((i - 16) << 4) | Range;
-    Ops.push_back(static_cast<uint8_t>(Op >> 8));
-    Ops.push_back(static_cast<uint8_t>(Op & 0xff));
+    EmitInt16(UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD_D16 |
+              ((i - 16) << 4) | Range);
   }
 
   while (i > 0) {
@@ -113,86 +147,75 @@ void UnwindOpcodeAssembler::EmitVFPRegSave(uint32_t VFPRegSave) {
       Bit >>= 1;
     }
 
-    uint32_t Op = UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD | (i << 4) | Range;
-    Ops.push_back(static_cast<uint8_t>(Op >> 8));
-    Ops.push_back(static_cast<uint8_t>(Op & 0xff));
+    EmitInt16(UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD | (i << 4) | Range);
   }
 }
 
-/// Emit unwind opcodes for .setfp directives
-void UnwindOpcodeAssembler::EmitSetFP(uint16_t FPReg) {
-  Ops.push_back(UNWIND_OPCODE_SET_VSP | FPReg);
+/// Emit unwind opcodes to copy address from source register to $sp.
+void UnwindOpcodeAssembler::EmitSetSP(uint16_t Reg) {
+  EmitInt8(UNWIND_OPCODE_SET_VSP | Reg);
 }
 
-/// Emit unwind opcodes to update stack pointer
+/// Emit unwind opcodes to add $sp with an offset.
 void UnwindOpcodeAssembler::EmitSPOffset(int64_t Offset) {
   if (Offset > 0x200) {
-    uint8_t Buff[10];
-    size_t Size = encodeULEB128((Offset - 0x204) >> 2, Buff);
-    Ops.push_back(UNWIND_OPCODE_INC_VSP_ULEB128);
-    Ops.append(Buff, Buff + Size);
+    uint8_t Buff[16];
+    Buff[0] = UNWIND_OPCODE_INC_VSP_ULEB128;
+    size_t ULEBSize = encodeULEB128((Offset - 0x204) >> 2, Buff + 1);
+    EmitBytes(Buff, ULEBSize + 1);
   } else if (Offset > 0) {
     if (Offset > 0x100) {
-      Ops.push_back(UNWIND_OPCODE_INC_VSP | 0x3fu);
+      EmitInt8(UNWIND_OPCODE_INC_VSP | 0x3fu);
       Offset -= 0x100;
     }
-    Ops.push_back(UNWIND_OPCODE_INC_VSP |
-                  static_cast<uint8_t>((Offset - 4) >> 2));
+    EmitInt8(UNWIND_OPCODE_INC_VSP | static_cast<uint8_t>((Offset - 4) >> 2));
   } else if (Offset < 0) {
     while (Offset < -0x100) {
-      Ops.push_back(UNWIND_OPCODE_DEC_VSP | 0x3fu);
+      EmitInt8(UNWIND_OPCODE_DEC_VSP | 0x3fu);
       Offset += 0x100;
     }
-    Ops.push_back(UNWIND_OPCODE_DEC_VSP |
-                  static_cast<uint8_t>(((-Offset) - 4) >> 2));
+    EmitInt8(UNWIND_OPCODE_DEC_VSP |
+             static_cast<uint8_t>(((-Offset) - 4) >> 2));
   }
 }
 
-void UnwindOpcodeAssembler::AddOpcodeSizePrefix(size_t Pos) {
-  size_t SizeInWords = (size() + 3) / 4;
-  assert(SizeInWords <= 0x100u &&
-         "Only 256 additional words are allowed for unwind opcodes");
-  Ops[Pos] = static_cast<uint8_t>(SizeInWords - 1);
-}
+void UnwindOpcodeAssembler::Finalize(unsigned &PersonalityIndex,
+                                     SmallVectorImpl<uint8_t> &Result) {
 
-void UnwindOpcodeAssembler::AddPersonalityIndexPrefix(size_t Pos, unsigned PI) {
-  assert(PI < NUM_PERSONALITY_INDEX && "Invalid personality prefix");
-  Ops[Pos] = EHT_COMPACT | PI;
-}
+  UnwindOpcodeStreamer OpStreamer(Result);
 
-void UnwindOpcodeAssembler::EmitFinishOpcodes() {
-  for (size_t i = (0x4u - (size() & 0x3u)) & 0x3u; i > 0; --i)
-    Ops.push_back(UNWIND_OPCODE_FINISH);
-}
-
-void UnwindOpcodeAssembler::Finalize() {
   if (HasPersonality) {
-    // Personality specified by .personality directive
-    Offset = 1;
-    AddOpcodeSizePrefix(1);
+    // User-specifed personality routine: [ SIZE , OP1 , OP2 , ... ]
+    PersonalityIndex = NUM_PERSONALITY_INDEX;
+    size_t TotalSize = Ops.size() + 1;
+    size_t RoundUpSize = (TotalSize + 3) / 4 * 4;
+    Result.resize(RoundUpSize);
+    OpStreamer.EmitSize(RoundUpSize);
   } else {
-    if (getOpcodeSize() <= 3) {
+    if (Ops.size() <= 3) {
       // __aeabi_unwind_cpp_pr0: [ 0x80 , OP1 , OP2 , OP3 ]
-      Offset = 1;
       PersonalityIndex = AEABI_UNWIND_CPP_PR0;
-      AddPersonalityIndexPrefix(Offset, PersonalityIndex);
+      Result.resize(4);
+      OpStreamer.EmitPersonalityIndex(PersonalityIndex);
     } else {
       // __aeabi_unwind_cpp_pr1: [ 0x81 , SIZE , OP1 , OP2 , ... ]
-      Offset = 0;
       PersonalityIndex = AEABI_UNWIND_CPP_PR1;
-      AddPersonalityIndexPrefix(Offset, PersonalityIndex);
-      AddOpcodeSizePrefix(1);
+      size_t TotalSize = Ops.size() + 2;
+      size_t RoundUpSize = (TotalSize + 3) / 4 * 4;
+      Result.resize(RoundUpSize);
+      OpStreamer.EmitPersonalityIndex(PersonalityIndex);
+      OpStreamer.EmitSize(RoundUpSize);
     }
   }
 
-  // Emit the padding finish opcodes if the size() is not multiple of 4.
-  EmitFinishOpcodes();
+  // Copy the unwind opcodes
+  for (size_t i = OpBegins.size() - 1; i > 0; --i)
+    for (size_t j = OpBegins[i - 1], end = OpBegins[i]; j < end; ++j)
+      OpStreamer.EmitByte(Ops[j]);
 
-  // Swap the byte order
-  uint8_t *Ptr = Ops.begin() + Offset;
-  assert(size() % 4 == 0 && "Final unwind opcodes should align to 4");
-  for (size_t i = 0, n = size(); i < n; i += 4) {
-    std::swap(Ptr[i], Ptr[i + 3]);
-    std::swap(Ptr[i + 1], Ptr[i + 2]);
-  }
+  // Emit the padding finish opcodes if the size is not multiple of 4.
+  OpStreamer.FillFinishOpcode();
+
+  // Reset the assembler state
+  Reset();
 }
index f6ecaeb8b29f4a51846c1d73dc1b6d623c751900..ac67c6efabb7a5d635d97620bce15210ddebc32d 100644 (file)
@@ -27,86 +27,61 @@ class MCSymbol;
 
 class UnwindOpcodeAssembler {
 private:
-  llvm::SmallVector<uint8_t, 8> Ops;
-
-  unsigned Offset;
-  unsigned PersonalityIndex;
+  llvm::SmallVector<uint8_t, 32> Ops;
+  llvm::SmallVector<unsigned, 8> OpBegins;
   bool HasPersonality;
 
-  enum {
-    // The number of bytes to be preserved for the size and personality index
-    // prefix of unwind opcodes.
-    NUM_PRESERVED_PREFIX_BUF = 2
-  };
-
 public:
   UnwindOpcodeAssembler()
-      : Ops(NUM_PRESERVED_PREFIX_BUF), Offset(NUM_PRESERVED_PREFIX_BUF),
-        PersonalityIndex(NUM_PERSONALITY_INDEX), HasPersonality(0) {
+      : HasPersonality(0) {
+    OpBegins.push_back(0);
   }
 
   /// Reset the unwind opcode assembler.
   void Reset() {
-    Ops.resize(NUM_PRESERVED_PREFIX_BUF);
-    Offset = NUM_PRESERVED_PREFIX_BUF;
-    PersonalityIndex = NUM_PERSONALITY_INDEX;
+    Ops.clear();
+    OpBegins.clear();
+    OpBegins.push_back(0);
     HasPersonality = 0;
   }
 
-  /// Get the size of the payload (including the size byte)
-  size_t size() const {
-    return Ops.size() - Offset;
-  }
-
-  /// Get the beginning of the payload
-  const uint8_t *begin() const {
-    return Ops.begin() + Offset;
-  }
-
-  /// Get the payload
-  StringRef data() const {
-    return StringRef(reinterpret_cast<const char *>(begin()), size());
-  }
-
   /// Set the personality index
   void setPersonality(const MCSymbol *Per) {
     HasPersonality = 1;
   }
 
-  /// Get the personality index
-  unsigned getPersonalityIndex() const {
-    return PersonalityIndex;
-  }
-
   /// Emit unwind opcodes for .save directives
   void EmitRegSave(uint32_t RegSave);
 
   /// Emit unwind opcodes for .vsave directives
   void EmitVFPRegSave(uint32_t VFPRegSave);
 
-  /// Emit unwind opcodes for .setfp directives
-  void EmitSetFP(uint16_t FPReg);
+  /// Emit unwind opcodes to copy address from source register to $sp.
+  void EmitSetSP(uint16_t Reg);
 
-  /// Emit unwind opcodes to update stack pointer
+  /// Emit unwind opcodes to add $sp with an offset.
   void EmitSPOffset(int64_t Offset);
 
   /// Finalize the unwind opcode sequence for EmitBytes()
-  void Finalize();
+  void Finalize(unsigned &PersonalityIndex,
+                SmallVectorImpl<uint8_t> &Result);
 
 private:
-  /// Get the size of the opcodes in bytes.
-  size_t getOpcodeSize() const {
-    return Ops.size() - NUM_PRESERVED_PREFIX_BUF;
+  void EmitInt8(unsigned Opcode) {
+    Ops.push_back(Opcode & 0xff);
+    OpBegins.push_back(OpBegins.back() + 1);
   }
 
-  /// Add the length prefix to the payload
-  void AddOpcodeSizePrefix(size_t Pos);
-
-  /// Add personality index prefix in some compact format
-  void AddPersonalityIndexPrefix(size_t Pos, unsigned PersonalityIndex);
+  void EmitInt16(unsigned Opcode) {
+    Ops.push_back((Opcode >> 8) & 0xff);
+    Ops.push_back(Opcode & 0xff);
+    OpBegins.push_back(OpBegins.back() + 2);
+  }
 
-  /// Fill the words with finish opcode if it is not aligned
-  void EmitFinishOpcodes();
+  void EmitBytes(const uint8_t *Opcode, size_t Size) {
+    Ops.insert(Ops.end(), Opcode, Opcode + Size);
+    OpBegins.push_back(OpBegins.back() + Size);
+  }
 };
 
 } // namespace llvm
index 4e6e468291485e08ba66ff2092d15b48fd05da7d..51ae25a3be427cae4bde8c67071394f835857da3 100644 (file)
@@ -60,7 +60,7 @@ declare void @_ZSt9terminatev()
 
 ; CHECK: section .test_section
 ; CHECK: section .ARM.extab.test_section
-; CHECK-NEXT: 0000 00000000 c9409b01 b0818484
+; CHECK-NEXT: 0000 00000000 c94a9b01 b0818484
 ; CHECK: section .ARM.exidx.test_section
 ; CHECK-NEXT: 0000 00000000 00000000
 
index 83b8425af7c49fce776f27318119952827796064..b0fc81e5f91faf43ad06f9f695cf98df79386899 100644 (file)
@@ -60,7 +60,7 @@ declare void @_ZSt9terminatev()
 
 ; CHECK: section .text
 ; CHECK: section .ARM.extab
-; CHECK-NEXT: 0000 00000000 c9409b01 b0818484
+; CHECK-NEXT: 0000 00000000 c94a9b01 b0818484
 ; CHECK: section .ARM.exidx
 ; CHECK-NEXT: 0000 00000000 00000000
 
diff --git a/test/MC/ARM/eh-directive-integrated-test.s b/test/MC/ARM/eh-directive-integrated-test.s
new file mode 100644 (file)
index 0000000..df2b290
--- /dev/null
@@ -0,0 +1,93 @@
+@ Integrated test for ARM unwind directive parser and assembler.
+
+@ This is a simplified real world test case generated from this C++ code
+@ (with and without -fomit-frame-pointer)
+@
+@   extern void print(int, int, int, int, int);
+@   extern void print(double, double, double, double, double);
+@
+@   void test(int a, int b, int c, int d, int e,
+@             double m, double n, double p, double q, double r) {
+@     try {
+@       print(a, b, c, d, e);
+@     } catch (...) {
+@       print(m, n, p, q, r);
+@     }
+@   }
+@
+@ This test case should check the unwind opcode to adjust the opcode and
+@ restore the general-purpose and VFP registers.
+
+
+@ RUN: llvm-mc %s -triple=armv7-unknown-linux-gnueabi -filetype=obj -o - \
+@ RUN:   | llvm-readobj -s -sd | FileCheck %s
+
+
+@-------------------------------------------------------------------------------
+@ Assembly without frame pointer elimination
+@-------------------------------------------------------------------------------
+       .syntax unified
+       .section        .TEST1
+       .globl  func1
+       .align  2
+       .type   func1,%function
+func1:
+       .fnstart
+       .save   {r4, r11, lr}
+       push    {r4, r11, lr}
+       .setfp  r11, sp, #4
+       add     r11, sp, #4
+       .vsave  {d8, d9, d10, d11, d12}
+       vpush   {d8, d9, d10, d11, d12}
+       .pad    #28
+       sub     sp, sp, #28
+       sub     sp, r11, #44
+       vpop    {d8, d9, d10, d11, d12}
+       pop     {r4, r11, pc}
+.Ltmp1:
+       .size   func1, .Ltmp1-func1
+       .globl  __gxx_personality_v0
+       .personality __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST1
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 C94A9B01 B0818484           |.....J......|
+@ CHECK:   )
+@ CHECK: }
+
+
+
+@-------------------------------------------------------------------------------
+@ Assembly with frame pointer elimination
+@-------------------------------------------------------------------------------
+       .section        .TEST2
+       .globl  func2
+       .align  2
+       .type   func2,%function
+func2:
+       .fnstart
+       .save   {r4, lr}
+       push    {r4, lr}
+       .vsave  {d8, d9, d10, d11, d12}
+       vpush   {d8, d9, d10, d11, d12}
+       .pad    #24
+       sub     sp, sp, #24
+       add     sp, sp, #24
+       vpop    {d8, d9, d10, d11, d12}
+       pop     {r4, pc}
+.Ltmp2:
+       .size   func2, .Ltmp2-func2
+       .globl  __gxx_personality_v0
+       .personality __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST2
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 84C90501 B0B0B0A8           |............|
+@ CHECK:   )
+@ CHECK: }
diff --git a/test/MC/ARM/eh-directive-multiple-offsets.s b/test/MC/ARM/eh-directive-multiple-offsets.s
new file mode 100644 (file)
index 0000000..6e81f41
--- /dev/null
@@ -0,0 +1,168 @@
+@ RUN: llvm-mc %s -triple=armv7-unknown-linux-gnueabi -filetype=obj -o - \
+@ RUN:   | llvm-readobj -s -sd | FileCheck %s
+
+@ Check for different combination of .setfp, .pad, .save and .vsave.
+
+       .syntax unified
+
+@-------------------------------------------------------------------------------
+@ TEST1: Check .pad before .setfp directive.
+@-------------------------------------------------------------------------------
+       .section        .TEST1
+       .globl  func1
+       .type   func1,%function
+       .align  2
+       .fnstart
+func1:
+       .pad    #12
+       sub     sp, sp, #12
+       .setfp  fp, sp, #8
+       add     fp, sp, #8
+       sub     sp, fp, #8
+       add     sp, sp, #12
+       bx      lr
+       .personality    __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST1
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 B0009B00                    |........|
+@ CHECK:   )
+@ CHECK: }
+
+
+
+@-------------------------------------------------------------------------------
+@ TEST2: Check .pad after .setfp directive.
+@-------------------------------------------------------------------------------
+       .section        .TEST2
+       .globl  func2
+       .type   func2,%function
+       .align  2
+       .fnstart
+func2:
+       .setfp  fp, sp, #8
+       add     fp, sp, #8
+       .pad    #12
+       sub     sp, sp, #12
+       add     sp, sp, #12
+       sub     sp, fp, #8
+       bx      lr
+       .personality    __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST2
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 B0419B00                    |.....A..|
+@ CHECK:   )
+@ CHECK: }
+
+
+
+@-------------------------------------------------------------------------------
+@ TEST3: Check .setfp, .pad, .setfp directive.
+@-------------------------------------------------------------------------------
+       .section        .TEST3
+       .globl  func3
+       .type   func3,%function
+       .align  2
+       .fnstart
+func3:
+       @ prologue:
+       .setfp  fp, sp, #4
+       add     fp, sp, #4
+       .pad    #8
+       sub     sp, sp, #8
+       .setfp  fp, sp, #4
+       add     fp, sp, #4
+
+       @ epilogue:
+       add     sp, fp, #4
+       bx      lr
+       .personality    __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST3
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 B0009B00                    |........|
+@ CHECK:   )
+@ CHECK: }
+
+
+
+@-------------------------------------------------------------------------------
+@ TEST4: Check ".setfp fp, sp" and ".setfp fp, fp" directive.
+@-------------------------------------------------------------------------------
+       .section        .TEST4
+       .globl  func4
+       .type   func4,%function
+       .align  2
+       .fnstart
+func4:
+       @ prologue:
+       .setfp  fp, sp, #8
+       add     fp, sp, #8
+       .setfp  fp, fp, #8
+       add     fp, fp, #8
+
+       @ epilogue:
+       sub     sp, fp, #16
+       bx      lr
+       .personality    __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST4
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 B0439B00                    |.....C..|
+@ CHECK:   )
+@ CHECK: }
+
+
+
+@-------------------------------------------------------------------------------
+@ TEST5: Check .setfp, .save, .setfp directive.
+@-------------------------------------------------------------------------------
+       .section        .TEST5
+       .globl  func5
+       .type   func5,%function
+       .align  2
+       .fnstart
+func5:
+       @ prologue:
+       .setfp  fp, sp, #16
+       add     fp, sp, #16
+       .save   {r4, r5, r6, r7, r8}
+       push    {r4, r5, r6, r7, r8}
+       .pad    #8
+       add     sp, sp, #8
+       .pad    #8
+       sub     sp, sp, #8
+       .save   {r9, r10}
+       push    {r9, r10}
+       .setfp  fp, sp, #24
+       add     fp, sp, #24
+
+       @ epilogue:
+       sub     sp, fp, #24
+       pop     {r9, r10}
+       add     sp, sp, #16
+       pop     {r4, r5, r6, r7, r8}
+       bx      lr
+       .personality    __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST5
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 80459B01 B0A40360           |.....E.....`|
+@ CHECK:   )
+@ CHECK: }
index ba850ffe77b17aa4eb79142179a2dd5b053ab490..f8263e6621f0e6dab7bc315e42b79d32ab792306 100644 (file)
@@ -121,7 +121,7 @@ func3b:
 @ CHECK: Section {
 @ CHECK:   Name: .ARM.extab.TEST3
 @ CHECK:   SectionData (
-@ CHECK:     0000: 00000000 B0003F00 00000000 B03F3F00  |......?......??.|
+@ CHECK:     0000: 00000000 B03F0000 00000000 B03F3F00  |.....?.......??.|
 @ CHECK:   )
 @ CHECK: }
 
@@ -220,7 +220,7 @@ func5c:
 @ CHECK: Section {
 @ CHECK:   Name: .ARM.extab.TEST5
 @ CHECK:   SectionData (
-@ CHECK:     0000: 00000000 B0B04000 00000000 B0407F00  |......@......@..|
-@ CHECK:     0010: 00000000 407F7F00                    |....@...|
+@ CHECK:     0000: 00000000 B0B04000 00000000 B07F4000  |......@.......@.|
+@ CHECK:     0010: 00000000 7F7F4000                    |......@.|
 @ CHECK:   )
 @ CHECK: }
index f9c8c5f03f49fece4e72d5fb0592341f4cc74555..652a7bb56c5cfc4910d9228e1025064235de2615 100644 (file)
@@ -296,3 +296,48 @@ func4e:
 @ CHECK:     0020: 00000000 B00E8400                    |........|
 @ CHECK:   )
 @ CHECK: }
+
+
+
+@-------------------------------------------------------------------------------
+@ TEST5
+@-------------------------------------------------------------------------------
+       .section        .TEST5
+       .globl  func5a
+       .align  2
+       .type   func5a,%function
+       .fnstart
+func5a:
+       .save   {r0, r1, r2, r3, r4, r5, r6}
+       push    {r0, r1, r2, r3, r4, r5, r6}
+       pop     {r0, r1, r2, r3, r4, r5, r6}
+       bx      lr
+       .personality __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+       .globl  func5b
+       .align  2
+       .type   func5b,%function
+       .fnstart
+func5b:
+       .save   {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r14}
+       push    {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r14}
+       pop     {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r14}
+       bx      lr
+       .personality __gxx_personality_v0
+       .handlerdata
+       .fnend
+
+@-------------------------------------------------------------------------------
+@ Check the order of unwind opcode to pop registers.
+@ 0xB10F "pop {r0-r3}" should be emitted before 0xA2 "pop {r4-r6}".
+@ 0xB10F "pop {r0-r3}" should be emitted before 0x85FF "pop {r4-r12, r14}".
+@-------------------------------------------------------------------------------
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.extab.TEST5
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 A20FB100 00000000 850FB101  |................|
+@ CHECK:     0010: B0B0B0FF                             |....|
+@ CHECK:   )
+@ CHECK: }
index 3fbab5a3e724b3b9799efaab44cab7842498a95d..dfa79e622d2e7aa35bd9deb16e29f14af132b625 100644 (file)
@@ -131,7 +131,7 @@ func3b:
 @ CHECK: Section {
 @ CHECK:   Name: .ARM.extab.TEST3
 @ CHECK:   SectionData (
-@ CHECK:     0000: 00000000 003F9B00 00000000 3F3F9B00  |.....?......??..|
+@ CHECK:     0000: 00000000 3F009B00 00000000 3F3F9B00  |....?.......??..|
 @ CHECK:   )
 @ CHECK: }
 
@@ -233,7 +233,7 @@ func5c:
 @ CHECK: Section {
 @ CHECK:   Name: .ARM.extab.TEST5
 @ CHECK:   SectionData (
-@ CHECK:     0000: 00000000 B0409B00 00000000 407F9B00  |.....@......@...|
-@ CHECK:     0010: 00000000 7F7F9B01 B0B0B040           |...........@|
+@ CHECK:     0000: 00000000 B0409B00 00000000 7F409B00  |.....@.......@..|
+@ CHECK:     0010: 00000000 7F409B01 B0B0B07F           |.....@......|
 @ CHECK:   )
 @ CHECK: }