Chromium Code Reviews| Index: runtime/vm/intermediate_language.cc |
| diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc |
| index 69911b0f0c0df2e95a7eb4f1e46ed1dc669d8f6c..71161fa934fd7ca6874702d17bf44ac63b33638f 100644 |
| --- a/runtime/vm/intermediate_language.cc |
| +++ b/runtime/vm/intermediate_language.cc |
| @@ -2077,6 +2077,14 @@ void Environment::DeepCopyToOuter(Instruction* instr) const { |
| } |
| +RangeBoundary RangeBoundary::FromDefinition(Definition* defn, intptr_t offs) { |
| + if (defn->IsConstant() && defn->AsConstant()->value().IsSmi()) { |
| + return FromConstant(Smi::Cast(defn->AsConstant()->value()).Value()); |
| + } |
| + return RangeBoundary(kSymbol, reinterpret_cast<intptr_t>(defn), offs); |
| +} |
| + |
| + |
| RangeBoundary RangeBoundary::LowerBound() const { |
| if (IsConstant()) return *this; |
| if (symbol()->range() == NULL) return MinSmi(); |
| @@ -2095,6 +2103,157 @@ RangeBoundary RangeBoundary::UpperBound() const { |
| } |
| +static bool DependOnSameSymbol(const RangeBoundary& a, const RangeBoundary& b) { |
| + if (!a.IsSymbol() || !b.IsSymbol()) return false; |
| + if (a.symbol() == b.symbol()) return true; |
| + |
| + return !a.symbol()->AffectedBySideEffect() && |
| + !b.symbol()->AffectedBySideEffect() && |
| + a.symbol()->Equals(b.symbol()); |
|
Florian Schneider
2012/10/26 11:10:07
I think !a->AffecteedBySideEffect() &&a->Equals(b)
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| +} |
| + |
| + |
| +static bool IsMinSmi(Range* range) { |
| + return (range == NULL) || |
| + (range->min().IsConstant() && |
| + (range->min().value() <= Smi::kMinValue)); |
| +} |
| + |
| + |
| +static bool IsMaxSmi(Range* range) { |
| + return (range == NULL) || |
| + (range->max().IsConstant() && |
| + (range->max().value() >= Smi::kMaxValue)); |
| +} |
| + |
| + |
| +static bool IsEqual(const RangeBoundary& a, const RangeBoundary& b) { |
| + if (a.IsConstant() && b.IsConstant()) { |
| + return a.value() == b.value(); |
| + } else if (a.IsSymbol() && b.IsSymbol()) { |
| + return (a.offset() == b.offset()) && DependOnSameSymbol(a, b); |
| + } else { |
| + return false; |
| + } |
| +} |
| + |
| +static RangeBoundary CanonicalizeBoundary(const RangeBoundary& a) { |
| + if (a.IsConstant()) return a; |
| + |
| + intptr_t offset = a.offset(); |
| + Definition* symbol = a.symbol(); |
| + |
| + bool changed; |
| + do { |
| + changed = false; |
| + if (symbol->IsConstraint()) { |
| + symbol = symbol->AsConstraint()->value()->definition(); |
| + changed = true; |
| + } else if (symbol->IsBinarySmiOp()) { |
| + BinarySmiOpInstr* op = symbol->AsBinarySmiOp(); |
| + Definition* left = op->left()->definition(); |
| + Definition* right = op->right()->definition(); |
| + switch (op->op_kind()) { |
| + case Token::kADD: |
| + if (right->IsConstant()) { |
| + offset += Smi::Cast(right->AsConstant()->value()).Value(); |
| + symbol = left; |
| + changed = true; |
| + } else if (left->IsConstant()) { |
| + offset += Smi::Cast(left->AsConstant()->value()).Value(); |
| + symbol = right; |
| + changed = true; |
| + } |
| + break; |
| + |
| + |
|
srdjan
2012/10/25 20:51:36
Remove one empty line
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| + case Token::kSUB: |
| + if (right->IsConstant()) { |
| + offset -= Smi::Cast(right->AsConstant()->value()).Value(); |
| + symbol = left; |
| + changed = true; |
| + } |
| + break; |
| + |
| + default: |
| + break; |
| + } |
| + } |
| + } while (changed); |
| + |
| + return RangeBoundary::FromDefinition(symbol, offset); |
| +} |
| + |
| + |
| +static bool CanonicalizeMaxBoundary(RangeBoundary* a) { |
| + if (!a->IsSymbol()) return false; |
| + |
| + Range* range = a->symbol()->range(); |
| + if ((range == NULL) || !range->max().IsSymbol()) return false; |
| + |
| + *a = CanonicalizeBoundary( |
| + RangeBoundary::FromDefinition(range->max().symbol(), |
| + range->max().offset() + a->offset())); |
| + |
| + return true; |
| +} |
| + |
| + |
| +static bool CanonicalizeMinBoundary(RangeBoundary* a) { |
| + if (!a->IsSymbol()) return false; |
| + |
| + Range* range = a->symbol()->range(); |
| + if ((range == NULL) || !range->min().IsSymbol()) return false; |
| + |
| + *a = CanonicalizeBoundary( |
| + RangeBoundary::FromDefinition(range->min().symbol(), |
| + range->min().offset() + a->offset())); |
| + |
| + return true; |
| +} |
| + |
| + |
| +#if 0 |
| +static bool KnownToBeLessEqual(const RangeBoundary& a, const RangeBoundary& b) { |
| + RangeBoundary canonical_a = CanonicalizeBoundary(a); |
| + RangeBoundary canonical_b = CanonicalizeBoundary(b); |
| + |
| + do { |
| + if (DependOnSameSymbol(canonical_a, canonical_b)) { |
| + return (canonical_a.offset() <= canonical_b.offset()); |
| + } |
| + } while (CanonicalizeMaxBoundary(&canonical_a) || |
| + CanonicalizeMinBoundary(&canonical_b)); |
| + |
| + return a.UpperBound().value() <= b.LowerBound().value(); |
| +} |
|
srdjan
2012/10/25 20:51:36
Remove the code above
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| +#endif |
| + |
| + |
| +RangeBoundary RangeBoundary::Min(RangeBoundary a, RangeBoundary b) { |
| + if (DependOnSameSymbol(a, b)) { |
| + return (a.offset() <= b.offset()) ? a : b; |
| + } |
| + |
| + const intptr_t min_a = a.LowerBound().value(); |
| + const intptr_t min_b = b.LowerBound().value(); |
| + |
| + return RangeBoundary::FromConstant(Utils::Minimum(min_a, min_b)); |
| +} |
| + |
| + |
| +RangeBoundary RangeBoundary::Max(RangeBoundary a, RangeBoundary b) { |
| + if (DependOnSameSymbol(a, b)) { |
| + return (a.offset() >= b.offset()) ? a : b; |
| + } |
| + |
| + const intptr_t max_a = a.UpperBound().value(); |
| + const intptr_t max_b = b.UpperBound().value(); |
| + |
| + return RangeBoundary::FromConstant(Utils::Maximum(max_a, max_b)); |
| +} |
| + |
| + |
| void Definition::InferRange() { |
| ASSERT(GetPropagatedCid() == kSmiCid); // Has meaning only for smis. |
| if (range_ == NULL) { |
| @@ -2116,12 +2275,77 @@ void ConstantInstr::InferRange() { |
| void ConstraintInstr::InferRange() { |
| Range* value_range = value()->definition()->range(); |
| - // Compute intersection of constraint and value ranges. |
| - range_ = new Range( |
| - RangeBoundary::Max(Range::ConstantMin(value_range), |
| - Range::ConstantMin(constraint())), |
| - RangeBoundary::Min(Range::ConstantMax(value_range), |
| - Range::ConstantMax(constraint()))); |
| + RangeBoundary min; |
| + RangeBoundary max; |
| + |
| + if (IsMinSmi(value_range) && !IsMinSmi(constraint())) { |
| + min = constraint()->min(); |
| + } else if (IsMinSmi(constraint()) && !IsMinSmi(value_range)) { |
| + min = value_range->min(); |
| + } else if ((value_range != NULL) && |
| + IsEqual(constraint()->min(), value_range->min())) { |
| + min = constraint()->min(); |
| + } else { |
| + if (value_range != NULL) { |
| + RangeBoundary canonical_a = CanonicalizeBoundary(constraint()->min()); |
| + RangeBoundary canonical_b = CanonicalizeBoundary(value_range->min()); |
| + |
| + do { |
| + if (DependOnSameSymbol(canonical_a, canonical_b)) { |
| + min = (canonical_a.offset() <= canonical_b.offset()) ? canonical_b |
| + : canonical_a; |
| + } |
| + } while (CanonicalizeMinBoundary(&canonical_a) || |
| + CanonicalizeMinBoundary(&canonical_b)); |
| + } |
| + |
| + if (min.IsUnknown()) { |
| + min = RangeBoundary::Max(Range::ConstantMin(value_range), |
| + Range::ConstantMin(constraint())); |
| + } |
| + } |
| + |
| + if (IsMaxSmi(value_range) && !IsMaxSmi(constraint())) { |
| + max = constraint()->max(); |
| + } else if (IsMaxSmi(constraint()) && !IsMaxSmi(value_range)) { |
| + max = value_range->max(); |
| + } else if ((value_range != NULL) && |
| + IsEqual(constraint()->max(), value_range->max())) { |
| + max = constraint()->max(); |
| + } else { |
| + if (value_range != NULL) { |
| + RangeBoundary canonical_b = CanonicalizeBoundary(value_range->max()); |
| + RangeBoundary canonical_a = CanonicalizeBoundary(constraint()->max()); |
| + |
| + do { |
| + if (DependOnSameSymbol(canonical_a, canonical_b)) { |
| + max = (canonical_a.offset() <= canonical_b.offset()) ? canonical_a |
| + : canonical_b; |
| + break; |
| + } |
| + } while (CanonicalizeMaxBoundary(&canonical_a) || |
| + CanonicalizeMaxBoundary(&canonical_b)); |
| + } |
| + |
| + if (max.IsUnknown()) { |
| + max = RangeBoundary::Min(Range::ConstantMax(value_range), |
| + Range::ConstantMax(constraint())); |
|
Florian Schneider
2012/10/26 11:10:07
Indentation.
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| + } |
| + } |
| + |
| + range_ = new Range(min, max); |
| +} |
| + |
| + |
| +void LoadFieldInstr::InferRange() { |
| + if ((range_ == NULL) && |
| + ((recognized_kind() == MethodRecognizer::kObjectArrayLength) || |
| + (recognized_kind() == MethodRecognizer::kImmutableArrayLength))) { |
| + range_ = new Range(RangeBoundary::FromConstant(0), |
| + RangeBoundary::FromConstant(Array::kMaxElements)); |
| + return; |
| + } |
| + Definition::InferRange(); |
| } |
| @@ -2159,8 +2383,41 @@ void PhiInstr::InferRange() { |
| } |
| +static bool SymbolicSub(const RangeBoundary& a, |
| + const RangeBoundary& b, |
| + RangeBoundary* result) { |
| + if (a.IsSymbol() && |
| + b.IsConstant() && |
| + ((b.value() > Smi::kMinValue) && (b.value() < Smi::kMaxValue))) { |
| + *result = RangeBoundary::FromDefinition(a.symbol(), a.offset() - b.value()); |
|
Florian Schneider
2012/10/26 11:10:07
Guard against smi-overflow here.
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| + |
| +static bool SymbolicAdd(const RangeBoundary& a, |
| + const RangeBoundary& b, |
| + RangeBoundary* result) { |
| + if (a.IsSymbol() && |
| + b.IsConstant() && |
| + ((b.value() > Smi::kMinValue) && (b.value() < Smi::kMaxValue))) { |
| + *result = RangeBoundary::FromDefinition(a.symbol(), a.offset() + b.value()); |
|
Florian Schneider
2012/10/26 11:10:07
Guard against overflow here, so that the result of
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| + return true; |
| + } else if (b.IsSymbol() && |
| + a.IsConstant() && |
| + ((a.value() > Smi::kMinValue) && (a.value() < Smi::kMaxValue))) { |
| + *result = RangeBoundary::FromDefinition(b.symbol(), b.offset() + a.value()); |
|
Florian Schneider
2012/10/26 11:10:07
Guard against smi-overflow here as well.
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| + |
| void BinarySmiOpInstr::InferRange() { |
| - Range* left_range = left()->definition()->range(); |
| + Definition* left_defn = left()->definition(); |
| + |
| + Range* left_range = left_defn->range(); |
| Range* right_range = right()->definition()->range(); |
| if ((left_range == NULL) || (right_range == NULL)) { |
| @@ -2168,29 +2425,47 @@ void BinarySmiOpInstr::InferRange() { |
| return; |
| } |
| - RangeBoundary new_min; |
| - RangeBoundary new_max; |
| + RangeBoundary left_min = |
| + left_defn->IsLoadField() ? |
|
Florian Schneider
2012/10/26 11:10:07
Does this need to be for every LoadField, or only
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Narrowed symbolic computation to length.
|
| + RangeBoundary::FromDefinition(left_defn) : left_range->min(); |
| + |
| + RangeBoundary left_max = |
| + left_defn->IsLoadField() ? |
| + RangeBoundary::FromDefinition(left_defn) : left_range->max(); |
|
Florian Schneider
2012/10/26 11:10:07
Maybe add a TODO to support the case where right()
Vyacheslav Egorov (Google)
2012/10/26 14:23:29
Done.
|
| + |
| + RangeBoundary min; |
| + RangeBoundary max; |
| switch (op_kind()) { |
| case Token::kADD: |
| - new_min = |
| - RangeBoundary::Add(Range::ConstantMin(left_range), |
| - Range::ConstantMin(right_range), |
| - RangeBoundary::OverflowedMinSmi()); |
| - new_max = |
| - RangeBoundary::Add(Range::ConstantMax(left_range), |
| - Range::ConstantMax(right_range), |
| - RangeBoundary::OverflowedMaxSmi()); |
| + if (!SymbolicAdd(left_min, right_range->min(), &min)) { |
| + min = |
| + RangeBoundary::Add(Range::ConstantMin(left_range), |
| + Range::ConstantMin(right_range), |
| + RangeBoundary::OverflowedMinSmi()); |
| + } |
| + |
| + if (!SymbolicAdd(left_max, right_range->max(), &max)) { |
| + max = |
| + RangeBoundary::Add(Range::ConstantMax(right_range), |
| + Range::ConstantMax(left_range), |
| + RangeBoundary::OverflowedMaxSmi()); |
| + } |
| break; |
| case Token::kSUB: |
| - new_min = |
| - RangeBoundary::Sub(Range::ConstantMin(left_range), |
| - Range::ConstantMax(right_range), |
| - RangeBoundary::OverflowedMinSmi()); |
| - new_max = |
| - RangeBoundary::Sub(Range::ConstantMax(left_range), |
| - Range::ConstantMin(right_range), |
| - RangeBoundary::OverflowedMaxSmi()); |
| + if (!SymbolicSub(left_min, right_range->max(), &min)) { |
| + min = |
| + RangeBoundary::Sub(Range::ConstantMin(left_range), |
| + Range::ConstantMax(right_range), |
| + RangeBoundary::OverflowedMinSmi()); |
| + } |
| + |
| + if (!SymbolicSub(left_max, right_range->min(), &max)) { |
| + max = |
| + RangeBoundary::Sub(Range::ConstantMax(left_range), |
| + Range::ConstantMin(right_range), |
| + RangeBoundary::OverflowedMaxSmi()); |
| + } |
| break; |
| default: |
| @@ -2200,10 +2475,49 @@ void BinarySmiOpInstr::InferRange() { |
| return; |
| } |
| - ASSERT(!new_min.IsUnknown() && !new_max.IsUnknown()); |
| - set_overflow(new_min.Overflowed() || new_max.Overflowed()); |
| + ASSERT(!min.IsUnknown() && !max.IsUnknown()); |
| + set_overflow(min.LowerBound().Overflowed() || max.UpperBound().Overflowed()); |
| + |
| + if (min.IsConstant()) min.Clamp(); |
| + if (max.IsConstant()) max.Clamp(); |
| - range_ = new Range(new_min.Clamp(), new_max.Clamp()); |
| + range_ = new Range(min, max); |
| +} |
| + |
| + |
| +static bool IsArrayLength(Definition* defn, Definition* array) { |
| + LoadFieldInstr* load = defn->AsLoadField(); |
| + return (load != NULL) && (load->value()->definition() == array) && |
| + ((load->recognized_kind() == MethodRecognizer::kObjectArrayLength) || |
| + (load->recognized_kind() == MethodRecognizer::kImmutableArrayLength)); |
| +} |
| + |
| + |
| +bool CheckArrayBoundInstr::IsRedundant() { |
| + // Check that array has an immutable length. |
| + if ((array_type() != kArrayCid) && (array_type() != kImmutableArrayCid)) { |
| + return false; |
| + } |
| + |
| + Range* index_range = index()->definition()->range(); |
| + |
| + // Range of the index is unknown can't decide if the check is redundant. |
| + if (index_range == NULL) return false; |
| + |
| + // Range of the index is not positive. Check can't be redundant. |
| + if (Range::ConstantMin(index_range).value() < 0) return false; |
| + |
| + RangeBoundary max = CanonicalizeBoundary(index_range->max()); |
| + do { |
| + if (max.IsSymbol() && |
| + (max.offset() < 0) && |
| + IsArrayLength(max.symbol(), array()->definition())) { |
| + return true; |
| + } |
| + } while (CanonicalizeMaxBoundary(&max)); |
| + |
| + // Failed to prove that maximum is bounded with array length. |
| + return false; |
| } |