Bug 1508962 part 4 - Abstract jump opcodes in BaselineCodeGen. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 29 Nov 2018 22:44:50 +0000
changeset 505308 863959eb64d1f39ad7c07e44f98263ee10d6ef5a
parent 505307 c315f5473734de31c574cb650c67a98916f9f64b
child 505309 ea4b9bbb0cf49ff47f18427132141e42f71982ee
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs1508962
milestone65.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 1508962 part 4 - Abstract jump opcodes in BaselineCodeGen. r=djvj Differential Revision: https://phabricator.services.mozilla.com/D12924
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -37,18 +37,23 @@
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 using mozilla::Maybe;
 
-BaselineCompilerHandler::BaselineCompilerHandler(JSScript* script)
-  : compileDebugInstrumentation_(script->isDebuggee())
+namespace js {
+namespace jit {
+
+BaselineCompilerHandler::BaselineCompilerHandler(TempAllocator& alloc, JSScript* script)
+  : alloc_(alloc),
+    script_(script),
+    compileDebugInstrumentation_(script->isDebuggee())
 {
 }
 
 BaselineInterpreterHandler::BaselineInterpreterHandler()
 {
 }
 
 template <typename Handler>
@@ -69,43 +74,56 @@ BaselineCodeGen<Handler>::BaselineCodeGe
 #ifdef DEBUG
     inCall_(false),
 #endif
     modifiesArguments_(false)
 {
 }
 
 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : BaselineCodeGen(cx, alloc, script, script),
+  : BaselineCodeGen(cx, alloc, script,
+                    /* HandlerArgs = */ alloc, script),
     pcMappingEntries_(),
     profilerPushToggleOffset_(),
     profilerEnterFrameToggleOffset_(),
     profilerExitFrameToggleOffset_(),
     traceLoggerScriptTextIdOffset_()
 {
 #ifdef JS_CODEGEN_NONE
     MOZ_CRASH();
 #endif
 }
 
 bool
+BaselineCompilerHandler::init()
+{
+    uint32_t len = script_->length();
+
+    if (!labels_.init(alloc_, len)) {
+        return false;
+    }
+
+    for (size_t i = 0; i < len; i++) {
+        new (&labels_[i]) Label();
+    }
+
+    return true;
+}
+
+bool
 BaselineCompiler::init()
 {
     if (!analysis_.init(alloc_, cx->caches().gsnCache)) {
         return false;
     }
 
-    if (!labels_.init(alloc_, script->length())) {
+    if (!handler.init()) {
         return false;
     }
 
-    for (size_t i = 0; i < script->length(); i++) {
-        new (&labels_[i]) Label();
-    }
-
     if (!frame.init(alloc_)) {
         return false;
     }
 
     return true;
 }
 
 bool
@@ -1183,17 +1201,17 @@ BaselineCompiler::emitBody()
             prevpc = pc;
             continue;
         }
 
         if (info->jumpTarget) {
             // Fully sync the stack if there are incoming jumps.
             frame.syncStack(0);
             frame.setStackDepth(info->stackDepth);
-            masm.bind(labelOf(pc));
+            masm.bind(handler.labelOf(pc));
         } else if (MOZ_UNLIKELY(compileDebugInstrumentation())) {
             // Also fully sync the stack if the debugger is enabled.
             frame.syncStack(0);
         } else {
             // At the beginning of any op, at most the top 2 stack-values are unsynced.
             if (frame.stackDepth() > 2) {
                 frame.syncStack(2);
             }
@@ -1431,24 +1449,62 @@ BaselineCodeGen<Handler>::emit_JSOP_UNPI
     }
 
     // Store R0 under the nth value.
     Address dest = frame.addressOfStackValue(frame.peek(depth));
     masm.storeValue(R0, dest);
     return true;
 }
 
+template <>
+void
+BaselineCompilerCodeGen::emitJump()
+{
+    MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)));
+    frame.assertSyncedStack();
+
+    jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
+    masm.jump(handler.labelOf(target));
+}
+
+template <>
+void
+BaselineInterpreterCodeGen::emitJump()
+{
+    // We have to add the current pc's jump offset to the frame's pc.
+    MOZ_CRASH("NYI: interpreter emitJump");
+}
+
+template <>
+void
+BaselineCompilerCodeGen::emitTestBooleanTruthy(bool branchIfTrue, ValueOperand val)
+{
+    MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)));
+    frame.assertSyncedStack();
+
+    jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
+    masm.branchTestBooleanTruthy(branchIfTrue, val, handler.labelOf(target));
+}
+
+template <>
+void
+BaselineInterpreterCodeGen::emitTestBooleanTruthy(bool branchIfTrue, ValueOperand val)
+{
+    Label done;
+    masm.branchTestBooleanTruthy(!branchIfTrue, val, &done);
+    emitJump();
+    masm.bind(&done);
+}
+
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_GOTO()
 {
     frame.syncStack(0);
-
-    jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
-    masm.jump(labelOf(target));
+    emitJump();
     return true;
 }
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emitToBoolean()
 {
     Label skipIC;
@@ -1472,17 +1528,17 @@ BaselineCodeGen<Handler>::emitTest(bool 
     // Keep top stack value in R0.
     frame.popRegsAndSync(1);
 
     if (!knownBoolean && !emitToBoolean()) {
         return false;
     }
 
     // IC will leave a BooleanValue in R0, just need to branch on it.
-    masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc)));
+    emitTestBooleanTruthy(branchIfTrue, R0);
     return true;
 }
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_IFEQ()
 {
     return emitTest(false);
@@ -1504,17 +1560,17 @@ BaselineCodeGen<Handler>::emitAndOr(bool
     // AND and OR leave the original value on the stack.
     frame.syncStack(0);
 
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
     if (!knownBoolean && !emitToBoolean()) {
         return false;
     }
 
-    masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc)));
+    emitTestBooleanTruthy(branchIfTrue, R0);
     return true;
 }
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_AND()
 {
     return emitAndOr(false);
@@ -2356,22 +2412,21 @@ BaselineCodeGen<Handler>::emit_JSOP_COND
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_CASE()
 {
     frame.popRegsAndSync(1);
 
     Label done;
-    jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
     masm.branchTestBooleanTruthy(/* branchIfTrue */ false, R0, &done);
     {
         // Pop the switch value if the case matches.
         masm.addToStackPtr(Imm32(sizeof(Value)));
-        masm.jump(labelOf(target));
+        emitJump();
     }
     masm.bind(&done);
     return true;
 }
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_DEFAULT()
@@ -2405,17 +2460,17 @@ BaselineCodeGen<Handler>::emit_JSOP_NEWA
         return false;
     }
 
     frame.push(R0);
     return true;
 }
 
 typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
