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,
o.encoding();
Emit(encoding);
}
#endif
// Moved to ARM32::AssemblerARM32::emitType05.
void Assembler::EmitType5(Condition cond, int32_t offset, bool link) {
ASSERT(cond != kNoCondition);
int32_t encoding = static_cast<int32_t>(cond) << kConditionShift |
......@@ -101,8 +101,6 @@ void Assembler::EmitType5(Condition cond, int32_t offset, bool link) {
Emit(Assembler::EncodeBranchOffset(offset, encoding));
}
#if 0
// Moved to ARM32::AssemblerARM32::emitMemOp()
void Assembler::EmitMemOp(Condition cond,
bool load,
......@@ -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) {
EmitType01(cond, o.type(), ADD, 1, rn, rd, o);
}
#endif
#if 0
// Moved to ARM32::AssemberARM32::sub()
void Assembler::subs(Register rd, Register rn, Operand o, Condition cond) {
EmitType01(cond, o.type(), SUB, 1, rn, rd, o);
......@@ -2083,13 +2079,16 @@ void Assembler::UpdateRangeFeedback(Register value,
str(scratch2, FieldAddress(ic_data, ICData::state_bits_offset()));
}
#if 0
// Moved to ::canEncodeBranchoffset in IceAssemblerARM32.cpp.
static bool CanEncodeBranchOffset(int32_t offset) {
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);
}
// Moved to AssemblerARM32::encodeBranchOffset.
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.
offset -= Instr::kPCReadOffset;
......@@ -2106,12 +2105,12 @@ int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t inst) {
return (inst & ~kBranchOffsetMask) | offset;
}
// Moved to AssemberARM32::decodeBranchOffset.
int Assembler::DecodeBranchOffset(int32_t inst) {
// Sign-extend, left-shift by 2, then add 8.
return ((((inst & kBranchOffsetMask) << 8) >> 6) + Instr::kPCReadOffset);
}
#endif
static int32_t DecodeARMv7LoadImmediate(int32_t movt, int32_t movw) {
int32_t offset = 0;
......
......@@ -147,18 +147,14 @@ class Operand : public ValueObject {
type_ = 1;
encoding_ = (rotate << kRotateShift) | (immed8 << kImmed8Shift);
}
#endif
#if 0
// Moved to decodeOperand() in IceAssemblerARM32.cpp
// Data-processing operands - Register.
explicit Operand(Register rm) {
type_ = 0;
encoding_ = static_cast<uint32_t>(rm);
}
#endif
#if 0
// Moved to encodeShiftRotateImm5()
// Data-processing operands - Logical shift/rotate by immediate.
Operand(Register rm, Shift shift, uint32_t shift_imm) {
......@@ -1121,11 +1117,10 @@ class Assembler : public ValueObject {
Register rn,
Register rd,
Operand o);
#endif
// Moved to ARM32::AssemblerARM32::emitType05()
void EmitType5(Condition cond, int32_t offset, bool link);
#if 0
// Moved to ARM32::AssemberARM32::emitMemOp()
void EmitMemOp(Condition cond,
bool load,
......@@ -1214,8 +1209,12 @@ class Assembler : public ValueObject {
void EmitFarBranch(Condition cond, int32_t offset, 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);
// Moved to ARM32::AssemberARM32::decodeBranchOffset().
static int32_t DecodeBranchOffset(int32_t inst);
#endif
int32_t EncodeTstOffset(int32_t offset, int32_t inst);
int32_t DecodeTstOffset(int32_t inst);
......
......@@ -47,6 +47,9 @@ public:
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
/// considered backward branches). Cannot be used for unused or linked labels.
intptr_t getPosition() const {
......@@ -77,13 +80,13 @@ public:
assert(isBound());
}
protected:
void linkTo(intptr_t position) {
assert(!isBound());
Position = position + kWordSize;
assert(isLinked());
}
protected:
intptr_t Position = 0;
// TODO(jvoung): why are labels offset by this?
......
......@@ -27,66 +27,78 @@
namespace {
using namespace Ice;
using namespace Ice::ARM32;
// The following define individual bits.
static constexpr uint32_t B0 = 1;
static constexpr uint32_t B1 = 1 << 1;
static constexpr uint32_t B2 = 1 << 2;
static constexpr uint32_t B3 = 1 << 3;
static constexpr uint32_t B4 = 1 << 4;
static constexpr uint32_t B5 = 1 << 5;
static constexpr uint32_t B6 = 1 << 6;
static constexpr uint32_t B21 = 1 << 21;
static constexpr uint32_t B24 = 1 << 24;
static constexpr IValueT B0 = 1;
static constexpr IValueT B1 = 1 << 1;
static constexpr IValueT B2 = 1 << 2;
static constexpr IValueT B3 = 1 << 3;
static constexpr IValueT B4 = 1 << 4;
static constexpr IValueT B5 = 1 << 5;
static constexpr IValueT B6 = 1 << 6;
static constexpr IValueT B21 = 1 << 21;
static constexpr IValueT B24 = 1 << 24;
// Constants used for the decoding or encoding of the individual fields of
// instructions. Based on ARM section A5.1.
static constexpr uint32_t L = 1 << 20; // load (or store)
static constexpr uint32_t W = 1 << 21; // writeback base register (or leave
// unchanged)
static constexpr uint32_t B = 1 << 22; // unsigned byte (or word)
static constexpr uint32_t U = 1 << 23; // positive (or negative) offset/index
static constexpr uint32_t P = 1 << 24; // offset/pre-indexed addressing (or
// post-indexed addressing)
static constexpr uint32_t kConditionShift = 28;
static constexpr uint32_t kOpcodeShift = 21;
static constexpr uint32_t kRdShift = 12;
static constexpr uint32_t kRmShift = 0;
static constexpr uint32_t kRnShift = 16;
static constexpr uint32_t kSShift = 20;
static constexpr uint32_t kTypeShift = 25;
static constexpr IValueT L = 1 << 20; // load (or store)
static constexpr IValueT W = 1 << 21; // writeback base register
// (or leave unchanged)
static constexpr IValueT B = 1 << 22; // unsigned byte (or word)
static constexpr IValueT U = 1 << 23; // positive (or negative)
// offset/index
static constexpr IValueT P = 1 << 24; // offset/pre-indexed
// addressing (or
// post-indexed addressing)
static constexpr IValueT kConditionShift = 28;
static constexpr IValueT kLinkShift = 24;
static constexpr IValueT kOpcodeShift = 21;
static constexpr IValueT kRdShift = 12;
static constexpr IValueT kRmShift = 0;
static constexpr IValueT kRnShift = 16;
static constexpr IValueT kSShift = 20;
static constexpr IValueT kTypeShift = 25;
// Immediate instruction fields encoding.
static constexpr uint32_t kImmed8Bits = 8;
static constexpr uint32_t kImmed8Shift = 0;
static constexpr uint32_t kRotateBits = 4;
static constexpr uint32_t kRotateShift = 8;
static constexpr IValueT kImmed8Bits = 8;
static constexpr IValueT kImmed8Shift = 0;
static constexpr IValueT kRotateBits = 4;
static constexpr IValueT kRotateShift = 8;
// Shift instruction register fields encodings.
static constexpr uint32_t kShiftImmShift = 7;
static constexpr uint32_t kShiftImmBits = 5;
static constexpr uint32_t kShiftShift = 5;
static constexpr IValueT kShiftImmShift = 7;
static constexpr IValueT kShiftImmBits = 5;
static constexpr IValueT kShiftShift = 5;
static constexpr uint32_t kImmed12Bits = 12;
static constexpr uint32_t kImm12Shift = 0;
static constexpr IValueT kImmed12Bits = 12;
static constexpr IValueT kImm12Shift = 0;
// Type of instruction encoding (bits 25-27). See ARM section A5.1
static constexpr uint32_t kInstTypeDataRegister = 0; // i.e. 000
static constexpr uint32_t kInstTypeDataImmediate = 1; // i.e. 001
static constexpr uint32_t kInstTypeMemImmediate = 2; // i.e. 010
static constexpr IValueT kInstTypeDataRegister = 0; // i.e. 000
static constexpr IValueT kInstTypeDataImmediate = 1; // i.e. 001
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) {
return static_cast<uint32_t>(Rn);
// Mask to pull out PC offset from branch (b) instruction.
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) {
return R != RegARM32::Encoded_Not_GPR;
}
inline bool isGPRRegisterDefined(uint32_t R) {
inline bool isGPRRegisterDefined(IValueT R) {
return R != encodeGPRRegister(RegARM32::Encoded_Not_GPR);
}
......@@ -94,11 +106,11 @@ inline bool isConditionDefined(CondARM32::Cond Cond) {
return Cond != CondARM32::kNone;
}
inline uint32_t encodeCondition(CondARM32::Cond Cond) {
return static_cast<uint32_t>(Cond);
inline IValueT encodeCondition(CondARM32::Cond 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".
switch (Shift) {
case OperandARM32::kNoShift:
......@@ -115,17 +127,17 @@ uint32_t encodeShift(OperandARM32::ShiftKind Shift) {
}
// 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);
}
// 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;
}
// 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);
}
......@@ -147,35 +159,33 @@ enum DecodedResult {
// Encodes iiiiitt0mmmm for data-processing (2nd) operands where iiiii=Imm5,
// tt=Shift, and mmmm=Rm.
uint32_t encodeShiftRotateImm5(uint32_t Rm, OperandARM32::ShiftKind Shift,
uint32_t imm5) {
IValueT encodeShiftRotateImm5(IValueT Rm, OperandARM32::ShiftKind Shift,
IValueT imm5) {
(void)kShiftImmBits;
assert(imm5 < (1 << kShiftImmBits));
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 (Var->hasReg()) {
Value = Var->getRegNum();
return DecodedAsRegister;
}
} else if (const auto *FlexImm = llvm::dyn_cast<OperandARM32FlexImm>(Opnd)) {
const uint32_t Immed8 = FlexImm->getImm();
const uint32_t Rotate = FlexImm->getRotateAmt();
assert((Rotate < (1 << kRotateBits)) && (Immed8 < (1 << kImmed8Bits)));
// TODO(kschimpf): Remove void casts when MINIMAL build allows.
(void)kRotateBits;
(void)kImmed8Bits;
const IValueT Immed8 = FlexImm->getImm();
const IValueT Rotate = FlexImm->getRotateAmt();
if (!((Rotate < (1 << kRotateBits)) && (Immed8 < (1 << kImmed8Bits))))
return CantDecode;
Value = (Rotate << kRotateShift) | (Immed8 << kImmed8Shift);
return DecodedAsRotatedImm8;
}
return CantDecode;
}
uint32_t decodeImmRegOffset(RegARM32::GPRRegister Reg, int32_t Offset,
OperandARM32Mem::AddrMode Mode) {
uint32_t Value = Mode | (encodeGPRRegister(Reg) << kRnShift);
IValueT decodeImmRegOffset(RegARM32::GPRRegister Reg, IOffsetT Offset,
OperandARM32Mem::AddrMode Mode) {
IValueT Value = Mode | (encodeGPRRegister(Reg) << kRnShift);
if (Offset < 0) {
Value = (Value ^ U) | -Offset; // Flip U to adjust sign.
} else {
......@@ -186,12 +196,12 @@ uint32_t decodeImmRegOffset(RegARM32::GPRRegister Reg, int32_t Offset,
// Decodes memory address Opnd, and encodes that information into Value,
// 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)) {
// Should be a stack variable, with an offset.
if (Var->hasReg())
return CantDecode;
const int32_t Offset = Var->getStackOffset();
const IOffsetT Offset = Var->getStackOffset();
if (!Utils::IsAbsoluteUint(12, Offset))
return CantDecode;
Value = decodeImmRegOffset(RegARM32::Encoded_Reg_sp, Offset,
......@@ -201,11 +211,18 @@ DecodedResult decodeAddress(const Operand *Opnd, uint32_t &Value) {
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
namespace Ice {
namespace ARM32 {
void ARM32::AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) {
void AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) {
if (BuildDefs::dump() && !Ctx->getFlags().getDisableHybridAssembly()) {
// Generate label name so that branches can find it.
constexpr SizeT InstSize = 0;
......@@ -217,8 +234,7 @@ void ARM32::AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) {
this->bind(L);
}
Label *ARM32::AssemblerARM32::getOrCreateLabel(SizeT Number,
LabelVector &Labels) {
Label *AssemblerARM32::getOrCreateLabel(SizeT Number, LabelVector &Labels) {
Label *L = nullptr;
if (Number == Labels.size()) {
L = new (this->allocate<Label>()) Label();
......@@ -236,21 +252,41 @@ Label *ARM32::AssemblerARM32::getOrCreateLabel(SizeT Number,
return L;
}
void ARM32::AssemblerARM32::bind(Label *label) {
intptr_t bound = Buffer.size();
assert(!label->isBound()); // Labels can only be bound once.
while (label->isLinked()) {
intptr_t position = label->getLinkPosition();
intptr_t next = Buffer.load<int32_t>(position);
Buffer.store<int32_t>(position, bound - (position + 4));
label->setPosition(next);
IValueT AssemblerARM32::encodeBranchOffset(IOffsetT Offset, IValueT Inst) {
// Adjust offset to the way ARM CPUs read PC.
Offset -= kPCReadOffset;
bool IsGoodOffset = canEncodeBranchOffset(Offset);
assert(IsGoodOffset);
// Note: Following cast is for MINIMAL build.
(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.
label->bindTo(bound);
L->bindTo(BoundPc);
}
void ARM32::AssemblerARM32::emitTextInst(const std::string &Text,
SizeT InstSize) {
void AssemblerARM32::emitTextInst(const std::string &Text, SizeT InstSize) {
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
AssemblerFixup *F = createTextFixup(Text, InstSize);
emitFixup(F);
......@@ -258,44 +294,72 @@ void ARM32::AssemblerARM32::emitTextInst(const std::string &Text,
Buffer.emit<char>(0);
}
void ARM32::AssemblerARM32::emitType01(CondARM32::Cond Cond, uint32_t Type,
uint32_t Opcode, bool SetCc, uint32_t Rn,
uint32_t Rd, uint32_t Imm12) {
void AssemblerARM32::emitType01(CondARM32::Cond Cond, IValueT Type,
IValueT Opcode, bool SetCc, IValueT Rn,
IValueT Rd, IValueT Imm12) {
assert(isGPRRegisterDefined(Rd));
// TODO(kschimpf): Remove void cast when MINIMAL build allows.
(void)isGPRRegisterDefined(Rd);
assert(Cond != CondARM32::kNone);
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) |
(Type << kTypeShift) | (Opcode << kOpcodeShift) |
(encodeBool(SetCc) << kSShift) | (Rn << kRnShift) |
(Rd << kRdShift) | Imm12;
const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) |
(Type << kTypeShift) | (Opcode << kOpcodeShift) |
(encodeBool(SetCc) << kSShift) | (Rn << kRnShift) |
(Rd << kRdShift) | Imm12;
emitInst(Encoding);
}
void ARM32::AssemblerARM32::emitMemOp(CondARM32::Cond Cond, uint32_t InstType,
bool IsLoad, bool IsByte, uint32_t Rt,
uint32_t Address) {
void AssemblerARM32::emitType05(CondARM32::Cond Cond, IOffsetT Offset,
bool Link) {
// 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(Cond != CondARM32::kNone);
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) |
(InstType << kTypeShift) | (IsLoad ? L : 0) |
(IsByte ? B : 0) | (Rt << kRdShift) | Address;
const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) |
(InstType << kTypeShift) | (IsLoad ? L : 0) |
(IsByte ? B : 0) | (Rt << kRdShift) | Address;
emitInst(Encoding);
}
void ARM32::AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags,
CondARM32::Cond Cond) {
uint32_t Rd;
void AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags,
CondARM32::Cond Cond) {
IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup();
uint32_t Rn;
IValueT Rn;
if (decodeOperand(OpRn, Rn) != DecodedAsRegister)
return setNeedsTextFixup();
constexpr uint32_t Add = B2; // 0100
uint32_t Src1Value;
constexpr IValueT Add = B2; // 0100
IValueT Src1Value;
// TODO(kschimpf) Other possible decodings of add.
switch (decodeOperand(OpSrc1, Src1Value)) {
default:
......@@ -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 #<Imm16>
//
// cccc00010010iiiiiiiiiiii0111iiii where cccc=AL and iiiiiiiiiiiiiiii=Imm16
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (CondARM32::AL << kConditionShift) | B24 | B21 |
((Imm16 >> 4) << 8) | B6 | B5 | B4 | (Imm16 & 0xf);
const IValueT Encoding = (CondARM32::AL << kConditionShift) | B24 | B21 |
((Imm16 >> 4) << 8) | B6 | B5 | B4 | (Imm16 & 0xf);
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<c> <Rm>
//
......@@ -351,18 +419,18 @@ void ARM32::AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) {
if (!(isGPRRegisterDefined(Rm) && isConditionDefined(Cond)))
return setNeedsTextFixup();
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) | B24 |
B21 | (0xfff << 8) | B4 |
(encodeGPRRegister(Rm) << kRmShift);
const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | B24 |
B21 | (0xfff << 8) | B4 |
(encodeGPRRegister(Rm) << kRmShift);
emitInst(Encoding);
}
void ARM32::AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond) {
uint32_t Rt;
void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond) {
IValueT Rt;
if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
return setNeedsTextFixup();
uint32_t Address;
IValueT Address;
if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset)
return setNeedsTextFixup();
// LDR (immediate) - ARM section A8.8.63, encoding A1:
......@@ -393,12 +461,12 @@ void ARM32::AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address);
}
void ARM32::AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
CondARM32::Cond Cond) {
uint32_t Rd;
void AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
CondARM32::Cond Cond) {
IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup();
uint32_t Src;
IValueT Src;
// TODO(kschimpf) Handle other forms of mov.
if (decodeOperand(OpSrc, Src) != DecodedAsRotatedImm8)
return setNeedsTextFixup();
......@@ -412,17 +480,17 @@ void ARM32::AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
if ((Rd == RegARM32::Encoded_Reg_pc && SetFlags))
// Conditions of rule violated.
return setNeedsTextFixup();
constexpr uint32_t Rn = 0;
constexpr uint32_t Mov = B3 | B2 | B0; // 1101.
constexpr IValueT Rn = 0;
constexpr IValueT Mov = B3 | B2 | B0; // 1101.
emitType01(Cond, kInstTypeDataImmediate, Mov, SetFlags, Rn, Rd, Src);
}
void ARM32::AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond) {
uint32_t Rt;
void AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond) {
IValueT Rt;
if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
return setNeedsTextFixup();
uint32_t Address;
IValueT Address;
if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset)
return setNeedsTextFixup();
// STR (immediate) - ARM section A8.8.204, encoding A1:
......@@ -453,17 +521,17 @@ void ARM32::AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address);
}
void ARM32::AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags,
CondARM32::Cond Cond) {
uint32_t Rd;
void AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags,
CondARM32::Cond Cond) {
IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup();
uint32_t Rn;
IValueT Rn;
if (decodeOperand(OpRn, Rn) != DecodedAsRegister)
return setNeedsTextFixup();
constexpr uint32_t Sub = B1; // 0010
uint32_t Src1Value;
constexpr IValueT Sub = B1; // 0010
IValueT Src1Value;
// TODO(kschimpf) Other possible decodings of sub.
switch (decodeOperand(OpSrc1, Src1Value)) {
default:
......@@ -500,4 +568,5 @@ void ARM32::AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
}
}
} // end of namespace ARM32
} // end of namespace Ice
......@@ -38,6 +38,12 @@
namespace Ice {
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 {
AssemblerARM32() = delete;
AssemblerARM32(const AssemblerARM32 &) = delete;
......@@ -63,8 +69,8 @@ public:
void alignFunction() override {
const SizeT Align = 1 << getBundleAlignLog2Bytes();
SizeT BytesNeeded = Utils::OffsetToAlignment(Buffer.getPosition(), Align);
constexpr uint32_t UndefinedInst = 0xe7fedef0; // udf #60896
constexpr SizeT InstSize = sizeof(int32_t);
constexpr IValueT UndefinedInst = 0xe7fedef0; // udf #60896
constexpr SizeT InstSize = sizeof(IValueT);
assert(BytesNeeded % InstSize == 0);
while (BytesNeeded > 0) {
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
......@@ -94,7 +100,13 @@ public:
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) {
Label *L = getOrCreateLocalLabel(Number);
......@@ -115,6 +127,8 @@ public:
void add(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
bool SetFlags, CondARM32::Cond Cond);
void b(Label *L, CondARM32::Cond Cond);
void bkpt(uint16_t Imm16);
void ldr(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond);
......@@ -132,7 +146,7 @@ public:
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:
// A vector of pool-allocated x86 labels for CFG nodes.
......@@ -142,26 +156,32 @@ private:
LabelVector LocalLabels;
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,
// 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,
bool SetCc, uint32_t Rn, uint32_t Rd, uint32_t imm12);
void emitType01(CondARM32::Cond Cond, IValueT Type, IValueT Opcode,
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,
// l=isLoad, b=isByte, and aaa0a0aaaa0000aaaaaaaaaaaa=Address. Note that
// Address is assumed to be defined by decodeAddress() in
// 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);
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
......
......@@ -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 {
if (!BuildDefs::dump())
return;
......
......@@ -861,6 +861,7 @@ public:
}
bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override;
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
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.
; TODO(kschimpf): Get this working.
; REQUIRES: allow_dump
; Compile using standalone assembler.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -Om1 \
; RUN: | FileCheck %s --check-prefix=ASM
......@@ -17,8 +19,6 @@
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -Om1 | FileCheck %s --check-prefix=DIS
; REQUIRES: allow_dump
define internal void @simple_uncond_branch() {
; DIS-LABEL: 00000000 <simple_uncond_branch>:
; ASM-LABEL: simple_uncond_branch:
......@@ -29,7 +29,10 @@ define internal void @simple_uncond_branch() {
br label %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
l1:
......@@ -38,7 +41,10 @@ l1:
br label %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
l2:
......@@ -47,7 +53,10 @@ l2:
br label %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
l3:
......@@ -60,5 +69,6 @@ l3:
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0x2f
; 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