ARMUnwindOpAsm.cpp [plain text]
#include "ARMUnwindOpAsm.h"
#include "llvm/Support/ARMEHABI.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LEB128.h"
using namespace llvm;
namespace {
class UnwindOpcodeStreamer {
private:
SmallVectorImpl<uint8_t> &Vec;
size_t Pos;
public:
UnwindOpcodeStreamer(SmallVectorImpl<uint8_t> &V) : Vec(V), Pos(3) {
}
inline void EmitByte(uint8_t elem) {
Vec[Pos] = elem;
Pos = (((Pos ^ 0x3u) + 1) ^ 0x3u);
}
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));
}
inline void EmitPersonalityIndex(unsigned PI) {
assert(PI < ARM::EHABI::NUM_PERSONALITY_INDEX &&
"Invalid personality prefix");
EmitByte(ARM::EHABI::EHT_COMPACT | PI);
}
inline void FillFinishOpcode() {
while (Pos < Vec.size())
EmitByte(ARM::EHABI::UNWIND_OPCODE_FINISH);
}
};
}
void UnwindOpcodeAssembler::EmitRegSave(uint32_t RegSave) {
if (RegSave == 0u)
return;
if (RegSave & (1u << 4)) {
uint32_t Range = 0;
uint32_t Mask = (1u << 4);
for (uint32_t Bit = (1u << 5); Bit < (1u << 12); Bit <<= 1) {
if ((RegSave & Bit) == 0u)
break;
++Range;
Mask |= Bit;
}
uint32_t UnmaskedReg = RegSave & 0xfff0u & (~Mask);
if (UnmaskedReg == 0u) {
EmitInt8(ARM::EHABI::UNWIND_OPCODE_POP_REG_RANGE_R4 | Range);
RegSave &= 0x000fu;
} else if (UnmaskedReg == (1u << 14)) {
EmitInt8(ARM::EHABI::UNWIND_OPCODE_POP_REG_RANGE_R4_R14 | Range);
RegSave &= 0x000fu;
}
}
if ((RegSave & 0xfff0u) != 0)
EmitInt16(ARM::EHABI::UNWIND_OPCODE_POP_REG_MASK_R4 | (RegSave >> 4));
if ((RegSave & 0x000fu) != 0)
EmitInt16(ARM::EHABI::UNWIND_OPCODE_POP_REG_MASK | (RegSave & 0x000fu));
}
void UnwindOpcodeAssembler::EmitVFPRegSave(uint32_t VFPRegSave) {
size_t i = 32;
while (i > 16) {
uint32_t Bit = 1u << (i - 1);
if ((VFPRegSave & Bit) == 0u) {
--i;
continue;
}
uint32_t Range = 0;
--i;
Bit >>= 1;
while (i > 16 && (VFPRegSave & Bit)) {
--i;
++Range;
Bit >>= 1;
}
EmitInt16(ARM::EHABI::UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD_D16 |
((i - 16) << 4) | Range);
}
while (i > 0) {
uint32_t Bit = 1u << (i - 1);
if ((VFPRegSave & Bit) == 0u) {
--i;
continue;
}
uint32_t Range = 0;
--i;
Bit >>= 1;
while (i > 0 && (VFPRegSave & Bit)) {
--i;
++Range;
Bit >>= 1;
}
EmitInt16(ARM::EHABI::UNWIND_OPCODE_POP_VFP_REG_RANGE_FSTMFDD | (i << 4) |
Range);
}
}
void UnwindOpcodeAssembler::EmitSetSP(uint16_t Reg) {
EmitInt8(ARM::EHABI::UNWIND_OPCODE_SET_VSP | Reg);
}
void UnwindOpcodeAssembler::EmitSPOffset(int64_t Offset) {
if (Offset > 0x200) {
uint8_t Buff[16];
Buff[0] = ARM::EHABI::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) {
EmitInt8(ARM::EHABI::UNWIND_OPCODE_INC_VSP | 0x3fu);
Offset -= 0x100;
}
EmitInt8(ARM::EHABI::UNWIND_OPCODE_INC_VSP |
static_cast<uint8_t>((Offset - 4) >> 2));
} else if (Offset < 0) {
while (Offset < -0x100) {
EmitInt8(ARM::EHABI::UNWIND_OPCODE_DEC_VSP | 0x3fu);
Offset += 0x100;
}
EmitInt8(ARM::EHABI::UNWIND_OPCODE_DEC_VSP |
static_cast<uint8_t>(((-Offset) - 4) >> 2));
}
}
void UnwindOpcodeAssembler::Finalize(unsigned &PersonalityIndex,
SmallVectorImpl<uint8_t> &Result) {
UnwindOpcodeStreamer OpStreamer(Result);
if (HasPersonality) {
PersonalityIndex = ARM::EHABI::NUM_PERSONALITY_INDEX;
size_t TotalSize = Ops.size() + 1;
size_t RoundUpSize = (TotalSize + 3) / 4 * 4;
Result.resize(RoundUpSize);
OpStreamer.EmitSize(RoundUpSize);
} else {
if (PersonalityIndex == ARM::EHABI::NUM_PERSONALITY_INDEX)
PersonalityIndex = (Ops.size() <= 3) ? ARM::EHABI::AEABI_UNWIND_CPP_PR0
: ARM::EHABI::AEABI_UNWIND_CPP_PR1;
if (PersonalityIndex == ARM::EHABI::AEABI_UNWIND_CPP_PR0) {
assert(Ops.size() <= 3 && "too many opcodes for __aeabi_unwind_cpp_pr0");
Result.resize(4);
OpStreamer.EmitPersonalityIndex(PersonalityIndex);
} else {
size_t TotalSize = Ops.size() + 2;
size_t RoundUpSize = (TotalSize + 3) / 4 * 4;
Result.resize(RoundUpSize);
OpStreamer.EmitPersonalityIndex(PersonalityIndex);
OpStreamer.EmitSize(RoundUpSize);
}
}
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]);
OpStreamer.FillFinishOpcode();
Reset();
}