Index: src/crankshaft/hydrogen.cc |
diff --git a/src/crankshaft/hydrogen.cc b/src/crankshaft/hydrogen.cc |
index 1836fe6b30ce875d8fdd53181c4fa9a5531119ac..b49c37e40dcea0f41d54c429534118346ecae890 100644 |
--- a/src/crankshaft/hydrogen.cc |
+++ b/src/crankshaft/hydrogen.cc |
@@ -5401,42 +5401,67 @@ void HOptimizedGraphBuilder::BuildForInBody(ForInStatement* stmt, |
set_current_block(loop_body); |
+ // Compute the next enumerated value. |
HValue* key = Add<HLoadKeyed>(array, index, index, nullptr, FAST_ELEMENTS); |
+ HBasicBlock* continue_block = nullptr; |
if (fast) { |
- // Check if the expected map still matches that of the enumerable. |
- // If not just deoptimize. |
+ // Check if expected map still matches that of the enumerable. |
Add<HCheckMapValue>(enumerable, type); |
- Bind(each_var, key); |
+ Add<HSimulate>(stmt->FilterId()); |
} else { |
+ // We need the continue block here to be able to skip over invalidated keys. |
+ continue_block = graph()->CreateBasicBlock(); |
+ |
+ // We cannot use the IfBuilder here, since we need to be able to jump |
+ // over the loop body in case of undefined result from %ForInFilter, |
+ // and the poor soul that is the IfBuilder get's really confused about |
+ // such "advanced control flow requirements". |
+ HBasicBlock* if_fast = graph()->CreateBasicBlock(); |
+ HBasicBlock* if_slow = graph()->CreateBasicBlock(); |
+ HBasicBlock* if_slow_pass = graph()->CreateBasicBlock(); |
+ HBasicBlock* if_slow_skip = graph()->CreateBasicBlock(); |
+ HBasicBlock* if_join = graph()->CreateBasicBlock(); |
+ |
+ // Check if expected map still matches that of the enumerable. |
HValue* enumerable_map = |
Add<HLoadNamedField>(enumerable, nullptr, HObjectAccess::ForMap()); |
- IfBuilder if_fast(this); |
- if_fast.If<HCompareObjectEqAndBranch>(enumerable_map, type); |
- if_fast.Then(); |
+ FinishCurrentBlock( |
+ New<HCompareObjectEqAndBranch>(enumerable_map, type, if_fast, if_slow)); |
+ set_current_block(if_fast); |
{ |
+ // The enum cache for enumerable is still valid, no need to check key. |
Push(key); |
- Add<HSimulate>(stmt->FilterId()); |
+ Goto(if_join); |
} |
- if_fast.Else(); |
+ set_current_block(if_slow); |
{ |
+ // Check if key is still valid for enumerable. |
Add<HPushArguments>(enumerable, key); |
Runtime::FunctionId function_id = Runtime::kForInFilter; |
Push(Add<HCallRuntime>(Runtime::FunctionForId(function_id), 2)); |
Add<HSimulate>(stmt->FilterId()); |
+ FinishCurrentBlock(New<HCompareObjectEqAndBranch>( |
+ Top(), graph()->GetConstantUndefined(), if_slow_skip, if_slow_pass)); |
} |
- if_fast.End(); |
+ set_current_block(if_slow_pass); |
+ { Goto(if_join); } |
+ set_current_block(if_slow_skip); |
+ { |
+ // The key is no longer valid for enumerable, skip it. |
+ Drop(1); |
+ Goto(continue_block); |
+ } |
+ if_join->SetJoinId(stmt->FilterId()); |
+ set_current_block(if_join); |
key = Pop(); |
- Bind(each_var, key); |
- IfBuilder if_undefined(this); |
- if_undefined.If<HCompareObjectEqAndBranch>(key, |
- graph()->GetConstantUndefined()); |
- if_undefined.ThenDeopt(Deoptimizer::kUndefined); |
- if_undefined.End(); |
- Add<HSimulate>(stmt->AssignmentId()); |
} |
+ Bind(each_var, key); |
+ Add<HSimulate>(stmt->AssignmentId()); |
+ |
BreakAndContinueInfo break_info(stmt, scope(), 5); |
+ break_info.set_continue_block(continue_block); |
{ |
BreakAndContinueScope push(&break_info, this); |
CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry)); |