| Index: src/hydrogen-instructions.h
|
| diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
|
| index 29e8c29739cc43e60004fd16033670d3151f0794..f741f292e8b1f0bf87591fc2c9dafb9e12c5bb78 100644
|
| --- a/src/hydrogen-instructions.h
|
| +++ b/src/hydrogen-instructions.h
|
| @@ -75,6 +75,7 @@ class LChunkBuilder;
|
| V(BitNot) \
|
| V(BlockEntry) \
|
| V(BoundsCheck) \
|
| + V(BoundsCheckBaseIndexInformation) \
|
| V(Branch) \
|
| V(CallConstantFunction) \
|
| V(CallFunction) \
|
| @@ -667,13 +668,130 @@ class NumericRelation {
|
| return false;
|
| }
|
|
|
| + // CompoundImplies returns true when
|
| + // "((x + my_offset) >> my_scale) rel y" implies
|
| + // "((x + other_offset) >> other_scale) other_relation y".
|
| + bool CompoundImplies(NumericRelation other_relation,
|
| + int my_offset,
|
| + int my_scale,
|
| + int other_offset = 0,
|
| + int other_scale = 0) {
|
| + return Implies(other_relation) && ComponentsImply(
|
| + my_offset, my_scale, other_offset, other_scale);
|
| + }
|
| +
|
| private:
|
| + // ComponentsImply returns true when
|
| + // "((x + my_offset) >> my_scale) rel y" implies
|
| + // "((x + other_offset) >> other_scale) rel y".
|
| + bool ComponentsImply(int my_offset,
|
| + int my_scale,
|
| + int other_offset,
|
| + int other_scale) {
|
| + switch (kind_) {
|
| + case NONE: break; // Fall through to UNREACHABLE().
|
| + case EQ:
|
| + case NE: return my_offset == other_offset && my_scale == other_scale;
|
| + case GT:
|
| + case GE: return my_offset <= other_offset && my_scale >= other_scale;
|
| + case LT:
|
| + case LE: return my_offset >= other_offset && my_scale <= other_scale;
|
| + }
|
| + UNREACHABLE();
|
| + return false;
|
| + }
|
| +
|
| explicit NumericRelation(Kind kind) : kind_(kind) {}
|
|
|
| Kind kind_;
|
| };
|
|
|
|
|
| +class DecompositionResult BASE_EMBEDDED {
|
| + public:
|
| + DecompositionResult() : base_(NULL), offset_(0), scale_(0) {}
|
| +
|
| + HValue* base() { return base_; }
|
| + int offset() { return offset_; }
|
| + int scale() { return scale_; }
|
| +
|
| + bool Apply(HValue* other_base, int other_offset, int other_scale = 0) {
|
| + if (base_ == NULL) {
|
| + base_ = other_base;
|
| + offset_ = other_offset;
|
| + scale_ = other_scale;
|
| + return true;
|
| + } else {
|
| + if (scale_ == 0) {
|
| + base_ = other_base;
|
| + offset_ += other_offset;
|
| + scale_ = other_scale;
|
| + return true;
|
| + } else {
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| +
|
| + void SwapValues(HValue** other_base, int* other_offset, int* other_scale) {
|
| + swap(&base_, other_base);
|
| + swap(&offset_, other_offset);
|
| + swap(&scale_, other_scale);
|
| + }
|
| +
|
| + private:
|
| + template <class T> void swap(T* a, T* b) {
|
| + T c(*a);
|
| + *a = *b;
|
| + *b = c;
|
| + }
|
| +
|
| + HValue* base_;
|
| + int offset_;
|
| + int scale_;
|
| +};
|
| +
|
| +
|
| +class RangeEvaluationContext BASE_EMBEDDED {
|
| + public:
|
| + RangeEvaluationContext(HValue* value, HValue* upper);
|
| +
|
| + HValue* lower_bound() { return lower_bound_; }
|
| + HValue* lower_bound_guarantee() { return lower_bound_guarantee_; }
|
| + HValue* candidate() { return candidate_; }
|
| + HValue* upper_bound() { return upper_bound_; }
|
| + HValue* upper_bound_guarantee() { return upper_bound_guarantee_; }
|
| + int offset() { return offset_; }
|
| + int scale() { return scale_; }
|
| +
|
| + bool is_range_satisfied() {
|
| + return lower_bound_guarantee() != NULL && upper_bound_guarantee() != NULL;
|
| + }
|
| +
|
| + void set_lower_bound_guarantee(HValue* guarantee) {
|
| + lower_bound_guarantee_ = ConvertGuarantee(guarantee);
|
| + }
|
| + void set_upper_bound_guarantee(HValue* guarantee) {
|
| + upper_bound_guarantee_ = ConvertGuarantee(guarantee);
|
| + }
|
| +
|
| + void swap_candidate(DecompositionResult* other_candicate) {
|
| + other_candicate->SwapValues(&candidate_, &offset_, &scale_);
|
| + }
|
| +
|
| + private:
|
| + HValue* ConvertGuarantee(HValue* guarantee);
|
| +
|
| + HValue* lower_bound_;
|
| + HValue* lower_bound_guarantee_;
|
| + HValue* candidate_;
|
| + HValue* upper_bound_;
|
| + HValue* upper_bound_guarantee_;
|
| + int offset_;
|
| + int scale_;
|
| +};
|
| +
|
| +
|
| typedef EnumSet<GVNFlag> GVNFlagSet;
|
|
|
|
|
| @@ -977,25 +1095,32 @@ class HValue: public ZoneObject {
|
| virtual void Verify() = 0;
|
| #endif
|
|
|
| - // This method is recursive but it is guaranteed to terminate because
|
| - // RedefinedOperand() always dominates "this".
|
| - bool IsRelationTrue(NumericRelation relation, HValue* other) {
|
| - if (this == other) {
|
| - return NumericRelation::Eq().Implies(relation);
|
| - }
|
| + bool IsRelationTrue(NumericRelation relation,
|
| + HValue* other,
|
| + int offset = 0,
|
| + int scale = 0);
|
|
|
| - bool result = IsRelationTrueInternal(relation, other) ||
|
| - other->IsRelationTrueInternal(relation.Reversed(), this);
|
| - if (!result) {
|
| - HValue* redefined = RedefinedOperand();
|
| - if (redefined != NULL) {
|
| - result = redefined->IsRelationTrue(relation, other);
|
| - }
|
| + bool TryGuaranteeRange(HValue* upper_bound);
|
| + virtual bool TryDecompose(DecompositionResult* decomposition) {
|
| + if (RedefinedOperand() != NULL) {
|
| + return RedefinedOperand()->TryDecompose(decomposition);
|
| + } else {
|
| + return false;
|
| }
|
| - return result;
|
| }
|
|
|
| protected:
|
| + void TryGuaranteeRangeRecursive(RangeEvaluationContext* context);
|
| +
|
| + enum RangeGuaranteeDirection {
|
| + DIRECTION_NONE = 0,
|
| + DIRECTION_UPPER = 1,
|
| + DIRECTION_LOWER = 2,
|
| + DIRECTION_BOTH = DIRECTION_UPPER | DIRECTION_LOWER
|
| + };
|
| + virtual void SetResponsibilityForRange(RangeGuaranteeDirection direction) {}
|
| + virtual void TryGuaranteeRangeChanging(RangeEvaluationContext* context) {}
|
| +
|
| // This function must be overridden for instructions with flag kUseGVN, to
|
| // compare the non-Operand parts of the instruction.
|
| virtual bool DataEquals(HValue* other) {
|
| @@ -1059,7 +1184,12 @@ class HValue: public ZoneObject {
|
|
|
| // Informative definitions can override this method to state any numeric
|
| // relation they provide on the redefined value.
|
| - virtual bool IsRelationTrueInternal(NumericRelation relation, HValue* other) {
|
| + // Returns true if it is guaranteed that:
|
| + // ((this + offset) >> scale) relation other
|
| + virtual bool IsRelationTrueInternal(NumericRelation relation,
|
| + HValue* other,
|
| + int offset = 0,
|
| + int scale = 0) {
|
| return false;
|
| }
|
|
|
| @@ -1308,9 +1438,11 @@ class HNumericConstraint : public HTemplateInstruction<2> {
|
| virtual void PrintDataTo(StringStream* stream);
|
|
|
| virtual bool IsRelationTrueInternal(NumericRelation other_relation,
|
| - HValue* other_related_value) {
|
| + HValue* other_related_value,
|
| + int offset = 0,
|
| + int scale = 0) {
|
| if (related_value() == other_related_value) {
|
| - return relation().Implies(other_relation);
|
| + return relation().CompoundImplies(other_relation, offset, scale);
|
| } else {
|
| return false;
|
| }
|
| @@ -1325,7 +1457,6 @@ class HNumericConstraint : public HTemplateInstruction<2> {
|
| : relation_(relation) {
|
| SetOperandAt(0, constrained_value);
|
| SetOperandAt(1, related_value);
|
| - set_representation(constrained_value->representation());
|
| }
|
|
|
| NumericRelation relation_;
|
| @@ -2992,7 +3123,10 @@ class HPhi: public HValue {
|
| inputs_[index] = value;
|
| }
|
|
|
| - virtual bool IsRelationTrueInternal(NumericRelation relation, HValue* other);
|
| + virtual bool IsRelationTrueInternal(NumericRelation relation,
|
| + HValue* other,
|
| + int offset = 0,
|
| + int scale = 0);
|
|
|
| private:
|
| ZoneList<HValue*> inputs_;
|
| @@ -3024,9 +3158,11 @@ class HInductionVariableAnnotation : public HUnaryOperation {
|
| virtual void PrintDataTo(StringStream* stream);
|
|
|
| virtual bool IsRelationTrueInternal(NumericRelation other_relation,
|
| - HValue* other_related_value) {
|
| + HValue* other_related_value,
|
| + int offset = 0,
|
| + int scale = 0) {
|
| if (induction_base() == other_related_value) {
|
| - return relation().Implies(other_relation);
|
| + return relation().CompoundImplies(other_relation, offset, scale);
|
| } else {
|
| return false;
|
| }
|
| @@ -3040,7 +3176,6 @@ class HInductionVariableAnnotation : public HUnaryOperation {
|
| int operand_index)
|
| : HUnaryOperation(phi),
|
| phi_(phi), relation_(relation), operand_index_(operand_index) {
|
| - set_representation(phi->representation());
|
| }
|
|
|
| // We need to store the phi both here and in the instruction operand because
|
| @@ -3445,6 +3580,9 @@ enum BoundsCheckKeyMode {
|
| };
|
|
|
|
|
| +class HBoundsCheckBaseIndexInformation;
|
| +
|
| +
|
| class HBoundsCheck: public HTemplateInstruction<2> {
|
| public:
|
| // Normally HBoundsCheck should be created using the
|
| @@ -3456,7 +3594,9 @@ class HBoundsCheck: public HTemplateInstruction<2> {
|
| HValue* length,
|
| BoundsCheckKeyMode key_mode = DONT_ALLOW_SMI_KEY,
|
| Representation r = Representation::None())
|
| - : key_mode_(key_mode), skip_check_(false) {
|
| + : key_mode_(key_mode), skip_check_(false),
|
| + base_(NULL), offset_(0), scale_(0),
|
| + responsibility_direction_(DIRECTION_NONE) {
|
| SetOperandAt(0, index);
|
| SetOperandAt(1, length);
|
| if (r.IsNone()) {
|
| @@ -3473,6 +3613,33 @@ class HBoundsCheck: public HTemplateInstruction<2> {
|
|
|
| bool skip_check() { return skip_check_; }
|
| void set_skip_check(bool skip_check) { skip_check_ = skip_check; }
|
| + HValue* base() { return base_; }
|
| + int offset() { return offset_; }
|
| + int scale() { return scale_; }
|
| + bool index_can_increase() {
|
| + return (responsibility_direction_ & DIRECTION_LOWER) == 0;
|
| + }
|
| + bool index_can_decrease() {
|
| + return (responsibility_direction_ & DIRECTION_UPPER) == 0;
|
| + }
|
| +
|
| + void ApplyIndexChange();
|
| + bool DetectCompoundIndex() {
|
| + ASSERT(base() == NULL);
|
| +
|
| + DecompositionResult decomposition;
|
| + if (index()->TryDecompose(&decomposition)) {
|
| + base_ = decomposition.base();
|
| + offset_ = decomposition.offset();
|
| + scale_ = decomposition.scale();
|
| + return true;
|
| + } else {
|
| + base_ = index();
|
| + offset_ = 0;
|
| + scale_ = 0;
|
| + return false;
|
| + }
|
| + }
|
|
|
| virtual Representation RequiredInputRepresentation(int arg_index) {
|
| return representation();
|
| @@ -3482,7 +3649,9 @@ class HBoundsCheck: public HTemplateInstruction<2> {
|
| }
|
|
|
| virtual bool IsRelationTrueInternal(NumericRelation relation,
|
| - HValue* related_value);
|
| + HValue* related_value,
|
| + int offset = 0,
|
| + int scale = 0);
|
|
|
| virtual void PrintDataTo(StringStream* stream);
|
| virtual void InferRepresentation(HInferRepresentation* h_infer);
|
| @@ -3497,9 +3666,61 @@ class HBoundsCheck: public HTemplateInstruction<2> {
|
| DECLARE_CONCRETE_INSTRUCTION(BoundsCheck)
|
|
|
| protected:
|
| + friend class HBoundsCheckBaseIndexInformation;
|
| +
|
| + virtual void SetResponsibilityForRange(RangeGuaranteeDirection direction) {
|
| + responsibility_direction_ = static_cast<RangeGuaranteeDirection>(
|
| + responsibility_direction_ | direction);
|
| + }
|
| +
|
| virtual bool DataEquals(HValue* other) { return true; }
|
| + virtual void TryGuaranteeRangeChanging(RangeEvaluationContext* context);
|
| BoundsCheckKeyMode key_mode_;
|
| bool skip_check_;
|
| + HValue* base_;
|
| + int offset_;
|
| + int scale_;
|
| + RangeGuaranteeDirection responsibility_direction_;
|
| +};
|
| +
|
| +
|
| +class HBoundsCheckBaseIndexInformation: public HTemplateInstruction<2> {
|
| + public:
|
| + explicit HBoundsCheckBaseIndexInformation(HBoundsCheck* check) {
|
| + DecompositionResult decomposition;
|
| + if (check->index()->TryDecompose(&decomposition)) {
|
| + SetOperandAt(0, decomposition.base());
|
| + SetOperandAt(1, check);
|
| + } else {
|
| + UNREACHABLE();
|
| + }
|
| + }
|
| +
|
| + HValue* base_index() { return OperandAt(0); }
|
| + HBoundsCheck* bounds_check() { return HBoundsCheck::cast(OperandAt(1)); }
|
| +
|
| + DECLARE_CONCRETE_INSTRUCTION(BoundsCheckBaseIndexInformation)
|
| +
|
| + virtual Representation RequiredInputRepresentation(int arg_index) {
|
| + return representation();
|
| + }
|
| +
|
| + virtual bool IsRelationTrueInternal(NumericRelation relation,
|
| + HValue* related_value,
|
| + int offset = 0,
|
| + int scale = 0);
|
| + virtual void PrintDataTo(StringStream* stream);
|
| +
|
| + virtual int RedefinedOperandIndex() { return 0; }
|
| + virtual bool IsPurelyInformativeDefinition() { return true; }
|
| +
|
| + protected:
|
| + virtual void SetResponsibilityForRange(RangeGuaranteeDirection direction) {
|
| + bounds_check()->SetResponsibilityForRange(direction);
|
| + }
|
| + virtual void TryGuaranteeRangeChanging(RangeEvaluationContext* context) {
|
| + bounds_check()->TryGuaranteeRangeChanging(context);
|
| + }
|
| };
|
|
|
|
|
| @@ -4092,21 +4313,16 @@ class HAdd: public HArithmeticBinaryOperation {
|
|
|
| virtual HValue* Canonicalize();
|
|
|
| - virtual bool IsRelationTrueInternal(NumericRelation relation, HValue* other) {
|
| - HValue* base = NULL;
|
| - int32_t offset = 0;
|
| + virtual bool TryDecompose(DecompositionResult* decomposition) {
|
| if (left()->IsInteger32Constant()) {
|
| - base = right();
|
| - offset = left()->GetInteger32Constant();
|
| + decomposition->Apply(right(), left()->GetInteger32Constant());
|
| + return true;
|
| } else if (right()->IsInteger32Constant()) {
|
| - base = left();
|
| - offset = right()->GetInteger32Constant();
|
| + decomposition->Apply(left(), right()->GetInteger32Constant());
|
| + return true;
|
| } else {
|
| return false;
|
| }
|
| -
|
| - return relation.IsExtendable(offset)
|
| - ? base->IsRelationTrue(relation, other) : false;
|
| }
|
|
|
| DECLARE_CONCRETE_INSTRUCTION(Add)
|
| @@ -4135,12 +4351,10 @@ class HSub: public HArithmeticBinaryOperation {
|
|
|
| virtual HValue* Canonicalize();
|
|
|
| - virtual bool IsRelationTrueInternal(NumericRelation relation, HValue* other) {
|
| + virtual bool TryDecompose(DecompositionResult* decomposition) {
|
| if (right()->IsInteger32Constant()) {
|
| - HValue* base = left();
|
| - int32_t offset = right()->GetInteger32Constant();
|
| - return relation.IsExtendable(-offset)
|
| - ? base->IsRelationTrue(relation, other) : false;
|
| + decomposition->Apply(left(), -right()->GetInteger32Constant());
|
| + return true;
|
| } else {
|
| return false;
|
| }
|
| @@ -4375,6 +4589,18 @@ class HShr: public HBitwiseBinaryOperation {
|
| HValue* left,
|
| HValue* right);
|
|
|
| + virtual bool TryDecompose(DecompositionResult* decomposition) {
|
| + if (right()->IsInteger32Constant()) {
|
| + if (decomposition->Apply(left(), 0, right()->GetInteger32Constant())) {
|
| + // This is intended to look for HAdd and HSub, to handle compounds
|
| + // like ((base + offset) >> scale) with one single decomposition.
|
| + left()->TryDecompose(decomposition);
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| virtual Range* InferRange(Zone* zone);
|
|
|
| DECLARE_CONCRETE_INSTRUCTION(Shr)
|
| @@ -4395,6 +4621,18 @@ class HSar: public HBitwiseBinaryOperation {
|
| HValue* left,
|
| HValue* right);
|
|
|
| + virtual bool TryDecompose(DecompositionResult* decomposition) {
|
| + if (right()->IsInteger32Constant()) {
|
| + if (decomposition->Apply(left(), 0, right()->GetInteger32Constant())) {
|
| + // This is intended to look for HAdd and HSub, to handle compounds
|
| + // like ((base + offset) >> scale) with one single decomposition.
|
| + left()->TryDecompose(decomposition);
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| virtual Range* InferRange(Zone* zone);
|
|
|
| DECLARE_CONCRETE_INSTRUCTION(Sar)
|
|
|