Bug 1425580 part 16 - Devirtualize LInstruction::getOperand. r=nbp
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 08 Mar 2018 11:10:50 +0100
changeset 462183 f9ed695dd18b42bf0acb87cf2983d2287df8af1c
parent 462182 5eb188b60de2014942aeba057ef953e34b030239
child 462184 f843ed5e013b895e1a0d146e22a2031dd61a9243
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1425580
milestone60.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 1425580 part 16 - Devirtualize LInstruction::getOperand. r=nbp
js/src/jit/LIR.h
js/src/jit/Lowering.cpp
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/shared/Lowering-shared.h
js/src/jit/x86-shared/Lowering-x86-shared.cpp
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -664,27 +664,31 @@ class LNode
     uint32_t id_;
 
   protected:
     // Bitfields below are all uint32_t to make sure MSVC packs them correctly.
     uint32_t isCall_ : 1;
     // LPhi::numOperands() may not fit in this bitfield, so we only use this
     // field for LInstruction.
     uint32_t nonPhiNumOperands_ : 6;
+    // For LInstruction, the first operand is stored at offset
+    // sizeof(LInstruction) + nonPhiOperandsOffset_ * sizeof(uintptr_t).
+    uint32_t nonPhiOperandsOffset_ : 5;
     uint32_t numDefs_ : 4;
     uint32_t numTemps_ : 4;
     uint32_t numSuccessors_ : 2;
 
   public:
     LNode(uint32_t nonPhiNumOperands, uint32_t numDefs, uint32_t numTemps)
       : mir_(nullptr),
         block_(nullptr),
         id_(0),
         isCall_(false),
         nonPhiNumOperands_(nonPhiNumOperands),
+        nonPhiOperandsOffset_(0),
         numDefs_(numDefs),
         numTemps_(numTemps),
         numSuccessors_(0)
     {
         MOZ_ASSERT(nonPhiNumOperands_ == nonPhiNumOperands,
                    "nonPhiNumOperands must fit in bitfield");
         MOZ_ASSERT(numDefs_ == numDefs, "numDefs must fit in bitfield");
         MOZ_ASSERT(numTemps_ == numTemps, "numTemps must fit in bitfield");
@@ -821,22 +825,36 @@ class LInstruction
     }
 
   public:
     inline LDefinition* getDef(size_t index);
 
     void setDef(size_t index, const LDefinition& def) {
         *getDef(index) = def;
     }
+
+    LAllocation* getOperand(size_t index) const {
+        MOZ_ASSERT(index < numOperands());
+        MOZ_ASSERT(nonPhiOperandsOffset_ > 0);
+        uintptr_t p = reinterpret_cast<uintptr_t>(this + 1) + nonPhiOperandsOffset_ * sizeof(uintptr_t);
+        return reinterpret_cast<LAllocation*>(p) + index;
+    }
     void setOperand(size_t index, const LAllocation& a) {
         *getOperand(index) = a;
     }
 
-    // Returns information about operands.
-    virtual LAllocation* getOperand(size_t index) = 0;
+    void initOperandsOffset(size_t offset) {
+        MOZ_ASSERT(nonPhiOperandsOffset_ == 0);
+        MOZ_ASSERT(offset >= sizeof(LInstruction));
+        MOZ_ASSERT(((offset - sizeof(LInstruction)) % sizeof(uintptr_t)) == 0);
+        offset = (offset - sizeof(LInstruction)) / sizeof(uintptr_t);
+        nonPhiOperandsOffset_ = offset;
+        MOZ_ASSERT(nonPhiOperandsOffset_ == offset,
+                   "offset must fit in bitfield");
+    }
 
     // Returns information about temporary registers needed. Each temporary
     // register is an LDefinition with a fixed or virtual register and
     // either GENERAL, FLOAT32, or DOUBLE type.
     size_t numTemps() const {
         return numTemps_;
     }
     inline LDefinition* getTemp(size_t index);
