Commit 137e62bd by Karl Schimpf

Handle branch relative to pc in ARM integrated assembler.

Adds an explicit branch instruction (near form only), which allows branching from the current pc up to 2**26 bytes (in either direction). For now, this near restriction (within a function) doesn't appear to be a bad restriction, and only near jumps have been implemented. Also fixes notationally the concepts of the following types: InstValueType : The 32-bit encoding of an instruction value. InstOffsetType : Offset (+/-) used within an instruction. BUG= https://code.google.com/p/nativeclient/issues/detail?id=4334 R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1418313003 .
parent 50a3331c
...@@ -91,8 +91,8 @@ void Assembler::EmitType01(Condition cond, ...@@ -91,8 +91,8 @@ void Assembler::EmitType01(Condition cond,
o.encoding(); o.encoding();
Emit(encoding); Emit(encoding);
} }
#endif
// Moved to ARM32::AssemblerARM32::emitType05.
void Assembler::EmitType5(Condition cond, int32_t offset, bool link) { void Assembler::EmitType5(Condition cond, int32_t offset, bool link) {
ASSERT(cond != kNoCondition); ASSERT(cond != kNoCondition);
int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | int32_t encoding = static_cast<int32_t>(cond) << kConditionShift |
...@@ -101,8 +101,6 @@ void Assembler::EmitType5(Condition cond, int32_t offset, bool link) { ...@@ -101,8 +101,6 @@ void Assembler::EmitType5(Condition cond, int32_t offset, bool link) {
Emit(Assembler::EncodeBranchOffset(offset, encoding)); Emit(Assembler::EncodeBranchOffset(offset, encoding));
} }
#if 0
// Moved to ARM32::AssemblerARM32::emitMemOp() // Moved to ARM32::AssemblerARM32::emitMemOp()
void Assembler::EmitMemOp(Condition cond, void Assembler::EmitMemOp(Condition cond,
bool load, bool load,
...@@ -222,9 +220,7 @@ void Assembler::add(Register rd, Register rn, Operand o, Condition cond) { ...@@ -222,9 +220,7 @@ void Assembler::add(Register rd, Register rn, Operand o, Condition cond) {
void Assembler::adds(Register rd, Register rn, Operand o, Condition cond) { void Assembler::adds(Register rd, Register rn, Operand o, Condition cond) {
EmitType01(cond, o.type(), ADD, 1, rn, rd, o); EmitType01(cond, o.type(), ADD, 1, rn, rd, o);
} }
#endif
#if 0
// Moved to ARM32::AssemberARM32::sub() // Moved to ARM32::AssemberARM32::sub()
void Assembler::subs(Register rd, Register rn, Operand o, Condition cond) { void Assembler::subs(Register rd, Register rn, Operand o, Condition cond) {
EmitType01(cond, o.type(), SUB, 1, rn, rd, o); EmitType01(cond, o.type(), SUB, 1, rn, rd, o);
...@@ -2083,13 +2079,16 @@ void Assembler::UpdateRangeFeedback(Register value, ...@@ -2083,13 +2079,16 @@ void Assembler::UpdateRangeFeedback(Register value,
str(scratch2, FieldAddress(ic_data, ICData::state_bits_offset())); str(scratch2, FieldAddress(ic_data, ICData::state_bits_offset()));
} }
#if 0
// Moved to ::canEncodeBranchoffset in IceAssemblerARM32.cpp.
static bool CanEncodeBranchOffset(int32_t offset) { static bool CanEncodeBranchOffset(int32_t offset) {
ASSERT(Utils::IsAligned(offset, 4)); ASSERT(Utils::IsAligned(offset, 4));
// Note: This check doesn't take advantage of the fact that offset>>2
// is stored (allowing two more bits in address space).
return Utils::IsInt(Utils::CountOneBits(kBranchOffsetMask), offset); return Utils::IsInt(Utils::CountOneBits(kBranchOffsetMask), offset);
} }
// Moved to AssemblerARM32::encodeBranchOffset.
int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t inst) { int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t inst) {
// The offset is off by 8 due to the way the ARM CPUs read PC. // The offset is off by 8 due to the way the ARM CPUs read PC.
offset -= Instr::kPCReadOffset; offset -= Instr::kPCReadOffset;
...@@ -2106,12 +2105,12 @@ int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t inst) { ...@@ -2106,12 +2105,12 @@ int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t inst) {
return (inst & ~kBranchOffsetMask) | offset; return (inst & ~kBranchOffsetMask) | offset;
} }
// Moved to AssemberARM32::decodeBranchOffset.
int Assembler::DecodeBranchOffset(int32_t inst) { int Assembler::DecodeBranchOffset(int32_t inst) {
// Sign-extend, left-shift by 2, then add 8. // Sign-extend, left-shift by 2, then add 8.
return ((((inst & kBranchOffsetMask) << 8) >> 6) + Instr::kPCReadOffset); return ((((inst & kBranchOffsetMask) << 8) >> 6) + Instr::kPCReadOffset);
} }
#endif
static int32_t DecodeARMv7LoadImmediate(int32_t movt, int32_t movw) { static int32_t DecodeARMv7LoadImmediate(int32_t movt, int32_t movw) {
int32_t offset = 0; int32_t offset = 0;
......
...@@ -147,18 +147,14 @@ class Operand : public ValueObject { ...@@ -147,18 +147,14 @@ class Operand : public ValueObject {
type_ = 1; type_ = 1;
encoding_ = (rotate << kRotateShift) | (immed8 << kImmed8Shift); encoding_ = (rotate << kRotateShift) | (immed8 << kImmed8Shift);
} }
#endif
#if 0
// Moved to decodeOperand() in IceAssemblerARM32.cpp // Moved to decodeOperand() in IceAssemblerARM32.cpp
// Data-processing operands - Register. // Data-processing operands - Register.
explicit Operand(Register rm) { explicit Operand(Register rm) {
type_ = 0; type_ = 0;
encoding_ = static_cast<uint32_t>(rm); encoding_ = static_cast<uint32_t>(rm);
} }
#endif
#if 0
// Moved to encodeShiftRotateImm5() // Moved to encodeShiftRotateImm5()
// Data-processing operands - Logical shift/rotate by immediate. // Data-processing operands - Logical shift/rotate by immediate.
Operand(Register rm, Shift shift, uint32_t shift_imm) { Operand(Register rm, Shift shift, uint32_t shift_imm) {
...@@ -1121,11 +1117,10 @@ class Assembler : public ValueObject { ...@@ -1121,11 +1117,10 @@ class Assembler : public ValueObject {
Register rn, Register rn,
Register rd, Register rd,
Operand o); Operand o);
#endif
// Moved to ARM32::AssemblerARM32::emitType05()
void EmitType5(Condition cond, int32_t offset, bool link); void EmitType5(Condition cond, int32_t offset, bool link);
#if 0
// Moved to ARM32::AssemberARM32::emitMemOp() // Moved to ARM32::AssemberARM32::emitMemOp()
void EmitMemOp(Condition cond, void EmitMemOp(Condition cond,
bool load, bool load,
...@@ -1214,8 +1209,12 @@ class Assembler : public ValueObject { ...@@ -1214,8 +1209,12 @@ class Assembler : public ValueObject {
void EmitFarBranch(Condition cond, int32_t offset, bool link); void EmitFarBranch(Condition cond, int32_t offset, bool link);
void EmitBranch(Condition cond, Label* label, bool link); void EmitBranch(Condition cond, Label* label, bool link);
#if 0
// Moved to ARM32::AssemblerARM32::encodeBranchoffset().
int32_t EncodeBranchOffset(int32_t offset, int32_t inst); int32_t EncodeBranchOffset(int32_t offset, int32_t inst);
// Moved to ARM32::AssemberARM32::decodeBranchOffset().
static int32_t DecodeBranchOffset(int32_t inst); static int32_t DecodeBranchOffset(int32_t inst);
#endif
int32_t EncodeTstOffset(int32_t offset, int32_t inst); int32_t EncodeTstOffset(int32_t offset, int32_t inst);
int32_t DecodeTstOffset(int32_t inst); int32_t DecodeTstOffset(int32_t inst);
......
...@@ -47,6 +47,9 @@ public: ...@@ -47,6 +47,9 @@ public:
assert(!isLinked()); assert(!isLinked());
} }
/// Returns the encoded position stored in the label.
intptr_t getEncodedPosition() const { return Position; }
/// Returns the position for bound labels (branches that come after this are /// Returns the position for bound labels (branches that come after this are
/// considered backward branches). Cannot be used for unused or linked labels. /// considered backward branches). Cannot be used for unused or linked labels.
intptr_t getPosition() const { intptr_t getPosition() const {
...@@ -77,13 +80,13 @@ public: ...@@ -77,13 +80,13 @@ public:
assert(isBound()); assert(isBound());
} }
protected:
void linkTo(intptr_t position) { void linkTo(intptr_t position) {
assert(!isBound()); assert(!isBound());
Position = position + kWordSize; Position = position + kWordSize;
assert(isLinked()); assert(isLinked());
} }
protected:
intptr_t Position = 0; intptr_t Position = 0;
// TODO(jvoung): why are labels offset by this? // TODO(jvoung): why are labels offset by this?
......
...@@ -27,66 +27,78 @@ ...@@ -27,66 +27,78 @@
namespace { namespace {
using namespace Ice; using namespace Ice;
using namespace Ice::ARM32;
// The following define individual bits. // The following define individual bits.
static constexpr uint32_t B0 = 1; static constexpr IValueT B0 = 1;
static constexpr uint32_t B1 = 1 << 1; static constexpr IValueT B1 = 1 << 1;
static constexpr uint32_t B2 = 1 << 2; static constexpr IValueT B2 = 1 << 2;
static constexpr uint32_t B3 = 1 << 3; static constexpr IValueT B3 = 1 << 3;
static constexpr uint32_t B4 = 1 << 4; static constexpr IValueT B4 = 1 << 4;
static constexpr uint32_t B5 = 1 << 5; static constexpr IValueT B5 = 1 << 5;
static constexpr uint32_t B6 = 1 << 6; static constexpr IValueT B6 = 1 << 6;
static constexpr uint32_t B21 = 1 << 21; static constexpr IValueT B21 = 1 << 21;
static constexpr uint32_t B24 = 1 << 24; static constexpr IValueT B24 = 1 << 24;
// Constants used for the decoding or encoding of the individual fields of // Constants used for the decoding or encoding of the individual fields of
// instructions. Based on ARM section A5.1. // instructions. Based on ARM section A5.1.
static constexpr uint32_t L = 1 << 20; // load (or store) static constexpr IValueT L = 1 << 20; // load (or store)
static constexpr uint32_t W = 1 << 21; // writeback base register (or leave static constexpr IValueT W = 1 << 21; // writeback base register
// unchanged) // (or leave unchanged)
static constexpr uint32_t B = 1 << 22; // unsigned byte (or word) static constexpr IValueT B = 1 << 22; // unsigned byte (or word)
static constexpr uint32_t U = 1 << 23; // positive (or negative) offset/index static constexpr IValueT U = 1 << 23; // positive (or negative)
static constexpr uint32_t P = 1 << 24; // offset/pre-indexed addressing (or // offset/index
static constexpr IValueT P = 1 << 24; // offset/pre-indexed
// addressing (or
// post-indexed addressing) // post-indexed addressing)
static constexpr uint32_t kConditionShift = 28; static constexpr IValueT kConditionShift = 28;
static constexpr uint32_t kOpcodeShift = 21; static constexpr IValueT kLinkShift = 24;
static constexpr uint32_t kRdShift = 12; static constexpr IValueT kOpcodeShift = 21;
static constexpr uint32_t kRmShift = 0; static constexpr IValueT kRdShift = 12;
static constexpr uint32_t kRnShift = 16; static constexpr IValueT kRmShift = 0;
static constexpr uint32_t kSShift = 20; static constexpr IValueT kRnShift = 16;
static constexpr uint32_t kTypeShift = 25; static constexpr IValueT kSShift = 20;
static constexpr IValueT kTypeShift = 25;
// Immediate instruction fields encoding. // Immediate instruction fields encoding.
static constexpr uint32_t kImmed8Bits = 8; static constexpr IValueT kImmed8Bits = 8;
static constexpr uint32_t kImmed8Shift = 0; static constexpr IValueT kImmed8Shift = 0;
static constexpr uint32_t kRotateBits = 4; static constexpr IValueT kRotateBits = 4;
static constexpr uint32_t kRotateShift = 8; static constexpr IValueT kRotateShift = 8;
// Shift instruction register fields encodings. // Shift instruction register fields encodings.
static constexpr uint32_t kShiftImmShift = 7; static constexpr IValueT kShiftImmShift = 7;
static constexpr uint32_t kShiftImmBits = 5; static constexpr IValueT kShiftImmBits = 5;
static constexpr uint32_t kShiftShift = 5; static constexpr IValueT kShiftShift = 5;
static constexpr uint32_t kImmed12Bits = 12; static constexpr IValueT kImmed12Bits = 12;
static constexpr uint32_t kImm12Shift = 0; static constexpr IValueT kImm12Shift = 0;
// Type of instruction encoding (bits 25-27). See ARM section A5.1 // Type of instruction encoding (bits 25-27). See ARM section A5.1
static constexpr uint32_t kInstTypeDataRegister = 0; // i.e. 000 static constexpr IValueT kInstTypeDataRegister = 0; // i.e. 000
static constexpr uint32_t kInstTypeDataImmediate = 1; // i.e. 001 static constexpr IValueT kInstTypeDataImmediate = 1; // i.e. 001
static constexpr uint32_t kInstTypeMemImmediate = 2; // i.e. 010 static constexpr IValueT kInstTypeMemImmediate = 2; // i.e. 010
inline uint32_t encodeBool(bool b) { return b ? 1 : 0; } // Offset modifier to current PC for next instruction. The offset is off by 8
// due to the way the ARM CPUs read PC.
static constexpr IOffsetT kPCReadOffset = 8;
inline uint32_t encodeGPRRegister(RegARM32::GPRRegister Rn) { // Mask to pull out PC offset from branch (b) instruction.
return static_cast<uint32_t>(Rn); static constexpr int kBranchOffsetBits = 24;
static constexpr IOffsetT kBranchOffsetMask = 0x00ffffff;
inline IValueT encodeBool(bool B) { return B ? 1 : 0; }
inline IValueT encodeGPRRegister(RegARM32::GPRRegister Rn) {
return static_cast<IValueT>(Rn);
} }
inline bool isGPRRegisterDefined(RegARM32::GPRRegister R) { inline bool isGPRRegisterDefined(RegARM32::GPRRegister R) {
return R != RegARM32::Encoded_Not_GPR; return R != RegARM32::Encoded_Not_GPR;
} }
inline bool isGPRRegisterDefined(uint32_t R) { inline bool isGPRRegisterDefined(IValueT R) {
return R != encodeGPRRegister(RegARM32::Encoded_Not_GPR); return R != encodeGPRRegister(RegARM32::Encoded_Not_GPR);
} }
...@@ -94,11 +106,11 @@ inline bool isConditionDefined(CondARM32::Cond Cond) { ...@@ -94,11 +106,11 @@ inline bool isConditionDefined(CondARM32::Cond Cond) {
return Cond != CondARM32::kNone; return Cond != CondARM32::kNone;
} }
inline uint32_t encodeCondition(CondARM32::Cond Cond) { inline IValueT encodeCondition(CondARM32::Cond Cond) {
return static_cast<uint32_t>(Cond); return static_cast<IValueT>(Cond);
} }
uint32_t encodeShift(OperandARM32::ShiftKind Shift) { IValueT encodeShift(OperandARM32::ShiftKind Shift) {
// Follows encoding in ARM section A8.4.1 "Constant shifts". // Follows encoding in ARM section A8.4.1 "Constant shifts".
switch (Shift) { switch (Shift) {
case OperandARM32::kNoShift: case OperandARM32::kNoShift:
...@@ -115,17 +127,17 @@ uint32_t encodeShift(OperandARM32::ShiftKind Shift) { ...@@ -115,17 +127,17 @@ uint32_t encodeShift(OperandARM32::ShiftKind Shift) {
} }
// Returns the bits in the corresponding masked value. // Returns the bits in the corresponding masked value.
inline uint32_t mask(uint32_t Value, uint32_t Shift, uint32_t Bits) { inline IValueT mask(IValueT Value, IValueT Shift, IValueT Bits) {
return (Value >> Shift) & ((1 << Bits) - 1); return (Value >> Shift) & ((1 << Bits) - 1);
} }
// Extract out a Bit in Value. // Extract out a Bit in Value.
inline bool isBitSet(uint32_t Bit, uint32_t Value) { inline bool isBitSet(IValueT Bit, IValueT Value) {
return (Value & Bit) == Bit; return (Value & Bit) == Bit;
} }
// Returns the GPR register at given Shift in Value. // Returns the GPR register at given Shift in Value.
inline RegARM32::GPRRegister getGPRReg(uint32_t Shift, uint32_t Value) { inline RegARM32::GPRRegister getGPRReg(IValueT Shift, IValueT Value) {
return static_cast<RegARM32::GPRRegister>((Value >> Shift) & 0xF); return static_cast<RegARM32::GPRRegister>((Value >> Shift) & 0xF);
} }
...@@ -147,35 +159,33 @@ enum DecodedResult { ...@@ -147,35 +159,33 @@ enum DecodedResult {
// Encodes iiiiitt0mmmm for data-processing (2nd) operands where iiiii=Imm5, // Encodes iiiiitt0mmmm for data-processing (2nd) operands where iiiii=Imm5,
// tt=Shift, and mmmm=Rm. // tt=Shift, and mmmm=Rm.
uint32_t encodeShiftRotateImm5(uint32_t Rm, OperandARM32::ShiftKind Shift, IValueT encodeShiftRotateImm5(IValueT Rm, OperandARM32::ShiftKind Shift,
uint32_t imm5) { IValueT imm5) {
(void)kShiftImmBits; (void)kShiftImmBits;
assert(imm5 < (1 << kShiftImmBits)); assert(imm5 < (1 << kShiftImmBits));
return (imm5 << kShiftImmShift) | (encodeShift(Shift) << kShiftShift) | Rm; return (imm5 << kShiftImmShift) | (encodeShift(Shift) << kShiftShift) | Rm;
} }
DecodedResult decodeOperand(const Operand *Opnd, uint32_t &Value) { DecodedResult decodeOperand(const Operand *Opnd, IValueT &Value) {
if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) { if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) {
if (Var->hasReg()) { if (Var->hasReg()) {
Value = Var->getRegNum(); Value = Var->getRegNum();
return DecodedAsRegister; return DecodedAsRegister;
} }
} else if (const auto *FlexImm = llvm::dyn_cast<OperandARM32FlexImm>(Opnd)) { } else if (const auto *FlexImm = llvm::dyn_cast<OperandARM32FlexImm>(Opnd)) {
const uint32_t Immed8 = FlexImm->getImm(); const IValueT Immed8 = FlexImm->getImm();
const uint32_t Rotate = FlexImm->getRotateAmt(); const IValueT Rotate = FlexImm->getRotateAmt();
assert((Rotate < (1 << kRotateBits)) && (Immed8 < (1 << kImmed8Bits))); if (!((Rotate < (1 << kRotateBits)) && (Immed8 < (1 << kImmed8Bits))))
// TODO(kschimpf): Remove void casts when MINIMAL build allows. return CantDecode;
(void)kRotateBits;
(void)kImmed8Bits;
Value = (Rotate << kRotateShift) | (Immed8 << kImmed8Shift); Value = (Rotate << kRotateShift) | (Immed8 << kImmed8Shift);
return DecodedAsRotatedImm8; return DecodedAsRotatedImm8;
} }
return CantDecode; return CantDecode;
} }
uint32_t decodeImmRegOffset(RegARM32::GPRRegister Reg, int32_t Offset, IValueT decodeImmRegOffset(RegARM32::GPRRegister Reg, IOffsetT Offset,
OperandARM32Mem::AddrMode Mode) { OperandARM32Mem::AddrMode Mode) {
uint32_t Value = Mode | (encodeGPRRegister(Reg) << kRnShift); IValueT Value = Mode | (encodeGPRRegister(Reg) << kRnShift);
if (Offset < 0) { if (Offset < 0) {
Value = (Value ^ U) | -Offset; // Flip U to adjust sign. Value = (Value ^ U) | -Offset; // Flip U to adjust sign.
} else { } else {
...@@ -186,12 +196,12 @@ uint32_t decodeImmRegOffset(RegARM32::GPRRegister Reg, int32_t Offset, ...@@ -186,12 +196,12 @@ uint32_t decodeImmRegOffset(RegARM32::GPRRegister Reg, int32_t Offset,
// Decodes memory address Opnd, and encodes that information into Value, // Decodes memory address Opnd, and encodes that information into Value,
// based on how ARM represents the address. Returns how the value was encoded. // based on how ARM represents the address. Returns how the value was encoded.
DecodedResult decodeAddress(const Operand *Opnd, uint32_t &Value) { DecodedResult decodeAddress(const Operand *Opnd, IValueT &Value) {
if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) { if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) {
// Should be a stack variable, with an offset. // Should be a stack variable, with an offset.
if (Var->hasReg()) if (Var->hasReg())
return CantDecode; return CantDecode;
const int32_t Offset = Var->getStackOffset(); const IOffsetT Offset = Var->getStackOffset();
if (!Utils::IsAbsoluteUint(12, Offset)) if (!Utils::IsAbsoluteUint(12, Offset))
return CantDecode; return CantDecode;
Value = decodeImmRegOffset(RegARM32::Encoded_Reg_sp, Offset, Value = decodeImmRegOffset(RegARM32::Encoded_Reg_sp, Offset,
...@@ -201,11 +211,18 @@ DecodedResult decodeAddress(const Operand *Opnd, uint32_t &Value) { ...@@ -201,11 +211,18 @@ DecodedResult decodeAddress(const Operand *Opnd, uint32_t &Value) {
return CantDecode; return CantDecode;
} }
// Checks that Offset can fit in imm24 constant of branch (b) instruction.
bool canEncodeBranchOffset(IOffsetT Offset) {
return Utils::IsAligned(Offset, 4) &&
Utils::IsInt(kBranchOffsetBits, Offset >> 2);
}
} // end of anonymous namespace } // end of anonymous namespace
namespace Ice { namespace Ice {
namespace ARM32 {
void ARM32::AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) { void AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) {
if (BuildDefs::dump() && !Ctx->getFlags().getDisableHybridAssembly()) { if (BuildDefs::dump() && !Ctx->getFlags().getDisableHybridAssembly()) {
// Generate label name so that branches can find it. // Generate label name so that branches can find it.
constexpr SizeT InstSize = 0; constexpr SizeT InstSize = 0;
...@@ -217,8 +234,7 @@ void ARM32::AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) { ...@@ -217,8 +234,7 @@ void ARM32::AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) {
this->bind(L); this->bind(L);
} }
Label *ARM32::AssemblerARM32::getOrCreateLabel(SizeT Number, Label *AssemblerARM32::getOrCreateLabel(SizeT Number, LabelVector &Labels) {
LabelVector &Labels) {
Label *L = nullptr; Label *L = nullptr;
if (Number == Labels.size()) { if (Number == Labels.size()) {
L = new (this->allocate<Label>()) Label(); L = new (this->allocate<Label>()) Label();
...@@ -236,21 +252,41 @@ Label *ARM32::AssemblerARM32::getOrCreateLabel(SizeT Number, ...@@ -236,21 +252,41 @@ Label *ARM32::AssemblerARM32::getOrCreateLabel(SizeT Number,
return L; return L;
} }
void ARM32::AssemblerARM32::bind(Label *label) { IValueT AssemblerARM32::encodeBranchOffset(IOffsetT Offset, IValueT Inst) {
intptr_t bound = Buffer.size(); // Adjust offset to the way ARM CPUs read PC.
assert(!label->isBound()); // Labels can only be bound once. Offset -= kPCReadOffset;
while (label->isLinked()) {
intptr_t position = label->getLinkPosition(); bool IsGoodOffset = canEncodeBranchOffset(Offset);
intptr_t next = Buffer.load<int32_t>(position); assert(IsGoodOffset);
Buffer.store<int32_t>(position, bound - (position + 4)); // Note: Following cast is for MINIMAL build.
label->setPosition(next); (void)IsGoodOffset;
// Properly preserve only the bits supported in the instruction.
Offset >>= 2;
Offset &= kBranchOffsetMask;
return (Inst & ~kBranchOffsetMask) | Offset;
}
IOffsetT AssemblerARM32::decodeBranchOffset(IValueT Inst) {
// Sign-extend, left-shift by 2, and adjust to the way ARM CPUs read PC.
IOffsetT Offset = static_cast<IOffsetT>((Inst & kBranchOffsetMask) << 8);
return (Offset >> 6) + kPCReadOffset;
}
void AssemblerARM32::bind(Label *L) {
IOffsetT BoundPc = Buffer.size();
assert(!L->isBound()); // Labels can only be bound once.
while (L->isLinked()) {
IOffsetT Position = L->getLinkPosition();
IOffsetT Dest = BoundPc - Position;
IValueT Inst = Buffer.load<IValueT>(Position);
Buffer.store<IValueT>(Position, encodeBranchOffset(Dest, Inst));
L->setPosition(decodeBranchOffset(Inst));
} }
// TODO(kschimpf) Decide if we have near jumps. L->bindTo(BoundPc);
label->bindTo(bound);
} }
void ARM32::AssemblerARM32::emitTextInst(const std::string &Text, void AssemblerARM32::emitTextInst(const std::string &Text, SizeT InstSize) {
SizeT InstSize) {
AssemblerBuffer::EnsureCapacity ensured(&Buffer); AssemblerBuffer::EnsureCapacity ensured(&Buffer);
AssemblerFixup *F = createTextFixup(Text, InstSize); AssemblerFixup *F = createTextFixup(Text, InstSize);
emitFixup(F); emitFixup(F);
...@@ -258,44 +294,72 @@ void ARM32::AssemblerARM32::emitTextInst(const std::string &Text, ...@@ -258,44 +294,72 @@ void ARM32::AssemblerARM32::emitTextInst(const std::string &Text,
Buffer.emit<char>(0); Buffer.emit<char>(0);
} }
void ARM32::AssemblerARM32::emitType01(CondARM32::Cond Cond, uint32_t Type, void AssemblerARM32::emitType01(CondARM32::Cond Cond, IValueT Type,
uint32_t Opcode, bool SetCc, uint32_t Rn, IValueT Opcode, bool SetCc, IValueT Rn,
uint32_t Rd, uint32_t Imm12) { IValueT Rd, IValueT Imm12) {
assert(isGPRRegisterDefined(Rd)); assert(isGPRRegisterDefined(Rd));
// TODO(kschimpf): Remove void cast when MINIMAL build allows. // TODO(kschimpf): Remove void cast when MINIMAL build allows.
(void)isGPRRegisterDefined(Rd); (void)isGPRRegisterDefined(Rd);
assert(Cond != CondARM32::kNone); assert(Cond != CondARM32::kNone);
AssemblerBuffer::EnsureCapacity ensured(&Buffer); AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) | const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) |
(Type << kTypeShift) | (Opcode << kOpcodeShift) | (Type << kTypeShift) | (Opcode << kOpcodeShift) |
(encodeBool(SetCc) << kSShift) | (Rn << kRnShift) | (encodeBool(SetCc) << kSShift) | (Rn << kRnShift) |
(Rd << kRdShift) | Imm12; (Rd << kRdShift) | Imm12;
emitInst(Encoding); emitInst(Encoding);
} }
void ARM32::AssemblerARM32::emitMemOp(CondARM32::Cond Cond, uint32_t InstType, void AssemblerARM32::emitType05(CondARM32::Cond Cond, IOffsetT Offset,
bool IsLoad, bool IsByte, uint32_t Rt, bool Link) {
uint32_t Address) { // cccc101liiiiiiiiiiiiiiiiiiiiiiii where cccc=Cond, l=Link, and
// iiiiiiiiiiiiiiiiiiiiiiii=
// EncodedBranchOffset(cccc101l000000000000000000000000, Offset);
if (!isConditionDefined(Cond))
return setNeedsTextFixup();
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
IValueT Encoding = static_cast<int32_t>(Cond) << kConditionShift |
5 << kTypeShift | (Link ? 1 : 0) << kLinkShift;
Encoding = encodeBranchOffset(Offset, Encoding);
emitInst(Encoding);
}
void AssemblerARM32::emitBranch(Label *L, CondARM32::Cond Cond, bool Link) {
// TODO(kschimpf): Handle far jumps.
if (L->isBound()) {
const int32_t Dest = L->getPosition() - Buffer.size();
emitType05(Cond, Dest, Link);
return;
}
const IOffsetT Position = Buffer.size();
// Use the offset field of the branch instruction for linking the sites.
emitType05(Cond, L->getEncodedPosition(), Link);
if (!needsTextFixup())
L->linkTo(Position);
}
void AssemblerARM32::emitMemOp(CondARM32::Cond Cond, IValueT InstType,
bool IsLoad, bool IsByte, IValueT Rt,
IValueT Address) {
assert(isGPRRegisterDefined(Rt)); assert(isGPRRegisterDefined(Rt));
assert(Cond != CondARM32::kNone); assert(Cond != CondARM32::kNone);
AssemblerBuffer::EnsureCapacity ensured(&Buffer); AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) | const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) |
(InstType << kTypeShift) | (IsLoad ? L : 0) | (InstType << kTypeShift) | (IsLoad ? L : 0) |
(IsByte ? B : 0) | (Rt << kRdShift) | Address; (IsByte ? B : 0) | (Rt << kRdShift) | Address;
emitInst(Encoding); emitInst(Encoding);
} }
void ARM32::AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn, void AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags, const Operand *OpSrc1, bool SetFlags,
CondARM32::Cond Cond) { CondARM32::Cond Cond) {
uint32_t Rd; IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister) if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup(); return setNeedsTextFixup();
uint32_t Rn; IValueT Rn;
if (decodeOperand(OpRn, Rn) != DecodedAsRegister) if (decodeOperand(OpRn, Rn) != DecodedAsRegister)
return setNeedsTextFixup(); return setNeedsTextFixup();
constexpr uint32_t Add = B2; // 0100 constexpr IValueT Add = B2; // 0100
uint32_t Src1Value; IValueT Src1Value;
// TODO(kschimpf) Other possible decodings of add. // TODO(kschimpf) Other possible decodings of add.
switch (decodeOperand(OpSrc1, Src1Value)) { switch (decodeOperand(OpSrc1, Src1Value)) {
default: default:
...@@ -332,18 +396,22 @@ void ARM32::AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn, ...@@ -332,18 +396,22 @@ void ARM32::AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
}; };
} }
void ARM32::AssemblerARM32::bkpt(uint16_t Imm16) { void AssemblerARM32::b(Label *L, CondARM32::Cond Cond) {
emitBranch(L, Cond, false);
}
void AssemblerARM32::bkpt(uint16_t Imm16) {
// BKPT - ARM section A*.8.24 - encoding A1: // BKPT - ARM section A*.8.24 - encoding A1:
// bkpt #<Imm16> // bkpt #<Imm16>
// //
// cccc00010010iiiiiiiiiiii0111iiii where cccc=AL and iiiiiiiiiiiiiiii=Imm16 // cccc00010010iiiiiiiiiiii0111iiii where cccc=AL and iiiiiiiiiiiiiiii=Imm16
AssemblerBuffer::EnsureCapacity ensured(&Buffer); AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (CondARM32::AL << kConditionShift) | B24 | B21 | const IValueT Encoding = (CondARM32::AL << kConditionShift) | B24 | B21 |
((Imm16 >> 4) << 8) | B6 | B5 | B4 | (Imm16 & 0xf); ((Imm16 >> 4) << 8) | B6 | B5 | B4 | (Imm16 & 0xf);
emitInst(Encoding); emitInst(Encoding);
} }
void ARM32::AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) { void AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) {
// BX - ARM section A8.8.27, encoding A1: // BX - ARM section A8.8.27, encoding A1:
// bx<c> <Rm> // bx<c> <Rm>
// //
...@@ -351,18 +419,18 @@ void ARM32::AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) { ...@@ -351,18 +419,18 @@ void ARM32::AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) {
if (!(isGPRRegisterDefined(Rm) && isConditionDefined(Cond))) if (!(isGPRRegisterDefined(Rm) && isConditionDefined(Cond)))
return setNeedsTextFixup(); return setNeedsTextFixup();
AssemblerBuffer::EnsureCapacity ensured(&Buffer); AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) | B24 | const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | B24 |
B21 | (0xfff << 8) | B4 | B21 | (0xfff << 8) | B4 |
(encodeGPRRegister(Rm) << kRmShift); (encodeGPRRegister(Rm) << kRmShift);
emitInst(Encoding); emitInst(Encoding);
} }
void ARM32::AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress, void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond) { CondARM32::Cond Cond) {
uint32_t Rt; IValueT Rt;
if (decodeOperand(OpRt, Rt) != DecodedAsRegister) if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
return setNeedsTextFixup(); return setNeedsTextFixup();
uint32_t Address; IValueT Address;
if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset) if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset)
return setNeedsTextFixup(); return setNeedsTextFixup();
// LDR (immediate) - ARM section A8.8.63, encoding A1: // LDR (immediate) - ARM section A8.8.63, encoding A1:
...@@ -393,12 +461,12 @@ void ARM32::AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress, ...@@ -393,12 +461,12 @@ void ARM32::AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address); emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address);
} }
void ARM32::AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc, void AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
CondARM32::Cond Cond) { CondARM32::Cond Cond) {
uint32_t Rd; IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister) if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup(); return setNeedsTextFixup();
uint32_t Src; IValueT Src;
// TODO(kschimpf) Handle other forms of mov. // TODO(kschimpf) Handle other forms of mov.
if (decodeOperand(OpSrc, Src) != DecodedAsRotatedImm8) if (decodeOperand(OpSrc, Src) != DecodedAsRotatedImm8)
return setNeedsTextFixup(); return setNeedsTextFixup();
...@@ -412,17 +480,17 @@ void ARM32::AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc, ...@@ -412,17 +480,17 @@ void ARM32::AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
if ((Rd == RegARM32::Encoded_Reg_pc && SetFlags)) if ((Rd == RegARM32::Encoded_Reg_pc && SetFlags))
// Conditions of rule violated. // Conditions of rule violated.
return setNeedsTextFixup(); return setNeedsTextFixup();
constexpr uint32_t Rn = 0; constexpr IValueT Rn = 0;
constexpr uint32_t Mov = B3 | B2 | B0; // 1101. constexpr IValueT Mov = B3 | B2 | B0; // 1101.
emitType01(Cond, kInstTypeDataImmediate, Mov, SetFlags, Rn, Rd, Src); emitType01(Cond, kInstTypeDataImmediate, Mov, SetFlags, Rn, Rd, Src);
} }
void ARM32::AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress, void AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond) { CondARM32::Cond Cond) {
uint32_t Rt; IValueT Rt;
if (decodeOperand(OpRt, Rt) != DecodedAsRegister) if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
return setNeedsTextFixup(); return setNeedsTextFixup();
uint32_t Address; IValueT Address;
if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset) if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset)
return setNeedsTextFixup(); return setNeedsTextFixup();
// STR (immediate) - ARM section A8.8.204, encoding A1: // STR (immediate) - ARM section A8.8.204, encoding A1:
...@@ -453,17 +521,17 @@ void ARM32::AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress, ...@@ -453,17 +521,17 @@ void ARM32::AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address); emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address);
} }
void ARM32::AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn, void AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags, const Operand *OpSrc1, bool SetFlags,
CondARM32::Cond Cond) { CondARM32::Cond Cond) {
uint32_t Rd; IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister) if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup(); return setNeedsTextFixup();
uint32_t Rn; IValueT Rn;
if (decodeOperand(OpRn, Rn) != DecodedAsRegister) if (decodeOperand(OpRn, Rn) != DecodedAsRegister)
return setNeedsTextFixup(); return setNeedsTextFixup();
constexpr uint32_t Sub = B1; // 0010 constexpr IValueT Sub = B1; // 0010
uint32_t Src1Value; IValueT Src1Value;
// TODO(kschimpf) Other possible decodings of sub. // TODO(kschimpf) Other possible decodings of sub.
switch (decodeOperand(OpSrc1, Src1Value)) { switch (decodeOperand(OpSrc1, Src1Value)) {
default: default:
...@@ -500,4 +568,5 @@ void ARM32::AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn, ...@@ -500,4 +568,5 @@ void ARM32::AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
} }
} }
} // end of namespace ARM32
} // end of namespace Ice } // end of namespace Ice
...@@ -38,6 +38,12 @@ ...@@ -38,6 +38,12 @@
namespace Ice { namespace Ice {
namespace ARM32 { namespace ARM32 {
/// Encoding of an ARM 32-bit instruction.
using IValueT = uint32_t;
/// An Offset value (+/-) used in an ARM 32-bit instruction.
using IOffsetT = int32_t;
class AssemblerARM32 : public Assembler { class AssemblerARM32 : public Assembler {
AssemblerARM32() = delete; AssemblerARM32() = delete;
AssemblerARM32(const AssemblerARM32 &) = delete; AssemblerARM32(const AssemblerARM32 &) = delete;
...@@ -63,8 +69,8 @@ public: ...@@ -63,8 +69,8 @@ public:
void alignFunction() override { void alignFunction() override {
const SizeT Align = 1 << getBundleAlignLog2Bytes(); const SizeT Align = 1 << getBundleAlignLog2Bytes();
SizeT BytesNeeded = Utils::OffsetToAlignment(Buffer.getPosition(), Align); SizeT BytesNeeded = Utils::OffsetToAlignment(Buffer.getPosition(), Align);
constexpr uint32_t UndefinedInst = 0xe7fedef0; // udf #60896 constexpr IValueT UndefinedInst = 0xe7fedef0; // udf #60896
constexpr SizeT InstSize = sizeof(int32_t); constexpr SizeT InstSize = sizeof(IValueT);
assert(BytesNeeded % InstSize == 0); assert(BytesNeeded % InstSize == 0);
while (BytesNeeded > 0) { while (BytesNeeded > 0) {
AssemblerBuffer::EnsureCapacity ensured(&Buffer); AssemblerBuffer::EnsureCapacity ensured(&Buffer);
...@@ -94,7 +100,13 @@ public: ...@@ -94,7 +100,13 @@ public:
return CfgNodeLabels[NodeNumber]; return CfgNodeLabels[NodeNumber];
} }
void bindCfgNodeLabel(const CfgNode *Node) override; Label *getOrCreateCfgNodeLabel(SizeT NodeNumber) {
return getOrCreateLabel(NodeNumber, CfgNodeLabels);
}
Label *getOrCreateLocalLabel(SizeT Number) {
return getOrCreateLabel(Number, LocalLabels);
}
void bindLocalLabel(SizeT Number) { void bindLocalLabel(SizeT Number) {
Label *L = getOrCreateLocalLabel(Number); Label *L = getOrCreateLocalLabel(Number);
...@@ -115,6 +127,8 @@ public: ...@@ -115,6 +127,8 @@ public:
void add(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, void add(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
bool SetFlags, CondARM32::Cond Cond); bool SetFlags, CondARM32::Cond Cond);
void b(Label *L, CondARM32::Cond Cond);
void bkpt(uint16_t Imm16); void bkpt(uint16_t Imm16);
void ldr(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond); void ldr(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond);
...@@ -132,7 +146,7 @@ public: ...@@ -132,7 +146,7 @@ public:
return Asm->getKind() == Asm_ARM32; return Asm->getKind() == Asm_ARM32;
} }
void emitTextInst(const std::string &Text, SizeT InstSize = sizeof(uint32_t)); void emitTextInst(const std::string &Text, SizeT InstSize = sizeof(IValueT));
private: private:
// A vector of pool-allocated x86 labels for CFG nodes. // A vector of pool-allocated x86 labels for CFG nodes.
...@@ -142,26 +156,32 @@ private: ...@@ -142,26 +156,32 @@ private:
LabelVector LocalLabels; LabelVector LocalLabels;
Label *getOrCreateLabel(SizeT Number, LabelVector &Labels); Label *getOrCreateLabel(SizeT Number, LabelVector &Labels);
Label *getOrCreateCfgNodeLabel(SizeT NodeNumber) {
return getOrCreateLabel(NodeNumber, CfgNodeLabels);
}
Label *getOrCreateLocalLabel(SizeT Number) {
return getOrCreateLabel(Number, LocalLabels);
}
void emitInst(uint32_t Value) { Buffer.emit<uint32_t>(Value); } void bindCfgNodeLabel(const CfgNode *Node) override;
void emitInst(IValueT Value) { Buffer.emit<IValueT>(Value); }
// Pattern cccctttoooosnnnnddddiiiiiiiiiiii where cccc=Cond, ttt=Type, // Pattern cccctttoooosnnnnddddiiiiiiiiiiii where cccc=Cond, ttt=Type,
// oooo=Opcode, nnnn=Rn, dddd=Rd, iiiiiiiiiiii=imm12 (See ARM section A5.2.3). // oooo=Opcode, nnnn=Rn, dddd=Rd, iiiiiiiiiiii=imm12 (See ARM section A5.2.3).
void emitType01(CondARM32::Cond Cond, uint32_t Type, uint32_t Opcode, void emitType01(CondARM32::Cond Cond, IValueT Type, IValueT Opcode,
bool SetCc, uint32_t Rn, uint32_t Rd, uint32_t imm12); bool SetCc, IValueT Rn, IValueT Rd, IValueT imm12);
void emitType05(CondARM32::Cond COnd, int32_t Offset, bool Link);
// Pattern ccccoooaabalnnnnttttaaaaaaaaaaaa where cccc=Cond, ooo=InstType, // Pattern ccccoooaabalnnnnttttaaaaaaaaaaaa where cccc=Cond, ooo=InstType,
// l=isLoad, b=isByte, and aaa0a0aaaa0000aaaaaaaaaaaa=Address. Note that // l=isLoad, b=isByte, and aaa0a0aaaa0000aaaaaaaaaaaa=Address. Note that
// Address is assumed to be defined by decodeAddress() in // Address is assumed to be defined by decodeAddress() in
// IceAssemblerARM32.cpp. // IceAssemblerARM32.cpp.
void emitMemOp(CondARM32::Cond Cond, uint32_t InstType, bool IsLoad, void emitMemOp(CondARM32::Cond Cond, IValueT InstType, bool IsLoad,
bool IsByte, uint32_t Rt, uint32_t Address); bool IsByte, uint32_t Rt, uint32_t Address);
void emitBranch(Label *L, CondARM32::Cond, bool Link);
// Encodes the given Offset into the branch instruction Inst.
IValueT encodeBranchOffset(IOffsetT Offset, IValueT Inst);
// Returns the offset encoded in the branch instruction Inst.
static IOffsetT decodeBranchOffset(IValueT Inst);
}; };
} // end of namespace ARM32 } // end of namespace ARM32
......
...@@ -734,6 +734,23 @@ void InstARM32Br::emit(const Cfg *Func) const { ...@@ -734,6 +734,23 @@ void InstARM32Br::emit(const Cfg *Func) const {
} }
} }
void InstARM32Br::emitIAS(const Cfg *Func) const {
ARM32::AssemblerARM32 *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
if (Label) {
Asm->b(Asm->getOrCreateLocalLabel(Label->getNumber()), getPredicate());
} else if (isUnconditionalBranch()) {
Asm->b(Asm->getOrCreateCfgNodeLabel(getTargetFalse()->getIndex()),
getPredicate());
} else {
Asm->b(Asm->getOrCreateCfgNodeLabel(getTargetTrue()->getIndex()),
getPredicate());
Asm->b(Asm->getOrCreateCfgNodeLabel(getTargetFalse()->getIndex()),
CondARM32::AL);
}
if (Asm->needsTextFixup())
emitUsingTextFixup(Func);
}
void InstARM32Br::dump(const Cfg *Func) const { void InstARM32Br::dump(const Cfg *Func) const {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
return; return;
......
...@@ -861,6 +861,7 @@ public: ...@@ -861,6 +861,7 @@ public:
} }
bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override; bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override;
void emit(const Cfg *Func) const override; void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override; void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return isClassof(Inst, Br); } static bool classof(const Inst *Inst) { return isClassof(Inst, Br); }
......
; Test that we correctly fix multiple forward branches.
; Compile using standalone assembler.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -Om1 \
; RUN: | FileCheck %s --check-prefix=ASM
; Show bytes in assembled standalone code.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -Om1 | FileCheck %s --check-prefix=DIS
; Compile using integrated assembler.
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --args -Om1 \
; RUN: | FileCheck %s --check-prefix=IASM
; Show bytes in assembled integrated code.
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -Om1 | FileCheck %s --check-prefix=DIS
; REQUIRES: allow_dump
define internal void @mult_fwd_branches(i32 %a, i32 %b) {
; ASM-LABEL:mult_fwd_branches:
; ASM-LABEL:.Lmult_fwd_branches$__0:
; ASM-NEXT: sub sp, sp, #12
; ASM-NEXT: str r0, [sp, #8]
; ASM-NEXT: str r1, [sp, #4]
; DIS-LABEL:00000000 <mult_fwd_branches>:
; DIS-NEXT: 0: e24dd00c
; DIS-NEXT: 4: e58d0008
; DIS-NEXT: 8: e58d1004
; IASM-LABEL:mult_fwd_branches:
; IASM-LABEL:.Lmult_fwd_branches$__0:
; IASM-NEXT: .byte 0xc
; IASM-NEXT: .byte 0xd0
; IASM-NEXT: .byte 0x4d
; IASM-NEXT: .byte 0xe2
; IASM-NEXT: .byte 0x8
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
%cmp = icmp slt i32 %a, %b
; ASM-NEXT: ldr r0, [sp, #8]
; ASM-NEXT: mov r1, #0
; ASM-NEXT: ldr r2, [sp, #4]
; ASM-NEXT: cmp r0, r2
; ASM-NEXT: movlt r1, #1
; ASM-NEXT: str r1, [sp]
; DIS-NEXT: c: e59d0008
; DIS-NEXT: 10: e3a01000
; DIS-NEXT: 14: e59d2004
; DIS-NEXT: 18: e1500002
; DIS-NEXT: 1c: b3a01001
; DIS-NEXT: 20: e58d1000
; IASM-NEXT: .byte 0x8
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; IASM-NEXT: mov r1, #0
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x20
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; IASM-NEXT: cmp r0, r2
; IASM-NEXT: movlt r1, #1
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
br i1 %cmp, label %then, label %else
; ASM-NEXT: ldr r0, [sp]
; ASM-NEXT: uxtb r0, r0
; ASM-NEXT: cmp r0, #0
; ASM-NEXT: bne .Lmult_fwd_branches$then
; ASM-NEXT: b .Lmult_fwd_branches$else
; DIS-NEXT: 24: e59d0000
; DIS-NEXT: 28: e6ef0070
; DIS-NEXT: 2c: e3500000
; DIS-NEXT: 30: 1a000000
; DIS-NEXT: 34: ea000000
; IASM-NEXT: ldr r0, [sp]
; IASM-NEXT: uxtb r0, r0
; IASM-NEXT: cmp r0, #0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x1a
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xea
then:
; ASM-LABEL:.Lmult_fwd_branches$then:
; IASM-LABEL:.Lmult_fwd_branches$then:
br label %end
; ASM-NEXT: b .Lmult_fwd_branches$end
; DIS-NEXT: 38: ea000000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xea
else:
; ASM-LABEL:.Lmult_fwd_branches$else:
; IASM-LABEL:.Lmult_fwd_branches$else:
br label %end
; ASM-NEXT: b .Lmult_fwd_branches$end
; DIS-NEXT: 3c: eaffffff
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0xea
end:
; ASM-LABEL:.Lmult_fwd_branches$end:
; IASM-LABEL: .Lmult_fwd_branches$end:
ret void
; ASM-NEXT: add sp, sp, #12
; ASM-NEXT: bx lr
; DIS-NEXT: 40: e28dd00c
; DIS-NEXT: 44: e12fff1e
; IASM-NEXT: .byte 0xc
; IASM-NEXT: .byte 0xd0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe2
; IASM-NEXT: .byte 0x1e
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0x2f
; IASM-NEXT: .byte 0xe1
}
; Test branching instructions. ; Test branching instructions.
; TODO(kschimpf): Get this working. ; TODO(kschimpf): Get this working.
; REQUIRES: allow_dump
; Compile using standalone assembler. ; Compile using standalone assembler.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -Om1 \ ; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -Om1 \
; RUN: | FileCheck %s --check-prefix=ASM ; RUN: | FileCheck %s --check-prefix=ASM
...@@ -17,8 +19,6 @@ ...@@ -17,8 +19,6 @@
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \ ; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -Om1 | FileCheck %s --check-prefix=DIS ; RUN: --args -Om1 | FileCheck %s --check-prefix=DIS
; REQUIRES: allow_dump
define internal void @simple_uncond_branch() { define internal void @simple_uncond_branch() {
; DIS-LABEL: 00000000 <simple_uncond_branch>: ; DIS-LABEL: 00000000 <simple_uncond_branch>:
; ASM-LABEL: simple_uncond_branch: ; ASM-LABEL: simple_uncond_branch:
...@@ -29,7 +29,10 @@ define internal void @simple_uncond_branch() { ...@@ -29,7 +29,10 @@ define internal void @simple_uncond_branch() {
br label %l2 br label %l2
; ASM-NEXT: b .Lsimple_uncond_branch$l2 ; ASM-NEXT: b .Lsimple_uncond_branch$l2
; IASM-NEXT: b .Lsimple_uncond_branch$l2 ; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xea
; DIS-NEXT: 0: ea000000 ; DIS-NEXT: 0: ea000000
l1: l1:
...@@ -38,7 +41,10 @@ l1: ...@@ -38,7 +41,10 @@ l1:
br label %l3 br label %l3
; ASM-NEXT: b .Lsimple_uncond_branch$l3 ; ASM-NEXT: b .Lsimple_uncond_branch$l3
; IASM-NEXT: b .Lsimple_uncond_branch$l3 ; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xea
; DIS-NEXT: 4: ea000000 ; DIS-NEXT: 4: ea000000
l2: l2:
...@@ -47,7 +53,10 @@ l2: ...@@ -47,7 +53,10 @@ l2:
br label %l1 br label %l1
; ASM-NEXT: b .Lsimple_uncond_branch$l1 ; ASM-NEXT: b .Lsimple_uncond_branch$l1
; IASM-NEXT: b .Lsimple_uncond_branch$l1 ; IASM-NEXT: .byte 0xfd
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0xea
; DIS-NEXT: 8: eafffffd ; DIS-NEXT: 8: eafffffd
l3: l3:
...@@ -60,5 +69,6 @@ l3: ...@@ -60,5 +69,6 @@ l3:
; IASM-NEXT: .byte 0xff ; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0x2f ; IASM-NEXT: .byte 0x2f
; IASM-NEXT: .byte 0xe1 ; IASM-NEXT: .byte 0xe1
; DIS-NEXT: c: e12fff1e
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment