Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Unified Diff: runtime/vm/assembler_arm.cc

Issue 21159002: Implements far-branches on ARM. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/assembler_arm.h ('k') | runtime/vm/assembler_mips.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/assembler_arm.cc
===================================================================
--- runtime/vm/assembler_arm.cc (revision 25616)
+++ 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 -= Instr::kPCReadOffset;
+ 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 -= Instr::kPCReadOffset;
+ 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) + Instr::kPCReadOffset);
+}
+
+
+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 IP, high
+ ((movw & 0xfff0f000) == 0xe300c000)) { // movw IP, 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(IP, low);
+ movt(IP, high);
+ if (link) {
+ blx(IP, cond);
+ } else {
+ bx(IP, 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);
}
@@ -2415,10 +2560,10 @@
const intptr_t offset = CodeSize();
Comment("EnterOsrFrame");
- mov(TMP, ShifterOperand(PC));
+ mov(IP, ShifterOperand(PC));
- AddImmediate(TMP, -offset);
- str(TMP, Address(FP, kPcMarkerSlotFromFp * kWordSize));
+ AddImmediate(IP, -offset);
+ str(IP, Address(FP, kPcMarkerSlotFromFp * kWordSize));
// Setup pool pointer for this dart function.
LoadPoolPointer();
@@ -2473,16 +2618,16 @@
AddImmediate(instance_reg, instance_size);
// instance_reg: potential next object start.
- LoadImmediate(TMP, heap->EndAddress());
- ldr(TMP, Address(TMP, 0));
- cmp(TMP, ShifterOperand(instance_reg));
+ LoadImmediate(IP, heap->EndAddress());
+ ldr(IP, Address(IP, 0));
+ cmp(IP, ShifterOperand(instance_reg));
// fail if heap end unsigned less than or equal to instance_reg.
b(failure, LS);
// Successfully allocated the object, now update top to point to
// next object start and store the class in the class field of object.
- LoadImmediate(TMP, heap->TopAddress());
- str(instance_reg, Address(TMP, 0));
+ LoadImmediate(IP, heap->TopAddress());
+ str(instance_reg, Address(IP, 0));
ASSERT(instance_size >= kHeapObjectTag);
AddImmediate(instance_reg, -instance_size + kHeapObjectTag);
@@ -2491,8 +2636,8 @@
tags = RawObject::SizeTag::update(instance_size, tags);
ASSERT(cls.id() != kIllegalCid);
tags = RawObject::ClassIdTag::update(cls.id(), tags);
- LoadImmediate(TMP, tags);
- str(TMP, FieldAddress(instance_reg, Object::tags_offset()));
+ LoadImmediate(IP, tags);
+ str(IP, FieldAddress(instance_reg, Object::tags_offset()));
} else {
b(failure);
}
@@ -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());
« no previous file with comments | « runtime/vm/assembler_arm.h ('k') | runtime/vm/assembler_mips.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698