[vm/compiler] Simplify range analysis by removing range sizes
Concept of range sizes is artificial and sometimes over-approximated as it doesn't match representations used in IL instructions. It is replaced by "full range" derived from representation. Also, this change removes a bit of Smi-specific logic which cannot be extended for arbitrary integers. TEST=ci Change-Id: Ifc3092b13e4655cd5790d39d3fb88bd9b40ba484 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/396481 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
f601692dd7
commit
bd44f1fd82
@@ -2177,10 +2177,9 @@ class PhiUnboxingHeuristic : public ValueObject {
|
||||
// If all the inputs are unboxed integers but with different
|
||||
// representations, then pick a representation based on the range
|
||||
// of values that flow into the phi node.
|
||||
new_representation =
|
||||
RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32)
|
||||
? kUnboxedInt32
|
||||
: kUnboxedInt64;
|
||||
new_representation = Range::Fits(phi->range(), kUnboxedInt32)
|
||||
? kUnboxedInt32
|
||||
: kUnboxedInt64;
|
||||
}
|
||||
|
||||
// Decide if it is worth to unbox an boxed integer phi.
|
||||
@@ -2202,10 +2201,9 @@ class PhiUnboxingHeuristic : public ValueObject {
|
||||
const bool flows_into_unboxed_use = FlowsIntoUnboxedUse(phi);
|
||||
|
||||
if (has_unboxed_incoming_value && flows_into_unboxed_use) {
|
||||
new_representation =
|
||||
RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32)
|
||||
? kUnboxedInt32
|
||||
: kUnboxedInt64;
|
||||
new_representation = Range::Fits(phi->range(), kUnboxedInt32)
|
||||
? kUnboxedInt32
|
||||
: kUnboxedInt64;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3182,7 +3182,8 @@ Definition* UnboxLaneInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
|
||||
bool BoxIntegerInstr::ValueFitsSmi() const {
|
||||
Range* range = value()->definition()->range();
|
||||
return RangeUtils::Fits(range, RangeBoundary::kRangeBoundarySmi);
|
||||
return RangeUtils::IsWithin(range, compiler::target::kSmiMin,
|
||||
compiler::target::kSmiMax);
|
||||
}
|
||||
|
||||
Definition* BoxIntegerInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
|
||||
@@ -59,27 +59,6 @@ static void CheckRangeForRepresentation(const Assert& assert,
|
||||
} while (false)
|
||||
#endif
|
||||
|
||||
static RangeBoundary::RangeSize RepresentationToRangeSize(Representation r) {
|
||||
switch (r) {
|
||||
case kTagged:
|
||||
return RangeBoundary::kRangeBoundarySmi;
|
||||
case kUnboxedInt8:
|
||||
return RangeBoundary::kRangeBoundaryInt8;
|
||||
case kUnboxedInt16:
|
||||
case kUnboxedUint8: // Overapproximate Uint8 as Int16.
|
||||
return RangeBoundary::kRangeBoundaryInt16;
|
||||
case kUnboxedInt32:
|
||||
case kUnboxedUint16: // Overapproximate Uint16 as Int32.
|
||||
return RangeBoundary::kRangeBoundaryInt32;
|
||||
case kUnboxedInt64:
|
||||
case kUnboxedUint32: // Overapproximate Uint32 as Int64.
|
||||
return RangeBoundary::kRangeBoundaryInt64;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return RangeBoundary::kRangeBoundarySmi;
|
||||
}
|
||||
}
|
||||
|
||||
void RangeAnalysis::Analyze() {
|
||||
CollectValues();
|
||||
InsertConstraints();
|
||||
@@ -181,25 +160,25 @@ void RangeAnalysis::CollectValues() {
|
||||
// [min, b - 1].
|
||||
Range* RangeAnalysis::ConstraintRange(Token::Kind op,
|
||||
Definition* boundary,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
switch (op) {
|
||||
case Token::kEQ:
|
||||
return new (Z) Range(RangeBoundary::FromDefinition(boundary),
|
||||
RangeBoundary::FromDefinition(boundary));
|
||||
case Token::kNE:
|
||||
return new (Z) Range(Range::Full(size));
|
||||
return new (Z) Range(full_range);
|
||||
case Token::kLT:
|
||||
return new (Z) Range(RangeBoundary::MinConstant(size),
|
||||
RangeBoundary::FromDefinition(boundary, -1));
|
||||
return new (Z)
|
||||
Range(full_range.min(), RangeBoundary::FromDefinition(boundary, -1));
|
||||
case Token::kGT:
|
||||
return new (Z) Range(RangeBoundary::FromDefinition(boundary, 1),
|
||||
RangeBoundary::MaxConstant(size));
|
||||
return new (Z)
|
||||
Range(RangeBoundary::FromDefinition(boundary, 1), full_range.max());
|
||||
case Token::kLTE:
|
||||
return new (Z) Range(RangeBoundary::MinConstant(size),
|
||||
RangeBoundary::FromDefinition(boundary));
|
||||
return new (Z)
|
||||
Range(full_range.min(), RangeBoundary::FromDefinition(boundary));
|
||||
case Token::kGTE:
|
||||
return new (Z) Range(RangeBoundary::FromDefinition(boundary),
|
||||
RangeBoundary::MaxConstant(size));
|
||||
return new (Z)
|
||||
Range(RangeBoundary::FromDefinition(boundary), full_range.max());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
@@ -251,23 +230,23 @@ bool RangeAnalysis::ConstrainValueAfterBranch(Value* use, Definition* defn) {
|
||||
// comparison if it is right operand flip the comparison.
|
||||
op_kind = Token::FlipComparison(rel_op->kind());
|
||||
}
|
||||
RangeBoundary::RangeSize size;
|
||||
if (rel_op->input_representation() == kTagged) {
|
||||
size = RangeBoundary::kRangeBoundarySmi;
|
||||
const Representation representation = rel_op->input_representation();
|
||||
Range full_range;
|
||||
if (representation == kTagged) {
|
||||
full_range = Range::Smi();
|
||||
} else {
|
||||
ASSERT(RepresentationUtils::IsUnboxedInteger(
|
||||
rel_op->input_representation()));
|
||||
ASSERT(RepresentationUtils::IsUnboxedInteger(representation));
|
||||
// Can only create symbolic boundaries based on Smi values.
|
||||
if (!Definition::IsLengthLoad(boundary)) {
|
||||
return false;
|
||||
}
|
||||
size = RepresentationToRangeSize(rel_op->input_representation());
|
||||
full_range = Range::Full(representation);
|
||||
}
|
||||
|
||||
// Constrain definition at the true successor.
|
||||
ConstraintInstr* true_constraint =
|
||||
InsertConstraintFor(use, defn, ConstraintRange(op_kind, boundary, size),
|
||||
branch->true_successor());
|
||||
ConstraintInstr* true_constraint = InsertConstraintFor(
|
||||
use, defn, ConstraintRange(op_kind, boundary, full_range),
|
||||
branch->true_successor());
|
||||
if (true_constraint != nullptr) {
|
||||
true_constraint->set_target(branch->true_successor());
|
||||
}
|
||||
@@ -275,7 +254,7 @@ bool RangeAnalysis::ConstrainValueAfterBranch(Value* use, Definition* defn) {
|
||||
// Constrain definition with a negated condition at the false successor.
|
||||
ConstraintInstr* false_constraint = InsertConstraintFor(
|
||||
use, defn,
|
||||
ConstraintRange(Token::NegateComparison(op_kind), boundary, size),
|
||||
ConstraintRange(Token::NegateComparison(op_kind), boundary, full_range),
|
||||
branch->false_successor());
|
||||
if (false_constraint != nullptr) {
|
||||
false_constraint->set_target(branch->false_successor());
|
||||
@@ -332,44 +311,6 @@ void RangeAnalysis::InsertConstraints() {
|
||||
}
|
||||
}
|
||||
|
||||
const Range* RangeAnalysis::GetSmiRange(Value* value) const {
|
||||
Definition* defn = value->definition();
|
||||
const Range* range = defn->range();
|
||||
|
||||
if ((range == nullptr) && (defn->Type()->ToCid() != kSmiCid)) {
|
||||
// Type propagator determined that reaching type for this use is Smi.
|
||||
// However the definition itself is not a smi-definition and
|
||||
// thus it will never have range assigned to it. Just return the widest
|
||||
// range possible for this value.
|
||||
// We don't need to handle kMintCid here because all external mints
|
||||
// (e.g. results of loads or function call) can be used only after they
|
||||
// pass through UnboxInt64Instr which is considered as mint-definition
|
||||
// and will have a range assigned to it.
|
||||
// Note: that we can't return nullptr here because it is used as lattice's
|
||||
// bottom element to indicate that the range was not computed *yet*.
|
||||
return &smi_range_;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
const Range* RangeAnalysis::GetIntRange(Value* value) const {
|
||||
Definition* defn = value->definition();
|
||||
const Range* range = defn->range();
|
||||
|
||||
if ((range == nullptr) && !defn->Type()->IsInt()) {
|
||||
// Type propagator determined that reaching type for this use is int.
|
||||
// However the definition itself is not a int-definition and
|
||||
// thus it will never have range assigned to it. Just return the widest
|
||||
// range possible for this value.
|
||||
// Note: that we can't return nullptr here because it is used as lattice's
|
||||
// bottom element to indicate that the range was not computed *yet*.
|
||||
return &int64_range_;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
static bool AreEqualDefinitions(Definition* a, Definition* b) {
|
||||
a = UnwrapConstraint(a);
|
||||
b = UnwrapConstraint(b);
|
||||
@@ -386,28 +327,25 @@ static bool DependOnSameSymbol(const RangeBoundary& a, const RangeBoundary& b) {
|
||||
// MinSmi.
|
||||
static RangeBoundary WidenMin(const Range* range,
|
||||
const Range* new_range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
RangeBoundary min = range->min();
|
||||
RangeBoundary new_min = new_range->min();
|
||||
|
||||
if (min.IsSymbol()) {
|
||||
if (min.LowerBound().Overflowed(size)) {
|
||||
return RangeBoundary::MinConstant(size);
|
||||
if (min.LowerBound().Overflowed(full_range)) {
|
||||
return full_range.min();
|
||||
} else if (DependOnSameSymbol(min, new_min)) {
|
||||
return min.offset() <= new_min.offset()
|
||||
? min
|
||||
: RangeBoundary::MinConstant(size);
|
||||
} else if (min.UpperBound(size) <= new_min.LowerBound(size)) {
|
||||
return min.offset() <= new_min.offset() ? min : full_range.min();
|
||||
} else if (min.UpperBound(full_range) <= new_min.LowerBound(full_range)) {
|
||||
return min;
|
||||
}
|
||||
}
|
||||
|
||||
min = Range::ConstantMin(range, size);
|
||||
new_min = Range::ConstantMin(new_range, size);
|
||||
min = Range::ConstantMin(range, full_range);
|
||||
new_min = Range::ConstantMin(new_range, full_range);
|
||||
|
||||
return (min.ConstantValue() <= new_min.ConstantValue())
|
||||
? min
|
||||
: RangeBoundary::MinConstant(size);
|
||||
return (min.ConstantValue() <= new_min.ConstantValue()) ? min
|
||||
: full_range.min();
|
||||
}
|
||||
|
||||
// Given the current range of a phi and a newly computed range check
|
||||
@@ -415,28 +353,25 @@ static RangeBoundary WidenMin(const Range* range,
|
||||
// MaxSmi.
|
||||
static RangeBoundary WidenMax(const Range* range,
|
||||
const Range* new_range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
RangeBoundary max = range->max();
|
||||
RangeBoundary new_max = new_range->max();
|
||||
|
||||
if (max.IsSymbol()) {
|
||||
if (max.UpperBound().Overflowed(size)) {
|
||||
return RangeBoundary::MaxConstant(size);
|
||||
if (max.UpperBound().Overflowed(full_range)) {
|
||||
return full_range.max();
|
||||
} else if (DependOnSameSymbol(max, new_max)) {
|
||||
return max.offset() >= new_max.offset()
|
||||
? max
|
||||
: RangeBoundary::MaxConstant(size);
|
||||
} else if (max.LowerBound(size) >= new_max.UpperBound(size)) {
|
||||
return max.offset() >= new_max.offset() ? max : full_range.max();
|
||||
} else if (max.LowerBound(full_range) >= new_max.UpperBound(full_range)) {
|
||||
return max;
|
||||
}
|
||||
}
|
||||
|
||||
max = Range::ConstantMax(range, size);
|
||||
new_max = Range::ConstantMax(new_range, size);
|
||||
max = Range::ConstantMax(range, full_range);
|
||||
new_max = Range::ConstantMax(new_range, full_range);
|
||||
|
||||
return (max.ConstantValue() >= new_max.ConstantValue())
|
||||
? max
|
||||
: RangeBoundary::MaxConstant(size);
|
||||
return (max.ConstantValue() >= new_max.ConstantValue()) ? max
|
||||
: full_range.max();
|
||||
}
|
||||
|
||||
// Given the current range of a phi and a newly computed range check
|
||||
@@ -447,13 +382,14 @@ static RangeBoundary WidenMax(const Range* range,
|
||||
// we are running after widening phase.
|
||||
static RangeBoundary NarrowMin(const Range* range,
|
||||
const Range* new_range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const RangeBoundary min = Range::ConstantMin(range, size);
|
||||
const RangeBoundary new_min = Range::ConstantMin(new_range, size);
|
||||
const Range& full_range) {
|
||||
const RangeBoundary min = Range::ConstantMin(range, full_range);
|
||||
const RangeBoundary new_min = Range::ConstantMin(new_range, full_range);
|
||||
if (min.ConstantValue() > new_min.ConstantValue()) return range->min();
|
||||
|
||||
// TODO(vegorov): consider using negative infinity to indicate widened bound.
|
||||
return range->min().IsMinimumOrBelow(size) ? new_range->min() : range->min();
|
||||
return range->min().IsLessOrEqual(full_range.min()) ? new_range->min()
|
||||
: range->min();
|
||||
}
|
||||
|
||||
// Given the current range of a phi and a newly computed range check
|
||||
@@ -464,13 +400,14 @@ static RangeBoundary NarrowMin(const Range* range,
|
||||
// we are running after widening phase.
|
||||
static RangeBoundary NarrowMax(const Range* range,
|
||||
const Range* new_range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const RangeBoundary max = Range::ConstantMax(range, size);
|
||||
const RangeBoundary new_max = Range::ConstantMax(new_range, size);
|
||||
const Range& full_range) {
|
||||
const RangeBoundary max = Range::ConstantMax(range, full_range);
|
||||
const RangeBoundary new_max = Range::ConstantMax(new_range, full_range);
|
||||
if (max.ConstantValue() < new_max.ConstantValue()) return range->max();
|
||||
|
||||
// TODO(vegorov): consider using positive infinity to indicate widened bound.
|
||||
return range->max().IsMaximumOrAbove(size) ? new_range->max() : range->max();
|
||||
return range->max().IsGreaterOrEqual(full_range.max()) ? new_range->max()
|
||||
: range->max();
|
||||
}
|
||||
|
||||
char RangeAnalysis::OpPrefix(JoinOperator op) {
|
||||
@@ -486,17 +423,20 @@ char RangeAnalysis::OpPrefix(JoinOperator op) {
|
||||
return ' ';
|
||||
}
|
||||
|
||||
static RangeBoundary::RangeSize RangeSizeForPhi(Definition* phi) {
|
||||
static Range FullRangeForPhi(Definition* phi) {
|
||||
ASSERT(phi->IsPhi());
|
||||
if (phi->Type()->ToCid() == kSmiCid) {
|
||||
return RangeBoundary::kRangeBoundarySmi;
|
||||
} else if (phi->representation() == kUnboxedInt32) {
|
||||
return RangeBoundary::kRangeBoundaryInt32;
|
||||
} else if (phi->Type()->IsInt()) {
|
||||
return RangeBoundary::kRangeBoundaryInt64;
|
||||
return Range::Smi();
|
||||
}
|
||||
const Representation rep = phi->representation();
|
||||
if (RepresentationUtils::IsUnboxedInteger(rep)) {
|
||||
return Range::Full(rep);
|
||||
}
|
||||
ASSERT(rep == kTagged);
|
||||
if (phi->Type()->IsInt()) {
|
||||
return Range::Int64();
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return RangeBoundary::kRangeBoundaryInt64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,13 +448,13 @@ bool RangeAnalysis::InferRange(JoinOperator op,
|
||||
|
||||
if (!Range::IsUnknown(&range)) {
|
||||
if (!Range::IsUnknown(defn->range()) && defn->IsPhi()) {
|
||||
const RangeBoundary::RangeSize size = RangeSizeForPhi(defn);
|
||||
const Range full_range = FullRangeForPhi(defn);
|
||||
if (op == WIDEN) {
|
||||
range = Range(WidenMin(defn->range(), &range, size),
|
||||
WidenMax(defn->range(), &range, size));
|
||||
range = Range(WidenMin(defn->range(), &range, full_range),
|
||||
WidenMax(defn->range(), &range, full_range));
|
||||
} else if (op == NARROW) {
|
||||
range = Range(NarrowMin(defn->range(), &range, size),
|
||||
NarrowMax(defn->range(), &range, size));
|
||||
range = Range(NarrowMin(defn->range(), &range, full_range),
|
||||
NarrowMax(defn->range(), &range, full_range));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,8 +782,7 @@ class BoundsCheckGeneralizer {
|
||||
GrowableArray<ConstraintInstr*> positive_constraints(
|
||||
non_positive_symbols.length());
|
||||
Range* positive_range =
|
||||
new Range(RangeBoundary::FromConstant(0),
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundarySmi));
|
||||
new Range(RangeBoundary::FromConstant(0), RangeBoundary::MaxSmi());
|
||||
for (intptr_t i = 0; i < non_positive_symbols.length(); i++) {
|
||||
Definition* symbol = non_positive_symbols[i];
|
||||
positive_constraints.Add(new ConstraintInstr(
|
||||
@@ -1451,11 +1390,9 @@ void RangeAnalysis::RemoveConstraints() {
|
||||
}
|
||||
|
||||
static void NarrowBinaryInt64Op(BinaryInt64OpInstr* int64_op) {
|
||||
if (RangeUtils::Fits(int64_op->range(), RangeBoundary::kRangeBoundaryInt32) &&
|
||||
RangeUtils::Fits(int64_op->left()->definition()->range(),
|
||||
RangeBoundary::kRangeBoundaryInt32) &&
|
||||
RangeUtils::Fits(int64_op->right()->definition()->range(),
|
||||
RangeBoundary::kRangeBoundaryInt32) &&
|
||||
if (Range::Fits(int64_op->range(), kUnboxedInt32) &&
|
||||
Range::Fits(int64_op->left()->definition()->range(), kUnboxedInt32) &&
|
||||
Range::Fits(int64_op->right()->definition()->range(), kUnboxedInt32) &&
|
||||
BinaryInt32OpInstr::IsSupported(int64_op->op_kind(), int64_op->left(),
|
||||
int64_op->right())) {
|
||||
BinaryInt32OpInstr* int32_op = new BinaryInt32OpInstr(
|
||||
@@ -1469,10 +1406,8 @@ static void NarrowBinaryInt64Op(BinaryInt64OpInstr* int64_op) {
|
||||
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
static void NarrowInt64ComparisonInstr(ComparisonInstr* int64_op) {
|
||||
if (RangeUtils::Fits(int64_op->left()->definition()->range(),
|
||||
RangeBoundary::kRangeBoundaryInt32) &&
|
||||
RangeUtils::Fits(int64_op->right()->definition()->range(),
|
||||
RangeBoundary::kRangeBoundaryInt32)) {
|
||||
if (Range::Fits(int64_op->left()->definition()->range(), kUnboxedInt32) &&
|
||||
Range::Fits(int64_op->right()->definition()->range(), kUnboxedInt32)) {
|
||||
int64_op->set_input_representation(kUnboxedInt32);
|
||||
}
|
||||
}
|
||||
@@ -1985,20 +1920,20 @@ static bool CanonicalizeMaxBoundary(RangeBoundary* a) {
|
||||
if ((range == nullptr) || !range->max().IsSymbol()) return false;
|
||||
|
||||
if (Utils::WillAddOverflow(range->max().offset(), a->offset())) {
|
||||
*a = RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*a = RangeBoundary::MaxInt64();
|
||||
return true;
|
||||
}
|
||||
|
||||
const int64_t offset = range->max().offset() + a->offset();
|
||||
|
||||
if (!RangeBoundary::IsValidOffsetForSymbolicRangeBoundary(offset)) {
|
||||
*a = RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*a = RangeBoundary::MaxInt64();
|
||||
return true;
|
||||
}
|
||||
|
||||
*a = CanonicalizeBoundary(
|
||||
RangeBoundary::FromDefinition(range->max().symbol(), offset),
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64));
|
||||
RangeBoundary::MaxInt64());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2010,20 +1945,20 @@ static bool CanonicalizeMinBoundary(RangeBoundary* a) {
|
||||
if ((range == nullptr) || !range->min().IsSymbol()) return false;
|
||||
|
||||
if (Utils::WillAddOverflow(range->min().offset(), a->offset())) {
|
||||
*a = RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*a = RangeBoundary::MinInt64();
|
||||
return true;
|
||||
}
|
||||
|
||||
const int64_t offset = range->min().offset() + a->offset();
|
||||
|
||||
if (!RangeBoundary::IsValidOffsetForSymbolicRangeBoundary(offset)) {
|
||||
*a = RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*a = RangeBoundary::MinInt64();
|
||||
return true;
|
||||
}
|
||||
|
||||
*a = CanonicalizeBoundary(
|
||||
RangeBoundary::FromDefinition(range->min().symbol(), offset),
|
||||
RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64));
|
||||
RangeBoundary::MinInt64());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2032,8 +1967,7 @@ typedef bool (*BoundaryOp)(RangeBoundary*);
|
||||
|
||||
static bool CanonicalizeForComparison(RangeBoundary* a,
|
||||
RangeBoundary* b,
|
||||
BoundaryOp op,
|
||||
const RangeBoundary& overflow) {
|
||||
BoundaryOp op) {
|
||||
if (!a->IsSymbol() || !b->IsSymbol()) {
|
||||
return false;
|
||||
}
|
||||
@@ -2054,24 +1988,23 @@ static bool CanonicalizeForComparison(RangeBoundary* a,
|
||||
|
||||
RangeBoundary RangeBoundary::JoinMin(RangeBoundary a,
|
||||
RangeBoundary b,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
if (a.Equals(b)) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (CanonicalizeForComparison(&a, &b, &CanonicalizeMinBoundary,
|
||||
RangeBoundary::MinConstant(size))) {
|
||||
if (CanonicalizeForComparison(&a, &b, &CanonicalizeMinBoundary)) {
|
||||
return (a.offset() <= b.offset()) ? a : b;
|
||||
}
|
||||
|
||||
const int64_t inf_a = a.LowerBound(size);
|
||||
const int64_t inf_b = b.LowerBound(size);
|
||||
const int64_t sup_a = a.UpperBound(size);
|
||||
const int64_t sup_b = b.UpperBound(size);
|
||||
const int64_t inf_a = a.LowerBound(full_range);
|
||||
const int64_t inf_b = b.LowerBound(full_range);
|
||||
const int64_t sup_a = a.UpperBound(full_range);
|
||||
const int64_t sup_b = b.UpperBound(full_range);
|
||||
|
||||
if ((sup_a <= inf_b) && !a.LowerBound().Overflowed(size)) {
|
||||
if ((sup_a <= inf_b) && !a.LowerBound().Overflowed(full_range)) {
|
||||
return a;
|
||||
} else if ((sup_b <= inf_a) && !b.LowerBound().Overflowed(size)) {
|
||||
} else if ((sup_b <= inf_a) && !b.LowerBound().Overflowed(full_range)) {
|
||||
return b;
|
||||
} else {
|
||||
return RangeBoundary::FromConstant(Utils::Minimum(inf_a, inf_b));
|
||||
@@ -2080,25 +2013,23 @@ RangeBoundary RangeBoundary::JoinMin(RangeBoundary a,
|
||||
|
||||
RangeBoundary RangeBoundary::JoinMax(RangeBoundary a,
|
||||
RangeBoundary b,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
if (a.Equals(b)) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (CanonicalizeForComparison(
|
||||
&a, &b, &CanonicalizeMaxBoundary,
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64))) {
|
||||
if (CanonicalizeForComparison(&a, &b, &CanonicalizeMaxBoundary)) {
|
||||
return (a.offset() >= b.offset()) ? a : b;
|
||||
}
|
||||
|
||||
const int64_t inf_a = a.LowerBound(size);
|
||||
const int64_t inf_b = b.LowerBound(size);
|
||||
const int64_t sup_a = a.UpperBound(size);
|
||||
const int64_t sup_b = b.UpperBound(size);
|
||||
const int64_t inf_a = a.LowerBound(full_range);
|
||||
const int64_t inf_b = b.LowerBound(full_range);
|
||||
const int64_t sup_a = a.UpperBound(full_range);
|
||||
const int64_t sup_b = b.UpperBound(full_range);
|
||||
|
||||
if ((sup_a <= inf_b) && !b.UpperBound().Overflowed(size)) {
|
||||
if ((sup_a <= inf_b) && !b.UpperBound().Overflowed(full_range)) {
|
||||
return b;
|
||||
} else if ((sup_b <= inf_a) && !a.UpperBound().Overflowed(size)) {
|
||||
} else if ((sup_b <= inf_a) && !a.UpperBound().Overflowed(full_range)) {
|
||||
return a;
|
||||
} else {
|
||||
return RangeBoundary::FromConstant(Utils::Maximum(sup_a, sup_b));
|
||||
@@ -2116,20 +2047,12 @@ RangeBoundary RangeBoundary::IntersectionMin(RangeBoundary a, RangeBoundary b) {
|
||||
return RangeBoundary(Utils::Maximum(a.ConstantValue(), b.ConstantValue()));
|
||||
}
|
||||
|
||||
if (a.IsMinimumOrBelow(RangeBoundary::kRangeBoundarySmi)) {
|
||||
return b;
|
||||
} else if (b.IsMinimumOrBelow(RangeBoundary::kRangeBoundarySmi)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
if (CanonicalizeForComparison(
|
||||
&a, &b, &CanonicalizeMinBoundary,
|
||||
RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64))) {
|
||||
if (CanonicalizeForComparison(&a, &b, &CanonicalizeMinBoundary)) {
|
||||
return (a.offset() >= b.offset()) ? a : b;
|
||||
}
|
||||
|
||||
const int64_t inf_a = a.SmiLowerBound();
|
||||
const int64_t inf_b = b.SmiLowerBound();
|
||||
const int64_t inf_a = a.LowerBound().ConstantValue();
|
||||
const int64_t inf_b = b.LowerBound().ConstantValue();
|
||||
|
||||
return (inf_a >= inf_b) ? a : b;
|
||||
}
|
||||
@@ -2145,30 +2068,48 @@ RangeBoundary RangeBoundary::IntersectionMax(RangeBoundary a, RangeBoundary b) {
|
||||
return RangeBoundary(Utils::Minimum(a.ConstantValue(), b.ConstantValue()));
|
||||
}
|
||||
|
||||
if (a.IsMaximumOrAbove(RangeBoundary::kRangeBoundarySmi)) {
|
||||
return b;
|
||||
} else if (b.IsMaximumOrAbove(RangeBoundary::kRangeBoundarySmi)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
if (CanonicalizeForComparison(
|
||||
&a, &b, &CanonicalizeMaxBoundary,
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64))) {
|
||||
if (CanonicalizeForComparison(&a, &b, &CanonicalizeMaxBoundary)) {
|
||||
return (a.offset() <= b.offset()) ? a : b;
|
||||
}
|
||||
|
||||
const int64_t sup_a = a.SmiUpperBound();
|
||||
const int64_t sup_b = b.SmiUpperBound();
|
||||
const int64_t sup_a = a.UpperBound().ConstantValue();
|
||||
const int64_t sup_b = b.UpperBound().ConstantValue();
|
||||
|
||||
return (sup_a <= sup_b) ? a : b;
|
||||
}
|
||||
|
||||
RangeBoundary RangeBoundary::Clamp(const Range& full_range) const {
|
||||
if (IsConstant()) {
|
||||
const RangeBoundary range_min = full_range.min();
|
||||
const RangeBoundary range_max = full_range.max();
|
||||
|
||||
if (ConstantValue() <= range_min.ConstantValue()) {
|
||||
return range_min;
|
||||
}
|
||||
if (ConstantValue() >= range_max.ConstantValue()) {
|
||||
return range_max;
|
||||
}
|
||||
}
|
||||
|
||||
// If this range is a symbolic range, we do not clamp it.
|
||||
// This could lead to some imprecision later on.
|
||||
return *this;
|
||||
}
|
||||
|
||||
int64_t RangeBoundary::ConstantValue() const {
|
||||
ASSERT(IsConstant());
|
||||
return value_;
|
||||
}
|
||||
|
||||
Range Range::Full(Representation rep) {
|
||||
Range Range::Full(Representation rep, TaggedMode tagged_mode) {
|
||||
if (rep == kTagged) {
|
||||
switch (tagged_mode) {
|
||||
case TaggedMode::kTaggedIsSmi:
|
||||
return Range::Smi();
|
||||
case TaggedMode::kTaggedNotAllowed:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
ASSERT(RepresentationUtils::IsUnboxedInteger(rep));
|
||||
return Range(RangeBoundary::FromConstant(RepresentationUtils::MinValue(rep)),
|
||||
RangeBoundary::FromConstant(RepresentationUtils::MaxValue(rep)));
|
||||
@@ -2224,14 +2165,14 @@ bool Range::IsUnsatisfiable() const {
|
||||
return DependOnSameSymbol(min(), max()) && min().offset() > max().offset();
|
||||
}
|
||||
|
||||
void Range::Clamp(RangeBoundary::RangeSize size) {
|
||||
min_ = min_.Clamp(size);
|
||||
max_ = max_.Clamp(size);
|
||||
void Range::Clamp(const Range& full_range) {
|
||||
min_ = min_.Clamp(full_range);
|
||||
max_ = max_.Clamp(full_range);
|
||||
}
|
||||
|
||||
void Range::ClampToConstant(RangeBoundary::RangeSize size) {
|
||||
min_ = min_.LowerBound().Clamp(size);
|
||||
max_ = max_.UpperBound().Clamp(size);
|
||||
void Range::ClampToConstant(const Range& full_range) {
|
||||
min_ = min_.LowerBound().Clamp(full_range);
|
||||
max_ = max_.UpperBound().Clamp(full_range);
|
||||
}
|
||||
|
||||
void Range::Shl(const Range* left,
|
||||
@@ -2270,10 +2211,8 @@ void Range::Shl(const Range* left,
|
||||
}
|
||||
}
|
||||
if (overflow) {
|
||||
*result_min =
|
||||
RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_max =
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_min = RangeBoundary::MinInt64();
|
||||
*result_max = RangeBoundary::MaxInt64();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2451,10 +2390,8 @@ void Range::Add(const Range* left_range,
|
||||
}
|
||||
}
|
||||
if (overflow) {
|
||||
*result_min =
|
||||
RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_max =
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_min = RangeBoundary::MinInt64();
|
||||
*result_max = RangeBoundary::MaxInt64();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2496,10 +2433,8 @@ void Range::Sub(const Range* left_range,
|
||||
}
|
||||
}
|
||||
if (overflow) {
|
||||
*result_min =
|
||||
RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_max =
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_min = RangeBoundary::MinInt64();
|
||||
*result_max = RangeBoundary::MaxInt64();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2538,8 +2473,8 @@ void Range::Mul(const Range* left_range,
|
||||
return;
|
||||
}
|
||||
|
||||
*result_min = RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_max = RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_min = RangeBoundary::MinInt64();
|
||||
*result_max = RangeBoundary::MaxInt64();
|
||||
}
|
||||
|
||||
void Range::TruncDiv(const Range* left_range,
|
||||
@@ -2563,8 +2498,8 @@ void Range::TruncDiv(const Range* left_range,
|
||||
return;
|
||||
}
|
||||
|
||||
*result_min = RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_max = RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64);
|
||||
*result_min = RangeBoundary::MinInt64();
|
||||
*result_max = RangeBoundary::MaxInt64();
|
||||
}
|
||||
|
||||
void Range::Mod(const Range* right_range,
|
||||
@@ -2600,7 +2535,7 @@ bool Range::OnlyNegativeOrZero(const Range& a, const Range& b) {
|
||||
// Return the maximum absolute value included in range.
|
||||
int64_t Range::ConstantAbsMax(const Range* range) {
|
||||
if (range == nullptr) {
|
||||
return RangeBoundary::kMax;
|
||||
return kMaxInt64;
|
||||
}
|
||||
const int64_t abs_min =
|
||||
Utils::AbsWithSaturation(Range::ConstantMin(range).ConstantValue());
|
||||
@@ -2676,9 +2611,7 @@ void Range::BinaryOp(const Token::Kind op,
|
||||
break;
|
||||
|
||||
default:
|
||||
*result =
|
||||
Range(RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64),
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64));
|
||||
*result = Range::Int64();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2700,13 +2633,13 @@ void Definition::set_range(const Range& range) {
|
||||
|
||||
void Definition::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
if (Type()->ToCid() == kSmiCid) {
|
||||
*range = Range::Full(RangeBoundary::kRangeBoundarySmi);
|
||||
*range = Range::Smi();
|
||||
} else if (IsInt64Definition()) {
|
||||
*range = Range::Full(RangeBoundary::kRangeBoundaryInt64);
|
||||
*range = Range::Int64();
|
||||
} else if (IsInt32Definition()) {
|
||||
*range = Range::Full(RangeBoundary::kRangeBoundaryInt32);
|
||||
*range = Range::Full(kUnboxedInt32);
|
||||
} else if (Type()->IsInt()) {
|
||||
*range = Range::Full(RangeBoundary::kRangeBoundaryInt64);
|
||||
*range = Range::Int64();
|
||||
} else {
|
||||
// Only Smi and Mint supported.
|
||||
FATAL("Unsupported type in: %s", ToCString());
|
||||
@@ -2733,7 +2666,7 @@ static bool DependsOnSymbol(const RangeBoundary& a, Definition* symbol) {
|
||||
static void Join(Range* range,
|
||||
Definition* defn,
|
||||
const Range* defn_range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
if (Range::IsUnknown(defn_range)) {
|
||||
return;
|
||||
}
|
||||
@@ -2760,10 +2693,10 @@ static void Join(Range* range,
|
||||
}
|
||||
|
||||
// First try to compare ranges based on their upper and lower bounds.
|
||||
const int64_t inf_range = range->min().LowerBound(size);
|
||||
const int64_t inf_other = other.min().LowerBound(size);
|
||||
const int64_t sup_range = range->max().UpperBound(size);
|
||||
const int64_t sup_other = other.max().UpperBound(size);
|
||||
const int64_t inf_range = range->min().LowerBound(full_range);
|
||||
const int64_t inf_other = other.min().LowerBound(full_range);
|
||||
const int64_t sup_range = range->max().UpperBound(full_range);
|
||||
const int64_t sup_other = other.max().UpperBound(full_range);
|
||||
|
||||
if (sup_range <= inf_other) {
|
||||
// The range is fully below defn's range. Keep the minimum and
|
||||
@@ -2775,8 +2708,9 @@ static void Join(Range* range,
|
||||
range->set_min(other.min());
|
||||
} else {
|
||||
// Can't compare ranges as whole. Join minimum and maximum separately.
|
||||
*range = Range(RangeBoundary::JoinMin(range->min(), other.min(), size),
|
||||
RangeBoundary::JoinMax(range->max(), other.max(), size));
|
||||
*range =
|
||||
Range(RangeBoundary::JoinMin(range->min(), other.min(), full_range),
|
||||
RangeBoundary::JoinMax(range->max(), other.max(), full_range));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2808,30 +2742,29 @@ static RangeBoundary EnsureAcyclicSymbol(BlockEntryInstr* phi_block,
|
||||
return limit;
|
||||
}
|
||||
|
||||
static const Range* GetInputRange(RangeAnalysis* analysis,
|
||||
RangeBoundary::RangeSize size,
|
||||
Value* input) {
|
||||
switch (size) {
|
||||
case RangeBoundary::kRangeBoundarySmi:
|
||||
return analysis->GetSmiRange(input);
|
||||
case RangeBoundary::kRangeBoundaryInt8:
|
||||
case RangeBoundary::kRangeBoundaryInt16:
|
||||
case RangeBoundary::kRangeBoundaryInt32:
|
||||
return input->definition()->range();
|
||||
case RangeBoundary::kRangeBoundaryInt64:
|
||||
return analysis->GetIntRange(input);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
static const Range* GetInputRange(Value* input, const Range* full_range) {
|
||||
Definition* defn = input->definition();
|
||||
const Range* range = defn->range();
|
||||
|
||||
if ((range == nullptr) && !RangeAnalysis::IsIntegerDefinition(defn)) {
|
||||
// Type propagator determined that reaching type for this use is int.
|
||||
// However the definition itself is not a int-definition and
|
||||
// thus it will never have range assigned to it. Just return the widest
|
||||
// range possible for this value.
|
||||
// Note: that we can't return nullptr here because it is used as lattice's
|
||||
// bottom element to indicate that the range was not computed *yet*.
|
||||
return full_range;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
void PhiInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
const RangeBoundary::RangeSize size = RangeSizeForPhi(this);
|
||||
const Range full_range = FullRangeForPhi(this);
|
||||
for (intptr_t i = 0; i < InputCount(); i++) {
|
||||
Value* input = InputAt(i);
|
||||
Join(range, input->definition(), GetInputRange(analysis, size, input),
|
||||
size);
|
||||
Join(range, input->definition(), GetInputRange(input, &full_range),
|
||||
full_range);
|
||||
}
|
||||
|
||||
BlockEntryInstr* phi_block = GetBlock();
|
||||
@@ -2853,8 +2786,9 @@ void ConstantInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
}
|
||||
|
||||
void ConstraintInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
const Range* value_range = GetInputRange(
|
||||
analysis, RepresentationToRangeSize(representation_), value());
|
||||
const Range full_range =
|
||||
Range::Full(representation_, TaggedMode::kTaggedIsSmi);
|
||||
const Range* value_range = GetInputRange(value(), &full_range);
|
||||
if (Range::IsUnknown(value_range)) {
|
||||
return;
|
||||
}
|
||||
@@ -2889,7 +2823,7 @@ void LoadFieldInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
|
||||
case Slot::Kind::kAbstractType_hash:
|
||||
case Slot::Kind::kTypeArguments_hash:
|
||||
*range = Range(RangeBoundary::MinSmi(), RangeBoundary::MaxSmi());
|
||||
*range = Range::Smi();
|
||||
break;
|
||||
|
||||
case Slot::Kind::kTypeArguments_length:
|
||||
@@ -3055,21 +2989,19 @@ void BinaryIntegerOpInstr::InferRangeHelper(const Range* left_range,
|
||||
range);
|
||||
ASSERT(!Range::IsUnknown(range));
|
||||
|
||||
const RangeBoundary::RangeSize range_size =
|
||||
RepresentationToRangeSize(representation());
|
||||
|
||||
// Calculate overflowed status before clamping if operation is
|
||||
// not truncating.
|
||||
if (!is_truncating()) {
|
||||
set_can_overflow(!range->Fits(range_size));
|
||||
set_can_overflow(
|
||||
!Range::Fits(range, representation(), TaggedMode::kTaggedIsSmi));
|
||||
}
|
||||
|
||||
range->Clamp(range_size);
|
||||
range->Clamp(Range::Full(representation(), TaggedMode::kTaggedIsSmi));
|
||||
}
|
||||
|
||||
static void CacheRange(Range** slot,
|
||||
const Range* range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
if (range != nullptr) {
|
||||
if (*slot == nullptr) {
|
||||
*slot = new Range();
|
||||
@@ -3077,31 +3009,30 @@ static void CacheRange(Range** slot,
|
||||
**slot = *range;
|
||||
|
||||
// Eliminate any symbolic dependencies from the range information.
|
||||
(*slot)->ClampToConstant(size);
|
||||
(*slot)->ClampToConstant(full_range);
|
||||
} else if (*slot != nullptr) {
|
||||
**slot = Range(); // Clear cached range information.
|
||||
}
|
||||
}
|
||||
|
||||
void BinaryIntegerOpInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
auto const left_size =
|
||||
RepresentationToRangeSize(RequiredInputRepresentation(0));
|
||||
auto const right_size =
|
||||
RepresentationToRangeSize(RequiredInputRepresentation(1));
|
||||
InferRangeHelper(GetInputRange(analysis, left_size, left()),
|
||||
GetInputRange(analysis, right_size, right()), range);
|
||||
const Range left_range =
|
||||
Range::Full(RequiredInputRepresentation(0), TaggedMode::kTaggedIsSmi);
|
||||
const Range right_range =
|
||||
Range::Full(RequiredInputRepresentation(1), TaggedMode::kTaggedIsSmi);
|
||||
InferRangeHelper(GetInputRange(left(), &left_range),
|
||||
GetInputRange(right(), &right_range), range);
|
||||
}
|
||||
|
||||
void BinarySmiOpInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
const Range* right_smi_range = analysis->GetSmiRange(right());
|
||||
// TODO(vegorov) completely remove this once GetSmiRange is eliminated.
|
||||
const Range smi_range = Range::Smi();
|
||||
const Range* right_smi_range = GetInputRange(right(), &smi_range);
|
||||
if (op_kind() == Token::kSHL || op_kind() == Token::kSHR ||
|
||||
op_kind() == Token::kUSHR || op_kind() == Token::kMOD ||
|
||||
op_kind() == Token::kTRUNCDIV) {
|
||||
CacheRange(&right_range_, right_smi_range,
|
||||
RangeBoundary::kRangeBoundarySmi);
|
||||
CacheRange(&right_range_, right_smi_range, smi_range);
|
||||
}
|
||||
InferRangeHelper(analysis->GetSmiRange(left()), right_smi_range, range);
|
||||
InferRangeHelper(GetInputRange(left(), &smi_range), right_smi_range, range);
|
||||
}
|
||||
|
||||
void BoxIntegerInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
@@ -3122,9 +3053,7 @@ void UnboxIntegerInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
*range = Range(v, v);
|
||||
return;
|
||||
}
|
||||
auto* const value_range = value()->Type()->ToCid() == kSmiCid
|
||||
? analysis->GetSmiRange(value())
|
||||
: value()->definition()->range();
|
||||
Range* const value_range = value()->definition()->range();
|
||||
const Range to_range = Range::Full(representation());
|
||||
|
||||
if (Range::IsUnknown(value_range)) {
|
||||
@@ -3186,7 +3115,7 @@ void AssertAssignableInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
if (!Range::IsUnknown(value_range)) {
|
||||
*range = *value_range;
|
||||
} else {
|
||||
*range = Range::Full(RangeBoundary::kRangeBoundaryInt64);
|
||||
*range = Range::Int64();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3260,9 +3189,8 @@ static bool IsRedundantBasedOnRangeInformation(Value* index, Value* length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RangeBoundary canonical_length = CanonicalizeBoundary(
|
||||
array_length,
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64));
|
||||
RangeBoundary canonical_length =
|
||||
CanonicalizeBoundary(array_length, RangeBoundary::MaxInt64());
|
||||
if (canonical_length.OverflowedSmi()) {
|
||||
TRACE_RANGE_ANALYSIS(
|
||||
THR_Print(" ... canonical length boundary (%s) overflows Smi\n",
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
|
||||
namespace dart {
|
||||
|
||||
// How to interpret values with kTagged representation.
|
||||
enum class TaggedMode {
|
||||
kTaggedNotAllowed,
|
||||
kTaggedIsSmi,
|
||||
};
|
||||
|
||||
class RangeBoundary : public ValueObject {
|
||||
public:
|
||||
#define FOR_EACH_RANGE_BOUNDARY_KIND(V) \
|
||||
@@ -25,14 +31,6 @@ class RangeBoundary : public ValueObject {
|
||||
enum Kind { FOR_EACH_RANGE_BOUNDARY_KIND(KIND_DEFN) };
|
||||
#undef KIND_DEFN
|
||||
|
||||
enum RangeSize {
|
||||
kRangeBoundarySmi,
|
||||
kRangeBoundaryInt8,
|
||||
kRangeBoundaryInt16,
|
||||
kRangeBoundaryInt32,
|
||||
kRangeBoundaryInt64,
|
||||
};
|
||||
|
||||
RangeBoundary() : kind_(kUnknown), value_(0), offset_(0) {}
|
||||
|
||||
RangeBoundary(const RangeBoundary& other)
|
||||
@@ -51,9 +49,6 @@ class RangeBoundary : public ValueObject {
|
||||
return *this;
|
||||
}
|
||||
|
||||
static constexpr int64_t kMin = kMinInt64;
|
||||
static constexpr int64_t kMax = kMaxInt64;
|
||||
|
||||
// Construct a RangeBoundary for a constant value.
|
||||
static RangeBoundary FromConstant(int64_t val) { return RangeBoundary(val); }
|
||||
|
||||
@@ -79,40 +74,9 @@ class RangeBoundary : public ValueObject {
|
||||
return FromConstant(compiler::target::kSmiMax);
|
||||
}
|
||||
|
||||
// Construct a RangeBoundary for the constant kMin value.
|
||||
static RangeBoundary MinConstant(RangeSize size) {
|
||||
switch (size) {
|
||||
case kRangeBoundarySmi:
|
||||
return FromConstant(compiler::target::kSmiMin);
|
||||
case kRangeBoundaryInt8:
|
||||
return FromConstant(kMinInt8);
|
||||
case kRangeBoundaryInt16:
|
||||
return FromConstant(kMinInt16);
|
||||
case kRangeBoundaryInt32:
|
||||
return FromConstant(kMinInt32);
|
||||
case kRangeBoundaryInt64:
|
||||
return FromConstant(kMinInt64);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return FromConstant(kMinInt64);
|
||||
}
|
||||
static RangeBoundary MinInt64() { return FromConstant(kMinInt64); }
|
||||
|
||||
static RangeBoundary MaxConstant(RangeSize size) {
|
||||
switch (size) {
|
||||
case kRangeBoundarySmi:
|
||||
return FromConstant(compiler::target::kSmiMax);
|
||||
case kRangeBoundaryInt8:
|
||||
return FromConstant(kMaxInt8);
|
||||
case kRangeBoundaryInt16:
|
||||
return FromConstant(kMaxInt16);
|
||||
case kRangeBoundaryInt32:
|
||||
return FromConstant(kMaxInt32);
|
||||
case kRangeBoundaryInt64:
|
||||
return FromConstant(kMaxInt64);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return FromConstant(kMaxInt64);
|
||||
}
|
||||
static RangeBoundary MaxInt64() { return FromConstant(kMaxInt64); }
|
||||
|
||||
// Given two boundaries a and b, select one of them as c so that
|
||||
//
|
||||
@@ -134,7 +98,7 @@ class RangeBoundary : public ValueObject {
|
||||
// as possible.
|
||||
static RangeBoundary JoinMin(RangeBoundary a,
|
||||
RangeBoundary b,
|
||||
RangeBoundary::RangeSize size);
|
||||
const Range& full_range);
|
||||
|
||||
// Given two boundaries a and b compute boundary c such that
|
||||
//
|
||||
@@ -144,45 +108,29 @@ class RangeBoundary : public ValueObject {
|
||||
// as possible.
|
||||
static RangeBoundary JoinMax(RangeBoundary a,
|
||||
RangeBoundary b,
|
||||
RangeBoundary::RangeSize size);
|
||||
const Range& full_range);
|
||||
|
||||
// Returns true when this is a constant that is outside of Smi range.
|
||||
bool OverflowedSmi() const {
|
||||
return IsConstant() && !compiler::target::IsSmi(ConstantValue());
|
||||
}
|
||||
|
||||
bool Overflowed(RangeBoundary::RangeSize size) const {
|
||||
bool Overflowed(const Range& full_range) const {
|
||||
ASSERT(IsConstant());
|
||||
return !Equals(Clamp(size));
|
||||
return !Equals(Clamp(full_range));
|
||||
}
|
||||
|
||||
// Clamp constant boundary to MinConstant/MaxConstant of the given size.
|
||||
RangeBoundary Clamp(RangeSize size) const {
|
||||
if (IsConstant()) {
|
||||
const RangeBoundary range_min = RangeBoundary::MinConstant(size);
|
||||
const RangeBoundary range_max = RangeBoundary::MaxConstant(size);
|
||||
// Clamp constant boundary to the given [full_range].
|
||||
RangeBoundary Clamp(const Range& full_range) const;
|
||||
|
||||
if (ConstantValue() <= range_min.ConstantValue()) {
|
||||
return range_min;
|
||||
}
|
||||
if (ConstantValue() >= range_max.ConstantValue()) {
|
||||
return range_max;
|
||||
}
|
||||
}
|
||||
|
||||
// If this range is a symbolic range, we do not clamp it.
|
||||
// This could lead to some imprecision later on.
|
||||
return *this;
|
||||
bool IsLessOrEqual(const RangeBoundary& other) const {
|
||||
ASSERT(other.IsConstant());
|
||||
return IsConstant() && (ConstantValue() <= other.ConstantValue());
|
||||
}
|
||||
|
||||
bool IsMinimumOrBelow(RangeSize size) const {
|
||||
return IsConstant() && (ConstantValue() <=
|
||||
RangeBoundary::MinConstant(size).ConstantValue());
|
||||
}
|
||||
|
||||
bool IsMaximumOrAbove(RangeSize size) const {
|
||||
return IsConstant() && (ConstantValue() >=
|
||||
RangeBoundary::MaxConstant(size).ConstantValue());
|
||||
bool IsGreaterOrEqual(const RangeBoundary& other) const {
|
||||
ASSERT(other.IsConstant());
|
||||
return IsConstant() && (ConstantValue() >= other.ConstantValue());
|
||||
}
|
||||
|
||||
intptr_t kind() const { return kind_; }
|
||||
@@ -250,18 +198,14 @@ class RangeBoundary : public ValueObject {
|
||||
|
||||
bool Equals(const RangeBoundary& other) const;
|
||||
|
||||
int64_t UpperBound(RangeSize size) const {
|
||||
return UpperBound().Clamp(size).ConstantValue();
|
||||
int64_t UpperBound(const Range& full_range) const {
|
||||
return UpperBound().Clamp(full_range).ConstantValue();
|
||||
}
|
||||
|
||||
int64_t LowerBound(RangeSize size) const {
|
||||
return LowerBound().Clamp(size).ConstantValue();
|
||||
int64_t LowerBound(const Range& full_range) const {
|
||||
return LowerBound().Clamp(full_range).ConstantValue();
|
||||
}
|
||||
|
||||
int64_t SmiUpperBound() const { return UpperBound(kRangeBoundarySmi); }
|
||||
|
||||
int64_t SmiLowerBound() const { return LowerBound(kRangeBoundarySmi); }
|
||||
|
||||
void Write(FlowGraphSerializer* s) const;
|
||||
explicit RangeBoundary(FlowGraphDeserializer* d);
|
||||
|
||||
@@ -298,12 +242,16 @@ class Range : public ZoneAllocated {
|
||||
return other->min().IsUnknown();
|
||||
}
|
||||
|
||||
static Range Full(RangeBoundary::RangeSize size) {
|
||||
return Range(RangeBoundary::MinConstant(size),
|
||||
RangeBoundary::MaxConstant(size));
|
||||
static Range Smi() {
|
||||
return Range(RangeBoundary::MinSmi(), RangeBoundary::MaxSmi());
|
||||
}
|
||||
|
||||
static Range Full(Representation rep);
|
||||
static Range Int64() {
|
||||
return Range(RangeBoundary::MinInt64(), RangeBoundary::MaxInt64());
|
||||
}
|
||||
|
||||
static Range Full(Representation rep,
|
||||
TaggedMode tagged_mode = TaggedMode::kTaggedNotAllowed);
|
||||
|
||||
void PrintTo(BaseTextBuffer* f) const;
|
||||
static const char* ToCString(const Range* range);
|
||||
@@ -324,35 +272,35 @@ class Range : public ZoneAllocated {
|
||||
void set_max(const RangeBoundary& value) { max_ = value; }
|
||||
|
||||
static RangeBoundary ConstantMinSmi(const Range* range) {
|
||||
return ConstantMin(range, RangeBoundary::kRangeBoundarySmi);
|
||||
return ConstantMin(range, Range::Smi());
|
||||
}
|
||||
|
||||
static RangeBoundary ConstantMaxSmi(const Range* range) {
|
||||
return ConstantMax(range, RangeBoundary::kRangeBoundarySmi);
|
||||
return ConstantMax(range, Range::Smi());
|
||||
}
|
||||
|
||||
static RangeBoundary ConstantMin(const Range* range) {
|
||||
return ConstantMin(range, RangeBoundary::kRangeBoundaryInt64);
|
||||
return ConstantMin(range, Range::Int64());
|
||||
}
|
||||
|
||||
static RangeBoundary ConstantMax(const Range* range) {
|
||||
return ConstantMax(range, RangeBoundary::kRangeBoundaryInt64);
|
||||
return ConstantMax(range, Range::Int64());
|
||||
}
|
||||
|
||||
static RangeBoundary ConstantMin(const Range* range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
if (range == nullptr) {
|
||||
return RangeBoundary::MinConstant(size);
|
||||
return full_range.min();
|
||||
}
|
||||
return range->min().LowerBound().Clamp(size);
|
||||
return range->min().LowerBound().Clamp(full_range);
|
||||
}
|
||||
|
||||
static RangeBoundary ConstantMax(const Range* range,
|
||||
RangeBoundary::RangeSize size) {
|
||||
const Range& full_range) {
|
||||
if (range == nullptr) {
|
||||
return RangeBoundary::MaxConstant(size);
|
||||
return full_range.max();
|
||||
}
|
||||
return range->max().UpperBound().Clamp(size);
|
||||
return range->max().UpperBound().Clamp(full_range);
|
||||
}
|
||||
|
||||
// [0, +inf]
|
||||
@@ -393,25 +341,21 @@ class Range : public ZoneAllocated {
|
||||
RangeBoundary::IntersectionMax(max(), other->max()));
|
||||
}
|
||||
|
||||
bool Fits(RangeBoundary::RangeSize size) const {
|
||||
return !min().LowerBound().Overflowed(size) &&
|
||||
!max().UpperBound().Overflowed(size);
|
||||
}
|
||||
|
||||
// Returns true if this range fits without truncation into
|
||||
// the given representation.
|
||||
static bool Fits(Range* range, Representation rep) {
|
||||
static bool Fits(Range* range,
|
||||
Representation rep,
|
||||
TaggedMode tagged_mode = TaggedMode::kTaggedNotAllowed) {
|
||||
if (range == nullptr) return false;
|
||||
if (!RepresentationUtils::IsUnboxedInteger(rep)) return false;
|
||||
const Range& other = Range::Full(rep);
|
||||
const Range& other = Range::Full(rep, tagged_mode);
|
||||
return range->IsWithin(&other);
|
||||
}
|
||||
|
||||
// Clamp this to be within size.
|
||||
void Clamp(RangeBoundary::RangeSize size);
|
||||
void Clamp(const Range& full_range);
|
||||
|
||||
// Clamp this to be within size and eliminate symbols.
|
||||
void ClampToConstant(RangeBoundary::RangeSize size);
|
||||
void ClampToConstant(const Range& full_range);
|
||||
|
||||
static void Add(const Range* left_range,
|
||||
const Range* right_range,
|
||||
@@ -492,10 +436,6 @@ class Range : public ZoneAllocated {
|
||||
|
||||
class RangeUtils : public AllStatic {
|
||||
public:
|
||||
static bool Fits(Range* range, RangeBoundary::RangeSize size) {
|
||||
return !Range::IsUnknown(range) && range->Fits(size);
|
||||
}
|
||||
|
||||
static bool IsWithin(const Range* range, int64_t min, int64_t max) {
|
||||
return !Range::IsUnknown(range) && range->IsWithin(min, max);
|
||||
}
|
||||
@@ -530,21 +470,12 @@ class RangeUtils : public AllStatic {
|
||||
class RangeAnalysis : public ValueObject {
|
||||
public:
|
||||
explicit RangeAnalysis(FlowGraph* flow_graph)
|
||||
: flow_graph_(flow_graph),
|
||||
smi_range_(Range::Full(RangeBoundary::kRangeBoundarySmi)),
|
||||
int64_range_(Range::Full(RangeBoundary::kRangeBoundaryInt64)) {}
|
||||
: flow_graph_(flow_graph), smi_range_(Range::Smi()) {}
|
||||
|
||||
// Infer ranges for all values and remove overflow checks from binary smi
|
||||
// operations when proven redundant.
|
||||
void Analyze();
|
||||
|
||||
// Helper that should be used to access ranges of inputs during range
|
||||
// inference.
|
||||
// Returns meaningful results for uses of non-smi/non-int definitions that
|
||||
// have smi/int as a reaching type.
|
||||
const Range* GetSmiRange(Value* value) const;
|
||||
const Range* GetIntRange(Value* value) const;
|
||||
|
||||
static bool IsIntegerDefinition(Definition* defn) {
|
||||
return defn->Type()->IsInt();
|
||||
}
|
||||
@@ -609,17 +540,16 @@ class RangeAnalysis : public ValueObject {
|
||||
|
||||
Range* ConstraintRange(Token::Kind op,
|
||||
Definition* boundary,
|
||||
RangeBoundary::RangeSize size);
|
||||
const Range& full_range);
|
||||
|
||||
Zone* zone() const { return flow_graph_->zone(); }
|
||||
|
||||
const Range& smi_range() const { return smi_range_; }
|
||||
|
||||
FlowGraph* flow_graph_;
|
||||
|
||||
// Range object representing full Smi range.
|
||||
Range smi_range_;
|
||||
|
||||
Range int64_range_;
|
||||
|
||||
// All values that are known to be smi or mint.
|
||||
GrowableArray<Definition*> values_;
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ TEST_CASE(RangeTests) {
|
||||
TEST_RANGE_OP_(Op, l_min, l_max, r_min, r_max, NO_CLAMP, result_min, \
|
||||
result_max)
|
||||
|
||||
#define CLAMP_TO_SMI(b) (b.Clamp(RangeBoundary::kRangeBoundarySmi))
|
||||
#define CLAMP_TO_SMI(b) (b.Clamp(Range::Smi()))
|
||||
#define TEST_RANGE_OP_SMI(Op, l_min, l_max, r_min, r_max, res_min, res_max) \
|
||||
TEST_RANGE_OP_(Op, l_min, l_max, r_min, r_max, CLAMP_TO_SMI, res_min, res_max)
|
||||
|
||||
@@ -101,7 +101,7 @@ TEST_CASE(RangeTests) {
|
||||
}
|
||||
|
||||
TEST_CASE(RangeTestsInt64Range) {
|
||||
const Range fullInt64Range = Range::Full(RangeBoundary::kRangeBoundaryInt64);
|
||||
const Range fullInt64Range = Range::Int64();
|
||||
|
||||
Range* all = new Range(RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64));
|
||||
EXPECT(all->Equals(&fullInt64Range));
|
||||
@@ -131,12 +131,10 @@ TEST_CASE(RangeTestsInt64Range) {
|
||||
}
|
||||
|
||||
TEST_CASE(RangeUtils) {
|
||||
// Use kMin/kMax instead of +/-inf as any range with a +/-inf bound is
|
||||
// converted to the full int64 range due to wrap-around.
|
||||
const RangeBoundary negativeInfinity =
|
||||
RangeBoundary::FromConstant(RangeBoundary::kMin);
|
||||
const RangeBoundary positiveInfinity =
|
||||
RangeBoundary::FromConstant(RangeBoundary::kMax);
|
||||
// Use MinInt64/MaxInt64 bounds as any wrap-around range is
|
||||
// converted to the full int64 range.
|
||||
const RangeBoundary negativeInfinity = RangeBoundary::MinInt64();
|
||||
const RangeBoundary positiveInfinity = RangeBoundary::MaxInt64();
|
||||
|
||||
// [-inf, +inf].
|
||||
const Range& range_0 = *(new Range(negativeInfinity, positiveInfinity));
|
||||
@@ -197,7 +195,7 @@ TEST_CASE(RangeUtils) {
|
||||
EXPECT(!Range::OnlyNegativeOrZero(range_0, range_0));
|
||||
EXPECT(!Range::OnlyPositiveOrZero(range_0, range_0));
|
||||
|
||||
EXPECT(Range::ConstantAbsMax(&range_0) == RangeBoundary::kMax);
|
||||
EXPECT(Range::ConstantAbsMax(&range_0) == kMaxInt64);
|
||||
EXPECT(Range::ConstantAbsMax(&range_h) == 2);
|
||||
EXPECT(Range::ConstantAbsMax(&range_i) == 1);
|
||||
|
||||
@@ -208,28 +206,26 @@ TEST_CASE(RangeUtils) {
|
||||
}
|
||||
|
||||
TEST_CASE(RangeBinaryOp) {
|
||||
Range* range_a = new Range(RangeBoundary::FromConstant(-1),
|
||||
RangeBoundary::FromConstant(RangeBoundary::kMax));
|
||||
range_a->Clamp(RangeBoundary::kRangeBoundaryInt32);
|
||||
Range* range_a =
|
||||
new Range(RangeBoundary::FromConstant(-1), RangeBoundary::MaxInt64());
|
||||
range_a->Clamp(Range::Full(kUnboxedInt32));
|
||||
EXPECT(range_a->min().ConstantValue() == -1);
|
||||
EXPECT(range_a->max().ConstantValue() == kMaxInt32);
|
||||
range_a->set_max(RangeBoundary::FromConstant(RangeBoundary::kMax));
|
||||
range_a->set_max(RangeBoundary::MaxInt64());
|
||||
|
||||
Range* range_b = new Range(RangeBoundary::FromConstant(RangeBoundary::kMin),
|
||||
RangeBoundary::FromConstant(1));
|
||||
range_b->Clamp(RangeBoundary::kRangeBoundaryInt32);
|
||||
Range* range_b =
|
||||
new Range(RangeBoundary::MinInt64(), RangeBoundary::FromConstant(1));
|
||||
range_b->Clamp(Range::Full(kUnboxedInt32));
|
||||
EXPECT(range_b->min().ConstantValue() == kMinInt32);
|
||||
EXPECT(range_b->max().ConstantValue() == 1);
|
||||
range_b->set_min(RangeBoundary::FromConstant(RangeBoundary::kMin));
|
||||
range_b->set_min(RangeBoundary::MinInt64());
|
||||
|
||||
{
|
||||
Range result;
|
||||
Range::BinaryOp(Token::kADD, range_a, range_b, nullptr, &result);
|
||||
ASSERT(!Range::IsUnknown(&result));
|
||||
EXPECT(result.min().Equals(
|
||||
RangeBoundary::MinConstant(RangeBoundary::kRangeBoundaryInt64)));
|
||||
EXPECT(result.max().Equals(
|
||||
RangeBoundary::MaxConstant(RangeBoundary::kRangeBoundaryInt64)));
|
||||
EXPECT(result.min().Equals(RangeBoundary::MinInt64()));
|
||||
EXPECT(result.max().Equals(RangeBoundary::MaxInt64()));
|
||||
}
|
||||
|
||||
// Test that [5, 10] + [0, 5] = [5, 15].
|
||||
@@ -527,66 +523,66 @@ TEST_CASE(RangeIntersectionMinMax) {
|
||||
TEST_CASE(RangeJoinMinMax) {
|
||||
// Test IntersectionMin and IntersectionMax methods which for constants are
|
||||
// simply defined as Min/Max respectively.
|
||||
const RangeBoundary::RangeSize size = RangeBoundary::kRangeBoundarySmi;
|
||||
const Range full_range = Range::Smi();
|
||||
|
||||
// Constants.
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(0),
|
||||
RangeBoundary::FromConstant(1), size)
|
||||
RangeBoundary::FromConstant(1), full_range)
|
||||
.ConstantValue() == 1);
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(0),
|
||||
RangeBoundary::FromConstant(-1), size)
|
||||
RangeBoundary::FromConstant(-1), full_range)
|
||||
.ConstantValue() == 0);
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(1),
|
||||
RangeBoundary::FromConstant(0), size)
|
||||
RangeBoundary::FromConstant(0), full_range)
|
||||
.ConstantValue() == 1);
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(-1),
|
||||
RangeBoundary::FromConstant(0), size)
|
||||
RangeBoundary::FromConstant(0), full_range)
|
||||
.ConstantValue() == 0);
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(0),
|
||||
RangeBoundary::FromConstant(1), size)
|
||||
RangeBoundary::FromConstant(1), full_range)
|
||||
.ConstantValue() == 0);
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(0),
|
||||
RangeBoundary::FromConstant(-1), size)
|
||||
RangeBoundary::FromConstant(-1), full_range)
|
||||
.ConstantValue() == -1);
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(1),
|
||||
RangeBoundary::FromConstant(0), size)
|
||||
RangeBoundary::FromConstant(0), full_range)
|
||||
.ConstantValue() == 0);
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(-1),
|
||||
RangeBoundary::FromConstant(0), size)
|
||||
RangeBoundary::FromConstant(0), full_range)
|
||||
.ConstantValue() == -1);
|
||||
|
||||
// Constants vs. kMinInt64 / kMaxInt64.
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary(kMinInt64),
|
||||
RangeBoundary::FromConstant(-1), size)
|
||||
.IsMinimumOrBelow(size));
|
||||
RangeBoundary::FromConstant(-1), full_range)
|
||||
.IsLessOrEqual(full_range.min()));
|
||||
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(-1),
|
||||
RangeBoundary(kMinInt64), size)
|
||||
.IsMinimumOrBelow(size));
|
||||
RangeBoundary(kMinInt64), full_range)
|
||||
.IsLessOrEqual(full_range.min()));
|
||||
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(1),
|
||||
RangeBoundary(kMinInt64), size)
|
||||
.IsMinimumOrBelow(size));
|
||||
RangeBoundary(kMinInt64), full_range)
|
||||
.IsLessOrEqual(full_range.min()));
|
||||
|
||||
EXPECT(RangeBoundary::JoinMin(RangeBoundary(kMinInt64),
|
||||
RangeBoundary::FromConstant(1), size)
|
||||
.IsMinimumOrBelow(size));
|
||||
RangeBoundary::FromConstant(1), full_range)
|
||||
.IsLessOrEqual(full_range.min()));
|
||||
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary(kMaxInt64),
|
||||
RangeBoundary::FromConstant(-1), size)
|
||||
.IsMaximumOrAbove(size));
|
||||
RangeBoundary::FromConstant(-1), full_range)
|
||||
.IsGreaterOrEqual(full_range.max()));
|
||||
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(-1),
|
||||
RangeBoundary(kMaxInt64), size)
|
||||
.IsMaximumOrAbove(size));
|
||||
RangeBoundary(kMaxInt64), full_range)
|
||||
.IsGreaterOrEqual(full_range.max()));
|
||||
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(1),
|
||||
RangeBoundary(kMaxInt64), size)
|
||||
.IsMaximumOrAbove(size));
|
||||
RangeBoundary(kMaxInt64), full_range)
|
||||
.IsGreaterOrEqual(full_range.max()));
|
||||
|
||||
EXPECT(RangeBoundary::JoinMax(RangeBoundary(kMaxInt64),
|
||||
RangeBoundary::FromConstant(1), size)
|
||||
.IsMaximumOrAbove(size));
|
||||
RangeBoundary::FromConstant(1), full_range)
|
||||
.IsGreaterOrEqual(full_range.max()));
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILER) && defined(TARGET_ARCH_IS_64_BIT)
|
||||
|
||||
Reference in New Issue
Block a user