Implement function calls on ARM using the blx instruction when
authorerik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 19 Mar 2010 14:05:11 +0000 (14:05 +0000)
committererik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 19 Mar 2010 14:05:11 +0000 (14:05 +0000)
available. Using blx will allow the CPU to predict the return address
fo the function, resulting in better overall performamce.
This is a copy of http://codereview.chromium.org/1113002 by
rodolph.perfetta@googlemail.com

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4198 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/assembler-arm-inl.h
src/arm/assembler-arm.cc
src/arm/assembler-arm.h
src/arm/constants-arm.h
src/arm/debug-arm.cc
src/arm/macro-assembler-arm.cc

index 354436cb14710492f952ea4fa32aadc746d5d03a..3f0854e33300806386d859c935e40bb29a8c5ef9 100644 (file)
@@ -144,12 +144,21 @@ void RelocInfo::set_call_object(Object* target) {
 
 
 bool RelocInfo::IsPatchedReturnSequence() {
-  // On ARM a "call instruction" is actually two instructions.
-  //   mov lr, pc
-  //   ldr pc, [pc, #XXX]
-  return (Assembler::instr_at(pc_) == kMovLrPc)
-          && ((Assembler::instr_at(pc_ + Assembler::kInstrSize) & kLdrPCPattern)
-              == kLdrPCPattern);
+  Instr current_instr = Assembler::instr_at(pc_);
+  Instr next_instr = Assembler::instr_at(pc_ + Assembler::kInstrSize);
+#ifdef USE_BLX
+  // A patched return sequence is:
+  //  ldr ip, [pc, #0]
+  //  blx ip
+  return ((current_instr & kLdrPCMask) == kLdrPCPattern)
+          && ((next_instr & kBlxRegMask) == kBlxRegPattern);
+#else
+  // A patched return sequence is:
+  //  mov lr, pc
+  //  ldr pc, [pc, #-4]
+  return (current_instr == kMovLrPc)
+          && ((next_instr & kLdrPCMask) == kLdrPCPattern);
+#endif
 }
 
 
@@ -225,6 +234,16 @@ Address Assembler::target_address_address_at(Address pc) {
     target_pc -= kInstrSize;
     instr = Memory::int32_at(target_pc);
   }
+
+#ifdef USE_BLX
+  // If we have a blx instruction, the instruction before it is
+  // what needs to be patched.
+  if ((instr & kBlxRegMask) == kBlxRegPattern) {
+    target_pc -= kInstrSize;
+    instr = Memory::int32_at(target_pc);
+  }
+#endif
+
   // Verify that the instruction to patch is a
   // ldr<cond> <Rd>, [pc +/- offset_12].
   ASSERT((instr & 0x0f7f0000) == 0x051f0000);
index 6b226fd3dfbe170c7762570a99735c06fb995072..d4cd818c17d73b2040a7022c6b9b86665f3b37e9 100644 (file)
@@ -240,8 +240,14 @@ static const Instr kPopRegPattern =
     al | B26 | L | 4 | PostIndex | sp.code() * B16;
 // mov lr, pc
 const Instr kMovLrPc = al | 13*B21 | pc.code() | lr.code() * B12;
-// ldr pc, [pc, #XXX]
-const Instr kLdrPCPattern = al | B26 | L | pc.code() * B16;
+// ldr rd, [pc, #offset]
+const Instr kLdrPCMask = CondMask | 15 * B24 | 7 * B20 | 15 * B16;
+const Instr kLdrPCPattern = al | 5 * B24 | L | pc.code() * B16;
+// blxcc rm
+const Instr kBlxRegMask =
+    15 * B24 | 15 * B20 | 15 * B16 | 15 * B12 | 15 * B8 | 15 * B4;
+const Instr kBlxRegPattern =
+    B24 | B21 | 15 * B16 | 15 * B12 | 15 * B8 | 3 * B4;
 
 // Spare buffer.
 static const int kMinimalBufferSize = 4*KB;
index c972c57b5d7c7ca495a8121faf6ca151e326a2b5..539a6b899002aa428beabf422879437d042e1989 100644 (file)
@@ -509,7 +509,10 @@ typedef int32_t Instr;
 
 
 extern const Instr kMovLrPc;
+extern const Instr kLdrPCMask;
 extern const Instr kLdrPCPattern;
+extern const Instr kBlxRegMask;
+extern const Instr kBlxRegPattern;
 
 
 class Assembler : public Malloced {
@@ -590,12 +593,34 @@ class Assembler : public Malloced {
   static const int kInstrSize = sizeof(Instr);
 
   // Distance between the instruction referring to the address of the call
-  // target (ldr pc, [target addr in const pool]) and the return address
+  // target and the return address.
+#ifdef USE_BLX
+  // Call sequence is:
+  //  ldr  ip, [pc, #...] @ call address
+  //  blx  ip
+  //                      @ return address
+  static const int kCallTargetAddressOffset = 2 * kInstrSize;
+#else
+  // Call sequence is:
+  //  mov  lr, pc
+  //  ldr  pc, [pc, #...] @ call address
+  //                      @ return address
   static const int kCallTargetAddressOffset = kInstrSize;
+#endif
 
   // Distance between start of patched return sequence and the emitted address
   // to jump to.
-  static const int kPatchReturnSequenceAddressOffset = kInstrSize;
+#ifdef USE_BLX
+  // Return sequence is:
+  //  ldr  ip, [pc, #0]   @ emited address and start
+  //  blx  ip
+  static const int kPatchReturnSequenceAddressOffset =  0 * kInstrSize;
+#else
+  // Return sequence is:
+  //  mov  lr, pc         @ start of sequence
+  //  ldr  pc, [pc, #-4]  @ emited address
+  static const int kPatchReturnSequenceAddressOffset =  kInstrSize;
+#endif
 
   // Difference between address of current opcode and value read from pc
   // register.
index 8a32c95b6880ba675d991ee89161f9ce8be86e54..2b883f3b348b2915d231a8be6337747733cf0555 100644 (file)
 # define CAN_USE_THUMB_INSTRUCTIONS 1
 #endif
 
+// Using blx may yield better code, so use it when required or when available
+#if defined(USE_THUMB_INTERWORK) || defined(CAN_USE_ARMV5_INSTRUCTIONS)
+#define USE_BLX 1
+#endif
+
 namespace assembler {
 namespace arm {
 
index e6b61b4d2d26e68c7553aba2579597957d4ac8c9..bc81b19d21acbe82aed2300ca764f66feabb83dd 100644 (file)
@@ -46,13 +46,23 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
   //   add sp, sp, #4
   //   bx lr
   // to a call to the debug break return code.
+  // #if USE_BLX
+  //   ldr ip, [pc, #0]
+  //   blx ip
+  // #else
   //   mov lr, pc
   //   ldr pc, [pc, #-4]
+  // #endif
   //   <debug break return code entry point address>
   //   bktp 0
   CodePatcher patcher(rinfo()->pc(), 4);
+#ifdef USE_BLX
+  patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
+  patcher.masm()->blx(v8::internal::ip);
+#else
   patcher.masm()->mov(v8::internal::lr, v8::internal::pc);
   patcher.masm()->ldr(v8::internal::pc, MemOperand(v8::internal::pc, -4));
+#endif
   patcher.Emit(Debug::debug_break_return()->entry());
   patcher.masm()->bkpt(0);
 }
index bc779eb8dece8a432fc318104d86c76c3a6c3717..691c08c4b6760b531dbb69524b61614e87e06129 100644 (file)
@@ -58,11 +58,6 @@ MacroAssembler::MacroAssembler(void* buffer, int size)
 #endif
 
 
-// Using blx may yield better code, so use it when required or when available
-#if defined(USE_THUMB_INTERWORK) || defined(CAN_USE_ARMV5_INSTRUCTIONS)
-#define USE_BLX 1
-#endif
-
 // Using bx does not yield better code, so use it only when required
 #if defined(USE_THUMB_INTERWORK)
 #define USE_BX 1
@@ -117,16 +112,33 @@ void MacroAssembler::Call(Register target, Condition cond) {
 
 void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode,
                           Condition cond) {
+#if USE_BLX
+  // On ARMv5 and after the recommended call sequence is:
+  //  ldr ip, [pc, #...]
+  //  blx ip
+
+  // The two instructions (ldr and blx) could be separated by a literal
+  // pool and the code would still work. The issue comes from the
+  // patching code which expect the ldr to be just above the blx.
+  BlockConstPoolFor(2);
+  // Statement positions are expected to be recorded when the target
+  // address is loaded. The mov method will automatically record
+  // positions when pc is the target, since this is not the case here
+  // we have to do it explicitly.
+  WriteRecordedPositions();
+
+  mov(ip, Operand(target, rmode), LeaveCC, cond);
+  blx(ip, cond);
+
+  ASSERT(kCallTargetAddressOffset == 2 * kInstrSize);
+#else
   // Set lr for return at current pc + 8.
   mov(lr, Operand(pc), LeaveCC, cond);
   // Emit a ldr<cond> pc, [pc + offset of target in constant pool].
   mov(pc, Operand(target, rmode), LeaveCC, cond);
-  // If USE_BLX is defined, we could emit a 'mov ip, target', followed by a
-  // 'blx ip'; however, the code would not be shorter than the above sequence
-  // and the target address of the call would be referenced by the first
-  // instruction rather than the second one, which would make it harder to patch
-  // (two instructions before the return address, instead of one).
+
   ASSERT(kCallTargetAddressOffset == kInstrSize);
+#endif
 }