Bug 1529559 - ARM64: PatchJump change the branching schema if the target is out of range. r=sstangl a=pascalc
authorNicolas B. Pierron <nicolas.b.pierron@nbp.name>
Tue, 19 Mar 2019 18:23:52 +0000
changeset 525900 a4c5f31c23abcfd6bf67cd6ed9cda4caa0aa9175
parent 525899 65b5cf7582293f90fbfbfa7c09ade328cec9fb71
child 525901 17126a22c0c7cc81829edb182be6c3e631bae564
push id2032
push userffxbld-merge
push dateMon, 13 May 2019 09:36:57 +0000
treeherdermozilla-release@455c1065dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstangl, pascalc
bugs1529559
milestone67.0
Bug 1529559 - ARM64: PatchJump change the branching schema if the target is out of range. r=sstangl a=pascalc Differential Revision: https://phabricator.services.mozilla.com/D23361
js/src/jit/arm64/Assembler-arm64.cpp
js/src/jit/arm64/MacroAssembler-arm64.h
js/src/jit/arm64/vixl/Instructions-vixl.h
--- a/js/src/jit/arm64/Assembler-arm64.cpp
+++ b/js/src/jit/arm64/Assembler-arm64.cpp
@@ -351,35 +351,42 @@ size_t Assembler::addPatchableJump(Buffe
     addJumpRelocation(src, reloc);
   }
 
   size_t extendedTableIndex = pendingJumps_.length();
   enoughMemory_ &= pendingJumps_.append(RelativePatch(src, nullptr, reloc));
   return extendedTableIndex;
 }
 
-// PatchJump() is only used by the IonCacheIRCompiler.
+// PatchJump() is only used by the IonCacheIRCompiler and patches code generated
+// by jumpWithPatch.
 //
 // The CodeLocationJump is the jump to be patched.
 // The code for the jump is emitted by jumpWithPatch().
 void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label) {
   MOZ_ASSERT(label.isSet());
 
   Instruction* load = (Instruction*)jump_.raw();
   MOZ_ASSERT(load->IsLDR());
 
   Instruction* branch = (Instruction*)load->NextInstruction()->skipPool();
   MOZ_ASSERT(branch->IsUncondB());
 
-  // FIXME: For the moment, just assume that the load isn't needed.
-  // FIXME: That assumption implies that the branch target is always in-range.
   if (branch->IsTargetReachable((Instruction*)label.raw())) {
     branch->SetImmPCOffsetTarget((Instruction*)label.raw());
   } else {
-    MOZ_CRASH("PatchJump target not reachable");
+    // Set the literal read by the load instruction to the target.
+    load->SetLiteral64(uint64_t(label.raw()));
+    // Get the scratch register set by the load instruction.
+    vixl::Register loadTarget = vixl::Register(load->Rt(), 64);
+    // Overwrite the branch instruction to branch on the same register as the
+    // load instruction.
+    Assembler::br(branch, loadTarget);
+    MOZ_ASSERT(branch->IsBR());
+    MOZ_ASSERT(load->Rt() == branch->Rn());
   }
 }
 
 void Assembler::PatchWrite_NearCall(CodeLocationLabel start,
                                     CodeLocationLabel toCall) {
   Instruction* dest = (Instruction*)start.raw();
   ptrdiff_t relTarget = (Instruction*)toCall.raw() - dest;
   ptrdiff_t relTarget00 = relTarget >> 2;
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -1250,17 +1250,19 @@ class MacroAssemblerCompat : public vixl
     MOZ_ASSERT(!label->bound());
 
     vixl::UseScratchRegisterScope temps(this);
     const ARMRegister scratch64 = temps.AcquireX();
 
     ARMBuffer::PoolEntry pe;
     BufferOffset load_bo;
 
-    // FIXME: This load is currently unused.
+    // This no-op load exists for PatchJump(), in the case of a target outside
+    // the range of +/- 128 MB. If the load is used, then the branch here is
+    // overwritten with a `BR` from the loaded register.
     load_bo = immPool64(scratch64, (uint64_t)label, &pe);
     BufferOffset branch_bo = b(-1, LabelDoc());
 
     label->use(branch_bo.getOffset());
     return CodeOffsetJump(load_bo.getOffset(), pe.index());
   }
 
   void compareDouble(DoubleCondition cond, FloatRegister lhs,
--- a/js/src/jit/arm64/vixl/Instructions-vixl.h
+++ b/js/src/jit/arm64/vixl/Instructions-vixl.h
@@ -457,16 +457,20 @@ class Instruction {
   }
 
   uint64_t Literal64() const {
     uint64_t literal;
     memcpy(&literal, LiteralAddress<const void*>(), sizeof(literal));
     return literal;
   }
 
+  void SetLiteral64(uint64_t literal) const {
+    memcpy(LiteralAddress<void*>(), &literal, sizeof(literal));
+  }
+
   float LiteralFP32() const {
     return rawbits_to_float(Literal32());
   }
 
   double LiteralFP64() const {
     return rawbits_to_double(Literal64());
   }