-const VMFunction jit::NewArrayCopyOnWriteInfo =
+const VMFunction NewArrayCopyOnWriteInfo =
     FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_NEWARRAY_COPYONWRITE()
 {
     RootedScript scriptRoot(cx, script);
     JSObject* obj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, pc);
@@ -3993,17 +4048,17 @@ BaselineCodeGen<Handler>::emit_JSOP_OPTI
 
     masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
 
 typedef bool (*ImplicitThisFn)(JSContext*, HandleObject, HandlePropertyName,
                                MutableHandleValue);
-const VMFunction jit::ImplicitThisInfo =
+const VMFunction ImplicitThisInfo =
     FunctionInfo<ImplicitThisFn>(ImplicitThisOperation, "ImplicitThisOperation");
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_IMPLICITTHIS()
 {
     frame.syncStack(0);
     masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
@@ -4124,18 +4179,17 @@ BaselineCodeGen<Handler>::emit_JSOP_FINA
 }
 
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_GOSUB()
 {
     // Jump to the finally block.
     frame.syncStack(0);
-    jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
-    masm.jump(labelOf(target));
+    emitJump();
     return true;
 }
 
 static void
 LoadBaselineScriptResumeEntries(MacroAssembler& masm, JSScript* script, Register dest,
                                 Register scratch)
 {
     MOZ_ASSERT(dest != scratch);
@@ -4754,48 +4808,65 @@ BaselineCodeGen<Handler>::emit_JSOP_TOST
 
     masm.tagValue(JSVAL_TYPE_STRING, ReturnReg, R0);
 
     masm.bind(&done);
     frame.push(R0);
     return true;
 }
 
+template <>
+void
+BaselineCompilerCodeGen::emitGetTableSwitchIndex(ValueOperand val, Register dest)
+{
+    jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
+    Label* defaultLabel = handler.labelOf(defaultpc);
+
+    int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
+    int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
+    int32_t length = high - low + 1;
+
+    // Jump to the 'default' pc if not int32 (tableswitch is only used when
+    // all cases are int32).
+    masm.branchTestInt32(Assembler::NotEqual, val, defaultLabel);
+    masm.unboxInt32(val, dest);
+
+    // Subtract 'low'. Bounds check.
+    if (low != 0) {
+        masm.sub32(Imm32(low), dest);
+    }
+    masm.branch32(Assembler::AboveOrEqual, dest, Imm32(length), defaultLabel);
+}
+
+template <>
+void
+BaselineInterpreterCodeGen::emitGetTableSwitchIndex(ValueOperand val, Register dest)
+{
+    MOZ_CRASH("NYI: interpreter emitTableSwitchJumpTableIndex");
+}
+
 template <typename Handler>
 bool
 BaselineCodeGen<Handler>::emit_JSOP_TABLESWITCH()
 {
     frame.popRegsAndSync(1);
 
-    jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
-    Label* defaultLabel = labelOf(defaultpc);
-
-    int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
-    int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
     uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN);
-    int32_t length = high - low + 1;
 
     Register key = R0.scratchReg();
     Register scratch1 = R1.scratchReg();
     Register scratch2 = R2.scratchReg();
 
     // Call a stub to convert R0 from double to int32 if needed.
     // Note: this stub may clobber scratch1.
     masm.call(cx->runtime()->jitRuntime()->getDoubleToInt32ValueStub());
 
