2010-04-22  Gabor Loki  <loki@webkit.org>

        Reviewed by Gavin Barraclough.

        Use BLX and BX to keep happy the return stack predictor above ARMv4
        https://bugs.webkit.org/show_bug.cgi?id=37862

        Inspired by Jacob Bramley's patch from JaegerMonkey

        * assembler/ARMAssembler.cpp:
        (JSC::ARMAssembler::executableCopy):
        * assembler/ARMAssembler.h:
        (JSC::ARMAssembler::):
        (JSC::ARMAssembler::bx):
        (JSC::ARMAssembler::blx):
        (JSC::ARMAssembler::loadBranchTarget):
        (JSC::ARMAssembler::jmp):
        (JSC::ARMAssembler::getLdrImmAddress):
        * assembler/MacroAssemblerARM.h:
        (JSC::MacroAssemblerARM::jump):
        (JSC::MacroAssemblerARM::nearCall):
        (JSC::MacroAssemblerARM::call):
        (JSC::MacroAssemblerARM::ret):
        (JSC::MacroAssemblerARM::prepareCall):
        (JSC::MacroAssemblerARM::call32):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@58091 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/assembler/ARMAssembler.cpp b/JavaScriptCore/assembler/ARMAssembler.cpp
index 7393aa1..a181b7e 100644
--- a/JavaScriptCore/assembler/ARMAssembler.cpp
+++ b/JavaScriptCore/assembler/ARMAssembler.cpp
@@ -357,7 +357,7 @@
         int pos = (*iter) & (~0x1);
         ARMWord* ldrAddr = reinterpret_cast<ARMWord*>(data + pos);
         ARMWord* addr = getLdrImmAddress(ldrAddr);
-        if (*addr != 0xffffffff) {
+        if (*addr != InvalidBranchTarget) {
             if (!(*iter & 1)) {
                 int diff = reinterpret_cast<ARMWord*>(data + *addr) - (ldrAddr + DefaultPrefetching);
 
diff --git a/JavaScriptCore/assembler/ARMAssembler.h b/JavaScriptCore/assembler/ARMAssembler.h
index f93db68..4c2e0d0 100644
--- a/JavaScriptCore/assembler/ARMAssembler.h
+++ b/JavaScriptCore/assembler/ARMAssembler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 University of Szeged
+ * Copyright (C) 2009, 2010 University of Szeged
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -131,6 +131,9 @@
             FDTR = 0x0d000b00,
             B = 0x0a000000,
             BL = 0x0b000000,
+#if WTF_ARM_ARCH_AT_LEAST(5) || defined(__ARM_ARCH_4T__)
+            BX = 0x012fff10,
+#endif
             FMSR = 0x0e000a10,
             FMRS = 0x0e100a10,
             FSITOD = 0x0eb80bc0,
@@ -139,6 +142,7 @@
 #if WTF_ARM_ARCH_AT_LEAST(5)
             CLZ = 0x016f0f10,
             BKPT = 0xe120070,
+            BLX = 0x012fff30,
 #endif
 #if WTF_ARM_ARCH_AT_LEAST(7)
             MOVW = 0x03000000,
@@ -182,6 +186,7 @@
         };
 
         static const ARMWord INVALID_IMM = 0xf0000000;
+        static const ARMWord InvalidBranchTarget = 0xffffffff;
         static const int DefaultPrefetching = 2;
 
         class JmpSrc {
@@ -547,6 +552,30 @@
 #endif
         }
 
+        void bx(int rm, Condition cc = AL)
+        {
+#if WTF_ARM_ARCH_AT_LEAST(5) || defined(__ARM_ARCH_4T__)
+            emitInst(static_cast<ARMWord>(cc) | BX, 0, 0, RM(rm));
+#else
+            mov_r(ARMRegisters::pc, RM(rm), cc);
+#endif
+        }
+
+        JmpSrc blx(int rm, Condition cc = AL)
+        {
+#if WTF_ARM_ARCH_AT_LEAST(5)
+            int s = m_buffer.uncheckedSize();
+            emitInst(static_cast<ARMWord>(cc) | BLX, 0, 0, RM(rm));
+#else
+            ASSERT(rm != 14);
+            ensureSpace(2 * sizeof(ARMWord), 0);
+            mov_r(ARMRegisters::lr, ARMRegisters::pc, cc);
+            int s = m_buffer.uncheckedSize();
+            bx(rm, cc);
+#endif
+            return JmpSrc(s);
+        }
+
         static ARMWord lsl(int reg, ARMWord value)
         {
             ASSERT(reg <= ARMRegisters::pc);
@@ -619,21 +648,34 @@
             return label();
         }
 
-        JmpSrc jmp(Condition cc = AL, int useConstantPool = 0)
+        JmpSrc loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0)
         {
             ensureSpace(sizeof(ARMWord), sizeof(ARMWord));
             int s = m_buffer.uncheckedSize();
-            ldr_un_imm(ARMRegisters::pc, 0xffffffff, cc);
+            ldr_un_imm(rd, InvalidBranchTarget, cc);
             m_jumps.append(s | (useConstantPool & 0x1));
             return JmpSrc(s);
         }
 
+        JmpSrc jmp(Condition cc = AL, int useConstantPool = 0)
+        {
+            return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool);
+        }
+
         void* executableCopy(ExecutablePool* allocator);
 
         // Patching helpers
 
         static ARMWord* getLdrImmAddress(ARMWord* insn)
         {
+#if WTF_ARM_ARCH_AT_LEAST(5)
+            // Check for call
+            if ((*insn & 0x0f7f0000) != 0x051f0000) {
+                // Must be BLX
+                ASSERT((*insn & 0x012fff30) == 0x012fff30);
+                insn--;
+            }
+#endif
             // Must be an ldr ..., [pc +/- imm]
             ASSERT((*insn & 0x0f7f0000) == 0x051f0000);
 
diff --git a/JavaScriptCore/assembler/MacroAssemblerARM.h b/JavaScriptCore/assembler/MacroAssemblerARM.h
index 52c4fa2..3389fa6 100644
--- a/JavaScriptCore/assembler/MacroAssemblerARM.h
+++ b/JavaScriptCore/assembler/MacroAssemblerARM.h
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2008 Apple Inc.
- * Copyright (C) 2009 University of Szeged
+ * Copyright (C) 2009, 2010 University of Szeged
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -474,7 +474,7 @@
 
     void jump(RegisterID target)
     {
-        move(target, ARMRegisters::pc);
+        m_assembler.bx(target);
     }
 
     void jump(Address address)
@@ -566,14 +566,19 @@
 
     Call nearCall()
     {
+#if WTF_ARM_ARCH_AT_LEAST(5)
+        ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord));
+        m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true);
+        return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear);
+#else
         prepareCall();
         return Call(m_assembler.jmp(ARMAssembler::AL, true), Call::LinkableNear);
+#endif
     }
 
     Call call(RegisterID target)
     {
-        prepareCall();
-        move(ARMRegisters::pc, target);
+        m_assembler.blx(target);
         JmpSrc jmpSrc;
         return Call(jmpSrc, Call::None);
     }
@@ -585,7 +590,7 @@
 
     void ret()
     {
-        m_assembler.mov_r(ARMRegisters::pc, linkRegister);
+        m_assembler.bx(linkRegister);
     }
 
     void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest)
@@ -681,8 +686,14 @@
 
     Call call()
     {
+#if WTF_ARM_ARCH_AT_LEAST(5)
+        ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord));
+        m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true);
+        return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable);
+#else
         prepareCall();
         return Call(m_assembler.jmp(ARMAssembler::AL, true), Call::Linkable);
+#endif
     }
 
     Call tailRecursiveCall()
@@ -886,44 +897,56 @@
 
     void prepareCall()
     {
+#if WTF_ARM_ARCH_VERSION < 5
         ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord));
 
         m_assembler.mov_r(linkRegister, ARMRegisters::pc);
+#endif
     }
 
     void call32(RegisterID base, int32_t offset)
     {
+#if WTF_ARM_ARCH_AT_LEAST(5)
+        int targetReg = ARMRegisters::S1;
+#else
+        int targetReg = ARMRegisters::pc;
+#endif
+        int tmpReg = ARMRegisters::S1;
+
         if (base == ARMRegisters::sp)
             offset += 4;
 
         if (offset >= 0) {
             if (offset <= 0xfff) {
                 prepareCall();
-                m_assembler.dtr_u(true, ARMRegisters::pc, base, offset);
+                m_assembler.dtr_u(true, targetReg, base, offset);
             } else if (offset <= 0xfffff) {
-                m_assembler.add_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
+                m_assembler.add_r(tmpReg, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
                 prepareCall();
-                m_assembler.dtr_u(true, ARMRegisters::pc, ARMRegisters::S0, offset & 0xfff);
+                m_assembler.dtr_u(true, targetReg, tmpReg, offset & 0xfff);
             } else {
-                ARMWord reg = m_assembler.getImm(offset, ARMRegisters::S0);
+                ARMWord reg = m_assembler.getImm(offset, tmpReg);
                 prepareCall();
-                m_assembler.dtr_ur(true, ARMRegisters::pc, base, reg);
+                m_assembler.dtr_ur(true, targetReg, base, reg);
             }
         } else  {
             offset = -offset;
             if (offset <= 0xfff) {
                 prepareCall();
-                m_assembler.dtr_d(true, ARMRegisters::pc, base, offset);
+                m_assembler.dtr_d(true, targetReg, base, offset);
             } else if (offset <= 0xfffff) {
-                m_assembler.sub_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
+                m_assembler.sub_r(tmpReg, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
                 prepareCall();
-                m_assembler.dtr_d(true, ARMRegisters::pc, ARMRegisters::S0, offset & 0xfff);
+                m_assembler.dtr_d(true, targetReg, tmpReg, offset & 0xfff);
             } else {
-                ARMWord reg = m_assembler.getImm(offset, ARMRegisters::S0);
+                ARMWord reg = m_assembler.getImm(offset, tmpReg);
                 prepareCall();
-                m_assembler.dtr_dr(true, ARMRegisters::pc, base, reg);
+                m_assembler.dtr_dr(true, targetReg, base, reg);
             }
         }
+#if WTF_ARM_ARCH_AT_LEAST(5)
+        m_assembler.blx(targetReg);
+#endif
     }
 
 private: