Index: runtime/vm/assembler_arm.cc |
=================================================================== |
--- runtime/vm/assembler_arm.cc (revision 33437) |
+++ runtime/vm/assembler_arm.cc (working copy) |
@@ -23,7 +23,6 @@ |
DEFINE_FLAG(bool, print_stop_message, true, "Print stop message."); |
DECLARE_FLAG(bool, inline_alloc); |
- |
// Instruction encoding bits. |
enum { |
H = 1 << 5, // halfword (or byte) |
@@ -1458,11 +1457,7 @@ |
if (ShifterOperand::CanHold(offset_hi, &shifter_op)) { |
add(rd, PP, shifter_op, cond); |
} else { |
- movw(rd, Utils::Low16Bits(offset_hi)); |
- const uint16_t value_high = Utils::High16Bits(offset_hi); |
- if (value_high != 0) { |
- movt(rd, value_high, cond); |
- } |
+ LoadImmediate(rd, offset_hi, cond); |
add(rd, PP, ShifterOperand(LR), cond); |
} |
ldr(rd, Address(rd, offset_lo), cond); |
@@ -1485,11 +1480,7 @@ |
} else if (object.InVMHeap()) { |
// Make sure that class CallPattern is able to decode this load immediate. |
const int32_t object_raw = reinterpret_cast<int32_t>(object.raw()); |
- movw(rd, Utils::Low16Bits(object_raw), cond); |
- const uint16_t value_high = Utils::High16Bits(object_raw); |
- if (value_high != 0) { |
- movt(rd, value_high, cond); |
- } |
+ LoadImmediate(rd, object_raw, cond); |
} else { |
// Make sure that class CallPattern is able to decode this load from the |
// object pool. |
@@ -1668,7 +1659,7 @@ |
} |
-static int32_t DecodeLoadImmediate(int32_t movt, int32_t movw) { |
+static int32_t DecodeARMv7LoadImmediate(int32_t movt, int32_t movw) { |
int32_t offset = 0; |
offset |= (movt & 0xf0000) << 12; |
offset |= (movt & 0xfff) << 16; |
@@ -1678,18 +1669,78 @@ |
} |
+static int32_t DecodeARMv6LoadImmediate(int32_t mov, int32_t or1, |
+ int32_t or2, int32_t or3) { |
+ int32_t offset = 0; |
+ offset |= (mov & 0xff) << 24; |
+ offset |= (or1 & 0xff) << 16; |
+ offset |= (or2 & 0xff) << 8; |
+ offset |= (or3 & 0xff); |
+ return offset; |
+} |
+ |
+ |
class PatchFarBranch : public AssemblerFixup { |
public: |
PatchFarBranch() {} |
void Process(const MemoryRegion& region, intptr_t position) { |
+ if (TargetCPUFeatures::arm_version() == ARMv6) { |
+ ProcessARMv6(region, position); |
+ } else { |
+ ASSERT(TargetCPUFeatures::arm_version() == ARMv7); |
+ ProcessARMv7(region, position); |
+ } |
+ } |
+ |
+ private: |
+ void ProcessARMv6(const MemoryRegion& region, intptr_t position) { |
+ const int32_t mov = region.Load<int32_t>(position); |
+ const int32_t or1 = region.Load<int32_t>(position + 1*Instr::kInstrSize); |
+ const int32_t or2 = region.Load<int32_t>(position + 2*Instr::kInstrSize); |
+ const int32_t or3 = region.Load<int32_t>(position + 3*Instr::kInstrSize); |
+ const int32_t bx = region.Load<int32_t>(position + 4*Instr::kInstrSize); |
+ |
+ if (((mov & 0xffffff00) == 0xe3a0c400) && // mov IP, (byte3 rot 4) |
+ ((or1 & 0xffffff00) == 0xe38cc800) && // orr IP, IP, (byte2 rot 8) |
+ ((or2 & 0xffffff00) == 0xe38ccc00) && // orr IP, IP, (byte1 rot 12) |
+ ((or3 & 0xffffff00) == 0xe38cc000)) { // orr IP, IP, byte0 |
+ const int32_t offset = DecodeARMv6LoadImmediate(mov, or1, or2, or3); |
+ const int32_t dest = region.start() + offset; |
+ const int32_t dest0 = (dest & 0x000000ff); |
+ const int32_t dest1 = (dest & 0x0000ff00) >> 8; |
+ const int32_t dest2 = (dest & 0x00ff0000) >> 16; |
+ const int32_t dest3 = (dest & 0xff000000) >> 24; |
+ const int32_t patched_mov = 0xe3a0c400 | dest3; |
+ const int32_t patched_or1 = 0xe38cc800 | dest2; |
+ const int32_t patched_or2 = 0xe38ccc00 | dest1; |
+ const int32_t patched_or3 = 0xe38cc000 | dest0; |
+ |
+ region.Store<int32_t>(position + 0 * Instr::kInstrSize, patched_mov); |
+ region.Store<int32_t>(position + 1 * Instr::kInstrSize, patched_or1); |
+ region.Store<int32_t>(position + 2 * Instr::kInstrSize, patched_or2); |
+ region.Store<int32_t>(position + 3 * Instr::kInstrSize, patched_or3); |
+ return; |
+ } |
+ |
+ // If the offset loading instructions aren't there, we must have replaced |
+ // the far branch with a near one, and so these instructions |
+ // should be NOPs. |
+ ASSERT((or1 == Instr::kNopInstruction) && |
+ (or2 == Instr::kNopInstruction) && |
+ (or3 == Instr::kNopInstruction) && |
+ (bx == Instr::kNopInstruction)); |
+ } |
+ |
+ |
+ void ProcessARMv7(const MemoryRegion& region, intptr_t position) { |
const int32_t movw = region.Load<int32_t>(position); |
const int32_t movt = region.Load<int32_t>(position + Instr::kInstrSize); |
const int32_t bx = region.Load<int32_t>(position + 2 * Instr::kInstrSize); |
if (((movt & 0xfff0f000) == 0xe340c000) && // movt IP, high |
((movw & 0xfff0f000) == 0xe300c000)) { // movw IP, low |
- const int32_t offset = DecodeLoadImmediate(movt, movw); |
+ const int32_t offset = DecodeARMv7LoadImmediate(movt, movw); |
const int32_t dest = region.start() + offset; |
const uint16_t dest_high = Utils::High16Bits(dest); |
const uint16_t dest_low = Utils::Low16Bits(dest); |
@@ -1704,7 +1755,8 @@ |
} |
// If the offset loading instructions aren't there, we must have replaced |
- // the far branch with a near one, and so these instructions should be NOPs. |
+ // the far branch with a near one, and so these instructions |
+ // should be NOPs. |
ASSERT((movt == Instr::kNopInstruction) && |
(bx == Instr::kNopInstruction)); |
} |
@@ -1714,11 +1766,8 @@ |
void Assembler::EmitFarBranch(Condition cond, int32_t offset, bool link) { |
- const uint16_t low = Utils::Low16Bits(offset); |
- const uint16_t high = Utils::High16Bits(offset); |
buffer_.EmitFixup(new PatchFarBranch()); |
- movw(IP, low); |
- movt(IP, high); |
+ LoadPatchableImmediate(IP, offset); |
if (link) { |
blx(IP, cond); |
} else { |
@@ -1749,7 +1798,7 @@ |
} |
-void Assembler::Bind(Label* label) { |
+void Assembler::BindARMv6(Label* label) { |
ASSERT(!label->IsBound()); |
intptr_t bound_pc = buffer_.Size(); |
while (label->IsLinked()) { |
@@ -1759,12 +1808,94 @@ |
// Far branches are enabled and we can't encode the branch offset. |
// Grab instructions that load the offset. |
+ const int32_t mov = |
+ buffer_.Load<int32_t>(position); |
+ const int32_t or1 = |
+ buffer_.Load<int32_t>(position + 1 * Instr::kInstrSize); |
+ const int32_t or2 = |
+ buffer_.Load<int32_t>(position + 2 * Instr::kInstrSize); |
+ const int32_t or3 = |
+ buffer_.Load<int32_t>(position + 3 * Instr::kInstrSize); |
+ |
+ // Change from relative to the branch to relative to the assembler |
+ // buffer. |
+ dest = buffer_.Size(); |
+ const int32_t dest0 = (dest & 0x000000ff); |
+ const int32_t dest1 = (dest & 0x0000ff00) >> 8; |
+ const int32_t dest2 = (dest & 0x00ff0000) >> 16; |
+ const int32_t dest3 = (dest & 0xff000000) >> 24; |
+ const int32_t patched_mov = 0xe3a0c400 | dest3; |
+ const int32_t patched_or1 = 0xe38cc800 | dest2; |
+ const int32_t patched_or2 = 0xe38ccc00 | dest1; |
+ const int32_t patched_or3 = 0xe38cc000 | dest0; |
+ |
+ // Rewrite the instructions. |
+ buffer_.Store<int32_t>(position + 0 * Instr::kInstrSize, patched_mov); |
+ buffer_.Store<int32_t>(position + 1 * Instr::kInstrSize, patched_or1); |
+ buffer_.Store<int32_t>(position + 2 * Instr::kInstrSize, patched_or2); |
+ buffer_.Store<int32_t>(position + 3 * Instr::kInstrSize, patched_or3); |
+ label->position_ = DecodeARMv6LoadImmediate(mov, or1, or2, or3); |
+ } else if (use_far_branches() && CanEncodeBranchOffset(dest)) { |
+ // Grab instructions that load the offset, and the branch. |
+ const int32_t mov = |
+ buffer_.Load<int32_t>(position); |
+ const int32_t or1 = |
+ buffer_.Load<int32_t>(position + 1 * Instr::kInstrSize); |
+ const int32_t or2 = |
+ buffer_.Load<int32_t>(position + 2 * Instr::kInstrSize); |
+ const int32_t or3 = |
+ buffer_.Load<int32_t>(position + 3 * Instr::kInstrSize); |
+ const int32_t branch = |
+ buffer_.Load<int32_t>(position + 4 * Instr::kInstrSize); |
+ |
+ // Grab the branch condition, and encode the link bit. |
+ const int32_t cond = branch & 0xf0000000; |
+ const int32_t link = (branch & 0x20) << 19; |
+ |
+ // Encode the branch and the offset. |
+ const int32_t new_branch = cond | link | 0x0a000000; |
+ const int32_t encoded = EncodeBranchOffset(dest, new_branch); |
+ |
+ // Write the encoded branch instruction followed by two nops. |
+ buffer_.Store<int32_t>(position, encoded); |
+ buffer_.Store<int32_t>(position + 1 * Instr::kInstrSize, |
+ Instr::kNopInstruction); |
+ buffer_.Store<int32_t>(position + 2 * Instr::kInstrSize, |
+ Instr::kNopInstruction); |
+ buffer_.Store<int32_t>(position + 3 * Instr::kInstrSize, |
+ Instr::kNopInstruction); |
+ buffer_.Store<int32_t>(position + 4 * Instr::kInstrSize, |
+ Instr::kNopInstruction); |
+ |
+ label->position_ = DecodeARMv6LoadImmediate(mov, or1, or2, or3); |
+ } else { |
+ int32_t next = buffer_.Load<int32_t>(position); |
+ int32_t encoded = Assembler::EncodeBranchOffset(dest, next); |
+ buffer_.Store<int32_t>(position, encoded); |
+ label->position_ = Assembler::DecodeBranchOffset(next); |
+ } |
+ } |
+ label->BindTo(bound_pc); |
+} |
+ |
+ |
+void Assembler::BindARMv7(Label* label) { |
+ ASSERT(!label->IsBound()); |
+ intptr_t bound_pc = buffer_.Size(); |
+ while (label->IsLinked()) { |
+ const int32_t position = label->Position(); |
+ int32_t dest = bound_pc - position; |
+ if (use_far_branches() && !CanEncodeBranchOffset(dest)) { |
+ // Far branches are enabled and we can't encode the branch offset. |
+ |
+ // Grab instructions that load the offset. |
const int32_t movw = |
- buffer_.Load<int32_t>(position); |
+ buffer_.Load<int32_t>(position + 0 * Instr::kInstrSize); |
const int32_t movt = |
buffer_.Load<int32_t>(position + 1 * Instr::kInstrSize); |
- // Change from relative to the branch to relative to the assembler buffer. |
+ // Change from relative to the branch to relative to the assembler |
+ // buffer. |
dest = buffer_.Size(); |
const uint16_t dest_high = Utils::High16Bits(dest); |
const uint16_t dest_low = Utils::Low16Bits(dest); |
@@ -1774,15 +1905,15 @@ |
0xe300c000 | ((dest_low >> 12) << 16) | (dest_low & 0xfff); |
// Rewrite the instructions. |
- buffer_.Store<int32_t>(position, patched_movw); |
+ buffer_.Store<int32_t>(position + 0 * Instr::kInstrSize, patched_movw); |
buffer_.Store<int32_t>(position + 1 * Instr::kInstrSize, patched_movt); |
- label->position_ = DecodeLoadImmediate(movt, movw); |
+ label->position_ = DecodeARMv7LoadImmediate(movt, movw); |
} else if (use_far_branches() && CanEncodeBranchOffset(dest)) { |
// Far branches are enabled, but we can encode the branch offset. |
// Grab instructions that load the offset, and the branch. |
const int32_t movw = |
- buffer_.Load<int32_t>(position); |
+ buffer_.Load<int32_t>(position + 0 * Instr::kInstrSize); |
const int32_t movt = |
buffer_.Load<int32_t>(position + 1 * Instr::kInstrSize); |
const int32_t branch = |
@@ -1797,13 +1928,14 @@ |
const int32_t encoded = EncodeBranchOffset(dest, new_branch); |
// Write the encoded branch instruction followed by two nops. |
- buffer_.Store<int32_t>(position, encoded); |
+ buffer_.Store<int32_t>(position + 0 * Instr::kInstrSize, |
+ encoded); |
buffer_.Store<int32_t>(position + 1 * Instr::kInstrSize, |
Instr::kNopInstruction); |
buffer_.Store<int32_t>(position + 2 * Instr::kInstrSize, |
Instr::kNopInstruction); |
- label->position_ = DecodeLoadImmediate(movt, movw); |
+ label->position_ = DecodeARMv7LoadImmediate(movt, movw); |
} else { |
int32_t next = buffer_.Load<int32_t>(position); |
int32_t encoded = Assembler::EncodeBranchOffset(dest, next); |
@@ -1815,6 +1947,16 @@ |
} |
+void Assembler::Bind(Label* label) { |
+ if (TargetCPUFeatures::arm_version() == ARMv6) { |
+ BindARMv6(label); |
+ } else { |
+ ASSERT(TargetCPUFeatures::arm_version() == ARMv7); |
+ BindARMv7(label); |
+ } |
+} |
+ |
+ |
bool Address::CanHoldLoadOffset(OperandSize type, |
int32_t offset, |
int32_t* offset_mask) { |
@@ -2024,8 +2166,7 @@ |
// with this branch sequence. |
// Contrarily to BranchLinkPatchable, BranchPatchable requires an instruction |
// cache flush upon patching. |
- movw(IP, Utils::Low16Bits(label->address())); |
- movt(IP, Utils::High16Bits(label->address())); |
+ LoadPatchableImmediate(IP, label->address()); |
bx(IP); |
} |
@@ -2064,6 +2205,43 @@ |
} |
+void Assembler::LoadPatchableImmediate( |
+ Register rd, int32_t value, Condition cond) { |
+ if (TargetCPUFeatures::arm_version() == ARMv6) { |
+ // This sequence is patched in a few places, and should remain fixed. |
+ const uint32_t byte0 = (value & 0x000000ff); |
+ const uint32_t byte1 = (value & 0x0000ff00) >> 8; |
+ const uint32_t byte2 = (value & 0x00ff0000) >> 16; |
+ const uint32_t byte3 = (value & 0xff000000) >> 24; |
+ mov(rd, ShifterOperand(4, byte3), cond); |
+ orr(rd, rd, ShifterOperand(8, byte2), cond); |
+ orr(rd, rd, ShifterOperand(12, byte1), cond); |
+ orr(rd, rd, ShifterOperand(byte0), cond); |
+ } else { |
+ ASSERT(TargetCPUFeatures::arm_version() == ARMv7); |
+ const uint16_t value_low = Utils::Low16Bits(value); |
+ const uint16_t value_high = Utils::High16Bits(value); |
+ movw(rd, value_low, cond); |
+ movt(rd, value_high, cond); |
+ } |
+} |
+ |
+ |
+void Assembler::LoadDecodableImmediate( |
+ Register rd, int32_t value, Condition cond) { |
+ if (TargetCPUFeatures::arm_version() == ARMv6) { |
+ LoadPatchableImmediate(rd, value, cond); |
+ } else { |
+ ASSERT(TargetCPUFeatures::arm_version() == ARMv7); |
+ movw(rd, Utils::Low16Bits(value), cond); |
+ const uint16_t value_high = Utils::High16Bits(value); |
+ if (value_high != 0) { |
+ movt(rd, value_high, cond); |
+ } |
+ } |
+} |
+ |
+ |
void Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) { |
ShifterOperand shifter_op; |
if (ShifterOperand::CanHold(value, &shifter_op)) { |
@@ -2071,11 +2249,7 @@ |
} else if (ShifterOperand::CanHold(~value, &shifter_op)) { |
mvn(rd, shifter_op, cond); |
} else { |
- movw(rd, Utils::Low16Bits(value), cond); |
- const uint16_t value_high = Utils::High16Bits(value); |
- if (value_high != 0) { |
- movt(rd, value_high, cond); |
- } |
+ LoadDecodableImmediate(rd, value, cond); |
} |
} |
@@ -2241,7 +2415,7 @@ |
void Assembler::AddImmediate(Register rd, Register rn, int32_t value, |
- Condition cond) { |
+ Condition cond) { |
if (value == 0) { |
if (rd != rn) { |
mov(rd, ShifterOperand(rn), cond); |
@@ -2265,11 +2439,7 @@ |
mvn(IP, shifter_op, cond); |
sub(rd, rn, ShifterOperand(IP), cond); |
} else { |
- movw(IP, Utils::Low16Bits(value), cond); |
- const uint16_t value_high = Utils::High16Bits(value); |
- if (value_high != 0) { |
- movt(IP, value_high, cond); |
- } |
+ LoadDecodableImmediate(IP, value, cond); |
add(rd, rn, ShifterOperand(IP), cond); |
} |
} |
@@ -2292,11 +2462,7 @@ |
mvn(IP, shifter_op, cond); |
subs(rd, rn, ShifterOperand(IP), cond); |
} else { |
- movw(IP, Utils::Low16Bits(value), cond); |
- const uint16_t value_high = Utils::High16Bits(value); |
- if (value_high != 0) { |
- movt(IP, value_high, cond); |
- } |
+ LoadDecodableImmediate(IP, value, cond); |
adds(rd, rn, ShifterOperand(IP), cond); |
} |
} |
@@ -2319,11 +2485,7 @@ |
mvn(IP, shifter_op, cond); |
sbc(rd, rn, ShifterOperand(IP), cond); |
} else { |
- movw(IP, Utils::Low16Bits(value), cond); |
- const uint16_t value_high = Utils::High16Bits(value); |
- if (value_high != 0) { |
- movt(IP, value_high, cond); |
- } |
+ LoadDecodableImmediate(IP, value, cond); |
adc(rd, rn, ShifterOperand(IP), cond); |
} |
} |