Index: src/hydrogen-instructions.cc |
diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc |
index 997b7c2fda9b29455d03faac59c5a49a60d18d5c..3eb4aa6c0e63de6dcba0cf5444387bf02e92904e 100644 |
--- a/src/hydrogen-instructions.cc |
+++ b/src/hydrogen-instructions.cc |
@@ -149,6 +149,116 @@ void HValue::AddDependantsToWorklist(HInferRepresentationPhase* h_infer) { |
} |
+// This method is recursive but it is guaranteed to terminate because |
+// RedefinedOperand() always dominates "this". |
+bool HValue::IsRelationTrue(NumericRelation relation, |
+ HValue* other, |
+ int offset, |
+ int scale) { |
+ if (this == other) { |
+ return scale == 0 && relation.IsExtendable(offset); |
+ } |
+ |
+ // Test the direct relation. |
+ if (IsRelationTrueInternal(relation, other, offset, scale)) return true; |
+ |
+ // If scale is 0 try the reversed relation. |
+ if (scale == 0 && |
+ // TODO(mmassi): do we need the full, recursive IsRelationTrue? |
+ other->IsRelationTrueInternal(relation.Reversed(), this, -offset)) { |
+ return true; |
+ } |
+ |
+ // Try decomposition (but do not accept scaled compounds). |
+ DecompositionResult decomposition; |
+ if (TryDecompose(&decomposition) && |
+ decomposition.scale() == 0 && |
+ decomposition.base()->IsRelationTrue(relation, other, |
+ offset + decomposition.offset(), |
+ scale)) { |
+ return true; |
+ } |
+ |
+ // Pass the request to the redefined value. |
+ HValue* redefined = RedefinedOperand(); |
+ return redefined != NULL && redefined->IsRelationTrue(relation, other, |
+ offset, scale); |
+} |
+ |
+ |
+bool HValue::TryGuaranteeRange(HValue* upper_bound) { |
+ RangeEvaluationContext context = RangeEvaluationContext(this, upper_bound); |
+ TryGuaranteeRangeRecursive(&context); |
+ bool result = context.is_range_satisfied(); |
+ if (result) { |
+ context.lower_bound_guarantee()->SetResponsibilityForRange(DIRECTION_LOWER); |
+ context.upper_bound_guarantee()->SetResponsibilityForRange(DIRECTION_UPPER); |
+ } |
+ return result; |
+} |
+ |
+ |
+void HValue::TryGuaranteeRangeRecursive(RangeEvaluationContext* context) { |
+ // Check if we already know that this value satisfies the lower bound. |
+ if (context->lower_bound_guarantee() == NULL) { |
+ if (IsRelationTrueInternal(NumericRelation::Ge(), context->lower_bound(), |
+ context->offset(), context->scale())) { |
+ context->set_lower_bound_guarantee(this); |
+ } |
+ } |
+ |
+ // Check if we already know that this value satisfies the upper bound. |
+ if (context->upper_bound_guarantee() == NULL) { |
+ if (IsRelationTrueInternal(NumericRelation::Lt(), context->upper_bound(), |
+ context->offset(), context->scale()) || |
+ (context->scale() == 0 && |
+ context->upper_bound()->IsRelationTrue(NumericRelation::Gt(), |
+ this, -context->offset()))) { |
+ context->set_upper_bound_guarantee(this); |
+ } |
+ } |
+ |
+ if (context->is_range_satisfied()) return; |
+ |
+ // See if our RedefinedOperand() satisfies the constraints. |
+ if (RedefinedOperand() != NULL) { |
+ RedefinedOperand()->TryGuaranteeRangeRecursive(context); |
+ } |
+ if (context->is_range_satisfied()) return; |
+ |
+ // See if the constraints can be satisfied by decomposition. |
+ DecompositionResult decomposition; |
+ if (TryDecompose(&decomposition)) { |
+ context->swap_candidate(&decomposition); |
+ context->candidate()->TryGuaranteeRangeRecursive(context); |
+ context->swap_candidate(&decomposition); |
+ } |
+ if (context->is_range_satisfied()) return; |
+ |
+ // Try to modify this to satisfy the constraint. |
+ |
+ TryGuaranteeRangeChanging(context); |
+} |
+ |
+ |
+RangeEvaluationContext::RangeEvaluationContext(HValue* value, HValue* upper) |
+ : lower_bound_(upper->block()->graph()->GetConstant0()), |
+ lower_bound_guarantee_(NULL), |
+ candidate_(value), |
+ upper_bound_(upper), |
+ upper_bound_guarantee_(NULL), |
+ offset_(0), |
+ scale_(0) { |
+} |
+ |
+ |
+HValue* RangeEvaluationContext::ConvertGuarantee(HValue* guarantee) { |
+ return guarantee->IsBoundsCheckBaseIndexInformation() |
+ ? HBoundsCheckBaseIndexInformation::cast(guarantee)->bounds_check() |
+ : guarantee; |
+} |
+ |
+ |
static int32_t ConvertAndSetOverflow(Representation r, |
int64_t result, |
bool* overflow) { |
@@ -374,6 +484,55 @@ HType HType::TypeFromValue(Handle<Object> value) { |
} |
+bool HValue::Dominates(HValue* dominator, HValue* dominated) { |
+ if (dominator->block() != dominated->block()) { |
+ // If they are in different blocks we can use the dominance relation |
+ // between the blocks. |
+ return dominator->block()->Dominates(dominated->block()); |
+ } else { |
+ // Otherwise we must see which instruction comes first, considering |
+ // that phis always precede regular instructions. |
+ if (dominator->IsInstruction()) { |
+ if (dominated->IsInstruction()) { |
+ for (HInstruction* next = HInstruction::cast(dominator)->next(); |
+ next != NULL; |
+ next = next->next()) { |
+ if (next == dominated) return true; |
+ } |
+ return false; |
+ } else if (dominated->IsPhi()) { |
+ return false; |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ } else if (dominator->IsPhi()) { |
+ if (dominated->IsInstruction()) { |
+ return true; |
+ } else { |
+ // We cannot compare which phi comes first. |
+ UNREACHABLE(); |
+ } |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ return false; |
+ } |
+} |
+ |
+ |
+bool HValue::TestDominanceUsingProcessedFlag(HValue* dominator, |
+ HValue* dominated) { |
+ if (dominator->block() != dominated->block()) { |
+ return dominator->block()->Dominates(dominated->block()); |
+ } else { |
+ // If both arguments are in the same block we check if dominator is a phi |
+ // or if dominated has not already been processed: in either case we know |
+ // that dominator precedes dominated. |
+ return dominator->IsPhi() || !dominated->CheckFlag(kIDefsProcessingDone); |
+ } |
+} |
+ |
+ |
bool HValue::IsDefinedAfter(HBasicBlock* other) const { |
return block()->block_id() > other->block_id(); |
} |
@@ -801,6 +960,58 @@ void HInstruction::Verify() { |
#endif |
+HNumericConstraint* HNumericConstraint::AddToGraph( |
+ HValue* constrained_value, |
+ NumericRelation relation, |
+ HValue* related_value, |
+ HInstruction* insertion_point) { |
+ if (insertion_point == NULL) { |
+ if (constrained_value->IsInstruction()) { |
+ insertion_point = HInstruction::cast(constrained_value); |
+ } else if (constrained_value->IsPhi()) { |
+ insertion_point = constrained_value->block()->first(); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ } |
+ HNumericConstraint* result = |
+ new(insertion_point->block()->zone()) HNumericConstraint( |
+ constrained_value, relation, related_value); |
+ result->InsertAfter(insertion_point); |
+ return result; |
+} |
+ |
+ |
+void HNumericConstraint::PrintDataTo(StringStream* stream) { |
+ stream->Add("("); |
+ constrained_value()->PrintNameTo(stream); |
+ stream->Add(" %s ", relation().Mnemonic()); |
+ related_value()->PrintNameTo(stream); |
+ stream->Add(")"); |
+} |
+ |
+ |
+HInductionVariableAnnotation* HInductionVariableAnnotation::AddToGraph( |
+ HPhi* phi, |
+ NumericRelation relation, |
+ int operand_index) { |
+ HInductionVariableAnnotation* result = |
+ new(phi->block()->zone()) HInductionVariableAnnotation(phi, relation, |
+ operand_index); |
+ result->InsertAfter(phi->block()->first()); |
+ return result; |
+} |
+ |
+ |
+void HInductionVariableAnnotation::PrintDataTo(StringStream* stream) { |
+ stream->Add("("); |
+ RedefinedOperand()->PrintNameTo(stream); |
+ stream->Add(" %s ", relation().Mnemonic()); |
+ induction_base()->PrintNameTo(stream); |
+ stream->Add(")"); |
+} |
+ |
+ |
void HDummyUse::PrintDataTo(StringStream* stream) { |
value()->PrintNameTo(stream); |
} |
@@ -827,6 +1038,40 @@ void HBinaryCall::PrintDataTo(StringStream* stream) { |
} |
+void HBoundsCheck::TryGuaranteeRangeChanging(RangeEvaluationContext* context) { |
+ if (context->candidate()->ActualValue() != base()->ActualValue() || |
+ context->scale() < scale()) { |
+ return; |
+ } |
+ |
+ // TODO(mmassi) |
+ // Instead of checking for "same basic block" we should check for |
+ // "dominates and postdominates". |
+ if (context->upper_bound() == length() && |
+ context->lower_bound_guarantee() != NULL && |
+ context->lower_bound_guarantee() != this && |
+ context->lower_bound_guarantee()->block() != block() && |
+ offset() < context->offset() && |
+ index_can_increase() && |
+ context->upper_bound_guarantee() == NULL) { |
+ offset_ = context->offset(); |
+ SetResponsibilityForRange(DIRECTION_UPPER); |
+ context->set_upper_bound_guarantee(this); |
+ isolate()->counters()->bounds_checks_eliminated()->Increment(); |
+ } else if (context->upper_bound_guarantee() != NULL && |
+ context->upper_bound_guarantee() != this && |
+ context->upper_bound_guarantee()->block() != block() && |
+ offset() > context->offset() && |
+ index_can_decrease() && |
+ context->lower_bound_guarantee() == NULL) { |
+ offset_ = context->offset(); |
+ SetResponsibilityForRange(DIRECTION_LOWER); |
+ context->set_lower_bound_guarantee(this); |
+ isolate()->counters()->bounds_checks_eliminated()->Increment(); |
+ } |
+} |
+ |
+ |
void HBoundsCheck::ApplyIndexChange() { |
if (skip_check()) return; |
@@ -874,6 +1119,40 @@ void HBoundsCheck::ApplyIndexChange() { |
base_ = NULL; |
offset_ = 0; |
scale_ = 0; |
+ responsibility_direction_ = DIRECTION_NONE; |
+} |
+ |
+ |
+void HBoundsCheck::AddInformativeDefinitions() { |
+ // TODO(mmassi): Executing this code during AddInformativeDefinitions |
+ // is a hack. Move it to some other HPhase. |
+ if (FLAG_array_bounds_checks_elimination) { |
+ if (index()->TryGuaranteeRange(length())) { |
+ set_skip_check(); |
+ } |
+ if (DetectCompoundIndex()) { |
+ HBoundsCheckBaseIndexInformation* base_index_info = |
+ new(block()->graph()->zone()) |
+ HBoundsCheckBaseIndexInformation(this); |
+ base_index_info->InsertAfter(this); |
+ } |
+ } |
+} |
+ |
+ |
+bool HBoundsCheck::IsRelationTrueInternal(NumericRelation relation, |
+ HValue* related_value, |
+ int offset, |
+ int scale) { |
+ if (related_value == length()) { |
+ // A HBoundsCheck is smaller than the length it compared against. |
+ return NumericRelation::Lt().CompoundImplies(relation, 0, 0, offset, scale); |
+ } else if (related_value == block()->graph()->GetConstant0()) { |
+ // A HBoundsCheck is greater than or equal to zero. |
+ return NumericRelation::Ge().CompoundImplies(relation, 0, 0, offset, scale); |
+ } else { |
+ return false; |
+ } |
} |
@@ -916,6 +1195,25 @@ void HBoundsCheck::InferRepresentation(HInferRepresentationPhase* h_infer) { |
} |
+bool HBoundsCheckBaseIndexInformation::IsRelationTrueInternal( |
+ NumericRelation relation, |
+ HValue* related_value, |
+ int offset, |
+ int scale) { |
+ if (related_value == bounds_check()->length()) { |
+ return NumericRelation::Lt().CompoundImplies( |
+ relation, |
+ bounds_check()->offset(), bounds_check()->scale(), offset, scale); |
+ } else if (related_value == block()->graph()->GetConstant0()) { |
+ return NumericRelation::Ge().CompoundImplies( |
+ relation, |
+ bounds_check()->offset(), bounds_check()->scale(), offset, scale); |
+ } else { |
+ return false; |
+ } |
+} |
+ |
+ |
void HBoundsCheckBaseIndexInformation::PrintDataTo(StringStream* stream) { |
stream->Add("base: "); |
base_index()->PrintNameTo(stream); |
@@ -1155,29 +1453,6 @@ void HLoadFieldByIndex::PrintDataTo(StringStream* stream) { |
} |
-static bool MatchLeftIsOnes(HValue* l, HValue* r, HValue** negated) { |
- if (!l->EqualsInteger32Constant(~0)) return false; |
- *negated = r; |
- return true; |
-} |
- |
- |
-static bool MatchNegationViaXor(HValue* instr, HValue** negated) { |
- if (!instr->IsBitwise()) return false; |
- HBitwise* b = HBitwise::cast(instr); |
- return (b->op() == Token::BIT_XOR) && |
- (MatchLeftIsOnes(b->left(), b->right(), negated) || |
- MatchLeftIsOnes(b->right(), b->left(), negated)); |
-} |
- |
- |
-static bool MatchDoubleNegation(HValue* instr, HValue** arg) { |
- HValue* negated; |
- return MatchNegationViaXor(instr, &negated) && |
- MatchNegationViaXor(negated, arg); |
-} |
- |
- |
HValue* HBitwise::Canonicalize() { |
if (!representation().IsSmiOrInteger32()) return this; |
// If x is an int32, then x & -1 == x, x | 0 == x and x ^ 0 == x. |
@@ -1190,10 +1465,18 @@ HValue* HBitwise::Canonicalize() { |
!left()->CheckFlag(kUint32)) { |
return left(); |
} |
- // Optimize double negation, a common pattern used for ToInt32(x). |
- HValue* arg; |
- if (MatchDoubleNegation(this, &arg) && !arg->CheckFlag(kUint32)) { |
- return arg; |
+ return this; |
+} |
+ |
+ |
+HValue* HBitNot::Canonicalize() { |
+ // Optimize ~~x, a common pattern used for ToInt32(x). |
+ if (value()->IsBitNot()) { |
+ HValue* result = HBitNot::cast(value())->value(); |
+ ASSERT(result->representation().IsInteger32()); |
+ if (!result->CheckFlag(kUint32)) { |
+ return result; |
+ } |
} |
return this; |
} |
@@ -1404,10 +1687,10 @@ void HCheckMaps::HandleSideEffectDominator(GVNFlag side_effect, |
// for which the map is known. |
if (HasNoUses() && dominator->IsStoreNamedField()) { |
HStoreNamedField* store = HStoreNamedField::cast(dominator); |
- if (!store->has_transition() || store->object() != value()) return; |
- HConstant* transition = HConstant::cast(store->transition()); |
+ UniqueValueId map_unique_id = store->transition_unique_id(); |
+ if (!map_unique_id.IsInitialized() || store->object() != value()) return; |
for (int i = 0; i < map_set()->length(); i++) { |
- if (transition->UniqueValueIdsMatch(map_unique_ids_.at(i))) { |
+ if (map_unique_id == map_unique_ids_.at(i)) { |
DeleteAndReplaceWith(NULL); |
return; |
} |
@@ -1458,6 +1741,13 @@ void HCheckInstanceType::PrintDataTo(StringStream* stream) { |
} |
+void HCheckPrototypeMaps::PrintDataTo(StringStream* stream) { |
+ stream->Add("[receiver_prototype=%p,holder=%p]%s", |
+ *prototypes_.first(), *prototypes_.last(), |
+ CanOmitPrototypeChecks() ? " (omitted)" : ""); |
+} |
+ |
+ |
void HCallStub::PrintDataTo(StringStream* stream) { |
stream->Add("%s ", |
CodeStub::MajorName(major_key_, false)); |
@@ -1660,6 +1950,60 @@ Range* HMod::InferRange(Zone* zone) { |
} |
+void HPhi::AddInformativeDefinitions() { |
+ if (OperandCount() == 2) { |
+ // If one of the operands is an OSR block give up (this cannot be an |
+ // induction variable). |
+ if (OperandAt(0)->block()->is_osr_entry() || |
+ OperandAt(1)->block()->is_osr_entry()) return; |
+ |
+ for (int operand_index = 0; operand_index < 2; operand_index++) { |
+ int other_operand_index = (operand_index + 1) % 2; |
+ |
+ static NumericRelation relations[] = { |
+ NumericRelation::Ge(), |
+ NumericRelation::Le() |
+ }; |
+ |
+ // Check if this phi is an induction variable. If, e.g., we know that |
+ // its first input is greater than the phi itself, then that must be |
+ // the back edge, and the phi is always greater than its second input. |
+ for (int relation_index = 0; relation_index < 2; relation_index++) { |
+ if (OperandAt(operand_index)->IsRelationTrue(relations[relation_index], |
+ this)) { |
+ HInductionVariableAnnotation::AddToGraph(this, |
+ relations[relation_index], |
+ other_operand_index); |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+bool HPhi::IsRelationTrueInternal(NumericRelation relation, |
+ HValue* other, |
+ int offset, |
+ int scale) { |
+ if (CheckFlag(kNumericConstraintEvaluationInProgress)) return false; |
+ |
+ SetFlag(kNumericConstraintEvaluationInProgress); |
+ bool result = true; |
+ for (int i = 0; i < OperandCount(); i++) { |
+ // Skip OSR entry blocks |
+ if (OperandAt(i)->block()->is_osr_entry()) continue; |
+ |
+ if (!OperandAt(i)->IsRelationTrue(relation, other, offset, scale)) { |
+ result = false; |
+ break; |
+ } |
+ } |
+ ClearFlag(kNumericConstraintEvaluationInProgress); |
+ |
+ return result; |
+} |
+ |
+ |
InductionVariableData* InductionVariableData::ExaminePhi(HPhi* phi) { |
if (phi->block()->loop_information() == NULL) return NULL; |
if (phi->OperandCount() != 2) return NULL; |
@@ -2411,14 +2755,6 @@ HConstant::HConstant(ExternalReference reference) |
} |
-static void PrepareConstant(Handle<Object> object) { |
- if (!object->IsJSObject()) return; |
- Handle<JSObject> js_object = Handle<JSObject>::cast(object); |
- if (!js_object->map()->is_deprecated()) return; |
- JSObject::TryMigrateInstance(js_object); |
-} |
- |
- |
void HConstant::Initialize(Representation r) { |
if (r.IsNone()) { |
if (has_smi_value_ && kSmiValueSize == 31) { |
@@ -2430,7 +2766,6 @@ void HConstant::Initialize(Representation r) { |
} else if (has_external_reference_value_) { |
r = Representation::External(); |
} else { |
- PrepareConstant(handle_); |
r = Representation::Tagged(); |
} |
} |
@@ -2755,6 +3090,16 @@ void HStringCompareAndBranch::PrintDataTo(StringStream* stream) { |
} |
+void HCompareNumericAndBranch::AddInformativeDefinitions() { |
+ NumericRelation r = NumericRelation::FromToken(token()); |
+ if (r.IsNone()) return; |
+ |
+ HNumericConstraint::AddToGraph(left(), r, right(), SuccessorAt(0)->first()); |
+ HNumericConstraint::AddToGraph( |
+ left(), r.Negated(), right(), SuccessorAt(1)->first()); |
+} |
+ |
+ |
void HCompareNumericAndBranch::PrintDataTo(StringStream* stream) { |
stream->Add(Token::Name(token())); |
stream->Add(" "); |
@@ -2953,7 +3298,6 @@ HCheckMaps* HCheckMaps::New(Zone* zone, |
HValue* typecheck) { |
HCheckMaps* check_map = new(zone) HCheckMaps(value, zone, typecheck); |
check_map->map_set_.Add(map, zone); |
- check_map->has_migration_target_ = map->is_migration_target(); |
if (map->CanOmitMapChecks() && |
value->IsConstant() && |
HConstant::cast(value)->InstanceOf(map)) { |
@@ -3178,8 +3522,8 @@ void HStoreNamedField::PrintDataTo(StringStream* stream) { |
if (NeedsWriteBarrier()) { |
stream->Add(" (write-barrier)"); |
} |
- if (has_transition()) { |
- stream->Add(" (transition map %p)", *transition_map()); |
+ if (!transition().is_null()) { |
+ stream->Add(" (transition map %p)", *transition()); |
} |
} |