mirror of
				https://github.com/c64scene-ar/llvm-6502.git
				synced 2025-10-31 08:16:47 +00:00 
			
		
		
		
	Fix ARM unwind opcode assembler in several cases.
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
			
			
This commit is contained in:
		| @@ -203,7 +203,7 @@ private: | |||||||
|   void Reset(); |   void Reset(); | ||||||
|  |  | ||||||
|   void EmitPersonalityFixup(StringRef Name); |   void EmitPersonalityFixup(StringRef Name); | ||||||
|   void CollectUnwindOpcodes(); |   void FlushPendingOffset(); | ||||||
|   void FlushUnwindOpcodes(bool AllowCompactModel0); |   void FlushUnwindOpcodes(bool AllowCompactModel0); | ||||||
|  |  | ||||||
|   void SwitchToEHSection(const char *Prefix, unsigned Type, unsigned Flags, |   void SwitchToEHSection(const char *Prefix, unsigned Type, unsigned Flags, | ||||||
| @@ -221,13 +221,14 @@ private: | |||||||
|   MCSymbol *ExTab; |   MCSymbol *ExTab; | ||||||
|   MCSymbol *FnStart; |   MCSymbol *FnStart; | ||||||
|   const MCSymbol *Personality; |   const MCSymbol *Personality; | ||||||
|   uint32_t VFPRegSave; // Register mask for {d31-d0} |   unsigned PersonalityIndex; | ||||||
|   uint32_t RegSave; // Register mask for {r15-r0} |   unsigned FPReg; // Frame pointer register | ||||||
|   int64_t SPOffset; |   int64_t FPOffset; // Offset: (final frame pointer) - (initial $sp) | ||||||
|   uint16_t FPReg; |   int64_t SPOffset; // Offset: (final $sp) - (initial $sp) | ||||||
|   int64_t FPOffset; |   int64_t PendingOffset; // Offset: (final $sp) - (emitted $sp) | ||||||
|   bool UsedFP; |   bool UsedFP; | ||||||
|   bool CantUnwind; |   bool CantUnwind; | ||||||
|  |   SmallVector<uint8_t, 64> Opcodes; | ||||||
|   UnwindOpcodeAssembler UnwindOpAsm; |   UnwindOpcodeAssembler UnwindOpAsm; | ||||||
| }; | }; | ||||||
| } // end anonymous namespace | } // end anonymous namespace | ||||||
| @@ -280,19 +281,18 @@ inline void ARMELFStreamer::SwitchToExIdxSection(const MCSymbol &FnStart) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void ARMELFStreamer::Reset() { | void ARMELFStreamer::Reset() { | ||||||
|   const MCRegisterInfo &MRI = getContext().getRegisterInfo(); |  | ||||||
|  |  | ||||||
|   ExTab = NULL; |   ExTab = NULL; | ||||||
|   FnStart = NULL; |   FnStart = NULL; | ||||||
|   Personality = NULL; |   Personality = NULL; | ||||||
|   VFPRegSave = 0; |   PersonalityIndex = NUM_PERSONALITY_INDEX; | ||||||
|   RegSave = 0; |   FPReg = ARM::SP; | ||||||
|   FPReg = MRI.getEncodingValue(ARM::SP); |  | ||||||
|   FPOffset = 0; |   FPOffset = 0; | ||||||
|   SPOffset = 0; |   SPOffset = 0; | ||||||
|  |   PendingOffset = 0; | ||||||
|   UsedFP = false; |   UsedFP = false; | ||||||
|   CantUnwind = false; |   CantUnwind = false; | ||||||
|  |  | ||||||
|  |   Opcodes.clear(); | ||||||
|   UnwindOpAsm.Reset(); |   UnwindOpAsm.Reset(); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -312,18 +312,6 @@ void ARMELFStreamer::EmitPersonalityFixup(StringRef Name) { | |||||||
|                     MCFixup::getKindForSize(4, false))); |                     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() { | void ARMELFStreamer::EmitFnStart() { | ||||||
|   assert(FnStart == 0); |   assert(FnStart == 0); | ||||||
|   FnStart = getContext().CreateTempSymbol(); |   FnStart = getContext().CreateTempSymbol(); | ||||||
| @@ -340,7 +328,6 @@ void ARMELFStreamer::EmitFnEnd() { | |||||||
|   // Emit the exception index table entry |   // Emit the exception index table entry | ||||||
|   SwitchToExIdxSection(*FnStart); |   SwitchToExIdxSection(*FnStart); | ||||||
|  |  | ||||||
|   unsigned PersonalityIndex = UnwindOpAsm.getPersonalityIndex(); |  | ||||||
|   if (PersonalityIndex < NUM_PERSONALITY_INDEX) |   if (PersonalityIndex < NUM_PERSONALITY_INDEX) | ||||||
|     EmitPersonalityFixup(GetAEABIUnwindPersonalityName(PersonalityIndex)); |     EmitPersonalityFixup(GetAEABIUnwindPersonalityName(PersonalityIndex)); | ||||||
|  |  | ||||||
| @@ -366,9 +353,10 @@ void ARMELFStreamer::EmitFnEnd() { | |||||||
|     // opcodes should always be 4 bytes. |     // opcodes should always be 4 bytes. | ||||||
|     assert(PersonalityIndex == AEABI_UNWIND_CPP_PR0 && |     assert(PersonalityIndex == AEABI_UNWIND_CPP_PR0 && | ||||||
|            "Compact model must use __aeabi_cpp_unwind_pr0 as personality"); |            "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"); |            "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 |   // Switch to the section containing FnStart | ||||||
| @@ -382,15 +370,31 @@ void ARMELFStreamer::EmitCantUnwind() { | |||||||
|   CantUnwind = true; |   CantUnwind = true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ARMELFStreamer::FlushPendingOffset() { | ||||||
|  |   if (PendingOffset != 0) { | ||||||
|  |     UnwindOpAsm.EmitSPOffset(-PendingOffset); | ||||||
|  |     PendingOffset = 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| void ARMELFStreamer::FlushUnwindOpcodes(bool AllowCompactModel0) { | void ARMELFStreamer::FlushUnwindOpcodes(bool AllowCompactModel0) { | ||||||
|   // Collect and finalize the unwind opcodes |   // Emit the unwind opcode to restore $sp. | ||||||
|   CollectUnwindOpcodes(); |   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 |   // 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.  Thus, we don't have to create an entry in the .ARM.extab | ||||||
|   // section. |   // section. | ||||||
|   if (AllowCompactModel0 && |   if (AllowCompactModel0 && PersonalityIndex == AEABI_UNWIND_CPP_PR0) | ||||||
|       UnwindOpAsm.getPersonalityIndex() == AEABI_UNWIND_CPP_PR0) |  | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
|   // Switch to .ARM.extab section. |   // Switch to .ARM.extab section. | ||||||
| @@ -412,7 +416,8 @@ void ARMELFStreamer::FlushUnwindOpcodes(bool AllowCompactModel0) { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Emit unwind opcodes |   // Emit unwind opcodes | ||||||
|   EmitBytes(UnwindOpAsm.data(), 0); |   EmitBytes(StringRef(reinterpret_cast<const char *>(Opcodes.data()), | ||||||
|  |                       Opcodes.size()), 0); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ARMELFStreamer::EmitHandlerData() { | void ARMELFStreamer::EmitHandlerData() { | ||||||
| @@ -427,44 +432,57 @@ void ARMELFStreamer::EmitPersonality(const MCSymbol *Per) { | |||||||
| void ARMELFStreamer::EmitSetFP(unsigned NewFPReg, | void ARMELFStreamer::EmitSetFP(unsigned NewFPReg, | ||||||
|                                unsigned NewSPReg, |                                unsigned NewSPReg, | ||||||
|                                int64_t Offset) { |                                int64_t Offset) { | ||||||
|   assert(SPOffset == 0 && |   assert((NewSPReg == ARM::SP || NewSPReg == FPReg) && | ||||||
|          "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) && |  | ||||||
|          "the operand of .setfp directive should be either $sp or $fp"); |          "the operand of .setfp directive should be either $sp or $fp"); | ||||||
|  |  | ||||||
|   UsedFP = true; |   UsedFP = true; | ||||||
|   FPReg = NewFPRegEncVal; |   FPReg = NewFPReg; | ||||||
|   FPOffset = Offset; |  | ||||||
|  |   if (NewSPReg == ARM::SP) | ||||||
|  |     FPOffset = SPOffset + Offset; | ||||||
|  |   else | ||||||
|  |     FPOffset += Offset; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ARMELFStreamer::EmitPad(int64_t 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, | void ARMELFStreamer::EmitRegSave(const SmallVectorImpl<unsigned> &RegList, | ||||||
|                                  bool IsVector) { |                                  bool IsVector) { | ||||||
|  |   // Collect the registers in the register list | ||||||
|  |   unsigned Count = 0; | ||||||
|  |   uint32_t Mask = 0; | ||||||
|   const MCRegisterInfo &MRI = getContext().getRegisterInfo(); |   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) { |   for (size_t i = 0; i < RegList.size(); ++i) { | ||||||
|     unsigned Reg = MRI.getEncodingValue(RegList[i]); |     unsigned Reg = MRI.getEncodingValue(RegList[i]); | ||||||
|     assert(Reg < Max && "Register encoded value out of range"); |     assert(Reg < (IsVector ? 32 : 16) && "Register out of range"); | ||||||
|     RegMask |= 1u << Reg; |     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 { | namespace llvm { | ||||||
|   MCELFStreamer* createARMELFStreamer(MCContext &Context, MCAsmBackend &TAB, |   MCELFStreamer* createARMELFStreamer(MCContext &Context, MCAsmBackend &TAB, | ||||||
|                                       raw_ostream &OS, MCCodeEmitter *Emitter, |                                       raw_ostream &OS, MCCodeEmitter *Emitter, | ||||||
|   | |||||||
| @@ -20,6 +20,48 @@ | |||||||
|  |  | ||||||
| using namespace llvm; | 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) { | void UnwindOpcodeAssembler::EmitRegSave(uint32_t RegSave) { | ||||||
|   if (RegSave == 0u) |   if (RegSave == 0u) | ||||||
|     return; |     return; | ||||||
| @@ -43,28 +85,22 @@ void UnwindOpcodeAssembler::EmitRegSave(uint32_t RegSave) { | |||||||
|     uint32_t UnmaskedReg = RegSave & 0xfff0u & (~Mask); |     uint32_t UnmaskedReg = RegSave & 0xfff0u & (~Mask); | ||||||
|     if (UnmaskedReg == 0u) { |     if (UnmaskedReg == 0u) { | ||||||
|       // Pop r[4 : (4 + n)] |       // 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; |       RegSave &= 0x000fu; | ||||||
|     } else if (UnmaskedReg == (1u << 14)) { |     } else if (UnmaskedReg == (1u << 14)) { | ||||||
|       // Pop r[14] + r[4 : (4 + n)] |       // 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; |       RegSave &= 0x000fu; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Two bytes opcode to save register r15-r4 |   // Two bytes opcode to save register r15-r4 | ||||||
|   if ((RegSave & 0xfff0u) != 0) { |   if ((RegSave & 0xfff0u) != 0) | ||||||
|     uint32_t Op = UNWIND_OPCODE_POP_REG_MASK_R4 | (RegSave >> 4); |     EmitInt16(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)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Opcode to save register r3-r0 |   // Opcode to save register r3-r0 | ||||||
|   if ((RegSave & 0x000fu) != 0) { |   if ((RegSave & 0x000fu) != 0) | ||||||
|     uint32_t Op = UNWIND_OPCODE_POP_REG_MASK | (RegSave & 0x000fu); |     EmitInt16(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)); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Emit unwind opcodes for .vsave directives | /// Emit unwind opcodes for .vsave directives | ||||||
| @@ -89,10 +125,8 @@ void UnwindOpcodeAssembler::EmitVFPRegSave(uint32_t VFPRegSave) { | |||||||
|       Bit >>= 1; |       Bit >>= 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint32_t Op = |     EmitInt16(UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD_D16 | | ||||||
|         UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD_D16 | ((i - 16) << 4) | Range; |               ((i - 16) << 4) | Range); | ||||||
|     Ops.push_back(static_cast<uint8_t>(Op >> 8)); |  | ||||||
|     Ops.push_back(static_cast<uint8_t>(Op & 0xff)); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   while (i > 0) { |   while (i > 0) { | ||||||
| @@ -113,86 +147,75 @@ void UnwindOpcodeAssembler::EmitVFPRegSave(uint32_t VFPRegSave) { | |||||||
|       Bit >>= 1; |       Bit >>= 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint32_t Op = UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD | (i << 4) | Range; |     EmitInt16(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)); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Emit unwind opcodes for .setfp directives | /// Emit unwind opcodes to copy address from source register to $sp. | ||||||
| void UnwindOpcodeAssembler::EmitSetFP(uint16_t FPReg) { | void UnwindOpcodeAssembler::EmitSetSP(uint16_t Reg) { | ||||||
|   Ops.push_back(UNWIND_OPCODE_SET_VSP | FPReg); |   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) { | void UnwindOpcodeAssembler::EmitSPOffset(int64_t Offset) { | ||||||
|   if (Offset > 0x200) { |   if (Offset > 0x200) { | ||||||
|     uint8_t Buff[10]; |     uint8_t Buff[16]; | ||||||
|     size_t Size = encodeULEB128((Offset - 0x204) >> 2, Buff); |     Buff[0] = UNWIND_OPCODE_INC_VSP_ULEB128; | ||||||
|     Ops.push_back(UNWIND_OPCODE_INC_VSP_ULEB128); |     size_t ULEBSize = encodeULEB128((Offset - 0x204) >> 2, Buff + 1); | ||||||
|     Ops.append(Buff, Buff + Size); |     EmitBytes(Buff, ULEBSize + 1); | ||||||
|   } else if (Offset > 0) { |   } else if (Offset > 0) { | ||||||
|     if (Offset > 0x100) { |     if (Offset > 0x100) { | ||||||
|       Ops.push_back(UNWIND_OPCODE_INC_VSP | 0x3fu); |       EmitInt8(UNWIND_OPCODE_INC_VSP | 0x3fu); | ||||||
|       Offset -= 0x100; |       Offset -= 0x100; | ||||||
|     } |     } | ||||||
|     Ops.push_back(UNWIND_OPCODE_INC_VSP | |     EmitInt8(UNWIND_OPCODE_INC_VSP | static_cast<uint8_t>((Offset - 4) >> 2)); | ||||||
|                   static_cast<uint8_t>((Offset - 4) >> 2)); |  | ||||||
|   } else if (Offset < 0) { |   } else if (Offset < 0) { | ||||||
|     while (Offset < -0x100) { |     while (Offset < -0x100) { | ||||||
|       Ops.push_back(UNWIND_OPCODE_DEC_VSP | 0x3fu); |       EmitInt8(UNWIND_OPCODE_DEC_VSP | 0x3fu); | ||||||
|       Offset += 0x100; |       Offset += 0x100; | ||||||
|     } |     } | ||||||
|     Ops.push_back(UNWIND_OPCODE_DEC_VSP | |     EmitInt8(UNWIND_OPCODE_DEC_VSP | | ||||||
|              static_cast<uint8_t>(((-Offset) - 4) >> 2)); |              static_cast<uint8_t>(((-Offset) - 4) >> 2)); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void UnwindOpcodeAssembler::AddOpcodeSizePrefix(size_t Pos) { | void UnwindOpcodeAssembler::Finalize(unsigned &PersonalityIndex, | ||||||
|   size_t SizeInWords = (size() + 3) / 4; |                                      SmallVectorImpl<uint8_t> &Result) { | ||||||
|   assert(SizeInWords <= 0x100u && |  | ||||||
|          "Only 256 additional words are allowed for unwind opcodes"); |  | ||||||
|   Ops[Pos] = static_cast<uint8_t>(SizeInWords - 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void UnwindOpcodeAssembler::AddPersonalityIndexPrefix(size_t Pos, unsigned PI) { |   UnwindOpcodeStreamer OpStreamer(Result); | ||||||
|   assert(PI < NUM_PERSONALITY_INDEX && "Invalid personality prefix"); |  | ||||||
|   Ops[Pos] = EHT_COMPACT | PI; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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) { |   if (HasPersonality) { | ||||||
|     // Personality specified by .personality directive |     // User-specifed personality routine: [ SIZE , OP1 , OP2 , ... ] | ||||||
|     Offset = 1; |     PersonalityIndex = NUM_PERSONALITY_INDEX; | ||||||
|     AddOpcodeSizePrefix(1); |     size_t TotalSize = Ops.size() + 1; | ||||||
|  |     size_t RoundUpSize = (TotalSize + 3) / 4 * 4; | ||||||
|  |     Result.resize(RoundUpSize); | ||||||
|  |     OpStreamer.EmitSize(RoundUpSize); | ||||||
|   } else { |   } else { | ||||||
|     if (getOpcodeSize() <= 3) { |     if (Ops.size() <= 3) { | ||||||
|       // __aeabi_unwind_cpp_pr0: [ 0x80 , OP1 , OP2 , OP3 ] |       // __aeabi_unwind_cpp_pr0: [ 0x80 , OP1 , OP2 , OP3 ] | ||||||
|       Offset = 1; |  | ||||||
|       PersonalityIndex = AEABI_UNWIND_CPP_PR0; |       PersonalityIndex = AEABI_UNWIND_CPP_PR0; | ||||||
|       AddPersonalityIndexPrefix(Offset, PersonalityIndex); |       Result.resize(4); | ||||||
|  |       OpStreamer.EmitPersonalityIndex(PersonalityIndex); | ||||||
|     } else { |     } else { | ||||||
|       // __aeabi_unwind_cpp_pr1: [ 0x81 , SIZE , OP1 , OP2 , ... ] |       // __aeabi_unwind_cpp_pr1: [ 0x81 , SIZE , OP1 , OP2 , ... ] | ||||||
|       Offset = 0; |  | ||||||
|       PersonalityIndex = AEABI_UNWIND_CPP_PR1; |       PersonalityIndex = AEABI_UNWIND_CPP_PR1; | ||||||
|       AddPersonalityIndexPrefix(Offset, PersonalityIndex); |       size_t TotalSize = Ops.size() + 2; | ||||||
|       AddOpcodeSizePrefix(1); |       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. |   // Copy the unwind opcodes | ||||||
|   EmitFinishOpcodes(); |   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 |   // Emit the padding finish opcodes if the size is not multiple of 4. | ||||||
|   uint8_t *Ptr = Ops.begin() + Offset; |   OpStreamer.FillFinishOpcode(); | ||||||
|   assert(size() % 4 == 0 && "Final unwind opcodes should align to 4"); |  | ||||||
|   for (size_t i = 0, n = size(); i < n; i += 4) { |   // Reset the assembler state | ||||||
|     std::swap(Ptr[i], Ptr[i + 3]); |   Reset(); | ||||||
|     std::swap(Ptr[i + 1], Ptr[i + 2]); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,86 +27,61 @@ class MCSymbol; | |||||||
|  |  | ||||||
| class UnwindOpcodeAssembler { | class UnwindOpcodeAssembler { | ||||||
| private: | private: | ||||||
|   llvm::SmallVector<uint8_t, 8> Ops; |   llvm::SmallVector<uint8_t, 32> Ops; | ||||||
|  |   llvm::SmallVector<unsigned, 8> OpBegins; | ||||||
|   unsigned Offset; |  | ||||||
|   unsigned PersonalityIndex; |  | ||||||
|   bool HasPersonality; |   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: | public: | ||||||
|   UnwindOpcodeAssembler() |   UnwindOpcodeAssembler() | ||||||
|       : Ops(NUM_PRESERVED_PREFIX_BUF), Offset(NUM_PRESERVED_PREFIX_BUF), |       : HasPersonality(0) { | ||||||
|         PersonalityIndex(NUM_PERSONALITY_INDEX), HasPersonality(0) { |     OpBegins.push_back(0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// Reset the unwind opcode assembler. |   /// Reset the unwind opcode assembler. | ||||||
|   void Reset() { |   void Reset() { | ||||||
|     Ops.resize(NUM_PRESERVED_PREFIX_BUF); |     Ops.clear(); | ||||||
|     Offset = NUM_PRESERVED_PREFIX_BUF; |     OpBegins.clear(); | ||||||
|     PersonalityIndex = NUM_PERSONALITY_INDEX; |     OpBegins.push_back(0); | ||||||
|     HasPersonality = 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 |   /// Set the personality index | ||||||
|   void setPersonality(const MCSymbol *Per) { |   void setPersonality(const MCSymbol *Per) { | ||||||
|     HasPersonality = 1; |     HasPersonality = 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// Get the personality index |  | ||||||
|   unsigned getPersonalityIndex() const { |  | ||||||
|     return PersonalityIndex; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Emit unwind opcodes for .save directives |   /// Emit unwind opcodes for .save directives | ||||||
|   void EmitRegSave(uint32_t RegSave); |   void EmitRegSave(uint32_t RegSave); | ||||||
|  |  | ||||||
|   /// Emit unwind opcodes for .vsave directives |   /// Emit unwind opcodes for .vsave directives | ||||||
|   void EmitVFPRegSave(uint32_t VFPRegSave); |   void EmitVFPRegSave(uint32_t VFPRegSave); | ||||||
|  |  | ||||||
|   /// Emit unwind opcodes for .setfp directives |   /// Emit unwind opcodes to copy address from source register to $sp. | ||||||
|   void EmitSetFP(uint16_t FPReg); |   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); |   void EmitSPOffset(int64_t Offset); | ||||||
|  |  | ||||||
|   /// Finalize the unwind opcode sequence for EmitBytes() |   /// Finalize the unwind opcode sequence for EmitBytes() | ||||||
|   void Finalize(); |   void Finalize(unsigned &PersonalityIndex, | ||||||
|  |                 SmallVectorImpl<uint8_t> &Result); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|   /// Get the size of the opcodes in bytes. |   void EmitInt8(unsigned Opcode) { | ||||||
|   size_t getOpcodeSize() const { |     Ops.push_back(Opcode & 0xff); | ||||||
|     return Ops.size() - NUM_PRESERVED_PREFIX_BUF; |     OpBegins.push_back(OpBegins.back() + 1); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// Add the length prefix to the payload |   void EmitInt16(unsigned Opcode) { | ||||||
|   void AddOpcodeSizePrefix(size_t Pos); |     Ops.push_back((Opcode >> 8) & 0xff); | ||||||
|  |     Ops.push_back(Opcode & 0xff); | ||||||
|  |     OpBegins.push_back(OpBegins.back() + 2); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /// Add personality index prefix in some compact format |   void EmitBytes(const uint8_t *Opcode, size_t Size) { | ||||||
|   void AddPersonalityIndexPrefix(size_t Pos, unsigned PersonalityIndex); |     Ops.insert(Ops.end(), Opcode, Opcode + Size); | ||||||
|  |     OpBegins.push_back(OpBegins.back() + Size); | ||||||
|   /// Fill the words with finish opcode if it is not aligned |   } | ||||||
|   void EmitFinishOpcodes(); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } // namespace llvm | } // namespace llvm | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ declare void @_ZSt9terminatev() | |||||||
|  |  | ||||||
| ; CHECK: section .test_section | ; CHECK: section .test_section | ||||||
| ; CHECK: section .ARM.extab.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: section .ARM.exidx.test_section | ||||||
| ; CHECK-NEXT: 0000 00000000 00000000 | ; CHECK-NEXT: 0000 00000000 00000000 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ declare void @_ZSt9terminatev() | |||||||
|  |  | ||||||
| ; CHECK: section .text | ; CHECK: section .text | ||||||
| ; CHECK: section .ARM.extab | ; CHECK: section .ARM.extab | ||||||
| ; CHECK-NEXT: 0000 00000000 c9409b01 b0818484 | ; CHECK-NEXT: 0000 00000000 c94a9b01 b0818484 | ||||||
| ; CHECK: section .ARM.exidx | ; CHECK: section .ARM.exidx | ||||||
| ; CHECK-NEXT: 0000 00000000 00000000 | ; CHECK-NEXT: 0000 00000000 00000000 | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										93
									
								
								test/MC/ARM/eh-directive-integrated-test.s
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								test/MC/ARM/eh-directive-integrated-test.s
									
									
									
									
									
										Normal file
									
								
							| @@ -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: } | ||||||
							
								
								
									
										168
									
								
								test/MC/ARM/eh-directive-multiple-offsets.s
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								test/MC/ARM/eh-directive-multiple-offsets.s
									
									
									
									
									
										Normal file
									
								
							| @@ -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: } | ||||||
| @@ -121,7 +121,7 @@ func3b: | |||||||
| @ CHECK: Section { | @ CHECK: Section { | ||||||
| @ CHECK:   Name: .ARM.extab.TEST3 | @ CHECK:   Name: .ARM.extab.TEST3 | ||||||
| @ CHECK:   SectionData ( | @ CHECK:   SectionData ( | ||||||
| @ CHECK:     0000: 00000000 B0003F00 00000000 B03F3F00  |......?......??.| | @ CHECK:     0000: 00000000 B03F0000 00000000 B03F3F00  |.....?.......??.| | ||||||
| @ CHECK:   ) | @ CHECK:   ) | ||||||
| @ CHECK: } | @ CHECK: } | ||||||
|  |  | ||||||
| @@ -220,7 +220,7 @@ func5c: | |||||||
| @ CHECK: Section { | @ CHECK: Section { | ||||||
| @ CHECK:   Name: .ARM.extab.TEST5 | @ CHECK:   Name: .ARM.extab.TEST5 | ||||||
| @ CHECK:   SectionData ( | @ CHECK:   SectionData ( | ||||||
| @ CHECK:     0000: 00000000 B0B04000 00000000 B0407F00  |......@......@..| | @ CHECK:     0000: 00000000 B0B04000 00000000 B07F4000  |......@.......@.| | ||||||
| @ CHECK:     0010: 00000000 407F7F00                    |....@...| | @ CHECK:     0010: 00000000 7F7F4000                    |......@.| | ||||||
| @ CHECK:   ) | @ CHECK:   ) | ||||||
| @ CHECK: } | @ CHECK: } | ||||||
|   | |||||||
| @@ -296,3 +296,48 @@ func4e: | |||||||
| @ CHECK:     0020: 00000000 B00E8400                    |........| | @ CHECK:     0020: 00000000 B00E8400                    |........| | ||||||
| @ CHECK:   ) | @ CHECK:   ) | ||||||
| @ 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: } | ||||||
|   | |||||||
| @@ -131,7 +131,7 @@ func3b: | |||||||
| @ CHECK: Section { | @ CHECK: Section { | ||||||
| @ CHECK:   Name: .ARM.extab.TEST3 | @ CHECK:   Name: .ARM.extab.TEST3 | ||||||
| @ CHECK:   SectionData ( | @ CHECK:   SectionData ( | ||||||
| @ CHECK:     0000: 00000000 003F9B00 00000000 3F3F9B00  |.....?......??..| | @ CHECK:     0000: 00000000 3F009B00 00000000 3F3F9B00  |....?.......??..| | ||||||
| @ CHECK:   ) | @ CHECK:   ) | ||||||
| @ CHECK: } | @ CHECK: } | ||||||
|  |  | ||||||
| @@ -233,7 +233,7 @@ func5c: | |||||||
| @ CHECK: Section { | @ CHECK: Section { | ||||||
| @ CHECK:   Name: .ARM.extab.TEST5 | @ CHECK:   Name: .ARM.extab.TEST5 | ||||||
| @ CHECK:   SectionData ( | @ CHECK:   SectionData ( | ||||||
| @ CHECK:     0000: 00000000 B0409B00 00000000 407F9B00  |.....@......@...| | @ CHECK:     0000: 00000000 B0409B00 00000000 7F409B00  |.....@.......@..| | ||||||
| @ CHECK:     0010: 00000000 7F7F9B01 B0B0B040           |...........@| | @ CHECK:     0010: 00000000 7F409B01 B0B0B07F           |.....@......| | ||||||
| @ CHECK:   ) | @ CHECK:   ) | ||||||
| @ CHECK: } | @ CHECK: } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user