Bug 1279312 - Handle call-preserved registers in register allocator. r=bhackett
authorJakob Stoklund Olesen <jolesen@mozilla.com>
Mon, 25 Jul 2016 07:57:36 -0700
changeset 348666 e3c513c3208856343feedd686a1997f9832f17fc
parent 348665 6816fc6e9b61b38d2558e672fe9708bf1e4a6a9c
child 348667 d1b51032934a3afa50ac756ae26700084d11f9d3
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1279312
milestone50.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 1279312 - Handle call-preserved registers in register allocator. r=bhackett - Add a virtual isCallPreserved() method to LNode which allows a call instruction to indicate that it preserves the values of some registers. Use this hook in BacktrackingAllocator when processing a call instruction. - Add a preservesTlsReg() property to MAsmJSCall and use this to implement the LAsmJSCall::isCallPreserved() method. - Mark intra-module WebAssembly calls as preserving the TLS pointer register. This change allows the backtracking register allocator to leave the TLS pointer register alone in small functions that don't need it for something else. There are probably more improvements to be done if we need to split the live range of the TLS pointer register. For example, BacktrackingAllocator::splitAcrossCalls() will still split that live range at all calls.
js/src/asmjs/WasmIonCompile.cpp
js/src/jit/BacktrackingAllocator.cpp
js/src/jit/LIR.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/shared/LIR-shared.h
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -785,25 +785,27 @@ class FunctionCompiler
     {
         uint32_t lineOrBytecode_;
         ABIArgGenerator abi_;
         uint32_t maxChildStackBytes_;
         uint32_t spIncrement_;
         MAsmJSCall::Args regArgs_;
         Vector<MAsmJSPassStackArg*, 0, SystemAllocPolicy> stackArgs_;
         bool childClobbers_;
+        bool preservesTlsReg_;
 
         friend class FunctionCompiler;
 
       public:
         CallArgs(FunctionCompiler& f, uint32_t lineOrBytecode)
           : lineOrBytecode_(lineOrBytecode),
             maxChildStackBytes_(0),
             spIncrement_(0),
-            childClobbers_(false)
+            childClobbers_(false),
+            preservesTlsReg_(false)
         { }
     };
 
     bool startCallArgs(CallArgs* args)
     {
         // Always push calls to maintain the invariant that if we're inDeadCode
         // in finishCallArgs, we have something to pop.
         return callStack_.append(args);
@@ -818,21 +820,23 @@ class FunctionCompiler
         if (arg.kind() != ABIArg::Stack)
             return args->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef));
 
         auto* mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
         curBlock_->add(mir);
         return args->stackArgs_.append(mir);
     }
 
-    // Add the hidden TLS pointer argument to CallArgs.
+    // Add the hidden TLS pointer argument to CallArgs, and assume that it will
+    // be preserved by the call.
     bool passTlsPointer(CallArgs* args)
     {
         if (inDeadCode())
             return true;
+        args->preservesTlsReg_ = true;
         return args->regArgs_.append(MAsmJSCall::Arg(AnyRegister(WasmTlsReg), tlsPointer_));
     }
 
     void propagateMaxStackArgBytes(uint32_t stackBytes)
     {
         if (callStack_.empty()) {
             // Outermost call
             maxStackArgBytes_ = Max(maxStackArgBytes_, stackBytes);
@@ -880,18 +884,19 @@ class FunctionCompiler
 
         CallSiteDesc::Kind kind = CallSiteDesc::Kind(-1);
         switch (callee.which()) {
           case MAsmJSCall::Callee::Internal: kind = CallSiteDesc::Relative; break;
           case MAsmJSCall::Callee::Dynamic:  kind = CallSiteDesc::Register; break;
           case MAsmJSCall::Callee::Builtin:  kind = CallSiteDesc::Register; break;
         }
 
-        MAsmJSCall* ins = MAsmJSCall::New(alloc(), CallSiteDesc(args.lineOrBytecode_, kind),
-                                          callee, args.regArgs_, ToMIRType(ret), args.spIncrement_);
+        MAsmJSCall* ins =
+          MAsmJSCall::New(alloc(), CallSiteDesc(args.lineOrBytecode_, kind), callee, args.regArgs_,
+                          ToMIRType(ret), args.spIncrement_, args.preservesTlsReg_);
         if (!ins)
             return false;
 
         curBlock_->add(ins);
         *def = ins;
         return true;
     }
 
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -564,17 +564,20 @@ BacktrackingAllocator::buildLivenessInfo
                     bool found = false;
                     for (size_t i = 0; i < ins->numDefs(); i++) {
                         if (ins->getDef(i)->isFixed() &&
                             ins->getDef(i)->output()->aliases(LAllocation(*iter))) {
                             found = true;
                             break;
                         }
                     }
-                    if (!found) {
+                    // If this register doesn't have an explicit def above, mark
+                    // it as clobbered by the call unless it is actually
+                    // call-preserved.
+                    if (!found && !ins->isCallPreserved(*iter)) {
                         if (!addInitialFixedRange(*iter, outputOf(*ins), outputOf(*ins).next()))
                             return false;
                     }
                 }
 
                 CallRange* callRange =
                     new(alloc().fallible()) CallRange(outputOf(*ins), outputOf(*ins).next());
                 if (!callRange)
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -721,16 +721,23 @@ class LNode
     // transfer instruction, or zero otherwise.
     virtual size_t numSuccessors() const = 0;
     virtual MBasicBlock* getSuccessor(size_t i) const = 0;
     virtual void setSuccessor(size_t i, MBasicBlock* successor) = 0;
 
     virtual bool isCall() const {
         return false;
     }
+
+    // Does this call preserve the given register?
+    // By default, it is assumed that all registers are clobbered by a call.
+    virtual bool isCallPreserved(AnyRegister reg) const {
+        return false;
+    }
+
     uint32_t id() const {
         return id_;
     }
     void setId(uint32_t id) {
         MOZ_ASSERT(!id_);
         MOZ_ASSERT(id);
         id_ = id;
     }
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5355,19 +5355,20 @@ MAsmJSUnsignedToFloat32::foldsTo(TempAll
             return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType::Float32);
     }
 
     return this;
 }
 
 MAsmJSCall*
 MAsmJSCall::New(TempAllocator& alloc, const wasm::CallSiteDesc& desc, Callee callee,
-                const Args& args, MIRType resultType, size_t spIncrement)
-{
-    MAsmJSCall* call = new(alloc) MAsmJSCall(desc, callee, spIncrement);
+                const Args& args, MIRType resultType, size_t spIncrement,
+                bool preservesTlsReg)
+{
+    MAsmJSCall* call = new(alloc) MAsmJSCall(desc, callee, spIncrement, preservesTlsReg);
     call->setResultType(resultType);
 
     if (!call->argRegs_.init(alloc, args.length()))
         return nullptr;
     for (size_t i = 0; i < call->argRegs_.length(); i++)
         call->argRegs_[i] = args[i].reg;
 
     if (!call->init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0)))
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13526,33 +13526,39 @@ class MAsmJSCall final
         }
     };
 
   private:
     wasm::CallSiteDesc desc_;
     Callee callee_;
     FixedList<AnyRegister> argRegs_;
     size_t spIncrement_;