@@ -1157,20 +1175,27 @@ LInstruction::getTemp(size_t index)
 template <size_t Defs, size_t Operands, size_t Temps>
 class LInstructionHelper : public details::LInstructionFixedDefsTempsHelper<Defs, Temps>
 {
     mozilla::Array<LAllocation, Operands> operands_;
 
   protected:
     LInstructionHelper()
       : details::LInstructionFixedDefsTempsHelper<Defs, Temps>(Operands)
-    {}
+    {
+        static_assert(Operands == 0 || sizeof(operands_) == Operands * sizeof(LAllocation),
+                      "mozilla::Array should not contain other fields");
+        if (Operands > 0) {
+            using T = LInstructionHelper<Defs, Operands, Temps>;
+            this->initOperandsOffset(offsetof(T, operands_));
+        }
+    }
 
   public:
-    LAllocation* getOperand(size_t index) final {
+    LAllocation* getOperand(size_t index) {
         return &operands_[index];
     }
     void setOperand(size_t index, const LAllocation& a) {
         operands_[index] = a;
     }
     void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
 #ifdef JS_NUNBOX32
         operands_[index + TYPE_INDEX] = alloc.type();
@@ -1195,39 +1220,28 @@ class LInstructionHelper : public detail
         return LInt64Allocation(operands_[offset]);
 #endif
     }
 };
 
 template<size_t Defs, size_t Temps>
 class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper<Defs, Temps>
 {
-    FixedList<LAllocation> operands_;
-
   protected:
     explicit LVariadicInstruction(size_t numOperands)
       : details::LInstructionFixedDefsTempsHelper<Defs, Temps>(numOperands)
     {}
 
   public:
-    MOZ_MUST_USE bool init(TempAllocator& alloc) {
-        return operands_.init(alloc, this->nonPhiNumOperands_);
-    }
-    LAllocation* getOperand(size_t index) final {
-        return &operands_[index];
-    }
-    void setOperand(size_t index, const LAllocation& a) {
-        operands_[index] = a;
-    }
     void setBoxOperand(size_t index, const LBoxAllocation& a) {
 #ifdef JS_NUNBOX32
-        operands_[index + TYPE_INDEX] = a.type();
-        operands_[index + PAYLOAD_INDEX] = a.payload();
+        this->setOperand(index + TYPE_INDEX, a.type());
+        this->setOperand(index + PAYLOAD_INDEX, a.payload());
 #else
-        operands_[index] = a.value();
+        this->setOperand(index, a.value());
 #endif
     }
 };
 
 template <size_t Defs, size_t Operands, size_t Temps>
 class LCallInstructionHelper : public LInstructionHelper<Defs, Operands, Temps>
 {
   public:
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3318,28 +3318,29 @@ LIRGenerator::visitLoadElementFromState(
     LDefinition temp1 = LDefinition::BogusTemp();
 #ifdef JS_NUNBOX32
     temp1 = temp();
 #endif
     MOZ_ASSERT(ins->array()->isArgumentState(),
                "LIRGenerator::visitLoadElementFromState: Unsupported state object");
     MArgumentState* array = ins->array()->toArgumentState();
 
-    size_t numOperands = 1 + BOX_PIECES * array->numElements();
-    LLoadElementFromStateV* lir = new(alloc()) LLoadElementFromStateV(temp(), temp1, tempDouble(),
-                                                                      numOperands);
-
     //   1                                 -- for the index as a register
     //   BOX_PIECES * array->numElements() -- for using as operand all the
     //                                        elements of the inlined array.
-    if (!lir->init(alloc())) {
+    size_t numOperands = 1 + BOX_PIECES * array->numElements();
+
+    auto* lir = allocateVariadic<LLoadElementFromStateV>(numOperands, temp(), temp1, tempDouble());
+    if (!lir) {
         abort(AbortReason::Alloc, "OOM: LIRGenerator::visitLoadElementFromState");
         return;
     }
+
     lir->setOperand(0, useRegister(ins->index())); // index
+
     for (size_t i = 0, e = array->numElements(); i < e; i++) {
         MDefinition* elem = array->getElement(i);
         if (elem->isConstant() && elem->isEmittedAtUses()) {
             lir->setOperand(1 + BOX_PIECES * i, LAllocation());
 #ifdef JS_NUNBOX32
             lir->setOperand(1 + BOX_PIECES * i + 1, LAllocation());
 #endif
             continue;
@@ -4687,18 +4688,18 @@ LIRGenerator::visitWasmStackArg(MWasmSta
         add(new(alloc()) LWasmStackArg(useRegisterOrConstantAtStart(ins->arg())), ins);
     }
 }
 
 template <typename LClass>
 LInstruction*
 LIRGenerator::lowerWasmCall(MWasmCall* ins, bool needsBoundsCheck)
 {
-    auto* lir = new(alloc()) LClass(ins->numOperands(), needsBoundsCheck);
-    if (!lir->init(alloc())) {
+    auto* lir = allocateVariadic<LClass>(ins->numOperands(), needsBoundsCheck);
+    if (!lir) {
         abort(AbortReason::Alloc, "Couldn't allocate for MWasmCall");
         return nullptr;
     }
 
     for (unsigned i = 0; i < ins->numArgs(); i++)
         lir->setOperand(i, useFixedAtStart(ins->getOperand(i), ins->registerForArg(i)));
 
     if (ins->callee().isTable()) {
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -400,17 +400,17 @@ class LSimdSwizzleF : public LSimdSwizzl
     LIR_HEADER(SimdSwizzleF);
     explicit LSimdSwizzleF(const LAllocation& base) : LSimdSwizzleBase(base)
     {}
 };
 
 class LSimdGeneralShuffleBase : public LVariadicInstruction<1, 1>
 {
   public:
-    LSimdGeneralShuffleBase(const LDefinition& temp, uint32_t numOperands)
+    LSimdGeneralShuffleBase(uint32_t numOperands, const LDefinition& temp)
       : LVariadicInstruction<1, 1>(numOperands)
     {
         setTemp(0, temp);
     }
     const LAllocation* vector(unsigned i) {
         MOZ_ASSERT(i < mir()->numVectors());
         return getOperand(i);
     }
@@ -426,28 +426,28 @@ class LSimdGeneralShuffleBase : public L
     }
 };
 
 class LSimdGeneralShuffleI : public LSimdGeneralShuffleBase
 {
   public:
     LIR_HEADER(SimdGeneralShuffleI);
 
-    LSimdGeneralShuffleI(const LDefinition& temp, uint32_t numOperands)
-      : LSimdGeneralShuffleBase(temp, numOperands)
+    LSimdGeneralShuffleI(uint32_t numOperands, const LDefinition& temp)
+      : LSimdGeneralShuffleBase(numOperands, temp)
     {}
 };
 
 class LSimdGeneralShuffleF : public LSimdGeneralShuffleBase
 {
   public:
     LIR_HEADER(SimdGeneralShuffleF);
 
-    LSimdGeneralShuffleF(const LDefinition& temp, uint32_t numOperands)
-      : LSimdGeneralShuffleBase(temp, numOperands)
+    LSimdGeneralShuffleF(uint32_t numOperands, const LDefinition& temp)
+      : LSimdGeneralShuffleBase(numOperands, temp)
     {}
 };
 
 // Base class for both int32x4 and float32x4 shuffle instructions.
 class LSimdShuffleX4 : public LInstructionHelper<1, 2, 1>
 {
   public:
     LIR_HEADER(SimdShuffleX4);
@@ -5841,18 +5841,18 @@ class LUnboxObjectOrNull : public LInstr
 // Each element is represented with BOX_PIECES allocations, even if 1 (typed
 // register) or 0 (constants) is enough. In such case, the unused allocations
 // would be bogus.
 class LLoadElementFromStateV : public LVariadicInstruction<BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(LoadElementFromStateV)
 
-    LLoadElementFromStateV(const LDefinition& temp0, const LDefinition& temp1,
-                           const LDefinition& tempD, uint32_t numOperands)
+    LLoadElementFromStateV(uint32_t numOperands, const LDefinition& temp0,
+                           const LDefinition& temp1, const LDefinition& tempD)
       : LVariadicInstruction<BOX_PIECES, 3>(numOperands)
     {
         setTemp(0, temp0);
         setTemp(1, temp1);
         setTemp(2, tempD);
     }
 
     const MLoadElementFromState* mir() const {
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -465,16 +465,36 @@ void
 LIRGeneratorShared::ensureDefined(MDefinition* mir)
 {
     if (mir->isEmittedAtUses()) {
         mir->toInstruction()->accept(this);
         MOZ_ASSERT(mir->isLowered());
     }
 }
 
+template <typename LClass, typename... Args>
+LClass*
+LIRGeneratorShared::allocateVariadic(uint32_t numOperands, Args&&... args)
+{
+    size_t numBytes = sizeof(LClass) + numOperands * sizeof(LAllocation);
+    void* buf = alloc().allocate(numBytes);
+    if (!buf)
+        return nullptr;
+
+    LClass* ins = static_cast<LClass*>(buf);
+    new(ins) LClass(numOperands, mozilla::Forward<Args>(args)...);
+
+    ins->initOperandsOffset(sizeof(LClass));
+
+    for (uint32_t i = 0; i < numOperands; i++)
+        ins->setOperand(i, LAllocation());
+
+    return ins;
+}
+
 LUse
 LIRGeneratorShared::useRegister(MDefinition* mir)
 {
     return use(mir, LUse(LUse::REGISTER));
 }
 
 LUse
 LIRGeneratorShared::useRegisterAtStart(MDefinition* mir)
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -226,16 +226,19 @@ class LIRGeneratorShared : public MDefin
 
     // Rather than defining a new virtual register, sets |ins| to have the same
     // virtual register as |as|.
     inline void redefine(MDefinition* ins, MDefinition* as);
 
     // Redefine a sin/cos call to sincos.
     inline void redefine(MDefinition* def, MDefinition* as, MMathFunction::Function func);
 
+    template <typename LClass, typename... Args>
+    inline LClass* allocateVariadic(uint32_t numOperands, Args&&... args);
+
     TempAllocator& alloc() const {
         return graph.alloc();
     }
 
     uint32_t getVirtualRegister() {
         uint32_t vreg = lirGraph_.getVirtualRegister();
 
         // If we run out of virtual registers, mark code generation as having
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -930,24 +930,24 @@ LIRGeneratorX86Shared::visitSimdGeneralS
         LDefinition t;
         if (ins->type() == MIRType::Int8x16)
             t = tempFixed(ebx);
         else
             t = temp();
 #else
         LDefinition t = temp();
 #endif
-        lir = new (alloc()) LSimdGeneralShuffleI(t, numOperands);
+        lir = allocateVariadic<LSimdGeneralShuffleI>(numOperands, t);
     } else if (ins->type() == MIRType::Float32x4) {
-        lir = new (alloc()) LSimdGeneralShuffleF(temp(), numOperands);
+        lir = allocateVariadic<LSimdGeneralShuffleF>(numOperands, temp());
     } else {
         MOZ_CRASH("Unknown SIMD kind when doing a shuffle");
     }
 
-    if (!lir->init(alloc()))
+    if (!lir)
         return;
 
     for (unsigned i = 0; i < ins->numVectors(); i++) {
         MOZ_ASSERT(IsSimdType(ins->vector(i)->type()));
         lir->setOperand(i, useRegister(ins->vector(i)));
     }
 
     for (unsigned i = 0; i < ins->numLanes(); i++) {