Bug 723290: Make new OSI mechanism build on ARM. (r=mjrosenb)
authorChris Leary <cdleary@mozilla.com>
Wed, 01 Feb 2012 13:59:50 -0800
changeset 112528 18d45f6608d89bda8c925b0adeb8bd7c1b082859
parent 112527 5e8230d92ca0bb3661da49c491e33ee5b895d44d
child 112529 c3797142f9281d11a5a56f6549862ed7eba08dce
push id239
push userakeybl@mozilla.com
push dateThu, 03 Jan 2013 21:54:43 +0000
treeherdermozilla-release@3a7b66445659 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmjrosenb
bugs723290
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 723290: Make new OSI mechanism build on ARM. (r=mjrosenb)
js/src/Makefile.in
js/src/ion/arm/Assembler-arm.cpp
js/src/ion/arm/Assembler-arm.h
js/src/ion/arm/Bailouts-arm.cpp
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/IonFrames-arm.cpp
js/src/ion/arm/IonFrames-arm.h
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/arm/MacroAssembler-arm.h
js/src/ion/shared/IonFrames-x86-shared.h
js/src/jit-test/jit_test.py
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -377,16 +377,17 @@ ifdef ENABLE_ION
 ifeq (arm, $(findstring arm, $(TARGET_CPU)))
 #CPPSRCS		+= only_on_arm.cpp
 VPATH +=	$(srcdir)/ion/arm
 CPPSRCS +=	Lowering-arm.cpp \
 		CodeGenerator-arm.cpp \
 		Trampoline-arm.cpp \
 		Assembler-arm.cpp \
 		Bailouts-arm.cpp \
+		IonFrames-arm.cpp \
 		MoveEmitter-arm.cpp \
 		Architecture-arm.cpp \
 		MacroAssembler-arm.cpp \
 		$(NULL)
 endif
 endif #ENABLE_ION
 ifeq (sparc, $(findstring sparc,$(TARGET_CPU)))
 ASFILES +=	TrampolineSparc.s
--- a/js/src/ion/arm/Assembler-arm.cpp
+++ b/js/src/ion/arm/Assembler-arm.cpp
@@ -1026,16 +1026,21 @@ Assembler::writeInstStatic(uint32 x, uin
 void
 Assembler::align(int alignment)
 {
     while (!m_buffer.isAligned(alignment))
         as_mov(r0, O2Reg(r0));
 
 }
 void
+Assembler::as_nop()
+{
+    writeInst(0xe320f000);
+}
+void
 Assembler::as_alu(Register dest, Register src1, Operand2 op2,
                 ALUOp op, SetCond_ sc, Condition c)
 {
     writeInst((int)op | (int)sc | (int) c | op2.encode() |
               ((dest == InvalidReg) ? 0 : RD(dest)) |
               ((src1 == InvalidReg) ? 0 : RN(src1)));
 }
 void
--- a/js/src/ion/arm/Assembler-arm.h
+++ b/js/src/ion/arm/Assembler-arm.h
@@ -894,30 +894,30 @@ class BOffImm
   public:
     uint32 encode() {
         return data;
     }
     int32 decode() {
         return ((((int32)data) << 8) >> 6) + 8;
     }
 
-    BOffImm(int offset)
-        : data ((offset - 8) >> 2 & 0x00ffffff)
+    explicit BOffImm(int offset)
+      : data ((offset - 8) >> 2 & 0x00ffffff)
     {
         JS_ASSERT ((offset & 0x3) == 0);
         JS_ASSERT ((offset - 8) >= -33554432);
         JS_ASSERT ((offset - 8) <= 33554428);
     }
     static const int INVALID = 0x00800000;
     BOffImm()
       : data(INVALID)
     { }
 
     bool isInvalid() {
-        return data == INVALID;
+        return data == uint32(INVALID);
     }
     Instruction *getDest(Instruction *src);
 
   private:
     friend class InstBranchImm;
     BOffImm(Instruction &inst);
 };
 