-    // Jump to the 'default' pc if not int32 (tableswitch is only used when
-    // all cases are int32).
-    masm.branchTestInt32(Assembler::NotEqual, R0, defaultLabel);
-    masm.unboxInt32(R0, key);
-
-    // Subtract 'low'. Bounds check.
-    if (low != 0) {
-        masm.sub32(Imm32(low), key);
-    }
-    masm.branch32(Assembler::AboveOrEqual, key, Imm32(length), defaultLabel);
+    // Load the index in the jump table in |key|, or branch to default pc if not
+    // int32 or out-of-range.
+    emitGetTableSwitchIndex(R0, key);
 
     // Jump to resumeEntries[firstResumeIndex + key].
     //
     // Note: BytecodeEmitter::allocateResumeIndex static_asserts
     // |firstResumeIndex * sizeof(uintptr_t)| fits in int32_t.
 
     LoadBaselineScriptResumeEntries(masm, script, scratch1, scratch2);
     masm.loadPtr(BaseIndex(scratch1, key, ScaleFromElemWidth(sizeof(uintptr_t)),
@@ -5804,8 +5875,11 @@ BaselineCodeGen<Handler>::emit_JSOP_DYNA
 
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
 
 // Instantiate explicitly for now to make sure it compiles.
 template class jit::BaselineCodeGen<BaselineInterpreterHandler>;
+
+} // namespace jit
+} // namespace js
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -267,17 +267,16 @@ class BaselineCodeGen
 
     TempAllocator& alloc_;
     BytecodeAnalysis analysis_;
     FrameInfo frame;
 
     js::Vector<RetAddrEntry, 16, SystemAllocPolicy> retAddrEntries_;
     js::Vector<CodeOffset> traceLoggerToggleOffsets_;
 
-    FixedList<Label> labels_;
     NonAssertingLabel return_;
     NonAssertingLabel postBarrierSlot_;
 
     // Index of the current ICEntry in the script's ICScript.
     uint32_t icEntryIndex_;
 
     uint32_t pushedBeforeCall_;
 #ifdef DEBUG
@@ -286,20 +285,16 @@ class BaselineCodeGen
 
     // Whether any on stack arguments are modified.
     bool modifiesArguments_;
 
     template <typename... HandlerArgs>
     BaselineCodeGen(JSContext* cx, TempAllocator& alloc, JSScript* script,
                     HandlerArgs&&... args);
 
-    Label* labelOf(jsbytecode* pc) {
-        return &labels_[script->pcToOffset(pc)];
-    }
-
     MOZ_MUST_USE bool appendRetAddrEntry(RetAddrEntry::Kind kind, uint32_t retOffset) {
         if (!retAddrEntries_.emplaceBack(script->pcToOffset(pc), kind, CodeOffset(retOffset))) {
             ReportOutOfMemory(cx);
             return false;
         }
         return true;
     }
 
@@ -363,16 +358,27 @@ class BaselineCodeGen
     MOZ_MUST_USE bool emitUnaryArith();
 
     // JSOP_BITXOR, JSOP_LSH, JSOP_ADD etc.
     MOZ_MUST_USE bool emitBinaryArith();
 
     // Handles JSOP_LT, JSOP_GT, and friends
     MOZ_MUST_USE bool emitCompare();
 
+    // For a JOF_JUMP op, jumps to the op's jump target.
+    void emitJump();
+
+    // For a JOF_JUMP op, jumps to the op's jump target depending on the Value
+    // in |val|.
+    void emitTestBooleanTruthy(bool branchIfTrue, ValueOperand val);
+
+    // Converts |val| to an index in the jump table and stores this in |dest|
+    // or branches to the default pc if not int32 or out-of-range.
+    void emitGetTableSwitchIndex(ValueOperand val, Register dest);
+
     MOZ_MUST_USE bool emitReturn();
 
     MOZ_MUST_USE bool emitToBoolean();
     MOZ_MUST_USE bool emitTest(bool branchIfTrue);
     MOZ_MUST_USE bool emitAndOr(bool branchIfTrue);
 
     MOZ_MUST_USE bool emitCall(JSOp op);
     MOZ_MUST_USE bool emitSpreadCall(JSOp op);
@@ -400,20 +406,29 @@ class BaselineCodeGen
     Address getEnvironmentCoordinateAddress(Register reg);
 
     void getThisEnvironmentCallee(Register reg);
 };
 
 // Interface used by BaselineCodeGen for BaselineCompiler.
 class BaselineCompilerHandler
 {
+    TempAllocator& alloc_;
+    FixedList<Label> labels_;
+    JSScript* script_;
     bool compileDebugInstrumentation_;
 
   public:
-    explicit BaselineCompilerHandler(JSScript* script);
+    BaselineCompilerHandler(TempAllocator& alloc, JSScript* script);
+
+    MOZ_MUST_USE bool init();
+
+    Label* labelOf(jsbytecode* pc) {
+        return &labels_[script_->pcToOffset(pc)];
+    }
 
     void setCompileDebugInstrumentation() {
         compileDebugInstrumentation_ = true;
     }
     bool compileDebugInstrumentation() const {
         return compileDebugInstrumentation_;
     }
 };