// Transform:
//
//   [AddI]
//   addl       $9, %esi
//   [LoadUnboxedScalar]
//   movsd      0x0(%rbx,%rsi,8), %xmm4
//
// into:
//
//   [LoadUnboxedScalar]
//   movsd      0x48(%rbx,%rsi,8), %xmm4
//
// This is possible when the AddI is only used by the LoadUnboxedScalar opcode.
static void
AnalyzeLoadUnboxedScalar(TempAllocator& alloc, MLoadUnboxedScalar* load)
{
    if (load->isRecoveredOnBailout())
        return;

    if (!load->getOperand(1)->isAdd())
        return;

    JitSpew(JitSpew_EAA, "analyze: %s%u", load->opName(), load->id());

    MAdd* add = load->getOperand(1)->toAdd();

    if (add->specialization() != MIRType::Int32 || !add->hasUses() ||
        add->truncateKind() != MDefinition::TruncateKind::Truncate)
    {
        return;
    }

    MDefinition* lhs = add->lhs();
    MDefinition* rhs = add->rhs();
    MDefinition* constant = nullptr;
    MDefinition* node = nullptr;

    if (lhs->isConstant()) {
        constant = lhs;
        node = rhs;
    } else if (rhs->isConstant()) {
        constant = rhs;
        node = lhs;
    } else
        return;

    MOZ_ASSERT(constant->type() == MIRType::Int32);

    size_t storageSize = Scalar::byteSize(load->storageType());
    int32_t c1 = load->offsetAdjustment();
    int32_t c2 = 0;
    if (!SafeMul(constant->maybeConstantValue()->toInt32(), storageSize, &c2))
        return;

    int32_t offset = 0;
    if (!SafeAdd(c1, c2, &offset))
        return;

    JitSpew(JitSpew_EAA, "set offset: %d + %d = %d on: %s%u", c1, c2, offset,
            load->opName(), load->id());
    load->setOffsetAdjustment(offset);
    load->replaceOperand(1, node);

    if (!add->hasLiveDefUses() && DeadIfUnused(add) && add->canRecoverOnBailout()) {
        JitSpew(JitSpew_EAA, "mark as recovered on bailout: %s%u",
                add->opName(), add->id());
        add->setRecoveredOnBailoutUnchecked();
    }
}
Example #2
0
// Fold AddIs with one variable and two or more constants into one AddI.
static void AnalyzeAdd(TempAllocator& alloc, MAdd* add) {
  if (add->specialization() != MIRType::Int32 || add->isRecoveredOnBailout()) {
    return;
  }

  if (!add->hasUses()) {
    return;
  }

  JitSpew(JitSpew_FLAC, "analyze add: %s%u", add->opName(), add->id());

  SimpleLinearSum sum = ExtractLinearSum(add);
  if (sum.constant == 0 || !sum.term) {
    return;
  }

  // Determine which operand is the constant.
  int idx = add->getOperand(0)->isConstant() ? 0 : 1;
  if (add->getOperand(idx)->isConstant()) {
    // Do not replace an add where the outcome is the same add instruction.
    MOZ_ASSERT(add->getOperand(idx)->toConstant()->type() == MIRType::Int32);
    if (sum.term == add->getOperand(1 - idx) ||
        sum.constant == add->getOperand(idx)->toConstant()->toInt32()) {
      return;
    }
  }

  MInstruction* rhs = MConstant::New(alloc, Int32Value(sum.constant));
  add->block()->insertBefore(add, rhs);

  MAdd* addNew =
      MAdd::New(alloc, sum.term, rhs, MIRType::Int32, add->truncateKind());

  add->replaceAllLiveUsesWith(addNew);
  add->block()->insertBefore(add, addNew);
  JitSpew(JitSpew_FLAC, "replaced with: %s%u", addNew->opName(), addNew->id());
  JitSpew(JitSpew_FLAC, "and constant: %s%u (%d)", rhs->opName(), rhs->id(),
          sum.constant);

  // Mark the stale nodes as RecoveredOnBailout since the Sink pass has
  // been run before this pass. DCE will then remove the unused nodes.
  markNodesAsRecoveredOnBailout(add);
}