Bug 1316814 - wasm baseline, use the iterator's control stack. r=luke
authorLars T Hansen <lhansen@mozilla.com>
Wed, 18 Jan 2017 19:58:26 +0100
changeset 377388 9042d15381359dc12bbce404f157cdde53e359eb
parent 377387 1571ba44e85026e0e5ded9ebf4df5f31371cff68
child 377389 082be147ff94724672c59bb62ef47d8c086cd191
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1316814
milestone53.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 1316814 - wasm baseline, use the iterator's control stack. r=luke
js/src/wasm/WasmBaselineCompile.cpp
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -118,41 +118,24 @@ using mozilla::IsPowerOfTwo;
 using mozilla::SpecificNaN;
 
 namespace js {
 namespace wasm {
 
 using namespace js::jit;
 using JS::GenericNaN;
 
-struct BaseCompilePolicy : OpIterPolicy
-{
-    static const bool Output = true;
-
-    // The baseline compiler tracks values on a stack of its own -- it
-    // needs to scan that stack for spilling -- and thus has no need
-    // for the values maintained by the iterator.
-    //
-    // The baseline compiler tracks control items on a stack of its
-    // own as well.
-    //
-    // TODO / REDUNDANT (Bug 1316814): It would be nice if we could
-    // make use of the iterator's ControlItems and not require our own
-    // stack for that.
-};
-
-typedef OpIter<BaseCompilePolicy> BaseOpIter;
-
+typedef bool HandleNaNSpecially;
+typedef bool InvertBranch;
+typedef bool IsKnownNotZero;
+typedef bool IsSigned;
 typedef bool IsUnsigned;
-typedef bool IsSigned;
+typedef bool PopStack;
 typedef bool ZeroOnOverflow;
-typedef bool IsKnownNotZero;
-typedef bool HandleNaNSpecially;
-typedef bool PopStack;
-typedef bool InvertBranch;
+
 typedef unsigned ByteSize;
 typedef unsigned BitSize;
 
 // UseABI::Wasm implies that the Tls/Heap/Global registers are nonvolatile,
 // except when InterModule::True is also set, when they are volatile.
 //
 // UseABI::System implies that the Tls/Heap/Global registers are volatile.
 // Additionally, the parameter passing mechanism may be slightly different from
@@ -406,33 +389,48 @@ class BaseCompiler
         MIRType type() const { MOZ_ASSERT(type_ != MIRType::None); return type_; }
         uint32_t offs() const { MOZ_ASSERT(offs_ != UINT32_MAX); return offs_; }
     };
 
     // Control node, representing labels and stack heights at join points.
 
     struct Control
     {
-        Control(uint32_t framePushed, uint32_t stackSize)
+        Control()
             : label(nullptr),
               otherLabel(nullptr),
-              framePushed(framePushed),
-              stackSize(stackSize),
+              framePushed(0),
+              stackSize(0),
               deadOnArrival(false),
               deadThenBranch(false)
         {}
 
         PooledLabel* label;
         PooledLabel* otherLabel;        // Used for the "else" branch of if-then-else
         uint32_t framePushed;           // From masm
         uint32_t stackSize;             // Value stack height
         bool deadOnArrival;             // deadCode_ was set on entry to the region
         bool deadThenBranch;            // deadCode_ was set on exit from "then"
     };
 
+    struct BaseCompilePolicy : OpIterPolicy
+    {
+        static const bool Output = true;
+
+        // The baseline compiler tracks values on a stack of its own -- it
+        // needs to scan that stack for spilling -- and thus has no need
+        // for the values maintained by the iterator.
+
+        // The baseline compiler uses the iterator's control stack, attaching
+        // its own control information.
+        typedef Control ControlItem;
+    };
+
+    typedef OpIter<BaseCompilePolicy> BaseOpIter;
+
     // Volatile registers except ReturnReg.
 
     static LiveRegisterSet VolatileReturnGPR;
 
     // The baseline compiler will use OOL code more sparingly than
     // Baldr since our code is not high performance and frills like
     // code density and branch prediction friendliness will be less
     // important.
@@ -570,17 +568,17 @@ class BaseCompiler
     // JoinRegI32 and joinRegI64 must overlap: emitBrIf and
     // emitBrTable assume that.
 
     RegI32 joinRegI32;
     RegI64 joinRegI64;
     RegF32 joinRegF32;
     RegF64 joinRegF64;
 
-    // More members: see the stk_ and ctl_ vectors, defined below.
+    // There are more members scattered throughout.
 
   public:
     BaseCompiler(const ModuleEnvironment& env,
                  Decoder& decoder,
                  const FuncBytes& func,
                  const ValTypeVector& locals,
                  bool debugEnabled,
                  TempAllocator* alloc,
@@ -2051,43 +2049,48 @@ class BaseCompiler
     Stk& peek(uint32_t relativeDepth) {
         return stk_[stk_.length()-1-relativeDepth];
     }
 
     ////////////////////////////////////////////////////////////
     //
     // Control stack
 
-    Vector<Control, 8, SystemAllocPolicy> ctl_;
-
-    MOZ_MUST_USE bool pushControl(UniquePooledLabel* label, UniquePooledLabel* otherLabel = nullptr)
+    void initControl(Control& item, UniquePooledLabel* label, UniquePooledLabel* otherLabel = nullptr)
     {
-        uint32_t framePushed = masm.framePushed();
-        uint32_t stackSize = stk_.length();
-
-        if (!ctl_.emplaceBack(Control(framePushed, stackSize)))
-            return false;
-        ctl_.back().label = label->release();
+        item.framePushed = masm.framePushed();
+        item.stackSize = stk_.length();
+        item.label = label->release();
         if (otherLabel)
-            ctl_.back().otherLabel = otherLabel->release();
-        ctl_.back().deadOnArrival = deadCode_;
-        return true;
-    }
-
-    void popControl() {
-        Control last = ctl_.popCopy();
-        if (last.label)
-            freeLabel(last.label);
-        if (last.otherLabel)
-            freeLabel(last.otherLabel);
+            item.otherLabel = otherLabel->release();
+        item.deadOnArrival = deadCode_;
+    }
+
+    void freeControl(Control& item) {
+        if (item.label)
+            freeLabel(item.label);
+        if (item.otherLabel)
+            freeLabel(item.otherLabel);
+    }
+
+    Control& controlItem() {
+        return iter_.controlItem();
     }
 
     Control& controlItem(uint32_t relativeDepth) {
-        return ctl_[ctl_.length() - 1 - relativeDepth];
-    }
+        return iter_.controlItem(relativeDepth);
+    }
+
+    Control& controlOutermost() {
+        return iter_.controlOutermost();
+    }
+
+    ////////////////////////////////////////////////////////////
+    //
+    // Labels
 
     MOZ_MUST_USE PooledLabel* newLabel() {
         // TODO / INVESTIGATE (Bug 1316819): allocate() is fallible, but we can
         // probably rely on an infallible allocator here.  That would simplify
         // code later.
         PooledLabel* candidate = labelPool_.allocate();
         if (!candidate)
             return nullptr;
@@ -2691,17 +2694,17 @@ class BaseCompiler
         if (call.usesSystemAbi && !call.hardFP)
             masm.ma_vxfer(r0, r1, rv);
 #endif
         return rv;
     }
 
     void returnCleanup(bool popStack) {
         if (popStack)
-            popStackBeforeBranch(ctl_[0].framePushed);
+            popStackBeforeBranch(controlOutermost().framePushed);
         masm.jump(&returnLabel_);
     }
 
     void pop2xI32ForIntMulDiv(RegI32* r0, RegI32* r1) {
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         // srcDest must be eax, and edx will be clobbered.
         need2xI32(specific_eax, specific_edx);
         *r1 = popI32();
@@ -5461,23 +5464,24 @@ BaseCompiler::emitBlock()
 
     UniquePooledLabel blockEnd(newLabel());
     if (!blockEnd)
         return false;
 
     if (!deadCode_)
         sync();                    // Simplifies branching out from block
 
-    return pushControl(&blockEnd);
+    initControl(controlItem(), &blockEnd);
+    return true;
 }
 
 void
 BaseCompiler::endBlock(ExprType type)
 {
-    Control& block = controlItem(0);
+    Control& block = controlItem();
 
     // Save the value.
     AnyReg r;
     if (!deadCode_)
         r = popJoinRegUnlessVoid(type);
 
     // Leave the block.
     popStackOnBlockExit(block.framePushed);
@@ -5488,17 +5492,17 @@ BaseCompiler::endBlock(ExprType type)
         masm.bind(block.label);
         // No value was provided by the fallthrough but the branch out will
         // have stored one in joinReg, so capture that.
         if (deadCode_)
             r = captureJoinRegUnlessVoid(type);
         deadCode_ = false;
     }
 
-    popControl();
+    freeControl(block);
 
     // Retain the value stored in joinReg by all paths, if there are any.
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 bool
 BaseCompiler::emitLoop()
@@ -5508,40 +5512,39 @@ BaseCompiler::emitLoop()
 
     UniquePooledLabel blockCont(newLabel());
     if (!blockCont)
         return false;
 
     if (!deadCode_)
         sync();                    // Simplifies branching out from block
 
-    if (!pushControl(&blockCont))
-        return false;
+    initControl(controlItem(), &blockCont);
 
     if (!deadCode_) {
         masm.bind(controlItem(0).label);
         addInterruptCheck();
     }
 
     return true;
 }
 
 void
 BaseCompiler::endLoop(ExprType type)
 {
-    Control& block = controlItem(0);
+    Control& block = controlItem();
 
     AnyReg r;
     if (!deadCode_)
         r = popJoinRegUnlessVoid(type);
 
     popStackOnBlockExit(block.framePushed);
     popValueStackTo(block.stackSize);
 
-    popControl();
+    freeControl(block);
 
     // Retain the value stored in joinReg by all paths.
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 // The bodies of the "then" and "else" arms can be arbitrary sequences
 // of expressions, they push control and increment the nesting and can
@@ -5575,49 +5578,49 @@ BaseCompiler::emitIf()
     BranchState b(elseLabel.get(), BranchState::NoPop, InvertBranch(true));
     if (!deadCode_) {
         emitBranchSetup(&b);
         sync();
     } else {
         resetLatentOp();
     }
 
-    if (!pushControl(&endLabel, &elseLabel))
-        return false;
+    initControl(controlItem(), &endLabel, &elseLabel);
 
     if (!deadCode_)
         emitBranchPerform(&b);
 
     return true;
 }
 
 void
 BaseCompiler::endIfThen()
 {
-    Control& ifThen = controlItem(0);
+    Control& ifThen = controlItem();
 
     popStackOnBlockExit(ifThen.framePushed);
     popValueStackTo(ifThen.stackSize);
 
     if (ifThen.otherLabel->used())
         masm.bind(ifThen.otherLabel);
 
     if (ifThen.label->used())
         masm.bind(ifThen.label);
 
     deadCode_ = ifThen.deadOnArrival;
 
-    popControl();
+    freeControl(ifThen);
 }
 
 bool
 BaseCompiler::emitElse()
 {
     ExprType thenType;
     Nothing unused_thenValue;
+
     if (!iter_.readElse(&thenType, &unused_thenValue))
         return false;
 
     Control& ifThenElse = controlItem(0);
 
     // See comment in endIfThenElse, below.
 
     // Exit the "then" branch.
@@ -5645,17 +5648,17 @@ BaseCompiler::emitElse()
     deadCode_ = ifThenElse.deadOnArrival;
 
     return true;
 }
 
 void
 BaseCompiler::endIfThenElse(ExprType type)
 {
-    Control& ifThenElse = controlItem(0);
+    Control& ifThenElse = controlItem();
 
     // The expression type is not a reliable guide to what we'll find
     // on the stack, we could have (if E (i32.const 1) (unreachable))
     // in which case the "else" arm is AnyType but the type of the
     // full expression is I32.  So restore whatever's there, not what
     // we want to find there.  The "then" arm has the same constraint.
 
     AnyReg r;
@@ -5675,41 +5678,42 @@ BaseCompiler::endIfThenElse(ExprType typ
     if (joinLive) {
         // No value was provided by the "then" path but capture the one
         // provided by the "else" path.
         if (deadCode_)
             r = captureJoinRegUnlessVoid(type);
         deadCode_ = false;
     }
 
-    popControl();
+    freeControl(ifThenElse);
 
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 bool
 BaseCompiler::emitEnd()
 {
     LabelKind kind;
     ExprType type;
     Nothing unused_value;
 
     if (!iter_.readEnd(&kind, &type, &unused_value))
         return false;
-    iter_.popEnd();
 
     switch (kind) {
       case LabelKind::Block: endBlock(type); break;
       case LabelKind::Loop:  endLoop(type); break;
       case LabelKind::UnreachableThen:
       case LabelKind::Then:  endIfThen(); break;
       case LabelKind::Else:  endIfThenElse(type); break;
     }
 
+    iter_.popEnd();
+
     return true;
 }
 
 bool
 BaseCompiler::emitBr()
 {
     uint32_t relativeDepth;
     ExprType type;
@@ -7661,38 +7665,28 @@ BaseCompiler::emitBody()
 
 done:
     return false;
 }
 
 bool
 BaseCompiler::emitFunction()
 {
-    // emitBody() will ensure that there is enough memory reserved in the
-    // vector for infallible allocation to succeed within the compiler, but we
-    // need a little headroom for the initial pushControl(), which pushes a
-    // void value onto the value stack.
-
-    if (!stk_.reserve(8))
-        return false;
-
     const Sig& sig = func_.sig();
 
     if (!iter_.readFunctionStart(sig.ret()))
         return false;
 
     beginFunction();
 
     UniquePooledLabel functionEnd(newLabel());
     if (!functionEnd)
         return false;
 
-    // For the function's block; it will be popped by endBlock().
-    if (!pushControl(&functionEnd))
-        return false;
+    initControl(controlItem(), &functionEnd);
 
     if (!emitBody())
         return false;
 
     if (!deadCode_)
         doReturn(sig.ret(), PopStack(false));
 
     if (!iter_.readFunctionEnd())