@@ -1171,26 +1171,27 @@ class Assembler
     // will be overwritten with a new instruction. In order to do this very
     // after assembly buffers no longer exist, when calling with a third
     // dest parameter, a this object is still needed.  dummy always happens
     // to be null, but we shouldn't be looking at it in any case.
     static Assembler *dummy;
     Pool pools_[4];
     Pool *int32Pool;
     Pool *doublePool;
-public:
+
+  public:
     Assembler()
       : dataBytesNeeded_(0),
         enoughMemory_(true),
         m_buffer(4, 4, 0, 2, &pools_[0], 8),
         int32Pool(m_buffer.getPool(1)),
         doublePool(m_buffer.getPool(0)),
+        isFinished(false),
         dtmActive(false),
-        dtmCond(Always),
-        isFinished(false)
+        dtmCond(Always)
     {
         // Set up the backwards double region
         new (&pools_[2]) Pool (1024, 8, 4, 8, 8, true);
         // Set up the backwards 32 bit region
         new (&pools_[3]) Pool (4096, 4, 4, 4, 4, true, true);
         // Set up the forwards double region
         new (doublePool) Pool (1024, 8, 4, 8, 8, false, false, &pools_[2]);
         // Set up the forwards 32 bit region
@@ -1253,16 +1254,17 @@ public:
     static void writeInstStatic(uint32 x, uint32 *dest);
 
   public:
     // resreve enough space in the instruction stream for a jumpPool.
     // return the reserved space.
     void as_jumpPool(uint32 size);
 
     void align(int alignment);
+    void as_nop();
     void as_alu(Register dest, Register src1, Operand2 op2,
                 ALUOp op, SetCond_ sc = NoSetCond, Condition c = Always);
 
     void as_mov(Register dest,
                 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
     void as_mvn(Register dest, Operand2 op2,
                 SetCond_ sc = NoSetCond, Condition c = Always);
     // logical operations
@@ -1590,16 +1592,42 @@ public:
     void flushBuffer();
     // this should return a BOffImm, but I didn't want to require everyplace that used the
     // AssemblerBuffer to make that class.
     static ptrdiff_t getBranchOffset(const Instruction *i);
     static void retargetBranch(Instruction *i, int32 offset);
     static void writePoolHeader(uint8 *start, Pool *p);
     static void writePoolFooter(uint8 *start, Pool *p);
     static void writePoolGuard(BufferOffset branch, Instruction *inst, BufferOffset dest);
+
+    // The size of an arbitrary 32-bit call in the instruction stream.
+    // On ARM this sequence is |pc = ldr pc - 4; imm32| given that we
+    // never reach the imm32.
+    static uint32 patchWrite_NearCallSize() {
+        return 2 * sizeof(uint32);
+    }
+    static void patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue,
+                                        ImmWord expectedValue) {
+        uint32 *ptr = (uint32 *) label.raw();
+        JS_ASSERT(*ptr == expectedValue.value);
+        *ptr = newValue.value;
+        JSC::ExecutableAllocator::cacheFlush(ptr, sizeof(uintptr_t));
+    }
+    static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm) {
+        *label.raw() = imm.value;
+        JSC::ExecutableAllocator::cacheFlush(label.raw(), sizeof(uintptr_t));
+    }
+    static void patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall) {
+        uint32 *inst = (uint32 *) start.raw();
+        // ldr pc = [pc - 4]; // -4 because of the implicit +8
+        // imm32
+        inst[0] = 0xe51ffffc;
+        inst[1] = uintptr_t(toCall.raw());
+        JSC::ExecutableAllocator::cacheFlush(inst, 2 * sizeof(uintptr_t));
+    }
 }; // Assembler
 
 // An Instruction is a structure for both encoding and decoding any and all ARM instructions.
 // many classes have not been implemented thusfar.
 class Instruction
 {
     uint32 data;
 
--- a/js/src/ion/arm/Bailouts-arm.cpp
+++ b/js/src/ion/arm/Bailouts-arm.cpp
@@ -139,43 +139,16 @@ class BailoutStack
     }
     uint8 *parentStackPointer() const {
         if (frameClass() == FrameSizeClass::None())
             return (uint8 *)this + sizeof(BailoutStack);
         return (uint8 *)this + offsetof(BailoutStack, snapshotOffset_);
     }
 };
 
-class InvalidationBailoutStack
-{
-    double fpregs_[FloatRegisters::Total];
-    uintptr_t regs_[Registers::Total];
-    uintptr_t pad[2];
-    uintptr_t frameDescriptor_;
-
-    size_t frameSize() const {
-        return frameDescriptor_ >> FRAMETYPE_BITS;
-    }
-    size_t frameDescriptorOffset() const {
-        return offsetof(InvalidationBailoutStack, frameDescriptor_);
-    }
-
-  public:
-    uint8 *sp() const {
-        return (uint8 *) this + frameDescriptorOffset() + sizeof(size_t) + sizeof(size_t);
-    }
-    uint8 *fp() const {
-        return sp() + frameSize();
-    }
-    MachineState machine() {
-        return MachineState(regs_, fpregs_);
-    }
-  public:
-};
-
 } // namespace ion
 } // namespace js
 
 FrameRecovery
 ion::FrameRecoveryFromBailout(IonCompartment *ion, BailoutStack *bailout)
 {
     uint8 *sp = bailout->parentStackPointer();
     uint8 *fp = sp + bailout->frameSize();
@@ -196,16 +169,15 @@ ion::FrameRecoveryFromBailout(IonCompart
     JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);
 
     return FrameRecovery::FromBailoutId(fp, sp, bailout->machine(), bailoutId);
 }
 
 FrameRecovery
 ion::FrameRecoveryFromInvalidation(IonCompartment *ion, InvalidationBailoutStack *bailout)
 {
-    IonJSFrameLayout *fp = (IonJSFrameLayout *) bailout->fp();
-    InvalidationRecord *record = CalleeTokenToInvalidationRecord(fp->calleeToken());
-    const IonFrameInfo *exitInfo = record->ionScript->getFrameInfo(record->returnAddress);
-    SnapshotOffset snapshotOffset = exitInfo->snapshotOffset();
-
-    return FrameRecovery::FromSnapshot(bailout->fp(), bailout->sp(), bailout->machine(),
-                                       snapshotOffset);
+    IonScript *ionScript = bailout->ionScript();
+    const OsiIndex *osiIndex = ionScript->getOsiIndex(bailout->osiPointReturnAddress());
+    FrameRecovery fr = FrameRecovery::FromSnapshot((uint8 *) bailout->fp(), bailout->sp(),
+                                                   bailout->machine(), osiIndex->snapshotOffset());
+    fr.setIonScript(ionScript);
+    return fr;
 }
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -112,16 +112,17 @@ CodeGeneratorARM::CodeGeneratorARM(MIRGe
   : CodeGeneratorShared(gen, graph),
     deoptLabel_(NULL)
 {
 }
 
 bool
 CodeGeneratorARM::generatePrologue()
 {
+    masm.breakpoint();
     // Note that this automatically sets MacroAssembler::framePushed().
     masm.reserveStack(frameSize());
     masm.checkStackAlignment();
     // Allocate returnLabel_ on the heap, so we don't run its destructor and
     // assert-not-bound in debug mode on compilation failure.
     returnLabel_ = new HeapLabel();
 
     return true;
new file mode 100644
--- /dev/null
+++ b/js/src/ion/arm/IonFrames-arm.cpp
@@ -0,0 +1,27 @@
+#include "ion/Ion.h"
+#include "ion/IonFrames.h"
+
+using namespace js;
+using namespace js::ion;
+
+IonJSFrameLayout *
+InvalidationBailoutStack::fp() const
+{
+    return (IonJSFrameLayout *) (sp() + ionScript_->frameSize());
+}
+
+void
+InvalidationBailoutStack::checkInvariants() const
+{
+#ifdef DEBUG
+    IonJSFrameLayout *frame = fp();
+    CalleeToken token = frame->calleeToken();
+    JS_ASSERT(token);
+    JS_ASSERT(GetCalleeTokenTag(token) == CalleeToken_Function);
+
+    uint8 *rawBase = ionScript()->method()->raw();
+    uint8 *rawLimit = rawBase + ionScript()->method()->instructionsSize();
+    uint8 *osiPoint = osiPointReturnAddress();
+    JS_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit);
+#endif
+}
--- a/js/src/ion/arm/IonFrames-arm.h
+++ b/js/src/ion/arm/IonFrames-arm.h
@@ -49,25 +49,31 @@ class IonFramePrefix;
 // Layout of the frame prefix. This assumes the stack architecture grows down.
 // If this is ever not the case, we'll have to refactor.
 class IonCommonFrameLayout
 {
     uint8 *returnAddress_;
     void *padding;
     uintptr_t descriptor_;
 
+    static const uintptr_t FrameTypeMask = (1 << FRAMETYPE_BITS) - 1;
+
   public:
     static size_t offsetOfDescriptor() {
         return offsetof(IonCommonFrameLayout, descriptor_);
     }
     static size_t offsetOfReturnAddress() {
         return offsetof(IonCommonFrameLayout, returnAddress_);
     }
     FrameType prevType() const {
-        return FrameType(descriptor_ & ((1 << FRAMETYPE_BITS) - 1));
+        return FrameType(descriptor_ & FrameTypeMask);
+    }
+    void changePrevType(FrameType type) {
+        descriptor_ &= ~FrameTypeMask;
+        descriptor_ |= type;
     }
     size_t prevFrameLocalSize() const {
         return descriptor_ >> FRAMETYPE_BITS;
     }
     void setFrameDescriptor(size_t size, FrameType type) {
         descriptor_ = (size << FRAMETYPE_BITS) | type;
     }
     uint8 *returnAddress() const {
@@ -92,16 +98,19 @@ class IonJSFrameLayout : public IonEntry
   public:
     void *calleeToken() const {
         return calleeToken_;
     }
 
     static size_t offsetOfCalleeToken() {
         return offsetof(IonJSFrameLayout, calleeToken_);
     }
+    void replaceCalleeToken(void *calleeToken) {
+        calleeToken_ = calleeToken;
+    }
 
     Value *argv() {
         return (Value *)(this + 1);
     }
 
     // Computes a reference to a slot, where a slot is a distance from the base
     // frame pointer (as would be used for LStackSlot).
     uintptr_t *slotRef(uint32 slot) {
@@ -136,12 +145,39 @@ class IonExitFrameLayout : public IonCom
     void *padding2;
 
   public:
     static inline size_t Size() {
         return sizeof(IonExitFrameLayout);
     }
 };
 
+// An invalidation bailout stack is at the stack pointer for the callee frame.
+class InvalidationBailoutStack
+{
+    double      fpregs_[FloatRegisters::Total];
+    uintptr_t   regs_[Registers::Total];
+    IonScript   *ionScript_;
+    uint8       *osiPointReturnAddress_;
+
+  public:
+    uint8 *sp() const {
+        return (uint8 *) this + sizeof(InvalidationBailoutStack);
+    }
+    IonJSFrameLayout *fp() const;
+    MachineState machine() {
+        return MachineState(regs_, fpregs_);
+    }
+
+    IonScript *ionScript() const {
+        return ionScript_;
+    }
+    uint8 *osiPointReturnAddress() const {
+        return osiPointReturnAddress_;
+    }
+
+    void checkInvariants() const;
+};
+
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ionframes_arm_h
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -211,16 +211,21 @@ MacroAssemblerARM::ma_alu(Register src1,
 }
 
 void
 MacroAssemblerARM::ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op, SetCond_ sc, Condition c)
 {
     as_alu(dest, src1, op2, op, sc, c);
 }
 void
+MacroAssemblerARM::ma_nop()
+{
+    as_nop();
+}
+void
 MacroAssemblerARM::ma_mov(Register src, Register dest,
             SetCond_ sc, Assembler::Condition c)
 {
     as_mov(dest, O2Reg(src), sc, c);
 }
 
 void
 MacroAssemblerARM::ma_mov(Imm32 imm, Register dest,
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -71,16 +71,17 @@ class MacroAssemblerARM : public Assembl
     void ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op,
                 SetCond_ sc = NoSetCond, Condition c = Always);
     void ma_alu(Register src1, Imm32 imm, Register dest,
                 ALUOp op,
                 SetCond_ sc =  NoSetCond, Condition c = Always);
 
     void ma_alu(Register src1, Operand op2, Register dest, ALUOp op,
                 SetCond_ sc = NoSetCond, Condition c = Always);
+    void ma_nop();
 
     // These should likely be wrapped up as a set of macros
     // or something like that.  I cannot think of a good reason
     // to explicitly have all of this code.
     // ALU based ops
     // mov
     void ma_mov(Register src, Register dest,
                 SetCond_ sc = NoSetCond, Condition c = Always);
@@ -394,17 +395,23 @@ class MacroAssemblerARMCompat : public M
          */
     }
     void call(void *dest) {
         ma_call(dest);
         /* we can blx to it if it close by, otherwise, we need to
          * set up a branch + link node.
          */
     }
+    void call(ImmWord word) {
+        call((void *) word.value);
+    }
 
+    void nop() {
+        ma_nop();
+    }
     void ret() {
         ma_pop(pc);
         m_buffer.markGuard();
     }
     void retn(Imm32 n) {
         // pc <- [sp]; sp += n
         ma_dtr(IsLoad, sp, n, pc, PostIndex);
         m_buffer.markGuard();
@@ -413,21 +420,26 @@ class MacroAssemblerARMCompat : public M
         ma_mov(imm, ScratchRegister);
         ma_push(ScratchRegister);
     }
     void push(ImmGCPtr imm) {
         ma_mov(imm, ScratchRegister);
         ma_push(ScratchRegister);
     }
 
+    CodeOffsetLabel pushWithPatch(ImmWord imm) {
+        CodeOffsetLabel label = currentOffset();
+        push(Imm32(imm.value));
+        return label;
+    }
+
     void jump(Label *label) {
         as_b(label);
     }
 
-
     // Returns the register containing the type tag.
     Register splitTagForTest(const ValueOperand &value) {
         return value.typeReg();
     }
 
     // higher level tag testing code
     Condition testInt32(Condition cond, const ValueOperand &value);
 
--- a/js/src/ion/shared/IonFrames-x86-shared.h
+++ b/js/src/ion/shared/IonFrames-x86-shared.h
@@ -45,28 +45,30 @@ namespace js {
 namespace ion {
 
 class IonCommonFrameLayout
 {
   private:
     uint8 *returnAddress_;
     uintptr_t descriptor_;
 
+    static const uintptr_t FrameTypeMask = (1 << FRAMETYPE_BITS) - 1;
+
   public:
     static size_t offsetOfDescriptor() {
         return offsetof(IonCommonFrameLayout, descriptor_);
     }
     static size_t offsetOfReturnAddress() {
         return offsetof(IonCommonFrameLayout, returnAddress_);
     }
     FrameType prevType() const {
-        return FrameType(descriptor_ & ((1 << FRAMETYPE_BITS) - 1));
+        return FrameType(descriptor_ & FrameTypeMask);
     }
     void changePrevType(FrameType type) {
-        descriptor_ &= ~(1 << uintptr_t(FRAMETYPE_BITS));
+        descriptor_ &= ~FrameTypeMask;
         descriptor_ |= type;
     }
     size_t prevFrameLocalSize() const {
         return descriptor_ >> FRAMETYPE_BITS;
     }
     void setFrameDescriptor(size_t size, FrameType type) {
         descriptor_ = (size << FRAMETYPE_BITS) | type;
     }
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -495,17 +495,17 @@ def main(argv):
         test_list = [ _ for _ in test_list if not _.slow ]
 
     # The full test list is ready. Now create copies for each JIT configuration.
     job_list = []
     if OPTIONS.ion_tbpl:
         # Running all bits would take forever. Instead, we test a few interesting combinations.
         ion_flags = [ 
                       ['--ion', '-n'],
-                      ['--ion', '-n', '--ion-gvn=off', '--ion-licm=off' ],
+                      #['--ion', '-n', '--ion-gvn=off', '--ion-licm=off' ],
                     ]
         for test in test_list:
             for variant in ion_flags:
                 new_test = test.copy()
                 new_test.jitflags.extend(variant)
                 job_list.append(new_test)
     elif OPTIONS.ion:
         args = ['--ion']