| 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);
|
| }
|
| }
|
|
|