Commit d9c091dd authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Remove for-in support from the AstGraphBuilder.

The AstGraphBuilder is used for asm.js only and for-in is not allowed in
valid asm.js, so we can just disable optimization for asm.js functions
that contain for-in loops. This way we only need to support for-in via
the BytecodeGraphBuilder in TurboFan now, which will make optimizations
way easier.

R=yangguo@chromium.org

Review-Url: https://codereview.chromium.org/2679913004
Cr-Commit-Position: refs/heads/master@{#43024}
parent 3c97959a
......@@ -1173,98 +1173,8 @@ void AstGraphBuilder::VisitForStatement(ForStatement* stmt) {
void AstGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
VisitForValue(stmt->subject());
Node* object = environment()->Pop();
BlockBuilder for_block(this);
for_block.BeginBlock();
// Check for null or undefined before entering loop.
Node* is_null_cond =
NewNode(javascript()->StrictEqual(CompareOperationHint::kAny), object,
jsgraph()->NullConstant());
for_block.BreakWhen(is_null_cond, BranchHint::kFalse);
Node* is_undefined_cond =
NewNode(javascript()->StrictEqual(CompareOperationHint::kAny), object,
jsgraph()->UndefinedConstant());
for_block.BreakWhen(is_undefined_cond, BranchHint::kFalse);
{
// Convert object to jsobject.
object = BuildToObject(object, stmt->ToObjectId());
environment()->Push(object);
// Prepare for-in cache.
Node* prepare = NewNode(javascript()->ForInPrepare(), object);
PrepareFrameState(prepare, stmt->PrepareId(),
OutputFrameStateCombine::Push(3));
Node* cache_type = NewNode(common()->Projection(0), prepare);
Node* cache_array = NewNode(common()->Projection(1), prepare);
Node* cache_length = NewNode(common()->Projection(2), prepare);
// Construct the rest of the environment.
environment()->Push(cache_type);
environment()->Push(cache_array);
environment()->Push(cache_length);
environment()->Push(jsgraph()->ZeroConstant());
// Build the actual loop body.
LoopBuilder for_loop(this);
for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt), CheckOsrEntry(stmt));
{
// These stack values are renamed in the case of OSR, so reload them
// from the environment.
Node* index = environment()->Peek(0);
Node* cache_length = environment()->Peek(1);
Node* cache_array = environment()->Peek(2);
Node* cache_type = environment()->Peek(3);
Node* object = environment()->Peek(4);
// Check loop termination condition (we know that the {index} is always
// in Smi range, so we can just set the hint on the comparison below).
PrepareEagerCheckpoint(stmt->EntryId());
Node* exit_cond =
NewNode(javascript()->LessThan(CompareOperationHint::kSignedSmall),
index, cache_length);
PrepareFrameState(exit_cond, BailoutId::None());
for_loop.BreakUnless(exit_cond);
// Compute the next enumerated value.
Node* value = NewNode(javascript()->ForInNext(), object, cache_array,
cache_type, index);
PrepareFrameState(value, stmt->FilterId(),
OutputFrameStateCombine::Push());
IfBuilder test_value(this);
Node* test_value_cond =
NewNode(javascript()->StrictEqual(CompareOperationHint::kAny), value,
jsgraph()->UndefinedConstant());
test_value.If(test_value_cond, BranchHint::kFalse);
test_value.Then();
test_value.Else();
{
environment()->Push(value);
PrepareEagerCheckpoint(stmt->FilterId());
value = environment()->Pop();
// Bind value and do loop body.
VectorSlotPair feedback =
CreateVectorSlotPair(stmt->EachFeedbackSlot());
VisitForInAssignment(stmt->each(), value, feedback,
stmt->AssignmentId());
VisitIterationBody(stmt, &for_loop, stmt->StackCheckId());
}
test_value.End();
for_loop.EndBody();
// Increment counter and continue (we know that the {index} is always
// in Smi range, so we can just set the hint on the increment below).
index = environment()->Peek(0);
PrepareEagerCheckpoint(stmt->IncrementId());
index = NewNode(javascript()->Add(BinaryOperationHint::kSignedSmall),
index, jsgraph()->OneConstant());
PrepareFrameState(index, BailoutId::None());
environment()->Poke(0, index);
}
for_loop.EndLoop();
environment()->Drop(5);
}
for_block.EndBlock();
// Only the BytecodeGraphBuilder supports for-in.
return SetStackOverflow();
}
......@@ -1533,51 +1443,6 @@ void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
ast_context()->ProduceValue(expr, environment()->Pop());
}
void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value,
const VectorSlotPair& feedback,
BailoutId bailout_id) {
DCHECK(expr->IsValidReferenceExpressionOrThis());
// Left-hand side can only be a property, a global or a variable slot.
Property* property = expr->AsProperty();
LhsKind assign_type = Property::GetAssignType(property);
// Evaluate LHS expression and store the value.
switch (assign_type) {
case VARIABLE: {
Variable* var = expr->AsVariableProxy()->var();
BuildVariableAssignment(var, value, Token::ASSIGN, feedback, bailout_id);
break;
}
case NAMED_PROPERTY: {
environment()->Push(value);
VisitForValue(property->obj());
Node* object = environment()->Pop();
value = environment()->Pop();
Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
Node* store = BuildNamedStore(object, name, value, feedback);
PrepareFrameState(store, bailout_id, OutputFrameStateCombine::Ignore());
break;
}
case KEYED_PROPERTY: {
environment()->Push(value);
VisitForValue(property->obj());
VisitForValue(property->key());
Node* key = environment()->Pop();
Node* object = environment()->Pop();
value = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value, feedback);
PrepareFrameState(store, bailout_id, OutputFrameStateCombine::Ignore());
break;
}
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
UNREACHABLE();
break;
}
}
void AstGraphBuilder::VisitAssignment(Assignment* expr) {
DCHECK(expr->target()->IsValidReferenceExpressionOrThis());
......
......@@ -391,11 +391,6 @@ class AstGraphBuilder : public AstVisitor<AstGraphBuilder> {
void VisitLiteralCompareTypeof(CompareOperation* expr, Expression* sub_expr,
Handle<String> check);
// Dispatched from VisitForInStatement.
void VisitForInAssignment(Expression* expr, Node* value,
const VectorSlotPair& feedback,
BailoutId bailout_id);
// Dispatched from VisitObjectLiteral.
void VisitObjectLiteralAccessor(Node* home_object,
ObjectLiteralProperty* property);
......
......@@ -104,70 +104,6 @@ TEST(ForStatement) {
T.CheckCall(T.Val("str"), T.Val("str"), T.Val("str"));
}
static void TestForIn(const char* code) {
FunctionTester T(code);
T.CheckCall(T.undefined(), T.undefined());
T.CheckCall(T.undefined(), T.null());
T.CheckCall(T.undefined(), T.NewObject("({})"));
T.CheckCall(T.undefined(), T.Val(1));
T.CheckCall(T.Val("2"), T.Val("str"));
T.CheckCall(T.Val("a"), T.NewObject("({'a' : 1})"));
T.CheckCall(T.Val("2"), T.NewObject("([1, 2, 3])"));
T.CheckCall(T.Val("a"), T.NewObject("({'a' : 1, 'b' : 1})"), T.Val("b"));
T.CheckCall(T.Val("1"), T.NewObject("([1, 2, 3])"), T.Val("2"));
}
TEST(ForInStatement) {
// Variable assignment.
TestForIn(
"(function(a, b) {"
"var last;"
"for (var x in a) {"
" if (b) { delete a[b]; b = undefined; }"
" last = x;"
"}"
"return last;})");
// Indexed assignment.
TestForIn(
"(function(a, b) {"
"var array = [0, 1, undefined];"
"for (array[2] in a) {"
" if (b) { delete a[b]; b = undefined; }"
"}"
"return array[2];})");
// Named assignment.
TestForIn(
"(function(a, b) {"
"var obj = {'a' : undefined};"
"for (obj.a in a) {"
" if (b) { delete a[b]; b = undefined; }"
"}"
"return obj.a;})");
}
TEST(ForInContinueStatement) {
const char* src =
"(function(a,b) {"
" var r = '-';"
" for (var x in a) {"
" r += 'A-';"
" if (b) continue;"
" r += 'B-';"
" }"
" return r;"
"})";
FunctionTester T(src);
T.CheckCall(T.Val("-A-B-"), T.NewObject("({x:1})"), T.false_value());
T.CheckCall(T.Val("-A-B-A-B-"), T.NewObject("({x:1,y:2})"), T.false_value());
T.CheckCall(T.Val("-A-"), T.NewObject("({x:1})"), T.true_value());
T.CheckCall(T.Val("-A-A-"), T.NewObject("({x:1,y:2})"), T.true_value());
}
TEST(ForOfContinueStatement) {
const char* src =
"(function(a,b) {"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment