bool EdgeCaseAnalysis::AllUsesTruncate(MInstruction *m) { for (MUseIterator use = m->usesBegin(); use != m->usesEnd(); use++) { // See #809485 why this is allowed if (use->node()->isResumePoint()) continue; MDefinition *def = use->node()->toDefinition(); if (def->isTruncateToInt32()) continue; if (def->isBitAnd()) continue; if (def->isBitOr()) continue; if (def->isBitXor()) continue; if (def->isLsh()) continue; if (def->isRsh()) continue; if (def->isBitNot()) continue; if (def->isAdd() && def->toAdd()->isTruncated()) continue; if (def->isSub() && def->toSub()->isTruncated()) continue; // cannot use divide, since |truncate(int32(x/y) + int32(a/b)) != truncate(x/y+a/b)| return false; } return true; }
static bool CheckOperandImpliesUse(MInstruction *ins, MDefinition *operand) { for (MUseIterator i = operand->usesBegin(); i != operand->usesEnd(); i++) { if (i->consumer()->isDefinition() && i->consumer()->toDefinition() == ins) return true; } return false; }
void MBasicBlock::assertUsesAreNotWithin(MUseIterator use, MUseIterator end) { #ifdef DEBUG for (; use != end; use++) { JS_ASSERT_IF(use->consumer()->isDefinition(), use->consumer()->toDefinition()->block()->id() < id()); } #endif }
static bool CheckMarkedAsUse(MInstruction *ins, MDefinition *operand) { for (MUseIterator i = operand->usesBegin(); i != operand->usesEnd(); i++) { if (i->node()->isDefinition()) { if (ins == i->node()->toDefinition()) return true; } } return false; }
int EdgeCaseAnalysis::AllUsesTruncate(MInstruction *m) { // If all uses truncate, the return value must be at least 1. If anything doesn't truncate // 0 is explicitly returned. int ret = 1; for (MUseIterator use = m->usesBegin(); use != m->usesEnd(); use++) { // See #809485 why this is allowed if (use->node()->isResumePoint()) continue; MDefinition *def = use->node()->toDefinition(); if (def->isTruncateToInt32()) continue; if (def->isBitAnd()) continue; if (def->isBitOr()) continue; if (def->isBitXor()) continue; if (def->isLsh()) continue; if (def->isRsh()) continue; if (def->isBitNot()) continue; if (def->isAdd() && def->toAdd()->isTruncated()) { ret = Max(ret, def->toAdd()->isTruncated() + 1); continue; } if (def->isSub() && def->toSub()->isTruncated()) { ret = Max(ret, def->toSub()->isTruncated() + 1); continue; } // cannot use divide, since |truncate(int32(x/y) + int32(a/b)) != truncate(x/y+a/b)| return 0; } return ret; }
MUseIterator MNode::replaceOperand(MUseIterator use, MDefinition *ins) { MDefinition *used = getOperand(use->index()); if (used == ins) return use; MUse *save = *use; MUseIterator result(used->removeUse(use)); if (ins) { setOperand(save->index(), ins); ins->linkUse(save); } return result; }
static void AnalyzeLsh(TempAllocator& alloc, MLsh* lsh) { if (lsh->specialization() != MIRType::Int32) return; if (lsh->isRecoveredOnBailout()) return; MDefinition* index = lsh->lhs(); MOZ_ASSERT(index->type() == MIRType::Int32); MConstant* shiftValue = lsh->rhs()->maybeConstantValue(); if (!shiftValue) return; if (shiftValue->type() != MIRType::Int32 || !IsShiftInScaleRange(shiftValue->toInt32())) return; Scale scale = ShiftToScale(shiftValue->toInt32()); int32_t displacement = 0; MInstruction* last = lsh; MDefinition* base = nullptr; while (true) { if (!last->hasOneUse()) break; MUseIterator use = last->usesBegin(); if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isAdd()) break; MAdd* add = use->consumer()->toDefinition()->toAdd(); if (add->specialization() != MIRType::Int32 || !add->isTruncated()) break; MDefinition* other = add->getOperand(1 - add->indexOf(*use)); if (MConstant* otherConst = other->maybeConstantValue()) { displacement += otherConst->toInt32(); } else { if (base) break; base = other; } last = add; if (last->isRecoveredOnBailout()) return; } if (!base) { uint32_t elemSize = 1 << ScaleToShift(scale); if (displacement % elemSize != 0) return; if (!last->hasOneUse()) return; MUseIterator use = last->usesBegin(); if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isBitAnd()) return; MBitAnd* bitAnd = use->consumer()->toDefinition()->toBitAnd(); if (bitAnd->isRecoveredOnBailout()) return; MDefinition* other = bitAnd->getOperand(1 - bitAnd->indexOf(*use)); MConstant* otherConst = other->maybeConstantValue(); if (!otherConst || otherConst->type() != MIRType::Int32) return; uint32_t bitsClearedByShift = elemSize - 1; uint32_t bitsClearedByMask = ~uint32_t(otherConst->toInt32()); if ((bitsClearedByShift & bitsClearedByMask) != bitsClearedByMask) return; bitAnd->replaceAllUsesWith(last); return; } if (base->isRecoveredOnBailout()) return; MEffectiveAddress* eaddr = MEffectiveAddress::New(alloc, base, index, scale, displacement); last->replaceAllUsesWith(eaddr); last->block()->insertAfter(last, eaddr); }
static void AnalyzeLsh(MBasicBlock *block, MLsh *lsh) { if (lsh->specialization() != MIRType_Int32) return; MDefinition *index = lsh->lhs(); JS_ASSERT(index->type() == MIRType_Int32); MDefinition *shift = lsh->rhs(); if (!shift->isConstant()) return; Value shiftValue = shift->toConstant()->value(); if (!shiftValue.isInt32() || !IsShiftInScaleRange(shiftValue.toInt32())) return; Scale scale = ShiftToScale(shiftValue.toInt32()); int32_t displacement = 0; MInstruction *last = lsh; MDefinition *base = nullptr; while (true) { if (!last->hasOneUse()) break; MUseIterator use = last->usesBegin(); if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isAdd()) break; MAdd *add = use->consumer()->toDefinition()->toAdd(); if (add->specialization() != MIRType_Int32 || !add->isTruncated()) break; MDefinition *other = add->getOperand(1 - use->index()); if (other->isConstant()) { displacement += other->toConstant()->value().toInt32(); } else { if (base) break; base = other; } last = add; } if (!base) { uint32_t elemSize = 1 << ScaleToShift(scale); if (displacement % elemSize != 0) return; if (!last->hasOneUse()) return; MUseIterator use = last->usesBegin(); if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isBitAnd()) return; MBitAnd *bitAnd = use->consumer()->toDefinition()->toBitAnd(); MDefinition *other = bitAnd->getOperand(1 - use->index()); if (!other->isConstant() || !other->toConstant()->value().isInt32()) return; uint32_t bitsClearedByShift = elemSize - 1; uint32_t bitsClearedByMask = ~uint32_t(other->toConstant()->value().toInt32()); if ((bitsClearedByShift & bitsClearedByMask) != bitsClearedByMask) return; bitAnd->replaceAllUsesWith(last); return; } MEffectiveAddress *eaddr = MEffectiveAddress::New(base, index, scale, displacement); last->replaceAllUsesWith(eaddr); block->insertAfter(last, eaddr); }
static inline bool NeedNegativeZeroCheck(MDefinition *def) { // Test if all uses have the same symantic for -0 and 0 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) { if (use->node()->isResumePoint()) return true; MDefinition *use_def = use->node()->toDefinition(); switch (use_def->op()) { case MDefinition::Op_Add: { // x + y gives -0, when both x and y are -0 // - When other operand can't produce -0 (i.e. all opcodes, except Mul/Div/ToInt32) // Remove negative zero check on this operand // - When both operands can produce -0 (both Mul/Div/ToInt32 opcode) // We can remove the check eagerly on this operand. MDefinition *operand = use_def->getOperand(0); if (operand == def) { operand = use_def->getOperand(1); // Don't remove check when both operands are same definition // As removing it from one operand, will remove it from both. if (operand == def) return true; } // Check if check is possibly eagerly removed on other operand // and don't remove check eagerly on this operand in that case. if (operand->isMul()) { MMul *mul = operand->toMul(); if (!mul->canBeNegativeZero()) return true; } else if (operand->isDiv()) { MDiv *div = operand->toDiv(); if (!div->canBeNegativeZero()) return true; } else if (operand->isToInt32()) { MToInt32 *int32 = operand->toToInt32(); if (!int32->canBeNegativeZero()) return true; } else if (operand->isPhi()) { return true; } break; } case MDefinition::Op_StoreElement: case MDefinition::Op_StoreElementHole: case MDefinition::Op_LoadElement: case MDefinition::Op_LoadElementHole: case MDefinition::Op_LoadTypedArrayElement: case MDefinition::Op_LoadTypedArrayElementHole: case MDefinition::Op_CharCodeAt: case MDefinition::Op_Mod: case MDefinition::Op_Sub: // Only allowed to remove check when definition is the second operand if (use_def->getOperand(0) == def) return true; if (use_def->numOperands() > 2) { for (size_t i = 2; i < use_def->numOperands(); i++) { if (use_def->getOperand(i) == def) return true; } } break; case MDefinition::Op_BoundsCheck: // Only allowed to remove check when definition is the first operand if (use_def->getOperand(1) == def) return true; break; case MDefinition::Op_ToString: case MDefinition::Op_FromCharCode: case MDefinition::Op_TableSwitch: case MDefinition::Op_Compare: case MDefinition::Op_BitAnd: case MDefinition::Op_BitOr: case MDefinition::Op_BitXor: case MDefinition::Op_Abs: // Always allowed to remove check. No matter which operand. break; default: return true; } } return false; }