f98a2138b7
When uploading CLs, the presubmit checks verify that the lines in the diff are formatted correctly according to `git cl format runtime`. However, when `buildtools/<os>-<arch>/clang/bin/clang-format` is updated, it does not force reformatting of files that would be reformatted. This leads to two issues: * Inconsistent style within the code base and within a single file. * Spurious reformatting in CLs when (1) clang-format is used on the whole file, or (2) the diff lines overlap. `clang-format` doesn't change that frequently, so in general this is not a large issue, but I've seen a bit too many "spurious formatting, please revert" comments on CLs recently. This CL formats the runtime to be in line with the current pinned `clang-format`: ``` $ find runtime/ -iname *.h -o -iname *.cc | xargs buildtools/mac-arm64/clang/bin/clang-format -i ``` `git cl format` (which only formats changed lines, and does so with `clang-format`) seems to not agree with itself, or clang-format, or cpplint in a handful of places. This CL adds `// clang-format off` for these. (See previous patchsets for the specific instances.) TEST=A variety of bots including GCC, MacOS and Windows. Change-Id: I470892e898971899fda14bb3b8f2c8efefd67686 Cq-Include-Trybots: luci.dart.try:vm-gcc-linux-try,vm-ffi-qemu-linux-release-riscv64-try,vm-ffi-qemu-linux-release-arm-try,vm-aot-win-debug-x64-try,vm-win-debug-x64c-try,vm-mac-debug-x64-try,vm-mac-debug-arm64-try,vm-aot-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362780 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
672 lines
21 KiB
C++
672 lines
21 KiB
C++
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_COMPILER_BACKEND_RANGE_ANALYSIS_H_
|
|
#define RUNTIME_VM_COMPILER_BACKEND_RANGE_ANALYSIS_H_
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
#error "AOT runtime should not use compiler sources (including header files)"
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include "vm/compiler/backend/flow_graph.h"
|
|
#include "vm/compiler/backend/il.h"
|
|
|
|
namespace dart {
|
|
|
|
class RangeBoundary : public ValueObject {
|
|
public:
|
|
#define FOR_EACH_RANGE_BOUNDARY_KIND(V) \
|
|
V(Unknown) \
|
|
V(Symbol) \
|
|
V(Constant)
|
|
|
|
#define KIND_DEFN(name) k##name,
|
|
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)
|
|
: ValueObject(),
|
|
kind_(other.kind_),
|
|
value_(other.value_),
|
|
offset_(other.offset_) {}
|
|
|
|
explicit RangeBoundary(int64_t val)
|
|
: kind_(kConstant), value_(val), offset_(0) {}
|
|
|
|
RangeBoundary& operator=(const RangeBoundary& other) {
|
|
kind_ = other.kind_;
|
|
value_ = other.value_;
|
|
offset_ = other.offset_;
|
|
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); }
|
|
|
|
// Construct a RangeBoundary from a definition and offset.
|
|
static RangeBoundary FromDefinition(Definition* defn, int64_t offs = 0);
|
|
|
|
static bool IsValidOffsetForSymbolicRangeBoundary(int64_t offset) {
|
|
if ((offset > (kMaxInt64 - compiler::target::kSmiMax)) ||
|
|
(offset < (kMinInt64 - compiler::target::kSmiMin))) {
|
|
// Avoid creating symbolic range boundaries which can wrap around.
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Construct a RangeBoundary for the constant MinSmi value.
|
|
static RangeBoundary MinSmi() {
|
|
return FromConstant(compiler::target::kSmiMin);
|
|
}
|
|
|
|
// Construct a RangeBoundary for the constant MaxSmi value.
|
|
static RangeBoundary MaxSmi() {
|
|
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 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);
|
|
}
|
|
|
|
// Given two boundaries a and b, select one of them as c so that
|
|
//
|
|
// inf {[a, ...) ^ [b, ...)} >= inf {c}
|
|
//
|
|
static RangeBoundary IntersectionMin(RangeBoundary a, RangeBoundary b);
|
|
|
|
// Given two boundaries a and b, select one of them as c so that
|
|
//
|
|
// sup {(..., a] ^ (..., b]} <= sup {c}
|
|
//
|
|
static RangeBoundary IntersectionMax(RangeBoundary a, RangeBoundary b);
|
|
|
|
// Given two boundaries a and b compute boundary c such that
|
|
//
|
|
// inf {[a, ...) U [b, ...)} >= inf {c}
|
|
//
|
|
// Try to select c such that it is as close to inf {[a, ...) U [b, ...)}
|
|
// as possible.
|
|
static RangeBoundary JoinMin(RangeBoundary a,
|
|
RangeBoundary b,
|
|
RangeBoundary::RangeSize size);
|
|
|
|
// Given two boundaries a and b compute boundary c such that
|
|
//
|
|
// sup {(..., a] U (..., b]} <= sup {c}
|
|
//
|
|
// Try to select c such that it is as close to sup {(..., a] U (..., b]}
|
|
// as possible.
|
|
static RangeBoundary JoinMax(RangeBoundary a,
|
|
RangeBoundary b,
|
|
RangeBoundary::RangeSize size);
|
|
|
|
// 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 {
|
|
ASSERT(IsConstant());
|
|
return !Equals(Clamp(size));
|
|
}
|
|
|
|
// 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);
|
|
|
|
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 IsMinimumOrBelow(RangeSize size) const {
|
|
return IsConstant() && (ConstantValue() <=
|
|
RangeBoundary::MinConstant(size).ConstantValue());
|
|
}
|
|
|
|
bool IsMaximumOrAbove(RangeSize size) const {
|
|
return IsConstant() && (ConstantValue() >=
|
|
RangeBoundary::MaxConstant(size).ConstantValue());
|
|
}
|
|
|
|
intptr_t kind() const { return kind_; }
|
|
|
|
// Kind tests.
|
|
bool IsUnknown() const { return kind_ == kUnknown; }
|
|
bool IsConstant() const { return kind_ == kConstant; }
|
|
bool IsSymbol() const { return kind_ == kSymbol; }
|
|
|
|
// Returns the value of a kConstant RangeBoundary.
|
|
int64_t ConstantValue() const;
|
|
|
|
// Returns the Definition associated with a kSymbol RangeBoundary.
|
|
Definition* symbol() const {
|
|
ASSERT(IsSymbol());
|
|
return reinterpret_cast<Definition*>(value_);
|
|
}
|
|
|
|
// Offset from symbol.
|
|
int64_t offset() const { return offset_; }
|
|
|
|
// Computes the LowerBound of this. Three cases:
|
|
// IsConstant() -> value().
|
|
// IsSymbol() -> lower bound computed from definition + offset.
|
|
RangeBoundary LowerBound() const;
|
|
|
|
// Computes the UpperBound of this. Three cases:
|
|
// IsConstant() -> value().
|
|
// IsSymbol() -> upper bound computed from definition + offset.
|
|
RangeBoundary UpperBound() const;
|
|
|
|
void PrintTo(BaseTextBuffer* f) const;
|
|
const char* ToCString() const;
|
|
|
|
static bool WillAddOverflow(const RangeBoundary& a, const RangeBoundary& b);
|
|
|
|
static RangeBoundary Add(const RangeBoundary& a, const RangeBoundary& b);
|
|
|
|
static bool WillSubOverflow(const RangeBoundary& a, const RangeBoundary& b);
|
|
|
|
static RangeBoundary Sub(const RangeBoundary& a, const RangeBoundary& b);
|
|
|
|
static bool WillShlOverflow(const RangeBoundary& a, int64_t shift_count);
|
|
|
|
static RangeBoundary Shl(const RangeBoundary& value_boundary,
|
|
int64_t shift_count);
|
|
|
|
static RangeBoundary Shr(const RangeBoundary& value_boundary,
|
|
int64_t shift_count);
|
|
|
|
// Attempts to calculate a + b when:
|
|
// a is a symbol and b is a constant OR
|
|
// a is a constant and b is a symbol
|
|
// returns true if it succeeds, output is in result.
|
|
static bool SymbolicAdd(const RangeBoundary& a,
|
|
const RangeBoundary& b,
|
|
RangeBoundary* result);
|
|
|
|
// Attempts to calculate a - b when:
|
|
// a is a symbol and b is a constant
|
|
// returns true if it succeeds, output is in result.
|
|
static bool SymbolicSub(const RangeBoundary& a,
|
|
const RangeBoundary& b,
|
|
RangeBoundary* result);
|
|
|
|
bool Equals(const RangeBoundary& other) const;
|
|
|
|
int64_t UpperBound(RangeSize size) const {
|
|
return UpperBound().Clamp(size).ConstantValue();
|
|
}
|
|
|
|
int64_t LowerBound(RangeSize size) const {
|
|
return LowerBound().Clamp(size).ConstantValue();
|
|
}
|
|
|
|
int64_t SmiUpperBound() const { return UpperBound(kRangeBoundarySmi); }
|
|
|
|
int64_t SmiLowerBound() const { return LowerBound(kRangeBoundarySmi); }
|
|
|
|
void Write(FlowGraphSerializer* s) const;
|
|
explicit RangeBoundary(FlowGraphDeserializer* d);
|
|
|
|
private:
|
|
RangeBoundary(Kind kind, int64_t value, int64_t offset)
|
|
: kind_(kind), value_(value), offset_(offset) {}
|
|
|
|
Kind kind_;
|
|
int64_t value_;
|
|
int64_t offset_;
|
|
};
|
|
|
|
class Range : public ZoneAllocated {
|
|
public:
|
|
Range() : min_(), max_() {}
|
|
|
|
Range(RangeBoundary min, RangeBoundary max) : min_(min), max_(max) {
|
|
ASSERT(min_.IsUnknown() == max_.IsUnknown());
|
|
}
|
|
|
|
Range(const Range& other)
|
|
: ZoneAllocated(), min_(other.min_), max_(other.max_) {}
|
|
|
|
Range& operator=(const Range& other) {
|
|
min_ = other.min_;
|
|
max_ = other.max_;
|
|
return *this;
|
|
}
|
|
|
|
static bool IsUnknown(const Range* other) {
|
|
if (other == nullptr) {
|
|
return true;
|
|
}
|
|
return other->min().IsUnknown();
|
|
}
|
|
|
|
static Range Full(RangeBoundary::RangeSize size) {
|
|
return Range(RangeBoundary::MinConstant(size),
|
|
RangeBoundary::MaxConstant(size));
|
|
}
|
|
|
|
static Range Full(Representation rep);
|
|
|
|
void PrintTo(BaseTextBuffer* f) const;
|
|
static const char* ToCString(const Range* range);
|
|
|
|
bool Equals(const Range* other) {
|
|
ASSERT(min_.IsUnknown() == max_.IsUnknown());
|
|
if (other == nullptr) {
|
|
return min_.IsUnknown();
|
|
}
|
|
return min_.Equals(other->min_) && max_.Equals(other->max_);
|
|
}
|
|
|
|
const RangeBoundary& min() const { return min_; }
|
|
const RangeBoundary& max() const { return max_; }
|
|
|
|
void set_min(const RangeBoundary& value) { min_ = value; }
|
|
|
|
void set_max(const RangeBoundary& value) { max_ = value; }
|
|
|
|
static RangeBoundary ConstantMinSmi(const Range* range) {
|
|
return ConstantMin(range, RangeBoundary::kRangeBoundarySmi);
|
|
}
|
|
|
|
static RangeBoundary ConstantMaxSmi(const Range* range) {
|
|
return ConstantMax(range, RangeBoundary::kRangeBoundarySmi);
|
|
}
|
|
|
|
static RangeBoundary ConstantMin(const Range* range) {
|
|
return ConstantMin(range, RangeBoundary::kRangeBoundaryInt64);
|
|
}
|
|
|
|
static RangeBoundary ConstantMax(const Range* range) {
|
|
return ConstantMax(range, RangeBoundary::kRangeBoundaryInt64);
|
|
}
|
|
|
|
static RangeBoundary ConstantMin(const Range* range,
|
|
RangeBoundary::RangeSize size) {
|
|
if (range == nullptr) {
|
|
return RangeBoundary::MinConstant(size);
|
|
}
|
|
return range->min().LowerBound().Clamp(size);
|
|
}
|
|
|
|
static RangeBoundary ConstantMax(const Range* range,
|
|
RangeBoundary::RangeSize size) {
|
|
if (range == nullptr) {
|
|
return RangeBoundary::MaxConstant(size);
|
|
}
|
|
return range->max().UpperBound().Clamp(size);
|
|
}
|
|
|
|
// [0, +inf]
|
|
bool IsPositive() const;
|
|
|
|
// [-inf, -1]
|
|
bool IsNegative() const;
|
|
|
|
// [-inf, val].
|
|
bool OnlyLessThanOrEqualTo(int64_t val) const;
|
|
|
|
// [val, +inf].
|
|
bool OnlyGreaterThanOrEqualTo(int64_t val) const;
|
|
|
|
// Inclusive.
|
|
bool IsWithin(int64_t min_int, int64_t max_int) const;
|
|
|
|
// Inclusive.
|
|
bool IsWithin(const Range* other) const;
|
|
|
|
// Inclusive.
|
|
bool Overlaps(int64_t min_int, int64_t max_int) const;
|
|
|
|
bool IsUnsatisfiable() const;
|
|
|
|
bool IsSingleton() const {
|
|
return min_.IsConstant() && max_.IsConstant() &&
|
|
min_.ConstantValue() == max_.ConstantValue();
|
|
}
|
|
|
|
int64_t Singleton() const {
|
|
ASSERT(IsSingleton());
|
|
return min_.ConstantValue();
|
|
}
|
|
|
|
Range Intersect(const Range* other) const {
|
|
return Range(RangeBoundary::IntersectionMin(min(), other->min()),
|
|
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) {
|
|
if (range == nullptr) return false;
|
|
if (!RepresentationUtils::IsUnboxedInteger(rep)) return false;
|
|
const Range& other = Range::Full(rep);
|
|
return range->IsWithin(&other);
|
|
}
|
|
|
|
// Clamp this to be within size.
|
|
void Clamp(RangeBoundary::RangeSize size);
|
|
|
|
// Clamp this to be within size and eliminate symbols.
|
|
void ClampToConstant(RangeBoundary::RangeSize size);
|
|
|
|
static void Add(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max,
|
|
Definition* left_defn);
|
|
|
|
static void Sub(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max,
|
|
Definition* left_defn);
|
|
|
|
static void Mul(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
static void TruncDiv(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
static void Mod(const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
static void Shr(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
static void Ushr(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
static void Shl(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
static void And(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
static void BitwiseOp(const Range* left_range,
|
|
const Range* right_range,
|
|
RangeBoundary* min,
|
|
RangeBoundary* max);
|
|
|
|
// Both the a and b ranges are >= 0.
|
|
static bool OnlyPositiveOrZero(const Range& a, const Range& b);
|
|
|
|
// Both the a and b ranges are <= 0.
|
|
static bool OnlyNegativeOrZero(const Range& a, const Range& b);
|
|
|
|
// Return the maximum absolute value included in range.
|
|
static int64_t ConstantAbsMax(const Range* range);
|
|
|
|
// Return the minimum absolute value included in range.
|
|
static int64_t ConstantAbsMin(const Range* range);
|
|
|
|
static void BinaryOp(const Token::Kind op,
|
|
const Range* left_range,
|
|
const Range* right_range,
|
|
Definition* left_defn,
|
|
Range* result);
|
|
|
|
void Write(FlowGraphSerializer* s) const;
|
|
explicit Range(FlowGraphDeserializer* d);
|
|
|
|
private:
|
|
RangeBoundary min_;
|
|
RangeBoundary max_;
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
static bool IsWithin(const Range* range, const Range* other) {
|
|
return !Range::IsUnknown(range) && range->IsWithin(other);
|
|
}
|
|
|
|
static bool IsPositive(Range* range) {
|
|
return !Range::IsUnknown(range) && range->IsPositive();
|
|
}
|
|
static bool IsNegative(Range* range) {
|
|
return !Range::IsUnknown(range) && range->IsNegative();
|
|
}
|
|
|
|
static bool Overlaps(Range* range, intptr_t min, intptr_t max) {
|
|
return Range::IsUnknown(range) || range->Overlaps(min, max);
|
|
}
|
|
|
|
static bool CanBeZero(Range* range) { return Overlaps(range, 0, 0); }
|
|
|
|
static bool OnlyLessThanOrEqualTo(Range* range, intptr_t value) {
|
|
return !Range::IsUnknown(range) && range->OnlyLessThanOrEqualTo(value);
|
|
}
|
|
|
|
static bool IsSingleton(Range* range) {
|
|
return !Range::IsUnknown(range) && range->IsSingleton();
|
|
}
|
|
};
|
|
|
|
// Range analysis for integer values.
|
|
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)) {}
|
|
|
|
// 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();
|
|
}
|
|
|
|
void AssignRangesRecursively(Definition* defn);
|
|
|
|
private:
|
|
enum JoinOperator { NONE, WIDEN, NARROW };
|
|
static char OpPrefix(JoinOperator op);
|
|
|
|
// Collect all integer values (smi or int), all 64-bit binary
|
|
// and shift operations, and all check bounds.
|
|
void CollectValues();
|
|
|
|
// Iterate over smi values and constrain them at branch successors.
|
|
// Additionally constraint values after CheckSmi instructions.
|
|
void InsertConstraints();
|
|
|
|
// Iterate over uses of the given definition and discover branches that
|
|
// constrain it. Insert appropriate Constraint instructions at true
|
|
// and false successor and rename all dominated uses to refer to a
|
|
// Constraint instead of this definition.
|
|
void InsertConstraintsFor(Definition* defn);
|
|
|
|
// Create a constraint for defn, insert it after given instruction and
|
|
// rename all uses that are dominated by it.
|
|
ConstraintInstr* InsertConstraintFor(Value* use,
|
|
Definition* defn,
|
|
Range* constraint,
|
|
Instruction* after);
|
|
|
|
bool ConstrainValueAfterBranch(Value* use, Definition* defn);
|
|
void ConstrainValueAfterCheckBound(Value* use,
|
|
CheckBoundBaseInstr* check,
|
|
Definition* defn);
|
|
|
|
// Infer ranges for integer (smi or mint) definitions.
|
|
void InferRanges();
|
|
|
|
// Collect integer definition in the reverse postorder.
|
|
void CollectDefinitions(BitVector* set);
|
|
|
|
// Recompute ranges of all definitions until they stop changing.
|
|
// Apply the given JoinOperator when computing Phi ranges.
|
|
void Iterate(JoinOperator op, intptr_t max_iterations);
|
|
bool InferRange(JoinOperator op, Definition* defn, intptr_t iteration);
|
|
|
|
// Based on computed ranges find and eliminate redundant CheckArrayBound
|
|
// instructions.
|
|
void EliminateRedundantBoundsChecks();
|
|
|
|
// Find unsatisfiable constraints and mark corresponding blocks unreachable.
|
|
void MarkUnreachableBlocks();
|
|
|
|
// Convert mint operations that stay within int32 range into Int32 operations.
|
|
void NarrowMintToInt32();
|
|
|
|
// Remove artificial Constraint instructions and replace them with actual
|
|
// unconstrained definitions.
|
|
void RemoveConstraints();
|
|
|
|
Range* ConstraintSmiRange(Token::Kind op, Definition* boundary);
|
|
|
|
Zone* zone() const { return flow_graph_->zone(); }
|
|
|
|
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_;
|
|
|
|
// All 64-bit binary and shift operations.
|
|
GrowableArray<BinaryInt64OpInstr*> binary_int64_ops_;
|
|
GrowableArray<ShiftIntegerOpInstr*> shift_int64_ops_;
|
|
|
|
// All CheckArrayBound/GenericCheckBound instructions.
|
|
GrowableArray<CheckBoundBaseInstr*> bounds_checks_;
|
|
|
|
// All Constraints inserted during InsertConstraints phase. They are treated
|
|
// as smi values.
|
|
GrowableArray<ConstraintInstr*> constraints_;
|
|
|
|
// List of integer (smi or mint) definitions including constraints sorted
|
|
// in the reverse postorder.
|
|
GrowableArray<Definition*> definitions_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(RangeAnalysis);
|
|
};
|
|
|
|
// Replaces Mint IL instructions with Uint32 IL instructions
|
|
// when possible. Uses output of RangeAnalysis.
|
|
class IntegerInstructionSelector : public ValueObject {
|
|
public:
|
|
explicit IntegerInstructionSelector(FlowGraph* flow_graph);
|
|
|
|
void Select();
|
|
|
|
private:
|
|
bool IsPotentialUint32Definition(Definition* def);
|
|
void FindPotentialUint32Definitions();
|
|
bool IsUint32NarrowingDefinition(Definition* def);
|
|
void FindUint32NarrowingDefinitions();
|
|
bool AllUsesAreUint32Narrowing(Value* list_head);
|
|
bool CanBecomeUint32(Definition* def);
|
|
void Propagate();
|
|
Definition* ConstructReplacementFor(Definition* def);
|
|
void ReplaceInstructions();
|
|
|
|
Zone* zone() const { return zone_; }
|
|
|
|
GrowableArray<Definition*> potential_uint32_defs_;
|
|
BitVector* selected_uint32_defs_;
|
|
|
|
FlowGraph* flow_graph_;
|
|
Zone* zone_;
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_COMPILER_BACKEND_RANGE_ANALYSIS_H_
|