-
-    MAsmJSCall(const wasm::CallSiteDesc& desc, Callee callee, size_t spIncrement)
-     : desc_(desc), callee_(callee), spIncrement_(spIncrement)
+    bool preservesTlsReg_;
+
+    MAsmJSCall(const wasm::CallSiteDesc& desc, Callee callee, size_t spIncrement,
+               bool preservesTlsReg)
+      : desc_(desc)
+      , callee_(callee)
+      , spIncrement_(spIncrement)
+      , preservesTlsReg_(preservesTlsReg)
     { }
 
   public:
     INSTRUCTION_HEADER(AsmJSCall)
 
     struct Arg {
         AnyRegister reg;
         MDefinition* def;
         Arg(AnyRegister reg, MDefinition* def) : reg(reg), def(def) {}
     };
     typedef Vector<Arg, 8, SystemAllocPolicy> Args;
 
     static MAsmJSCall* New(TempAllocator& alloc, const wasm::CallSiteDesc& desc, Callee callee,
-                           const Args& args, MIRType resultType, size_t spIncrement);
+                           const Args& args, MIRType resultType, size_t spIncrement,
+                           bool preservesTlsReg);
 
     size_t numArgs() const {
         return argRegs_.length();
     }
     AnyRegister registerForArg(size_t index) const {
         MOZ_ASSERT(index < numArgs());
         return argRegs_[index];
     }
@@ -13566,16 +13572,21 @@ class MAsmJSCall final
         MOZ_ASSERT(callee_.which() == Callee::Dynamic);
         MOZ_ASSERT(numArgs() == numOperands() - 1);
         return numArgs();
     }
     size_t spIncrement() const {
         return spIncrement_;
     }
 
+    // Does this call preserve the value of the TLS pointer register?
+    bool preservesTlsReg() const {
+        return preservesTlsReg_;
+    }
+
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MAsmSelect
   : public MTernaryInstruction,
     public NoTypePolicy::Data
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8152,16 +8152,23 @@ class LAsmJSCall final : public LInstruc
     MAsmJSCall* mir() const {
         return mir_->toAsmJSCall();
     }
 
     bool isCall() const {
         return true;
     }
 
+    bool isCallPreserved(AnyRegister reg) const {
+        // WebAssembly functions preserve the TLS pointer register.
+        if (reg.isFloat() || reg.gpr() != WasmTlsReg)
+            return false;
+        return mir()->preservesTlsReg();
+    }
+
     // LInstruction interface
     size_t numDefs() const {
         return def_.isBogusTemp() ? 0 : 1;
     }
     LDefinition* getDef(size_t index) {
         MOZ_ASSERT(numDefs() == 1);
         MOZ_ASSERT(index == 0);
         return &def_;