Chromium Code Reviews| Index: runtime/vm/assembler_arm.cc |
| =================================================================== |
| --- runtime/vm/assembler_arm.cc (revision 25583) |
| +++ runtime/vm/assembler_arm.cc (working copy) |
| @@ -305,18 +305,6 @@ |
| } |
| -void Assembler::EmitBranch(Condition cond, Label* label, bool link) { |
| - if (label->IsBound()) { |
| - EmitType5(cond, label->Position() - buffer_.Size(), link); |
| - } else { |
| - int position = buffer_.Size(); |
| - // Use the offset field of the branch instruction for linking the sites. |
| - EmitType5(cond, label->position_, link); |
| - label->LinkTo(position); |
| - } |
| -} |
| - |
| - |
| void Assembler::and_(Register rd, Register rn, ShifterOperand so, |
| Condition cond) { |
| EmitType01(cond, so.type(), AND, 0, rn, rd, so); |
| @@ -1725,15 +1713,172 @@ |
| } |
| +static bool CanEncodeBranchOffset(int32_t offset) { |
| + offset -= 8; |
|
regis
2013/07/29 21:48:04
Opportunity for cleanup:
Use Instr::kPCReadOffset
zra
2013/08/01 15:15:58
Done.
|
| + ASSERT(Utils::IsAligned(offset, 4)); |
| + return Utils::IsInt(Utils::CountOneBits(kBranchOffsetMask), offset); |
| +} |
| + |
| + |
| +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 -= 8; |
|
regis
2013/07/29 21:48:04
ditto
zra
2013/08/01 15:15:58
Done.
|
| + ASSERT(Utils::IsAligned(offset, 4)); |
| + ASSERT(Utils::IsInt(Utils::CountOneBits(kBranchOffsetMask), offset)); |
| + |
| + // Properly preserve only the bits supported in the instruction. |
| + offset >>= 2; |
| + offset &= kBranchOffsetMask; |
| + return (inst & ~kBranchOffsetMask) | offset; |
| +} |
| + |
| + |
| +int Assembler::DecodeBranchOffset(int32_t inst) { |
| + // Sign-extend, left-shift by 2, then add 8. |
| + return ((((inst & kBranchOffsetMask) << 8) >> 6) + 8); |
|
regis
2013/07/29 21:48:04
ditto
zra
2013/08/01 15:15:58
Done.
|
| +} |
| + |
| + |
| +static int32_t DecodeLoadImmediate(int32_t movt, int32_t movw) { |
| + int32_t offset = 0; |
| + offset |= (movt & 0xf0000) << 12; |
| + offset |= (movt & 0xfff) << 16; |
| + offset |= (movw & 0xf0000) >> 4; |
| + offset |= movw & 0xfff; |
| + return offset; |
| +} |
| + |
| + |
| +class PatchFarBranch : public AssemblerFixup { |
| + public: |
| + PatchFarBranch() {} |
| + |
| + void Process(const MemoryRegion& region, int 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 TMP, high |
|
regis
2013/07/29 21:48:04
For clarity, I would use the name IP instead of TM
zra
2013/08/01 15:15:58
Renamed all instances in this file.
|
| + ((movw & 0xfff0f000) == 0xe300c000)) { // movw TMP, low |
| + const int32_t offset = DecodeLoadImmediate(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); |
| + const int32_t patched_movt = |
| + 0xe340c000 | ((dest_high >> 12) << 16) | (dest_high & 0xfff); |
| + const int32_t patched_movw = |
| + 0xe300c000 | ((dest_low >> 12) << 16) | (dest_low & 0xfff); |
| + |
| + region.Store<int32_t>(position, patched_movw); |
| + region.Store<int32_t>(position + Instr::kInstrSize, patched_movt); |
| + 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((movt == Instr::kNopInstruction) && |
| + (bx == Instr::kNopInstruction)); |
| + } |
| +}; |
| + |
| + |
| +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(TMP, low); |
| + movt(TMP, high); |
| + if (link) { |
| + blx(TMP, cond); |
| + } else { |
| + bx(TMP, cond); |
| + } |
| +} |
| + |
| + |
| +void Assembler::EmitBranch(Condition cond, Label* label, bool link) { |
| + if (label->IsBound()) { |
| + const int32_t dest = label->Position() - buffer_.Size(); |
| + if (FLAG_use_far_branches && !CanEncodeBranchOffset(dest)) { |
| + EmitFarBranch(cond, label->Position(), link); |
| + } else { |
| + EmitType5(cond, dest, link); |
| + } |
| + } else { |
| + const int position = buffer_.Size(); |
| + if (FLAG_use_far_branches) { |
| + const int32_t dest = label->position_; |
| + EmitFarBranch(cond, dest, link); |
| + } else { |
| + // Use the offset field of the branch instruction for linking the sites. |
| + EmitType5(cond, label->position_, link); |
| + } |
| + label->LinkTo(position); |
| + } |
| +} |
| + |
| + |
| void Assembler::Bind(Label* label) { |
| ASSERT(!label->IsBound()); |
| int bound_pc = buffer_.Size(); |
| while (label->IsLinked()) { |
| - int32_t position = label->Position(); |
| - int32_t next = buffer_.Load<int32_t>(position); |
| - int32_t encoded = Assembler::EncodeBranchOffset(bound_pc - position, next); |
| - buffer_.Store<int32_t>(position, encoded); |
| - label->position_ = Assembler::DecodeBranchOffset(next); |
| + const int32_t position = label->Position(); |
| + int32_t dest = bound_pc - position; |
| + if (FLAG_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); |
| + const int32_t movt = |
| + buffer_.Load<int32_t>(position + 1 * Instr::kInstrSize); |
| + |
| + // 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); |
| + const int32_t patched_movt = |
| + 0xe340c000 | ((dest_high >> 12) << 16) | (dest_high & 0xfff); |
| + const int32_t patched_movw = |
| + 0xe300c000 | ((dest_low >> 12) << 16) | (dest_low & 0xfff); |
| + |
| + // Rewrite the instructions. |
| + buffer_.Store<int32_t>(position, patched_movw); |
| + buffer_.Store<int32_t>(position + 1 * Instr::kInstrSize, patched_movt); |
| + label->position_ = DecodeLoadImmediate(movt, movw); |
| + } else if (FLAG_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); |
| + const int32_t movt = |
| + buffer_.Load<int32_t>(position + 1 * Instr::kInstrSize); |
| + const int32_t branch = |
| + buffer_.Load<int32_t>(position + 2 * 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); |
| + |
| + label->position_ = DecodeLoadImmediate(movt, movw); |
| + } 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); |
| } |
| @@ -2518,25 +2663,6 @@ |
| } |
| -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 -= 8; |
| - ASSERT(Utils::IsAligned(offset, 4)); |
| - ASSERT(Utils::IsInt(Utils::CountOneBits(kBranchOffsetMask), offset)); |
| - |
| - // Properly preserve only the bits supported in the instruction. |
| - offset >>= 2; |
| - offset &= kBranchOffsetMask; |
| - return (inst & ~kBranchOffsetMask) | offset; |
| -} |
| - |
| - |
| -int Assembler::DecodeBranchOffset(int32_t inst) { |
| - // Sign-extend, left-shift by 2, then add 8. |
| - return ((((inst & kBranchOffsetMask) << 8) >> 6) + 8); |
| -} |
| - |
| - |
| int32_t Assembler::AddObject(const Object& obj) { |
| ASSERT(obj.IsNotTemporaryScopedHandle()); |
| ASSERT(obj.IsOld()); |