Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Unified Diff: src/hydrogen.cc

Issue 11415173: Array bounds check elimination. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Refactoring (and fixing refactoring issues). Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/hydrogen.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/hydrogen.cc
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 1590ab36698d9d5b98605111966ed17b8c7bc2a8..9828220d172cf958b21aec3b83dd9e31ad1b6895 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -71,7 +71,8 @@ HBasicBlock::HBasicBlock(HGraph* graph)
parent_loop_header_(NULL),
is_inline_return_target_(false),
is_deoptimizing_(false),
- dominates_loop_successors_(false) { }
+ dominates_loop_successors_(false),
+ is_osr_entry_(false) { }
void HBasicBlock::AttachLoopInformation() {
@@ -596,6 +597,11 @@ HConstant* HGraph::GetConstantInt32(SetOncePointer<HConstant>* pointer,
}
+HConstant* HGraph::GetConstant0() {
+ return GetConstantInt32(&constant_0_, 0);
+}
+
+
HConstant* HGraph::GetConstant1() {
return GetConstantInt32(&constant_1_, 1);
}
@@ -3326,7 +3332,11 @@ bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
HStackCheckEliminator sce(this);
sce.Process();
- EliminateRedundantBoundsChecks();
+ if (FLAG_array_bounds_checks_dehoisting) {
+ EliminateOrDehoistBoundsChecks();
+ } else {
+ EliminateRedundantBoundsChecks();
+ }
DehoistSimpleArrayIndexComputations();
if (FLAG_dead_code_elimination) DeadCodeElimination();
@@ -3670,6 +3680,886 @@ void HGraph::EliminateRedundantBoundsChecks() {
}
+// Helper class for array access index manipulation in
+// BoundsCheckValueInfo::CoverCheckWithBase.
+// The indexes have the form "base + constant" and the manipulations involve
+// changing the constant. Since the original constant HValue could be used
+// elsewhere we cannot change it and must create a new one.
+// However we need to create the HAdd only the first time we manipulate an
+// index, and later on we just reuse the instruction.
+class BoundsCheckManipulationData: public ZoneObject {
+ public:
+ HValue* Base() { return base_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ HBoundsCheck* Check() { return check_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ int Offset() { return offset_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+
+ // Change the index to use a new offset.
+ void ChangeOffset(int new_offset) {
+ HConstant* new_constant = new(zone())
Jakob Kummerow 2012/12/05 15:20:02 nit: please break the line after '=', and indent t
+ HConstant(new_offset, Representation::Integer32());
+ if (added_add_ == NULL) {
+ new_constant->InsertBefore(Check());
+ added_add_ = new(zone()) HAdd(NULL, Base(), new_constant);
+ added_add_->AssumeRepresentation(Check()->index()->representation());
+ added_add_->InsertBefore(Check());
+ } else {
+ new_constant->InsertBefore(added_add_);
+ added_offset_->DeleteAndReplaceWith(new_constant);
+ }
+ added_offset_ = new_constant;
+ Check()->SetOperandAt(0, added_add_);
Jakob Kummerow 2012/12/05 15:20:02 I'd prefer introducing a method HBoundsCheck::SetI
+ offset_ = new_offset;
+ }
+
+ // If we ended up creating an "add zero" instruction we can remove it.
+ void RemoveNullAdd() {
Jakob Kummerow 2012/12/05 15:20:02 Why do we have to do this as an additional step? C
+ if (added_add_ != NULL && added_offset_->Integer32Value() == 0) {
+ added_add_->DeleteAndReplaceWith(added_add_->left());
+ added_offset_->DeleteAndReplaceWith(NULL);
+ added_add_ = NULL;
+ added_offset_ = NULL;
+ }
+ }
+
+ BoundsCheckManipulationData(HValue* base, HBoundsCheck* check, int offset) :
Jakob Kummerow 2012/12/05 15:20:02 nit: ':' goes on the next line, with 4-space inden
+ base_(base), check_(check), offset_(offset),
+ added_add_(NULL), added_offset_(NULL) {}
+
+ private:
+ HValue* base_;
+ HBoundsCheck* check_;
+ int offset_;
+ HAdd* added_add_;
+ HConstant* added_offset_;
+
+ Zone* zone() { return Check()->block()->zone(); }
Jakob Kummerow 2012/12/05 15:20:02 nit: methods before data members
+};
+
+
+class BoundsCheckValueInfo;
+
+
+static bool BoundsCheckValueInfoMatch(void* v1, void* v2);
+
+
+// The "knowledge base" used for array bounds check elimination.
+// A hash table indexed by HValue ID and containing a list of known facts for
+// the given value.
+class BoundsCheckValueInfoTable : public ZoneObject {
+ public:
+ Zone* zone() { return zone_; }
+
+ // If the entry is in the table but the list is empty, remove it.
+ void CleanupKey(HValue* key);
+
+ BoundsCheckValueInfo** LookupKey(HValue* key);
+ BoundsCheckValueInfo** LookupOrInsert(HValue* key);
+ BoundsCheckValueInfo** Insert(BoundsCheckValueInfo* data);
+ void Delete(HValue* key);
+
+ explicit BoundsCheckValueInfoTable(Zone* zone)
Jakob Kummerow 2012/12/05 15:20:02 nit: the constructor should be the first thing in
+ : zone_(zone),
+ map_(BoundsCheckValueInfoMatch,
+ ZoneHashMap::kDefaultHashMapCapacity,
+ ZoneAllocationPolicy(zone)){ }
Jakob Kummerow 2012/12/05 15:20:02 nit: space before '{'
+ private:
Jakob Kummerow 2012/12/05 15:20:02 nit: empty line before "private:" (doesn't presubm
+ Zone* zone_;
+ ZoneHashMap map_;
+};
+
+
+enum ValueInfoRelation {
+ NONE,
+ NE,
Jakob Kummerow 2012/12/05 15:20:02 Feel free to put all values onto the same line (I
+ GT,
+ GE,
+ EQ,
+ LE,
+ LT
+};
+
+
+class BoundsCheckRemovalBlockContext BASE_EMBEDDED {
+ public:
+ HBasicBlock* block() { return block_; }
+ BoundsCheckValueInfoTable* table() { return table_; }
+ BoundsCheckValueInfo* data_list_head() { return bb_data_list_; }
+ BoundsCheckValueInfo** data_insertion_point() { return &bb_data_list_; }
+ BoundsCheckRemovalBlockContext* previous() { return previous_; }
+
+ BoundsCheckValueInfo* NewValueInfo(HValue* v,
+ HValue* c,
+ ValueInfoRelation r,
+ bool is_incremental,
+ HBoundsCheck* bounds_check = NULL,
+ int check_offset = 0);
+
+ BoundsCheckRemovalBlockContext(
Jakob Kummerow 2012/12/05 15:20:02 again, constructor comes first
+ HBasicBlock* block,
+ BoundsCheckRemovalBlockContext* previous)
+ : table_(previous != NULL ? previous->table() :
Jakob Kummerow 2012/12/05 15:20:02 proper indentation please: : table_(previous
+ new(block->graph()->zone())
+ BoundsCheckValueInfoTable(block->graph()->zone())),
+ block_(block), bb_data_list_(NULL), previous_(previous) {}
+
+ private:
+ BoundsCheckValueInfoTable* table_;
+ HBasicBlock* block_;
+ BoundsCheckValueInfo* bb_data_list_;
+ BoundsCheckRemovalBlockContext* previous_;
+};
+
+
+// The individual "fact" in the knowledge base.
+// Each fact has the form "value relation constraint", where value and
Jakob Kummerow 2012/12/05 15:20:02 suggestion: // Each fact has the form (value, rela
+// constraint are both HValue instances and "relation" can be one of the
+// usual relational operators (less than, greater than, ecc).
+class BoundsCheckValueInfo: public ZoneObject {
+ public:
+ HValue* Value() { return value_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ ValueInfoRelation Relation() { return relation_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ HValue* Constraint() { return constraint_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ // True if Value() will "cover" every single integer value in the range
Jakob Kummerow 2012/12/05 15:20:02 s/"cover"/take/
+ // defined by the relation.
+ // This could be used to detect in advance that an array is not holey
+ // because a loop will write every single array element.
+ bool IsIncremental() { return is_incremental_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+
+ bool ConstraintIsZero() {
+ if (!constraint_->IsConstant()) { return false; }
+ HConstant* c = HConstant::cast(constraint_);
+ return c->DoubleValue() == 0.0;
Jakob Kummerow 2012/12/05 15:20:02 This won't work for non-number constants. Use "ret
+ }
+ Zone* zone() { return Value()->block()->zone(); }
+
+ // A HBoundsCheck on the same index; it is equivalent to having...
Jakob Kummerow 2012/12/05 15:20:02 ...?
+ HBoundsCheck* BoundsCheck() { return bounds_check_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+
+ // The following properties are all helpers for CoverCheckWithBase.
Jakob Kummerow 2012/12/05 15:20:02 If they're helpers for another member, why are the
+ // To make things easier at construction time every list head will just copy
+ // the values from the next element (which is the previous one in the
+ // dominator tree traversal).
+ // This simplifies both searching (the list head always holds current values)
+ // and backtracking (the remaining list head has naturally the right values).
+
+ // Offset used by CoverCheckWithBase.
+ int CheckOffset() { return check_offset_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ // Lower check used by CoverCheckWithBase.
+ BoundsCheckManipulationData* LowerCheck() { return lower_check_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ // Upper check used by CoverCheckWithBase.
+ BoundsCheckManipulationData* UpperCheck() { return upper_check_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ // Initial state for CoverCheckWithBase (both checks should be NULL).
+ bool HasNoBaseHandledCheck() { return lower_check_ == NULL; }
Jakob Kummerow 2012/12/05 15:20:02 What's a BaseHandledCheck? Why not just "HasNoChec
+ // "single check" state for CoverCheckWithBase.
+ bool HasSingleBaseHandledCheck() {
+ return lower_check_ != NULL && lower_check_ == upper_check_;
+ }
+ // End of CoverCheckWithBase helpers.
+
+ // Basic block in which this fact was created.
+ HBasicBlock* BasicBlock() const { return basic_block_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ // Next known fact for this value.
+ BoundsCheckValueInfo* NextForValue() { return next_for_value_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+ // Next fact created in this basic block.
+ BoundsCheckValueInfo* NextInBasicBlock() { return next_in_basic_block_; }
Jakob Kummerow 2012/12/05 15:20:02 nit: lower-case names for simple accessors.
+
+
+ // This method handles the special case of indexes like "base + constant".
+ // For any given HValue "exp", two bounds checks "exp + c1" and exp + c2"
+ // will obviously "cover" every other array access where the index value is
+ // between "exp + c1" and exp + c2".
+ // Now, suppose that we meet an index "exp + c3" which is out of this range.
+ // If we are in the same basic block we can easily change either c1 or c2
+ // in one of the two previous checks so that the 3rd check is redundant.
+ // (if we are not in the same BB the change would be optimistic and could
+ // lead to "unneeded" deoptimizations so we don't do it).
+ // This method does exactly that, handling changes between these states:
+ // - No previous check was met (HasNoBaseHandledCheck() == true)
+ // - We have one previous check (HasSingleBaseHandledCheck() == true)
+ // - We have two previous checks.
+ // The class BoundsCheckManipulationData helps keeping track of the changed
+ // instructions so that it is easy to change than again if needed.
Jakob Kummerow 2012/12/05 15:20:02 s/than/them/
+ bool CoverCheckWithBase(
Jakob Kummerow 2012/12/05 15:20:02 nit: for method declarations (as opposed to calls)
+ HValue* base, HBoundsCheck* new_check, int32_t new_offset) {
+ ASSERT(new_check->index()->representation().IsInteger32());
+ bool remove_new_check = false;
Jakob Kummerow 2012/12/05 15:20:02 Why do we need a variable for this? Looks like ass
+
+ if (HasNoBaseHandledCheck()) {
+ lower_check_ = new(zone())
+ BoundsCheckManipulationData(base, new_check, new_offset);
+ upper_check_ = lower_check_;
+ } else {
+ if (new_offset > UpperCheck()->Offset()) {
+ if (HasSingleBaseHandledCheck() ||
+ upper_check_->Check()->block() != BasicBlock()) {
Jakob Kummerow 2012/12/05 15:20:02 Why is this check necessary? BoundsCheckValueInfo
+ upper_check_ = new(zone())
+ BoundsCheckManipulationData(base, new_check, new_offset);
+ } else {
+ upper_check_->ChangeOffset(new_offset);
+ remove_new_check = true;
+ }
+ } else if (new_offset < LowerCheck()->Offset()) {
+ if (HasSingleBaseHandledCheck() ||
+ lower_check_->Check()->block() != BasicBlock()) {
+ lower_check_ = new(zone())
+ BoundsCheckManipulationData(base, new_check, new_offset);
+ } else {
+ lower_check_->ChangeOffset(new_offset);
+ remove_new_check = true;
+ }
+ } else {
+ remove_new_check = true;
+ }
+ }
+
+ return remove_new_check;
+ }
+
+ void RemoveNullAdds() {
+ if (lower_check_ == NULL) return;
+ lower_check_->RemoveNullAdd();
+ if (upper_check_ == lower_check_) return;
+ upper_check_->RemoveNullAdd();
+ }
+
+ uint32_t Hash() {
+ return static_cast<uint32_t>(value_->Hashcode());
+ }
+
+ // Instead of doing a graph walk we attempt to simplify the index.
+ // Since simplifications can be recursive we put a limit on the number
+ // of steps we take.
+ // This is roughly equivalent to limiting the graph walks to only a
+ // given number of steps (but is way easier to implement...).
+ static const int kDepthLimit = 3;
+
+ // The main method that queries the knowledge base to see if a given value
+ // has a relation with a given limit ("value" is the array index and
+ // "limit" is the array length).
+ // If, as a result, both *lower_covered and *upper_covered are true then the
+ // bounds check can be eliminated: the lower part stands for "value >= 0"
+ // and the upper part for "value < limit".
+ // Either of them is NULL it means that the caller is not interested in that
+ // part of the relation.
+ // Depth limits the complexity of the attempted value decompositions.
+ static void ComputeIndexRelation(HValue* value,
+ HValue* limit,
+ HBasicBlock* block,
+ HValue** hoisted_limit,
+ bool* lower_covered,
+ bool* upper_covered,
+ BoundsCheckValueInfoTable* table,
+ int depth) {
+ BoundsCheckValueInfo** value_head;
+ if (lower_covered == NULL && upper_covered == NULL) return;
+ if (depth == 0) {
+ value_head = table->LookupOrInsert(value);
+ } else if (depth > kDepthLimit) {
+ return;
+ } else {
+ value_head = table->LookupKey(value);
+ if (value_head == NULL) {
+ return;
+ }
+ }
+
+ for (BoundsCheckValueInfo* value_info = *value_head;
+ value_info != NULL;
Jakob Kummerow 2012/12/05 15:20:02 nit: aligning "value_info" with "BoundsCheckValueI
+ value_info = value_info->NextForValue()) {
+ if (lower_covered != NULL) {
+ if (value_info->ConstraintIsZero() && value_info->Relation() == GE) {
+ *lower_covered = true;
+ }
+ }
+ if (upper_covered != NULL) {
Jakob Kummerow 2012/12/05 15:20:02 Ugh. 5 levels of nested "if"s? I'd prefer the foll
+ if (value_info->Relation() == LT) {
+ if (value_info->Constraint() == limit) {
+ *upper_covered = true;
+ } else if (hoisted_limit != NULL) {
+ HValue* constraint = value_info->Constraint();
+ if (constraint->block()->Dominates(
+ block->loop_information()->loop_header())) {
+ if (*hoisted_limit == NULL ||
+ constraint->block()->Dominates((*hoisted_limit)->block())) {
+ *hoisted_limit = constraint;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!((lower_covered == NULL || *lower_covered) &&
+ (upper_covered == NULL || *upper_covered))) {
Jakob Kummerow 2012/12/05 15:20:02 Please indent this so that the indentation reflect
+ bool skip_lower = false;
+ bool skip_upper = false;
+ int32_t constant;
+ HValue* simplified_value = SimplifyValue(
+ value, &skip_lower, &skip_upper, &constant);
+ if (simplified_value != NULL) {
+ ComputeIndexRelation(simplified_value, limit, block, hoisted_limit,
+ skip_lower ? NULL : lower_covered,
+ skip_upper ? NULL : upper_covered,
+ table, depth + 1);
+ }
+ }
+
+ if (!((lower_covered == NULL || *lower_covered) &&
Jakob Kummerow 2012/12/05 15:20:02 same here
+ (upper_covered == NULL || *upper_covered))) {
+ if (value->IsPhi()) {
+ HPhi* phi = HPhi::cast(value);
+ bool lower_skip_state = false;
+ bool upper_skip_state = false;
+ for (int i = 0;
+ i < phi->OperandCount() && !(lower_skip_state && upper_skip_state);
+ i++) {
+ bool lower_skip_argument = false;
+ bool upper_skip_argument = false;
+ ComputeIndexRelation(phi->OperandAt(i), limit, block, hoisted_limit,
+ &lower_skip_argument,
+ &upper_skip_argument,
+ table, depth + 1);
+ if (lower_skip_argument) {
+ lower_skip_state = true;
+ }
+ if (upper_skip_argument) {
+ upper_skip_state = true;
+ }
+ }
+
+ if (lower_covered != NULL && !lower_skip_state) *lower_covered = true;
+ if (upper_covered != NULL && !upper_skip_state) *upper_covered = true;
+ }
+ }
+ }
+
+ static bool CoverBasePlusConstant(HBoundsCheck* check,
Jakob Kummerow 2012/12/05 15:20:02 Another method that doesn't tell me what the metho
+ BoundsCheckRemovalBlockContext* context) {
+ HValue* index_base;
+ int32_t offset;
+ if (GetCheckIndexBase(check->index(), &index_base, &offset)) {
+ BoundsCheckValueInfo** base_head =
+ context->table()->LookupOrInsert(index_base);
+ BoundsCheckValueInfo* base_info = *base_head;
+ if (base_info == NULL) {
+ base_info = context->NewValueInfo(
+ index_base, check->length(), NONE, false, check, offset);
+ }
+ return base_info->CoverCheckWithBase(index_base, check, offset);
+ } else {
Jakob Kummerow 2012/12/05 15:20:02 nit: no need for "else", just "return false".
+ return false;
+ }
+ }
+
+ static void HandleCheck(HBoundsCheck* check,
+ BoundsCheckRemovalBlockContext* context) {
+ HValue* index = check->index();
+ HValue* length = check->length();
+ bool lower_covered = false;
+ bool upper_covered = false;
+ HBasicBlock* block = context->block();
+ HValue* hoisted_limit = NULL;
+ HValue** hoisted_limit_pointer;
Jakob Kummerow 2012/12/05 15:20:02 initialize this to NULL here...
+
+ if (block->graph()->use_optimistic_licm() &&
+ block->loop_information() != NULL &&
+ length->block()->Dominates(block->loop_information()->loop_header())) {
+ hoisted_limit_pointer = &hoisted_limit;
+ } else {
+ hoisted_limit_pointer = NULL;
Jakob Kummerow 2012/12/05 15:20:02 ...and drop this.
+ }
+
+ ComputeIndexRelation(
+ index, length, block, hoisted_limit_pointer,
Jakob Kummerow 2012/12/05 15:20:02 nit: no need to break the first line of arguments
+ &lower_covered, &upper_covered, context->table(), 0);
+
+ if (!(lower_covered && upper_covered)) {
+ if (CoverBasePlusConstant(check, context)) {
+ lower_covered = true;
+ upper_covered = true;
+ }
+ }
+
+ if ((!(lower_covered && upper_covered)) && hoisted_limit != NULL) {
Jakob Kummerow 2012/12/05 15:20:02 simplification: you can remove one set of parenthe
+ HBasicBlock* hoisting_block =
+ block->loop_information()->loop_pre_header();
+ BoundsCheckRemovalBlockContext* hoisting_context = context;
+ while (hoisting_context != NULL &&
+ hoisting_context->block() != hoisting_block) {
Jakob Kummerow 2012/12/05 15:20:02 nit: prefer alignment at '(' (i.e. in this case th
+ hoisting_context = hoisting_context->previous();
+ }
+
+ if (hoisting_context != NULL) {
+ HBoundsCheck* new_check =
+ new(context->table()->zone()) HBoundsCheck(hoisted_limit, length);
+ new_check->InsertBefore(hoisting_block->last());
+ context->NewValueInfo(
+ index, block->graph()->GetConstant0(), GE, false);
+ context->NewValueInfo(index, length, LT, false);
+ lower_covered = true;
+ upper_covered = true;
+ }
+ }
+
+ bool remove_check = true;
+ if (!lower_covered) {
+ remove_check = false;
+ context->NewValueInfo(index, block->graph()->GetConstant0(), GE, false);
+ }
+ if (!upper_covered) {
+ remove_check = false;
+ context->NewValueInfo(index, length, LT, false);
+ }
+ if (remove_check) {
+ check->DeleteAndReplaceWith(NULL);
+ }
+ }
+
+ // Handles a HPhi definition, eventually creating a new fact.
+ static void FromPhi(HPhi* phi,
Jakob Kummerow 2012/12/05 15:20:02 nit: some other name please. We commonly use "stat
+ BoundsCheckRemovalBlockContext* context) {
+ if (phi->OperandCount() != 2) return;
+
+ ValueInfoRelation relation;
+ bool is_incremental;
+
+ relation = RelationFromMonotonicOperation(
+ phi, phi->OperandAt(0), &is_incremental);
+ if (relation != NONE) {
+ context->NewValueInfo(phi, phi->OperandAt(1), relation, is_incremental);
+ return;
+ }
+
+ relation = RelationFromMonotonicOperation(
+ phi, phi->OperandAt(1), &is_incremental);
+ if (relation != NONE) {
+ context->NewValueInfo(phi, phi->OperandAt(0), relation, is_incremental);
+ return;
+ }
+ }
+
+ // Handles a conditional branch, eventually creating new facts.
+ static void FromConditionalBranch(
Jakob Kummerow 2012/12/05 15:20:02 Again, please choose some name that doesn't sound
+ HCompareIDAndBranch* condition,
+ BoundsCheckRemovalBlockContext* context) {
+ HValue* value;
+ HValue* constraint;
+ ValueInfoRelation relation;
+ bool isElseBranch;
Jakob Kummerow 2012/12/05 15:20:02 nit: unix_hacker_style names for variables
+ if (context->block() == condition->SuccessorAt(0)) {
+ isElseBranch = false;
+ } else if (context->block() == condition->SuccessorAt(1)) {
+ isElseBranch = true;
+ } else {
+ return;
+ }
+
+ relation = FromToken(condition->token());
+ if (relation == NONE) return;
+
+ if (isElseBranch) {
+ value = condition->right();
+ constraint = condition->left();
+ } else {
+ value = condition->left();
+ constraint = condition->right();
+ }
+
+ context->NewValueInfo(value, constraint, relation, false);
+ context->NewValueInfo(constraint, value, Switched(relation), false);
Jakob Kummerow 2012/12/05 15:20:02 Whoa. Do we really need to store every fact twice?
+ }
+
+ static const char* RelationName(ValueInfoRelation r) {
+ switch (r) {
+ case NE:
+ return "NE";
+ case GT:
+ return "GT";
+ case GE:
+ return "GE";
+ case EQ:
+ return "EQ";
+ case LE:
+ return "LE";
+ case LT:
+ return "LT";
+ case NONE:
+ return "NONE";
+ default:
Jakob Kummerow 2012/12/05 15:20:02 since you're handling all cases explicitly, you do
+ UNREACHABLE();
+ return "UNREACHABLE";
+ }
+ }
+
+ bool UnlinkFromValue() {
+ *previous_for_value_ = next_for_value_;
Jakob Kummerow 2012/12/05 15:20:02 Having different levels of pointer indirection for
+ if (next_for_value_ != NULL) {
+ next_for_value_->previous_for_value_ = previous_for_value_;
+ }
+ bool result = (next_for_value_ == NULL);
+ previous_for_value_ = NULL;
+ next_for_value_ = NULL;
+ RemoveNullAdds();
+ return result;
+ }
+
+ BoundsCheckValueInfo(HValue* v,
Jakob Kummerow 2012/12/05 15:20:02 nit: constructor should be the first thing in the
+ HValue* c,
Jakob Kummerow 2012/12/05 15:20:02 what do "v" and "c" mean?
+ ValueInfoRelation r,
+ bool is_incremental,
+ BoundsCheckRemovalBlockContext* context,
+ HBoundsCheck* bounds_check = NULL,
+ int check_offset = 0) :
Jakob Kummerow 2012/12/05 15:20:02 nit: ':' goes on the next line
+ value_(DiscardOsrValue(v)), constraint_(DiscardOsrValue(c)),
+ relation_(r), is_incremental_(is_incremental),
+ bounds_check_(bounds_check),
+ check_offset_(check_offset),
+ basic_block_(context->block()),
+ previous_for_value_(context->table()->LookupOrInsert(value_)),
+ next_for_value_(*previous_for_value_),
+ next_in_basic_block_(*context->data_insertion_point()),
+ lower_check_(next_for_value_ != NULL ?
Jakob Kummerow 2012/12/05 15:20:02 nit: if a ternary operator doesn't fit on one line
+ next_for_value_->LowerCheck() : NULL),
+ upper_check_(next_for_value_ != NULL ?
+ next_for_value_->UpperCheck() : NULL) {
+ if (next_for_value_ != NULL) {
+ next_for_value_->previous_for_value_ = &(next_for_value_);
+ }
+ *previous_for_value_ = this;
+ *context->data_insertion_point() = this;
+ }
+
+ private:
+ HValue* value_;
Jakob Kummerow 2012/12/05 15:20:02 nit: member variables come last in the "private:"
+ HValue* constraint_;
+ ValueInfoRelation relation_;
+ bool is_incremental_;
+ HBoundsCheck* bounds_check_;
+ int check_offset_;
+
+ HBasicBlock* basic_block_;
+ BoundsCheckValueInfo** previous_for_value_;
+ BoundsCheckValueInfo* next_for_value_;
+ BoundsCheckValueInfo* next_in_basic_block_;
+ BoundsCheckManipulationData* lower_check_;
+ BoundsCheckManipulationData* upper_check_;
+
+ // Detect if a value has the form "base plus constant".
+ static bool GetCheckIndexBase(HValue* index,
Jakob Kummerow 2012/12/05 15:20:02 Another method name that puzzles me, especially in
+ HValue** index_base,
+ int32_t* offset) {
+ *index_base = NULL;
+ *offset = 0;
+ if (!index->representation().IsInteger32()) return NULL;
+
+ HConstant* constant = NULL;
+ bool is_sub = false;
+
+ if (index->IsAdd()) {
+ HAdd* add = HAdd::cast(index);
+ if (add->left()->IsConstant()) {
+ constant = HConstant::cast(add->left());
+ *index_base = add->right();
+ } else if (add->right()->IsConstant()) {
+ constant = HConstant::cast(add->right());
+ *index_base = add->left();
+ }
+ } else if (index->IsSub()) {
+ HSub* sub = HSub::cast(index);
+ is_sub = true;
+ if (sub->left()->IsConstant()) {
Jakob Kummerow 2012/12/05 15:20:02 This was probably supposed to be sub->right()->IsC
+ constant = HConstant::cast(sub->left());
+ *index_base = sub->right();
+ }
+ }
+
+ if (constant != NULL && constant->HasInteger32Value()) {
+ *offset = is_sub ? - constant->Integer32Value()
+ : constant->Integer32Value();
+ return true;
+ }
+ return false;
+ }
+
+ // Attempts value simplification or decomposition.
+ static HValue* SimplifyValue(HValue* value,
+ bool* skip_lower,
Jakob Kummerow 2012/12/05 15:20:02 nit: indentation
+ bool* skip_upper,
+ int32_t* constant) {
+ HValue* result = NULL;
+ *constant = 0;
+
+ if (value->IsAdd()) {
+ HAdd* add = HAdd::cast(value);
+ HConstant* constant_operand = NULL;
+ if (add->left()->IsConstant()) {
Jakob Kummerow 2012/12/05 15:20:02 This code here is an almost exact duplicate of the
+ result = add->right();
+ constant_operand = HConstant::cast(add->left());
+ } else if (add->right()->IsConstant()) {
+ result = add->left();
+ constant_operand = HConstant::cast(add->right());
+ }
+ if (constant_operand != NULL && constant_operand->HasInteger32Value()) {
+ *constant = constant_operand->Integer32Value();
+ if (*constant > 0) {
+ *skip_upper = true;
+ } else if (*constant < 0) {
+ *skip_lower = true;
+ } else {
+ result = value;
+ }
+ } else {
+ result = NULL;
+ }
+ } else if (value->IsSub()) {
+ HSub* sub = HSub::cast(value);
+ HConstant* constant_operand = NULL;
+ if (sub->left()->IsConstant()) {
+ result = sub->right();
+ constant_operand = HConstant::cast(sub->left());
+ } else if (sub->right()->IsConstant()) {
+ result = sub->left();
+ constant_operand = HConstant::cast(sub->right());
+ }
+ if (constant_operand != NULL && constant_operand->HasInteger32Value()) {
+ *constant = constant_operand->Integer32Value();
+ if (*constant > 0) {
+ *skip_lower = true;
+ } else if (*constant > 0) {
Jakob Kummerow 2012/12/05 15:20:02 This is the same condition as before. You probably
+ *skip_upper = true;
+ } else {
+ result = value;
+ }
+ } else {
+ result = NULL;
+ }
+ }
+
+ return result;
+ }
+
+
+ static ValueInfoRelation FromToken(Token::Value token) {
Jakob Kummerow 2012/12/05 15:20:02 Having such a simple mapping from Token::Value to
+ switch (token) {
+ case Token::EQ:
+ case Token::EQ_STRICT:
+ return EQ;
+ case Token::LT:
+ return LT;
+ case Token::GT:
+ return GT;
+ case Token::LTE:
+ return LE;
+ case Token::GTE:
+ return GE;
+ default:
+ return NONE;
+ }
+ }
+
+ // Detects if a HPhi defines a monotonic operation, and if it does
+ // returns the appropriate relation.
+ // *value_increment is true if the value will assume every integer value
+ // in the range.
+ static ValueInfoRelation RelationFromMonotonicOperation(
+ HPhi* base, HValue* operation, bool* value_increment) {
Jakob Kummerow 2012/12/05 15:20:02 nit: for method/function declarations, either ever
+ *value_increment = false;
+
+ if (operation->opcode() == HValue::kAdd) {
+ HAdd* add = HAdd::cast(operation);
Jakob Kummerow 2012/12/05 15:20:02 nit: indentation
+ HValue* maybe_constant;
+ if (add->right() == base) {
+ maybe_constant = add->left();
+ } else if (add->left() == base) {
+ maybe_constant = add->right();
+ } else {
+ return NONE;
+ }
+
+ if (maybe_constant->IsConstant()) {
+ HConstant* c = HConstant::cast(maybe_constant);
+ if ((c->DoubleValue() == 1) || (c->DoubleValue() == -1))
Jakob Kummerow 2012/12/05 15:20:02 Again: 1) why DoubleValue() and not Integer32Value
+ *value_increment = true;
+ if (c->DoubleValue() > 0) {
+ return GE;
+ } else if (c->DoubleValue() < 0) {
+ return LE;
+ } else if (c->DoubleValue() == 0) {
+ return EQ;
+ } else {
+ return NONE;
+ }
+ }
+ return NONE;
+ } else if (operation->opcode() == HValue::kSub) {
+ HSub* sub = HSub::cast(operation);
+ if (sub->right() == base && sub->left()->IsConstant()) {
Jakob Kummerow 2012/12/05 15:20:02 this looks like another left/right mixup.
+ HConstant* c = HConstant::cast(sub->left());
+ if ((c->DoubleValue() == 1) || (c->DoubleValue() == -1))
+ *value_increment = true;
+ if (c->DoubleValue() > 0) {
+ return LE;
+ } else if (c->DoubleValue() < 0) {
+ return GE;
+ } else if (c->DoubleValue() == 0) {
+ return EQ;
+ } else {
+ return NONE;
+ }
+ } else {
+ return NONE;
+ }
+ }
+ return NONE;
+ }
+
+ static ValueInfoRelation Switched(ValueInfoRelation other) {
Jakob Kummerow 2012/12/05 15:20:02 "Switched"? Into shuffle mode? Also, we already h
+ switch (other) {
+ case NE:
+ return EQ;
+ case GT:
+ return LE;
+ case GE:
+ return LT;
+ case EQ:
+ return NE;
+ case LE:
+ return GT;
+ case LT:
+ return GE;
+ default:
+ UNREACHABLE();
+ return NONE;
+ }
+ }
+
+ // Helper method to discard a HPhi induced by OSR.
+ static HValue* DiscardOsrValue(HValue* v) {
+ if (!v->IsPhi()) return v;
+ HPhi* phi = HPhi::cast(v);
+ if (phi->OperandCount() != 2) return v;
+ if (phi->OperandAt(0)->block()->is_osr_entry()) {
+ return phi->OperandAt(1);
+ } else if (phi->OperandAt(1)->block()->is_osr_entry()) {
+ return phi->OperandAt(0);
+ } else {
+ return v;
+ }
+ }
+};
+
+
+BoundsCheckValueInfo* BoundsCheckRemovalBlockContext::NewValueInfo(
+ HValue* v,
+ HValue* c,
Jakob Kummerow 2012/12/05 15:20:02 what do "v" and "c" stand for?
+ ValueInfoRelation r,
+ bool is_incremental,
+ HBoundsCheck* bounds_check,
+ int check_offset) {
+ return new (table()->zone()) BoundsCheckValueInfo(
+ v, c, r, is_incremental, this, bounds_check, check_offset);
+}
+
+
+static bool BoundsCheckValueInfoMatch(void* v1, void* v2) {
+ BoundsCheckValueInfo* k1 = static_cast<BoundsCheckValueInfo*>(v1);
+ BoundsCheckValueInfo* k2 = static_cast<BoundsCheckValueInfo*>(v2);
+ return k1->Value() == k2->Value();
+}
+
+
+void BoundsCheckValueInfoTable::CleanupKey(HValue* key) {
+ ZoneHashMap::Entry* e = map_.Lookup(
+ key, static_cast<uint32_t>(key->Hashcode()), false,
+ ZoneAllocationPolicy(zone()));
+ if (e != NULL && e->value == NULL) {
+ map_.Remove(key, key->Hashcode());
+ }
+}
+
+
+BoundsCheckValueInfo** BoundsCheckValueInfoTable::LookupKey(HValue* key) {
+ ZoneHashMap::Entry* entry = map_.Lookup(
+ key, static_cast<uint32_t>(key->Hashcode()), false,
+ ZoneAllocationPolicy(zone()));
+ if (entry != NULL) {
+ return reinterpret_cast<BoundsCheckValueInfo**>(&(entry->value));
+ } else {
+ return NULL;
+ }
+}
+
+
+BoundsCheckValueInfo** BoundsCheckValueInfoTable::LookupOrInsert(HValue* key) {
+ return reinterpret_cast<BoundsCheckValueInfo**>(
+ &(map_.Lookup(key, static_cast<uint32_t>(key->Hashcode()), true,
+ ZoneAllocationPolicy(zone()))->value));
Jakob Kummerow 2012/12/05 15:20:02 nit: please align with "key"
+}
+
+
+BoundsCheckValueInfo** BoundsCheckValueInfoTable::Insert(
+ BoundsCheckValueInfo* data) {
+ BoundsCheckValueInfo** result = LookupOrInsert(data->Value());
+ *result = data;
+ return result;
+}
+
+
+void BoundsCheckValueInfoTable::Delete(HValue* key) {
+ map_.Remove(key, key->Hashcode());
+}
+
+
+// Recursive traversal of the dominator tree to remove array bounds checks.
+void HGraph::EliminateOrDehoistBoundsChecks(
+ HBasicBlock* bb,
+ BoundsCheckRemovalBlockContext* previous_context) {
+ BoundsCheckRemovalBlockContext context(bb, previous_context);
+
+ // Add facts deduced from conditional branches.
+ if (bb->predecessors()->length() == 1) {
+ HBasicBlock* predecessor = bb->predecessors()->at(0);
+ HInstruction* end = predecessor->last();
+ if (end->IsCompareIDAndBranch()) {
+ BoundsCheckValueInfo::FromConditionalBranch(
Jakob Kummerow 2012/12/05 15:20:02 Looks like "static void Foo::Bar(..., Baz*)" could
+ HCompareIDAndBranch::cast(end), &context);
+ }
+ }
+
+ // Add facts deduced from phi definitions.
+ for (int i = 0; i < bb->phis()->length(); i++) {
+ BoundsCheckValueInfo::FromPhi(bb->phis()->at(i), &context);
Jakob Kummerow 2012/12/05 15:20:02 again, why "void A::B(C, &context)" instead of "co
+ }
+
+ for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
+ if (!i->IsBoundsCheck()) continue;
+
+ HBoundsCheck* check = HBoundsCheck::cast(i);
+ check->ReplaceAllUsesWith(check->index());
Jakob Kummerow 2012/12/05 15:20:02 Why this line?
+ BoundsCheckValueInfo::HandleCheck(check, &context);
Jakob Kummerow 2012/12/05 15:20:02 again, why "void A::B(C, &context)" instead of "co
+ }
+
+ for (int i = 0; i < bb->dominated_blocks()->length(); ++i) {
+ EliminateOrDehoistBoundsChecks(
+ bb->dominated_blocks()->at(i), &context);
+ }
+
+ for (BoundsCheckValueInfo* value_info = context.data_list_head();
+ value_info != NULL;
Jakob Kummerow 2012/12/05 15:20:02 nit: aligning 'v' with 'B' looks better
+ value_info = value_info->NextInBasicBlock()) {
+ if (value_info->UnlinkFromValue()) {
+ context.table()->CleanupKey(value_info->Value());
+ }
+ }
+}
+
+
+void HGraph::EliminateOrDehoistBoundsChecks() {
+ HPhase phase("H_Eliminate or dehoist bounds checks", this);
+ EliminateOrDehoistBoundsChecks(entry_block(), NULL);
+}
+
+
static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
HValue* index = array_operation->GetKey();
if (!index->representation().IsInteger32()) return;
@@ -4303,6 +5193,7 @@ bool HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
non_osr_entry->Goto(loop_predecessor);
set_current_block(osr_entry);
+ osr_entry->set_osr_entry();
BailoutId osr_entry_id = statement->OsrEntryId();
int first_expression_index = environment()->first_expression_index();
int length = environment()->length();
« no previous file with comments | « src/hydrogen.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698