From 19d98493fcc86afc868a6f67d726ff0e3ba766ab Mon Sep 17 00:00:00 2001 From: "sgjesse@chromium.org" Date: Wed, 19 May 2010 07:32:32 +0000 Subject: [PATCH] Add complete implementation of full compiler for the ia32 architecture This makes the full compiler handle all constructs on ia32. However the syntax checker for the full compiler is still the same so for both normal operation and with the flag --always-full-compiler the coverage of the full compiler will be the same. This is on preparation for improving the debugger break point experience where the plan is to only use code from full code generator when debugging JavaScript. Runs all tests on all three platforms in release and debug mode. The tests also run with both the following flags to the test runner --special-command="@ --nofull-compiler" --special-command="@ --always-full-compiler" The changes to the x64 and ARM architectures are mainly structural due to the change to EmitVariableAssignment to handle initialization of const variables. Review URL: http://codereview.chromium.org/1989012 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4676 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 108 ++- src/codegen.h | 2 +- src/full-codegen.cc | 35 +- src/full-codegen.h | 76 ++- src/ia32/full-codegen-ia32.cc | 1485 +++++++++++++++++++++++++++++++++++++---- src/x64/full-codegen-x64.cc | 131 ++-- 6 files changed, 1610 insertions(+), 227 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 4c83003..6c765c4 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -399,10 +399,10 @@ void FullCodeGenerator::Apply(Expression::Context context, case Expression::kValue: { Label done; __ bind(materialize_true); - __ mov(result_register(), Operand(Factory::true_value())); + __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); __ jmp(&done); __ bind(materialize_false); - __ mov(result_register(), Operand(Factory::false_value())); + __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); __ bind(&done); switch (location_) { case kAccumulator: @@ -419,7 +419,7 @@ void FullCodeGenerator::Apply(Expression::Context context, case Expression::kValueTest: __ bind(materialize_true); - __ mov(result_register(), Operand(Factory::true_value())); + __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); switch (location_) { case kAccumulator: break; @@ -432,7 +432,7 @@ void FullCodeGenerator::Apply(Expression::Context context, case Expression::kTestValue: __ bind(materialize_false); - __ mov(result_register(), Operand(Factory::false_value())); + __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); switch (location_) { case kAccumulator: break; @@ -663,19 +663,29 @@ void FullCodeGenerator::DeclareGlobals(Handle pairs) { } -void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { - Comment cmnt(masm_, "[ FunctionLiteral"); +void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { + UNREACHABLE(); +} + + +void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { + UNREACHABLE(); +} - // Build the shared function info and instantiate the function based - // on it. - Handle function_info = - Compiler::BuildFunctionInfo(expr, script(), this); - if (HasStackOverflow()) return; - // Create a new closure. - __ mov(r0, Operand(function_info)); - __ stm(db_w, sp, cp.bit() | r0.bit()); - __ CallRuntime(Runtime::kNewClosure, 2); +void FullCodeGenerator::EmitNewClosure(Handle info) { + // Use the fast case closure allocation code that allocates in new + // space for nested functions that don't need literals cloning. + if (scope()->is_function_scope() && info->num_literals() == 0) { + FastNewClosureStub stub; + __ mov(r0, Operand(info)); + __ push(r0); + __ CallStub(&stub); + } else { + __ mov(r0, Operand(info)); + __ stm(db_w, sp, cp.bit() | r0.bit()); + __ CallRuntime(Runtime::kNewClosure, 2); + } Apply(context_, r0); } @@ -906,7 +916,13 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { void FullCodeGenerator::VisitAssignment(Assignment* expr) { Comment cmnt(masm_, "[ Assignment"); - ASSERT(expr->op() != Token::INIT_CONST); + // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' + // on the left-hand side. + if (!expr->target()->IsValidLeftHandSide()) { + VisitForEffect(expr->target()); + return; + } + // Left-hand side can only be a property, a global or a (parameter or local) // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; @@ -986,6 +1002,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { switch (assign_type) { case VARIABLE: EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + expr->op(), context_); break; case NAMED_PROPERTY: @@ -1026,14 +1043,13 @@ void FullCodeGenerator::EmitBinaryOp(Token::Value op, void FullCodeGenerator::EmitVariableAssignment(Variable* var, + Token::Value op, Expression::Context context) { - // Three main cases: global variables, lookup slots, and all other - // types of slots. Left-hand-side parameters that rewrite to - // explicit property accesses do not reach here. + // Left-hand sides that rewrite to explicit property accesses do not reach + // here. ASSERT(var != NULL); ASSERT(var->is_global() || var->slot() != NULL); - Slot* slot = var->slot(); if (var->is_global()) { ASSERT(!var->is_this()); // Assignment to a global variable. Use inline caching for the @@ -1044,43 +1060,61 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); - } else if (slot != NULL && slot->type() == Slot::LOOKUP) { - __ push(result_register()); // Value. - __ mov(r1, Operand(var->name())); - __ stm(db_w, sp, cp.bit() | r1.bit()); // Context and name. - __ CallRuntime(Runtime::kStoreContextSlot, 3); - - } else if (var->slot() != NULL) { + } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { + // Perform the assignment for non-const variables and for initialization + // of const variables. Const assignments are simply skipped. + Label done; Slot* slot = var->slot(); switch (slot->type()) { - case Slot::LOCAL: case Slot::PARAMETER: + case Slot::LOCAL: + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ ldr(r1, MemOperand(fp, SlotOffset(slot))); + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); + __ cmp(r1, ip); + __ b(ne, &done); + } + // Perform the assignment. __ str(result_register(), MemOperand(fp, SlotOffset(slot))); break; case Slot::CONTEXT: { MemOperand target = EmitSlotSearch(slot, r1); + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ ldr(r1, target); + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); + __ cmp(r1, ip); + __ b(ne, &done); + } + // Perform the assignment and issue the write barrier. __ str(result_register(), target); - // RecordWrite may destroy all its register arguments. __ mov(r3, result_register()); int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; - __ mov(r2, Operand(offset)); __ RecordWrite(r1, r2, r3); break; } case Slot::LOOKUP: - UNREACHABLE(); + // Call the runtime for the assignment. The runtime will ignore + // const reinitialization. + __ push(r0); // Value. + __ mov(r0, Operand(slot->var()->name())); + __ Push(cp, r0); // Context and name. + if (op == Token::INIT_CONST) { + // The runtime will ignore const redeclaration. + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); + } else { + __ CallRuntime(Runtime::kStoreContextSlot, 3); + } break; } - - } else { - // Variables rewritten as properties are not treated as variables in - // assignments. - UNREACHABLE(); + __ bind(&done); } + Apply(context, result_register()); } @@ -1645,6 +1679,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case VARIABLE: if (expr->is_postfix()) { EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, Expression::kEffect); // For all contexts except kEffect: We have the result on // top of the stack. @@ -1653,6 +1688,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { } } else { EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, context_); } break; diff --git a/src/codegen.h b/src/codegen.h index 667d100..358c6fc 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -114,7 +114,7 @@ namespace internal { F(CharFromCode, 1, 1) \ F(ObjectEquals, 2, 1) \ F(Log, 3, 1) \ - F(RandomHeapNumber, 0, 1) \ + F(RandomHeapNumber, 0, 1) \ F(IsObject, 1, 1) \ F(IsFunction, 1, 1) \ F(IsUndetectableObject, 1, 1) \ diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 699a1e9..2ccbca8 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -760,11 +760,6 @@ void FullCodeGenerator::VisitWithExitStatement(WithExitStatement* stmt) { } -void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { - UNREACHABLE(); -} - - void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { Comment cmnt(masm_, "[ DoWhileStatement"); SetStatementPosition(stmt); @@ -810,6 +805,7 @@ void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { Visit(stmt->body()); __ bind(loop_statement.continue_target()); + // Check stack before looping. __ StackLimitCheck(&stack_limit_hit); __ bind(&stack_check_success); @@ -872,11 +868,6 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) { } -void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { - UNREACHABLE(); -} - - void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { Comment cmnt(masm_, "[ TryCatchStatement"); SetStatementPosition(stmt); @@ -995,12 +986,6 @@ void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { } -void FullCodeGenerator::VisitSharedFunctionInfoLiteral( - SharedFunctionInfoLiteral* expr) { - UNREACHABLE(); -} - - void FullCodeGenerator::VisitConditional(Conditional* expr) { Comment cmnt(masm_, "[ Conditional"); Label true_case, false_case, done; @@ -1034,6 +1019,24 @@ void FullCodeGenerator::VisitLiteral(Literal* expr) { } +void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { + Comment cmnt(masm_, "[ FunctionLiteral"); + + // Build the function boilerplate and instantiate it. + Handle function_info = + Compiler::BuildFunctionInfo(expr, script(), this); + if (HasStackOverflow()) return; + EmitNewClosure(function_info); +} + + +void FullCodeGenerator::VisitSharedFunctionInfoLiteral( + SharedFunctionInfoLiteral* expr) { + Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); + EmitNewClosure(expr->shared_function_info()); +} + + void FullCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { // Call runtime routine to allocate the catch extension object and // assign the exception value to the catch variable. diff --git a/src/full-codegen.h b/src/full-codegen.h index 96d0f3e..c7d0093 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -31,6 +31,7 @@ #include "v8.h" #include "ast.h" +#include "compiler.h" namespace v8 { namespace internal { @@ -229,8 +230,6 @@ class FullCodeGenerator: public AstVisitor { return stack_depth + kForInStackElementCount; } private: - // TODO(lrn): Check that this value is correct when implementing - // for-in. static const int kForInStackElementCount = 5; DISALLOW_COPY_AND_ASSIGN(ForIn); }; @@ -258,12 +257,22 @@ class FullCodeGenerator: public AstVisitor { // context. void DropAndApply(int count, Expression::Context context, Register reg); + // Set up branch labels for a test expression. + void PrepareTest(Label* materialize_true, + Label* materialize_false, + Label** if_true, + Label** if_false); + // Emit code to convert pure control flow to a pair of labels into the // result expected according to an expression context. void Apply(Expression::Context context, Label* materialize_true, Label* materialize_false); + // Emit code to convert constant control flow (true or false) into + // the result expected according to an expression context. + void Apply(Expression::Context context, bool flag); + // Helper function to convert a pure value into a test context. The value // is expected on the stack or the accumulator, depending on the platform. // See the platform-specific implementation for details. @@ -348,6 +357,12 @@ class FullCodeGenerator: public AstVisitor { void VisitDeclarations(ZoneList* declarations); void DeclareGlobals(Handle pairs); + // Platform-specific code for a variable, constant, or function + // declaration. Functions have an initial value. + void EmitDeclaration(Variable* variable, + Variable::Mode mode, + FunctionLiteral* function); + // Platform-specific return sequence void EmitReturnSequence(int position); @@ -355,9 +370,48 @@ class FullCodeGenerator: public AstVisitor { void EmitCallWithStub(Call* expr); void EmitCallWithIC(Call* expr, Handle name, RelocInfo::Mode mode); + + // Platform-specific code for inline runtime calls. + void EmitInlineRuntimeCall(CallRuntime* expr); + void EmitIsSmi(ZoneList* arguments); + void EmitIsNonNegativeSmi(ZoneList* arguments); + void EmitIsObject(ZoneList* arguments); + void EmitIsUndetectableObject(ZoneList* arguments); + void EmitIsFunction(ZoneList* arguments); + void EmitIsArray(ZoneList* arguments); + void EmitIsRegExp(ZoneList* arguments); + void EmitIsConstructCall(ZoneList* arguments); + void EmitObjectEquals(ZoneList* arguments); + void EmitArguments(ZoneList* arguments); + void EmitArgumentsLength(ZoneList* arguments); + void EmitClassOf(ZoneList* arguments); + void EmitValueOf(ZoneList* arguments); + void EmitSetValueOf(ZoneList* arguments); + void EmitNumberToString(ZoneList* arguments); + void EmitCharFromCode(ZoneList* arguments); + void EmitFastCharCodeAt(ZoneList* arguments); + void EmitStringCompare(ZoneList* arguments); + void EmitStringAdd(ZoneList* arguments); + void EmitLog(ZoneList* arguments); + void EmitRandomHeapNumber(ZoneList* arguments); + void EmitSubString(ZoneList* arguments); + void EmitRegExpExec(ZoneList* arguments); + void EmitMathPow(ZoneList* arguments); + void EmitMathSin(ZoneList* arguments); + void EmitMathCos(ZoneList* arguments); + void EmitMathSqrt(ZoneList* arguments); + void EmitCallFunction(ZoneList* arguments); + void EmitRegExpConstructResult(ZoneList* arguments); + void EmitSwapElements(ZoneList* arguments); + void EmitGetFromCache(ZoneList* arguments); + // Platform-specific code for loading variables. void EmitVariableLoad(Variable* expr, Expression::Context context); + // Platform-specific support for allocating a new closure based on + // the given function info. + void EmitNewClosure(Handle info); + // Platform-specific support for compiling assignments. // Load a value from a named property. @@ -372,9 +426,15 @@ class FullCodeGenerator: public AstVisitor { // of the stack and the right one in the accumulator. void EmitBinaryOp(Token::Value op, Expression::Context context); + // Assign to the given expression as if via '='. The right-hand-side value + // is expected in the accumulator. + void EmitAssignment(Expression* expr); + // Complete a variable assignment. The right-hand-side value is expected // in the accumulator. - void EmitVariableAssignment(Variable* var, Expression::Context context); + void EmitVariableAssignment(Variable* var, + Token::Value op, + Expression::Context context); // Complete a named property assignment. The receiver is expected on top // of the stack and the right-hand-side value in the accumulator. @@ -385,6 +445,14 @@ class FullCodeGenerator: public AstVisitor { // accumulator. void EmitKeyedPropertyAssignment(Assignment* expr); + // Helper for compare operations. Expects the null-value in a register. + void EmitNullCompare(bool strict, + Register obj, + Register null_const, + Label* if_true, + Label* if_false, + Register scratch); + void SetFunctionPosition(FunctionLiteral* fun); void SetReturnPosition(FunctionLiteral* fun); void SetStatementPosition(Statement* stmt); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 79c0a66..efc0bd7 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -81,11 +81,17 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) { bool function_in_register = true; // Possibly allocate a local context. - if (scope()->num_heap_slots() > 0) { + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; + if (heap_slots > 0) { Comment cmnt(masm_, "[ Allocate local context"); // Argument to NewContext is the function, which is still in edi. __ push(edi); - __ CallRuntime(Runtime::kNewContext, 1); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { + FastNewContextStub stub(heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); + } function_in_register = false; // Context is returned in both eax and esi. It replaces the context // passed to us. It's saved in the stack and kept live in esi. @@ -142,7 +148,18 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) { } { Comment cmnt(masm_, "[ Declarations"); - VisitDeclarations(scope()->declarations()); + // For named function expressions, declare the function name as a + // constant. + if (scope()->is_function_scope() && scope()->function() != NULL) { + EmitDeclaration(scope()->function(), Variable::CONST, NULL); + } + // Visit all the explicit declarations unless there is an illegal + // redeclaration. + if (scope()->HasIllegalRedeclaration()) { + scope()->VisitIllegalRedeclaration(this); + } else { + VisitDeclarations(scope()->declarations()); + } } { Comment cmnt(masm_, "[ Stack check"); @@ -427,6 +444,39 @@ void FullCodeGenerator::DropAndApply(int count, } +void FullCodeGenerator::PrepareTest(Label* materialize_true, + Label* materialize_false, + Label** if_true, + Label** if_false) { + switch (context_) { + case Expression::kUninitialized: + UNREACHABLE(); + break; + case Expression::kEffect: + // In an effect context, the true and the false case branch to the + // same label. + *if_true = *if_false = materialize_true; + break; + case Expression::kValue: + *if_true = materialize_true; + *if_false = materialize_false; + break; + case Expression::kTest: + *if_true = true_label_; + *if_false = false_label_; + break; + case Expression::kValueTest: + *if_true = materialize_true; + *if_false = false_label_; + break; + case Expression::kTestValue: + *if_true = true_label_; + *if_false = materialize_false; + break; + } +} + + void FullCodeGenerator::Apply(Expression::Context context, Label* materialize_true, Label* materialize_false) { @@ -492,6 +542,61 @@ void FullCodeGenerator::Apply(Expression::Context context, } +// Convert constant control flow (true or false) to the result expected for +// a given expression context. +void FullCodeGenerator::Apply(Expression::Context context, bool flag) { + switch (context) { + case Expression::kUninitialized: + UNREACHABLE(); + break; + case Expression::kEffect: + break; + case Expression::kValue: { + Handle value = + flag ? Factory::true_value() : Factory::false_value(); + switch (location_) { + case kAccumulator: + __ mov(result_register(), value); + break; + case kStack: + __ push(Immediate(value)); + break; + } + break; + } + case Expression::kTest: + __ jmp(flag ? true_label_ : false_label_); + break; + case Expression::kTestValue: + switch (location_) { + case kAccumulator: + // If value is false it's needed. + if (!flag) __ mov(result_register(), Factory::false_value()); + break; + case kStack: + // If value is false it's needed. + if (!flag) __ push(Immediate(Factory::false_value())); + break; + } + __ jmp(flag ? true_label_ : false_label_); + break; + case Expression::kValueTest: + switch (location_) { + case kAccumulator: + // If value is true it's needed. + if (flag) __ mov(result_register(), Factory::true_value()); + break; + case kStack: + // If value is true it's needed. + if (flag) __ push(Immediate(Factory::true_value())); + break; + } + __ jmp(flag ? true_label_ : false_label_); + break; + } +} + + void FullCodeGenerator::DoTest(Expression::Context context) { // The value to test is in the accumulator. If the value might be needed // on the stack (value/test and test/value contexts with a stack location @@ -667,22 +772,22 @@ void FullCodeGenerator::Move(Slot* dst, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { +void FullCodeGenerator::EmitDeclaration(Variable* variable, + Variable::Mode mode, + FunctionLiteral* function) { Comment cmnt(masm_, "[ Declaration"); - Variable* var = decl->proxy()->var(); - ASSERT(var != NULL); // Must have been resolved. - Slot* slot = var->slot(); - Property* prop = var->AsProperty(); - + ASSERT(variable != NULL); // Must have been resolved. + Slot* slot = variable->slot(); + Property* prop = variable->AsProperty(); if (slot != NULL) { switch (slot->type()) { case Slot::PARAMETER: case Slot::LOCAL: - if (decl->mode() == Variable::CONST) { + if (mode == Variable::CONST) { __ mov(Operand(ebp, SlotOffset(slot)), Immediate(Factory::the_hole_value())); - } else if (decl->fun() != NULL) { - VisitForValue(decl->fun(), kAccumulator); + } else if (function != NULL) { + VisitForValue(function, kAccumulator); __ mov(Operand(ebp, SlotOffset(slot)), result_register()); } break; @@ -692,7 +797,7 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { // this specific context. // The variable in the decl always resides in the current context. - ASSERT_EQ(0, scope()->ContextChainLength(var->scope())); + ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); if (FLAG_debug_code) { // Check if we have the correct context pointer. __ mov(ebx, @@ -700,12 +805,12 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { __ cmp(ebx, Operand(esi)); __ Check(equal, "Unexpected declaration in current context."); } - if (decl->mode() == Variable::CONST) { + if (mode == Variable::CONST) { __ mov(eax, Immediate(Factory::the_hole_value())); __ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax); // No write barrier since the hole value is in old space. - } else if (decl->fun() != NULL) { - VisitForValue(decl->fun(), kAccumulator); + } else if (function != NULL) { + VisitForValue(function, kAccumulator); __ mov(CodeGenerator::ContextOperand(esi, slot->index()), result_register()); int offset = Context::SlotOffset(slot->index()); @@ -716,21 +821,21 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { case Slot::LOOKUP: { __ push(esi); - __ push(Immediate(var->name())); + __ push(Immediate(variable->name())); // Declaration nodes are always introduced in one of two modes. - ASSERT(decl->mode() == Variable::VAR || - decl->mode() == Variable::CONST); + ASSERT(mode == Variable::VAR || + mode == Variable::CONST); PropertyAttributes attr = - (decl->mode() == Variable::VAR) ? NONE : READ_ONLY; + (mode == Variable::VAR) ? NONE : READ_ONLY; __ push(Immediate(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as // 'undefined') because we may have a (legal) redeclaration and we // must not destroy the current value. - if (decl->mode() == Variable::CONST) { + if (mode == Variable::CONST) { __ push(Immediate(Factory::the_hole_value())); - } else if (decl->fun() != NULL) { - VisitForValue(decl->fun(), kStack); + } else if (function != NULL) { + VisitForValue(function, kStack); } else { __ push(Immediate(Smi::FromInt(0))); // No initial value! } @@ -740,13 +845,13 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } } else if (prop != NULL) { - if (decl->fun() != NULL || decl->mode() == Variable::CONST) { + if (function != NULL || mode == Variable::CONST) { // We are declaring a function or constant that rewrites to a // property. Use (keyed) IC to set the initial value. VisitForValue(prop->obj(), kStack); - if (decl->fun() != NULL) { + if (function != NULL) { VisitForValue(prop->key(), kStack); - VisitForValue(decl->fun(), kAccumulator); + VisitForValue(function, kAccumulator); __ pop(ecx); } else { VisitForValue(prop->key(), kAccumulator); @@ -765,6 +870,11 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } +void FullCodeGenerator::VisitDeclaration(Declaration* decl) { + EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); +} + + void FullCodeGenerator::DeclareGlobals(Handle pairs) { // Call the runtime to declare the globals. __ push(esi); // The context is the first argument. @@ -775,19 +885,225 @@ void FullCodeGenerator::DeclareGlobals(Handle pairs) { } -void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { - Comment cmnt(masm_, "[ FunctionLiteral"); +void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { + Comment cmnt(masm_, "[ SwitchStatement"); + Breakable nested_statement(this, stmt); + SetStatementPosition(stmt); + // Keep the switch value on the stack until a case matches. + VisitForValue(stmt->tag(), kStack); + + ZoneList* clauses = stmt->cases(); + CaseClause* default_clause = NULL; // Can occur anywhere in the list. - // Build the shared function info and instantiate the function based - // on it. - Handle function_info = - Compiler::BuildFunctionInfo(expr, script(), this); - if (HasStackOverflow()) return; + Label next_test; // Recycled for each test. + // Compile all the tests with branches to their bodies. + for (int i = 0; i < clauses->length(); i++) { + CaseClause* clause = clauses->at(i); + // The default is not a test, but remember it as final fall through. + if (clause->is_default()) { + default_clause = clause; + continue; + } + + Comment cmnt(masm_, "[ Case comparison"); + __ bind(&next_test); + next_test.Unuse(); + + // Compile the label expression. + VisitForValue(clause->label(), kAccumulator); + + // Perform the comparison as if via '==='. The comparison stub expects + // the smi vs. smi case to be handled before it is called. + Label slow_case; + __ mov(edx, Operand(esp, 0)); // Switch value. + __ mov(ecx, edx); + __ or_(ecx, Operand(eax)); + __ test(ecx, Immediate(kSmiTagMask)); + __ j(not_zero, &slow_case, not_taken); + __ cmp(edx, Operand(eax)); + __ j(not_equal, &next_test); + __ Drop(1); // Switch value is no longer needed. + __ jmp(clause->body_target()->entry_label()); + + __ bind(&slow_case); + CompareStub stub(equal, true); + __ CallStub(&stub); + __ test(eax, Operand(eax)); + __ j(not_equal, &next_test); + __ Drop(1); // Switch value is no longer needed. + __ jmp(clause->body_target()->entry_label()); + } + + // Discard the test value and jump to the default if present, otherwise to + // the end of the statement. + __ bind(&next_test); + __ Drop(1); // Switch value is no longer needed. + if (default_clause == NULL) { + __ jmp(nested_statement.break_target()); + } else { + __ jmp(default_clause->body_target()->entry_label()); + } - // Create a new closure. - __ push(esi); - __ push(Immediate(function_info)); - __ CallRuntime(Runtime::kNewClosure, 2); + // Compile all the case bodies. + for (int i = 0; i < clauses->length(); i++) { + Comment cmnt(masm_, "[ Case body"); + CaseClause* clause = clauses->at(i); + __ bind(clause->body_target()->entry_label()); + VisitStatements(clause->statements()); + } + + __ bind(nested_statement.break_target()); +} + + +void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { + Comment cmnt(masm_, "[ ForInStatement"); + SetStatementPosition(stmt); + + Label loop, exit; + ForIn loop_statement(this, stmt); + increment_loop_depth(); + + // Get the object to enumerate over. Both SpiderMonkey and JSC + // ignore null and undefined in contrast to the specification; see + // ECMA-262 section 12.6.4. + VisitForValue(stmt->enumerable(), kAccumulator); + __ cmp(eax, Factory::undefined_value()); + __ j(equal, &exit); + __ cmp(eax, Factory::null_value()); + __ j(equal, &exit); + + // Convert the object to a JS object. + Label convert, done_convert; + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &convert); + __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx); + __ j(above_equal, &done_convert); + __ bind(&convert); + __ push(eax); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ bind(&done_convert); + __ push(eax); + + // TODO(kasperl): Check cache validity in generated code. This is a + // fast case for the JSObject::IsSimpleEnum cache validity + // checks. If we cannot guarantee cache validity, call the runtime + // system to check cache validity or get the property names in a + // fixed array. + + // Get the set of properties to enumerate. + __ push(eax); // Duplicate the enumerable object on the stack. + __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); + + // If we got a map from the runtime call, we can do a fast + // modification check. Otherwise, we got a fixed array, and we have + // to do a slow check. + Label fixed_array; + __ cmp(FieldOperand(eax, HeapObject::kMapOffset), Factory::meta_map()); + __ j(not_equal, &fixed_array); + + // We got a map in register eax. Get the enumeration cache from it. + __ mov(ecx, FieldOperand(eax, Map::kInstanceDescriptorsOffset)); + __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset)); + __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + + // Setup the four remaining stack slots. + __ push(eax); // Map. + __ push(edx); // Enumeration cache. + __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset)); + __ SmiTag(eax); + __ push(eax); // Enumeration cache length (as smi). + __ push(Immediate(Smi::FromInt(0))); // Initial index. + __ jmp(&loop); + + // We got a fixed array in register eax. Iterate through that. + __ bind(&fixed_array); + __ push(Immediate(Smi::FromInt(0))); // Map (0) - force slow check. + __ push(eax); + __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset)); + __ SmiTag(eax); + __ push(eax); // Fixed array length (as smi). + __ push(Immediate(Smi::FromInt(0))); // Initial index. + + // Generate code for doing the condition check. + __ bind(&loop); + __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index. + __ cmp(eax, Operand(esp, 1 * kPointerSize)); // Compare to the array length. + __ j(above_equal, loop_statement.break_target()); + + // Get the current entry of the array into register ebx. + __ mov(ebx, Operand(esp, 2 * kPointerSize)); + __ mov(ebx, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize)); + + // Get the expected map from the stack or a zero map in the + // permanent slow case into register edx. + __ mov(edx, Operand(esp, 3 * kPointerSize)); + + // Check if the expected map still matches that of the enumerable. + // If not, we have to filter the key. + Label update_each; + __ mov(ecx, Operand(esp, 4 * kPointerSize)); + __ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset)); + __ j(equal, &update_each); + + // Convert the entry to a string or null if it isn't a property + // anymore. If the property has been removed while iterating, we + // just skip it. + __ push(ecx); // Enumerable. + __ push(ebx); // Current entry. + __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION); + __ cmp(eax, Factory::null_value()); + __ j(equal, loop_statement.continue_target()); + __ mov(ebx, Operand(eax)); + + // Update the 'each' property or variable from the possibly filtered + // entry in register ebx. + __ bind(&update_each); + __ mov(result_register(), ebx); + // Perform the assignment as if via '='. + EmitAssignment(stmt->each()); + + // Generate code for the body of the loop. + Label stack_limit_hit, stack_check_done; + Visit(stmt->body()); + + __ StackLimitCheck(&stack_limit_hit); + __ bind(&stack_check_done); + + // Generate code for the going to the next element by incrementing + // the index (smi) stored on top of the stack. + __ bind(loop_statement.continue_target()); + __ add(Operand(esp, 0 * kPointerSize), Immediate(Smi::FromInt(1))); + __ jmp(&loop); + + // Slow case for the stack limit check. + StackCheckStub stack_check_stub; + __ bind(&stack_limit_hit); + __ CallStub(&stack_check_stub); + __ jmp(&stack_check_done); + + // Remove the pointers stored on the stack. + __ bind(loop_statement.break_target()); + __ add(Operand(esp), Immediate(5 * kPointerSize)); + + // Exit and decrement the loop depth. + __ bind(&exit); + decrement_loop_depth(); +} + + +void FullCodeGenerator::EmitNewClosure(Handle info) { + // Use the fast case closure allocation code that allocates in new + // space for nested functions that don't need literals cloning. + if (scope()->is_function_scope() && info->num_literals() == 0) { + FastNewClosureStub stub; + __ push(Immediate(info)); + __ CallStub(&stub); + } else { + __ push(esi); + __ push(Immediate(info)); + __ CallRuntime(Runtime::kNewClosure, 2); + } Apply(context_, eax); } @@ -832,7 +1148,20 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) ? "Context slot" : "Stack slot"); - Apply(context, slot); + if (var->mode() == Variable::CONST) { + // Constants may be the hole value if they have not been initialized. + // Unhole them. + Label done; + MemOperand slot_operand = EmitSlotSearch(slot, eax); + __ mov(eax, slot_operand); + __ cmp(eax, Factory::the_hole_value()); + __ j(not_equal, &done); + __ mov(eax, Factory::undefined_value()); + __ bind(&done); + Apply(context, eax); + } else { + Apply(context, slot); + } } else { Comment cmnt(masm_, "Rewritten parameter"); @@ -968,22 +1297,28 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { Comment cmnt(masm_, "[ ArrayLiteral"); + + ZoneList* subexprs = expr->values(); + int length = subexprs->length(); + __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(expr->literal_index()))); __ push(Immediate(expr->constant_elements())); if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateArrayLiteral, 3); - } else { + } else if (length > FastCloneShallowArrayStub::kMaximumLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); + } else { + FastCloneShallowArrayStub stub(length); + __ CallStub(&stub); } bool result_saved = false; // Is the result saved to the stack? // Emit code to evaluate all the non-constant subexpressions and to store // them into the newly cloned array. - ZoneList* subexprs = expr->values(); - for (int i = 0, len = subexprs->length(); i < len; i++) { + for (int i = 0; i < length; i++) { Expression* subexpr = subexprs->at(i); // If the subexpression is a literal or a simple materialized literal it // is already set in the cloned array. @@ -1018,7 +1353,13 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { void FullCodeGenerator::VisitAssignment(Assignment* expr) { Comment cmnt(masm_, "[ Assignment"); - ASSERT(expr->op() != Token::INIT_CONST); + // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' + // on the left-hand side. + if (!expr->target()->IsValidLeftHandSide()) { + VisitForEffect(expr->target()); + return; + } + // Left-hand side can only be a property, a global or a (parameter or local) // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; @@ -1097,6 +1438,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { switch (assign_type) { case VARIABLE: EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + expr->op(), context_); break; case NAMED_PROPERTY: @@ -1139,15 +1481,66 @@ void FullCodeGenerator::EmitBinaryOp(Token::Value op, } +void FullCodeGenerator::EmitAssignment(Expression* expr) { + // Invalid left-hand sides are rewritten to have a 'throw + // ReferenceError' on the left-hand side. + if (!expr->IsValidLeftHandSide()) { + VisitForEffect(expr); + return; + } + + // Left-hand side can only be a property, a global or a (parameter or local) + // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. + enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; + LhsKind assign_type = VARIABLE; + Property* prop = expr->AsProperty(); + if (prop != NULL) { + assign_type = (prop->key()->IsPropertyName()) + ? NAMED_PROPERTY + : KEYED_PROPERTY; + } + + switch (assign_type) { + case VARIABLE: { + Variable* var = expr->AsVariableProxy()->var(); + EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect); + break; + } + case NAMED_PROPERTY: { + __ push(eax); // Preserve value. + VisitForValue(prop->obj(), kAccumulator); + __ mov(edx, eax); + __ pop(eax); // Restore value. + __ mov(ecx, prop->key()->AsLiteral()->handle()); + Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + __ nop(); // Signal no inlined code. + break; + } + case KEYED_PROPERTY: { + __ push(eax); // Preserve value. + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kAccumulator); + __ mov(ecx, eax); + __ pop(edx); + __ pop(eax); // Restore value. + Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + __ nop(); // Signal no inlined code. + break; + } + } +} + + void FullCodeGenerator::EmitVariableAssignment(Variable* var, + Token::Value op, Expression::Context context) { - // Three main cases: global variables, lookup slots, and all other - // types of slots. Left-hand-side parameters that rewrite to - // explicit property accesses do not reach here. + // Left-hand sides that rewrite to explicit property accesses do not reach + // here. ASSERT(var != NULL); ASSERT(var->is_global() || var->slot() != NULL); - Slot* slot = var->slot(); if (var->is_global()) { ASSERT(!var->is_this()); // Assignment to a global variable. Use inline caching for the @@ -1158,44 +1551,61 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); __ nop(); - Apply(context, eax); - } else if (slot != NULL && slot->type() == Slot::LOOKUP) { - __ push(result_register()); // Value. - __ push(esi); // Context. - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kStoreContextSlot, 3); - Apply(context, eax); - - } else if (slot != NULL) { + } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { + // Perform the assignment for non-const variables and for initialization + // of const variables. Const assignments are simply skipped. + Label done; + Slot* slot = var->slot(); switch (slot->type()) { - case Slot::LOCAL: case Slot::PARAMETER: - __ mov(Operand(ebp, SlotOffset(slot)), result_register()); + case Slot::LOCAL: + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ mov(edx, Operand(ebp, SlotOffset(slot))); + __ cmp(edx, Factory::the_hole_value()); + __ j(not_equal, &done); + } + // Perform the assignment. + __ mov(Operand(ebp, SlotOffset(slot)), eax); break; case Slot::CONTEXT: { MemOperand target = EmitSlotSearch(slot, ecx); - __ mov(target, result_register()); - - // RecordWrite may destroy all its register arguments. - __ mov(edx, result_register()); + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ mov(edx, target); + __ cmp(edx, Factory::the_hole_value()); + __ j(not_equal, &done); + } + // Perform the assignment and issue the write barrier. + __ mov(target, eax); + // The value of the assignment is in eax. RecordWrite clobbers its + // register arguments. + __ mov(edx, eax); int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; __ RecordWrite(ecx, offset, edx, ebx); break; } case Slot::LOOKUP: - UNREACHABLE(); + // Call the runtime for the assignment. The runtime will ignore + // const reinitialization. + __ push(eax); // Value. + __ push(esi); // Context. + __ push(Immediate(var->name())); + if (op == Token::INIT_CONST) { + // The runtime will ignore const redeclaration. + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); + } else { + __ CallRuntime(Runtime::kStoreContextSlot, 3); + } break; } - Apply(context, result_register()); - - } else { - // Variables rewritten as properties are not treated as variables in - // assignments. - UNREACHABLE(); + __ bind(&done); } + + Apply(context, eax); } @@ -1329,7 +1739,8 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { } // Record source position for debugger. SetSourcePosition(expr->position()); - CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE); + InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); @@ -1343,16 +1754,62 @@ void FullCodeGenerator::VisitCall(Call* expr) { Variable* var = fun->AsVariableProxy()->AsVariable(); if (var != NULL && var->is_possibly_eval()) { - // Call to the identifier 'eval'. - UNREACHABLE(); + // In a call to eval, we first call %ResolvePossiblyDirectEval to + // resolve the function we need to call and the receiver of the + // call. Then we call the resolved function using the given + // arguments. + VisitForValue(fun, kStack); + __ push(Immediate(Factory::undefined_value())); // Reserved receiver slot. + + // Push the arguments. + ZoneList* args = expr->arguments(); + int arg_count = args->length(); + for (int i = 0; i < arg_count; i++) { + VisitForValue(args->at(i), kStack); + } + + // Push copy of the function - found below the arguments. + __ push(Operand(esp, (arg_count + 1) * kPointerSize)); + + // Push copy of the first argument or undefined if it doesn't exist. + if (arg_count > 0) { + __ push(Operand(esp, arg_count * kPointerSize)); + } else { + __ push(Immediate(Factory::undefined_value())); + } + + // Push the receiver of the enclosing function and do runtime call. + __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize)); + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + + // The runtime call returns a pair of values in eax (function) and + // edx (receiver). Touch up the stack with the right values. + __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx); + __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax); + + // Record source position for debugger. + SetSourcePosition(expr->position()); + InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); + __ CallStub(&stub); + // Restore context register. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + DropAndApply(1, context_, eax); } else if (var != NULL && !var->is_this() && var->is_global()) { // Push global object as receiver for the call IC. __ push(CodeGenerator::GlobalObject()); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { - // Call to a lookup slot. - UNREACHABLE(); + // Call to a lookup slot (dynamically introduced variable). Call the + // runtime to find the function to call (returned in eax) and the object + // holding it (returned in edx). + __ push(context_register()); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kLoadContextSlot, 2); + __ push(eax); // Function. + __ push(edx); // Receiver. + EmitCallWithStub(expr); } else if (fun->AsProperty() != NULL) { // Call to an object property. Property* prop = fun->AsProperty(); @@ -1449,7 +1906,730 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { } +void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) { + Handle name = expr->name(); + if (strcmp("_IsSmi", *name->ToCString()) == 0) { + EmitIsSmi(expr->arguments()); + } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) { + EmitIsNonNegativeSmi(expr->arguments()); + } else if (strcmp("_IsObject", *name->ToCString()) == 0) { + EmitIsObject(expr->arguments()); + } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) { + EmitIsUndetectableObject(expr->arguments()); + } else if (strcmp("_IsFunction", *name->ToCString()) == 0) { + EmitIsFunction(expr->arguments()); + } else if (strcmp("_IsArray", *name->ToCString()) == 0) { + EmitIsArray(expr->arguments()); + } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) { + EmitIsRegExp(expr->arguments()); + } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) { + EmitIsConstructCall(expr->arguments()); + } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) { + EmitObjectEquals(expr->arguments()); + } else if (strcmp("_Arguments", *name->ToCString()) == 0) { + EmitArguments(expr->arguments()); + } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) { + EmitArgumentsLength(expr->arguments()); + } else if (strcmp("_ClassOf", *name->ToCString()) == 0) { + EmitClassOf(expr->arguments()); + } else if (strcmp("_Log", *name->ToCString()) == 0) { + EmitLog(expr->arguments()); + } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) { + EmitRandomHeapNumber(expr->arguments()); + } else if (strcmp("_SubString", *name->ToCString()) == 0) { + EmitSubString(expr->arguments()); + } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) { + EmitRegExpExec(expr->arguments()); + } else if (strcmp("_ValueOf", *name->ToCString()) == 0) { + EmitValueOf(expr->arguments()); + } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) { + EmitSetValueOf(expr->arguments()); + } else if (strcmp("_NumberToString", *name->ToCString()) == 0) { + EmitNumberToString(expr->arguments()); + } else if (strcmp("_CharFromCode", *name->ToCString()) == 0) { + EmitCharFromCode(expr->arguments()); + } else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) { + EmitFastCharCodeAt(expr->arguments()); + } else if (strcmp("_StringAdd", *name->ToCString()) == 0) { + EmitStringAdd(expr->arguments()); + } else if (strcmp("_StringCompare", *name->ToCString()) == 0) { + EmitStringCompare(expr->arguments()); + } else if (strcmp("_MathPow", *name->ToCString()) == 0) { + EmitMathPow(expr->arguments()); + } else if (strcmp("_MathSin", *name->ToCString()) == 0) { + EmitMathSin(expr->arguments()); + } else if (strcmp("_MathCos", *name->ToCString()) == 0) { + EmitMathCos(expr->arguments()); + } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) { + EmitMathSqrt(expr->arguments()); + } else if (strcmp("_CallFunction", *name->ToCString()) == 0) { + EmitCallFunction(expr->arguments()); + } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) { + EmitRegExpConstructResult(expr->arguments()); + } else if (strcmp("_SwapElements", *name->ToCString()) == 0) { + EmitSwapElements(expr->arguments()); + } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) { + EmitGetFromCache(expr->arguments()); + } else { + UNREACHABLE(); + } +} + + +void FullCodeGenerator::EmitIsSmi(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask | 0x80000000)); + __ j(zero, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsObject(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_false); + __ cmp(eax, Factory::null_value()); + __ j(equal, if_true); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + // Undetectable objects behave like undefined when tested with typeof. + __ movzx_b(ecx, FieldOperand(ebx, Map::kBitFieldOffset)); + __ test(ecx, Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, if_false); + __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceTypeOffset)); + __ cmp(ecx, FIRST_JS_OBJECT_TYPE); + __ j(less, if_false); + __ cmp(ecx, LAST_JS_OBJECT_TYPE); + __ j(less_equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsUndetectableObject(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_false); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset)); + __ test(ebx, Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsFunction(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_false); + __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsArray(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(equal, if_false); + __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsRegExp(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(equal, if_false); + __ CmpObjectType(eax, JS_REGEXP_TYPE, ebx); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + + +void FullCodeGenerator::EmitIsConstructCall(ZoneList* args) { + ASSERT(args->length() == 0); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + // Get the frame pointer for the calling frame. + __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + + // Skip the arguments adaptor frame if it exists. + Label check_frame_marker; + __ cmp(Operand(eax, StandardFrameConstants::kContextOffset), + Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ j(not_equal, &check_frame_marker); + __ mov(eax, Operand(eax, StandardFrameConstants::kCallerFPOffset)); + + // Check the marker in the calling frame. + __ bind(&check_frame_marker); + __ cmp(Operand(eax, StandardFrameConstants::kMarkerOffset), + Immediate(Smi::FromInt(StackFrame::CONSTRUCT))); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitObjectEquals(ZoneList* args) { + ASSERT(args->length() == 2); + + // Load the two objects into registers and perform the comparison. + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ pop(ebx); + __ cmp(eax, Operand(ebx)); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitArguments(ZoneList* args) { + ASSERT(args->length() == 1); + + // ArgumentsAccessStub expects the key in edx and the formal + // parameter count in eax. + VisitForValue(args->at(0), kAccumulator); + __ mov(edx, eax); + __ mov(eax, Immediate(Smi::FromInt(scope()->num_parameters()))); + ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitArgumentsLength(ZoneList* args) { + ASSERT(args->length() == 0); + + Label exit; + // Get the number of formal parameters. + __ Set(eax, Immediate(Smi::FromInt(scope()->num_parameters()))); + + // Check if the calling frame is an arguments adaptor frame. + __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + __ cmp(Operand(ebx, StandardFrameConstants::kContextOffset), + Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ j(not_equal, &exit); + + // Arguments adaptor case: Read the arguments length from the + // adaptor frame. + __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); + + __ bind(&exit); + if (FLAG_debug_code) __ AbortIfNotSmi(eax); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitClassOf(ZoneList* args) { + ASSERT(args->length() == 1); + Label done, null, function, non_function_constructor; + + VisitForValue(args->at(0), kAccumulator); + + // If the object is a smi, we return null. + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &null); + + // Check that the object is a JS object but take special care of JS + // functions to make sure they have 'Function' as their class. + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(eax, Map::kInstanceTypeOffset)); + __ cmp(ebx, FIRST_JS_OBJECT_TYPE); + __ j(less, &null); + + // As long as JS_FUNCTION_TYPE is the last instance type and it is + // right after LAST_JS_OBJECT_TYPE, we can avoid checking for + // LAST_JS_OBJECT_TYPE. + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); + __ cmp(ebx, JS_FUNCTION_TYPE); + __ j(equal, &function); + + // Check if the constructor in the map is a function. + __ mov(eax, FieldOperand(eax, Map::kConstructorOffset)); + __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); + __ j(not_equal, &non_function_constructor); + + // eax now contains the constructor function. Grab the + // instance class name from there. + __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); + __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset)); + __ jmp(&done); + + // Functions have class 'Function'. + __ bind(&function); + __ mov(eax, Factory::function_class_symbol()); + __ jmp(&done); + + // Objects with a non-function constructor have class 'Object'. + __ bind(&non_function_constructor); + __ mov(eax, Factory::Object_symbol()); + __ jmp(&done); + + // Non-JS objects have class null. + __ bind(&null); + __ mov(eax, Factory::null_value()); + + // All done. + __ bind(&done); + + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitLog(ZoneList* args) { + // Conditionally generate a log call. + // Args: + // 0 (literal string): The type of logging (corresponds to the flags). + // This is used to determine whether or not to generate the log call. + // 1 (string): Format string. Access the string at argument index 2 + // with '%2s' (see Logger::LogRuntime for all the formats). + // 2 (array): Arguments to the format string. + ASSERT_EQ(args->length(), 3); +#ifdef ENABLE_LOGGING_AND_PROFILING + if (CodeGenerator::ShouldGenerateLog(args->at(0))) { + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallRuntime(Runtime::kLog, 2); + } +#endif + // Finally, we're expected to leave a value on the top of the stack. + __ mov(eax, Factory::undefined_value()); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitRandomHeapNumber(ZoneList* args) { + ASSERT(args->length() == 0); + + Label slow_allocate_heapnumber; + Label heapnumber_allocated; + + __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber); + __ jmp(&heapnumber_allocated); + + __ bind(&slow_allocate_heapnumber); + // To allocate a heap number, and ensure that it is not a smi, we + // call the runtime function FUnaryMinus on 0, returning the double + // -0.0. A new, distinct heap number is returned each time. + __ push(Immediate(Smi::FromInt(0))); + __ CallRuntime(Runtime::kNumberUnaryMinus, 1); + __ mov(edi, eax); + + __ bind(&heapnumber_allocated); + + __ PrepareCallCFunction(0, ebx); + __ CallCFunction(ExternalReference::random_uint32_function(), 0); + + // Convert 32 random bits in eax to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + // This is implemented on both SSE2 and FPU. + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope fscope(SSE2); + __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single. + __ movd(xmm1, Operand(ebx)); + __ movd(xmm0, Operand(eax)); + __ cvtss2sd(xmm1, xmm1); + __ pxor(xmm0, xmm1); + __ subsd(xmm0, xmm1); + __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0); + } else { + // 0x4130000000000000 is 1.0 x 2^20 as a double. + __ mov(FieldOperand(edi, HeapNumber::kExponentOffset), + Immediate(0x41300000)); + __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax); + __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset)); + __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0)); + __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset)); + __ fsubp(1); + __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset)); + } + __ mov(eax, edi); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitSubString(ZoneList* args) { + // Load the arguments on the stack and call the stub. + SubStringStub stub; + ASSERT(args->length() == 3); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitRegExpExec(ZoneList* args) { + // Load the arguments on the stack and call the stub. + RegExpExecStub stub; + ASSERT(args->length() == 4); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + VisitForValue(args->at(3), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitValueOf(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); // Load the object. + + Label done; + // If the object is a smi return the object. + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &done); + // If the object is not a value type, return the object. + __ CmpObjectType(eax, JS_VALUE_TYPE, ebx); + __ j(not_equal, &done); + __ mov(eax, FieldOperand(eax, JSValue::kValueOffset)); + + __ bind(&done); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathPow(ZoneList* args) { + // Load the arguments on the stack and call the runtime function. + ASSERT(args->length() == 2); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + __ CallRuntime(Runtime::kMath_pow, 2); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitSetValueOf(ZoneList* args) { + ASSERT(args->length() == 2); + + VisitForValue(args->at(0), kStack); // Load the object. + VisitForValue(args->at(1), kAccumulator); // Load the value. + __ pop(ebx); // eax = value. ebx = object. + + Label done; + // If the object is a smi, return the value. + __ test(ebx, Immediate(kSmiTagMask)); + __ j(zero, &done); + + // If the object is not a value type, return the value. + __ CmpObjectType(ebx, JS_VALUE_TYPE, ecx); + __ j(not_equal, &done); + + // Store the value. + __ mov(FieldOperand(ebx, JSValue::kValueOffset), eax); + // Update the write barrier. Save the value as it will be + // overwritten by the write barrier code and is needed afterward. + __ mov(edx, eax); + __ RecordWrite(ebx, JSValue::kValueOffset, edx, ecx); + + __ bind(&done); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitNumberToString(ZoneList* args) { + ASSERT_EQ(args->length(), 1); + + // Load the argument on the stack and call the stub. + VisitForValue(args->at(0), kStack); + + NumberToStringStub stub; + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitCharFromCode(ZoneList* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label slow_case, done; + // Fast case of Heap::LookupSingleCharacterStringFromCode. + ASSERT(kSmiTag == 0); + ASSERT(kSmiShiftSize == 0); + ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1)); + __ test(eax, + Immediate(kSmiTagMask | + ((~String::kMaxAsciiCharCode) << kSmiTagSize))); + __ j(not_zero, &slow_case); + __ Set(ebx, Immediate(Factory::single_character_string_cache())); + ASSERT(kSmiTag == 0); + ASSERT(kSmiTagSize == 1); + ASSERT(kSmiShiftSize == 0); + // At this point code register contains smi tagged ascii char code. + __ mov(ebx, FieldOperand(ebx, + eax, times_half_pointer_size, + FixedArray::kHeaderSize)); + __ cmp(ebx, Factory::undefined_value()); + __ j(equal, &slow_case); + __ mov(eax, ebx); + __ jmp(&done); + + __ bind(&slow_case); + __ push(eax); + __ CallRuntime(Runtime::kCharFromCode, 1); + + __ bind(&done); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitFastCharCodeAt(ZoneList* args) { + // TODO(fsc): Port the complete implementation from the classic back-end. + // Move the undefined value into the result register, which will + // trigger the slow case. + __ Set(eax, Immediate(Factory::undefined_value())); + Apply(context_, eax); +} + +void FullCodeGenerator::EmitStringAdd(ZoneList* args) { + ASSERT_EQ(2, args->length()); + + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + + StringAddStub stub(NO_STRING_ADD_FLAGS); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitStringCompare(ZoneList* args) { + ASSERT_EQ(2, args->length()); + + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + + StringCompareStub stub; + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathSin(ZoneList* args) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::SIN); + ASSERT(args->length() == 1); + VisitForValue(args->at(0), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathCos(ZoneList* args) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::COS); + ASSERT(args->length() == 1); + VisitForValue(args->at(0), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathSqrt(ZoneList* args) { + // Load the argument on the stack and call the runtime function. + ASSERT(args->length() == 1); + VisitForValue(args->at(0), kStack); + __ CallRuntime(Runtime::kMath_sqrt, 1); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitCallFunction(ZoneList* args) { + ASSERT(args->length() >= 2); + + int arg_count = args->length() - 2; // For receiver and function. + VisitForValue(args->at(0), kStack); // Receiver. + for (int i = 0; i < arg_count; i++) { + VisitForValue(args->at(i + 1), kStack); + } + VisitForValue(args->at(arg_count + 1), kAccumulator); // Function. + + // InvokeFunction requires function in edi. Move it in there. + if (!result_register().is(edi)) __ mov(edi, result_register()); + ParameterCount count(arg_count); + __ InvokeFunction(edi, count, CALL_FUNCTION); + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitRegExpConstructResult(ZoneList* args) { + ASSERT(args->length() == 3); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallRuntime(Runtime::kRegExpConstructResult, 3); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitSwapElements(ZoneList* args) { + ASSERT(args->length() == 3); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallRuntime(Runtime::kSwapElements, 3); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitGetFromCache(ZoneList* args) { + ASSERT_EQ(2, args->length()); + + ASSERT_NE(NULL, args->at(0)->AsLiteral()); + int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); + + Handle jsfunction_result_caches( + Top::global_context()->jsfunction_result_caches()); + if (jsfunction_result_caches->length() <= cache_id) { + __ Abort("Attempt to use undefined cache."); + __ mov(eax, Factory::undefined_value()); + Apply(context_, eax); + return; + } + + VisitForValue(args->at(1), kAccumulator); + + Register key = eax; + Register cache = ebx; + Register tmp = ecx; + __ mov(cache, CodeGenerator::ContextOperand(esi, Context::GLOBAL_INDEX)); + __ mov(cache, + FieldOperand(cache, GlobalObject::kGlobalContextOffset)); + __ mov(cache, + CodeGenerator::ContextOperand( + cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); + __ mov(cache, + FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); + + Label done, not_found; + // tmp now holds finger offset as a smi. + ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset)); + __ cmp(key, CodeGenerator::FixedArrayElementOperand(cache, tmp)); + __ j(not_equal, ¬_found); + + __ mov(eax, CodeGenerator::FixedArrayElementOperand(cache, tmp, 1)); + __ jmp(&done); + + __ bind(¬_found); + // Call runtime to perform the lookup. + __ push(cache); + __ push(key); + __ CallRuntime(Runtime::kGetFromCache, 2); + + __ bind(&done); + Apply(context_, eax); +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { + Handle name = expr->name(); + if (name->length() > 0 && name->Get(0) == '_') { + Comment cmnt(masm_, "[ InlineRuntimeCall"); + EmitInlineRuntimeCall(expr); + return; + } + Comment cmnt(masm_, "[ CallRuntime"); ZoneList* args = expr->arguments(); @@ -1483,6 +2663,46 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { + case Token::DELETE: { + Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); + Property* prop = expr->expression()->AsProperty(); + Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); + if (prop == NULL && var == NULL) { + // Result of deleting non-property, non-variable reference is true. + // The subexpression may have side effects. + VisitForEffect(expr->expression()); + Apply(context_, true); + } else if (var != NULL && + !var->is_global() && + var->slot() != NULL && + var->slot()->type() != Slot::LOOKUP) { + // Result of deleting non-global, non-dynamic variables is false. + // The subexpression does not have side effects. + Apply(context_, false); + } else { + // Property or variable reference. Call the delete builtin with + // object and property name as arguments. + if (prop != NULL) { + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kStack); + } else if (var->is_global()) { + __ push(CodeGenerator::GlobalObject()); + __ push(Immediate(var->name())); + } else { + // Non-global variable. Call the runtime to look up the context + // where the variable was introduced. + __ push(context_register()); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kLookupContext, 2); + __ push(eax); + __ push(Immediate(var->name())); + } + __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); + Apply(context_, eax); + } + break; + } + case Token::VOID: { Comment cmnt(masm_, "[ UnaryOperation (VOID)"); VisitForEffect(expr->expression()); @@ -1523,33 +2743,15 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { case Token::NOT: { Comment cmnt(masm_, "[ UnaryOperation (NOT)"); - Label materialize_true, materialize_false, done; - // Initially assume a pure test context. Notice that the labels are - // swapped. - Label* if_true = false_label_; - Label* if_false = true_label_; - switch (context_) { - case Expression::kUninitialized: - UNREACHABLE(); - break; - case Expression::kEffect: - if_true = &done; - if_false = &done; - break; - case Expression::kValue: - if_true = &materialize_false; - if_false = &materialize_true; - break; - case Expression::kTest: - break; - case Expression::kValueTest: - if_false = &materialize_true; - break; - case Expression::kTestValue: - if_true = &materialize_false; - break; - } + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + + // Notice that the labels are swapped. + PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true); + VisitForControl(expr->expression(), if_true, if_false); + Apply(context_, if_false, if_true); // Labels swapped. break; } @@ -1645,6 +2847,12 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { Comment cmnt(masm_, "[ CountOperation"); + // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' + // as the left-hand side. + if (!expr->expression()->IsValidLeftHandSide()) { + VisitForEffect(expr->expression()); + return; + } // Expression can only be a property, a global or a (parameter or local) // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. @@ -1666,7 +2874,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), Expression::kValue); location_ = saved_location; - } else { + } else { // Reserve space for result of postfix operation. if (expr->is_postfix() && context_ != Expression::kEffect) { __ push(Immediate(Smi::FromInt(0))); @@ -1756,7 +2964,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { switch (assign_type) { case VARIABLE: if (expr->is_postfix()) { + // Perform the assignment as if via '='. EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, Expression::kEffect); // For all contexts except kEffect: We have the result on // top of the stack. @@ -1764,7 +2974,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { ApplyTOS(context_); } } else { + // Perform the assignment as if via '='. EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, context_); } break; @@ -1842,36 +3054,41 @@ void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { } +void FullCodeGenerator::EmitNullCompare(bool strict, + Register obj, + Register null_const, + Label* if_true, + Label* if_false, + Register scratch) { + __ cmp(obj, Operand(null_const)); + if (strict) { + __ j(equal, if_true); + } else { + __ j(equal, if_true); + __ cmp(obj, Factory::undefined_value()); + __ j(equal, if_true); + __ test(obj, Immediate(kSmiTagMask)); + __ j(zero, if_false); + // It can be an undetectable object. + __ mov(scratch, FieldOperand(obj, HeapObject::kMapOffset)); + __ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset)); + __ test(scratch, Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, if_true); + } + __ jmp(if_false); +} + + void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. - Label materialize_true, materialize_false, done; - // Initially assume we are in a test context. - Label* if_true = true_label_; - Label* if_false = false_label_; - switch (context_) { - case Expression::kUninitialized: - UNREACHABLE(); - break; - case Expression::kEffect: - if_true = &done; - if_false = &done; - break; - case Expression::kValue: - if_true = &materialize_true; - if_false = &materialize_false; - break; - case Expression::kTest: - break; - case Expression::kValueTest: - if_true = &materialize_true; - break; - case Expression::kTestValue: - if_false = &materialize_false; - break; - } + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); VisitForValue(expr->left(), kStack); switch (expr->op()) { @@ -1901,10 +3118,24 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::EQ_STRICT: strict = true; // Fall through - case Token::EQ: + case Token::EQ: { cc = equal; __ pop(edx); + // If either operand is constant null we do a fast compare + // against null. + Literal* right_literal = expr->right()->AsLiteral(); + Literal* left_literal = expr->left()->AsLiteral(); + if (right_literal != NULL && right_literal->handle()->IsNull()) { + EmitNullCompare(strict, edx, eax, if_true, if_false, ecx); + Apply(context_, if_true, if_false); + return; + } else if (left_literal != NULL && left_literal->handle()->IsNull()) { + EmitNullCompare(strict, eax, edx, if_true, if_false, ecx); + Apply(context_, if_true, if_false); + return; + } break; + } case Token::LT: cc = less; __ pop(edx); diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 348861b..8b9358e 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -779,19 +779,28 @@ void FullCodeGenerator::DeclareGlobals(Handle pairs) { } -void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { - Comment cmnt(masm_, "[ FunctionLiteral"); - - // Build the shared function info and instantiate the function based - // on it. - Handle function_info = - Compiler::BuildFunctionInfo(expr, script(), this); - if (HasStackOverflow()) return; - - // Create a new closure. - __ push(rsi); - __ Push(function_info); - __ CallRuntime(Runtime::kNewClosure, 2); +void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { + UNREACHABLE(); +} + + +void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { + UNREACHABLE(); +} + + +void FullCodeGenerator::EmitNewClosure(Handle info) { + // Use the fast case closure allocation code that allocates in new + // space for nested functions that don't need literals cloning. + if (scope()->is_function_scope() && info->num_literals() == 0) { + FastNewClosureStub stub; + __ Push(info); + __ CallStub(&stub); + } else { + __ push(rsi); + __ Push(info); + __ CallRuntime(Runtime::kNewClosure, 2); + } Apply(context_, rax); } @@ -1021,7 +1030,13 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { void FullCodeGenerator::VisitAssignment(Assignment* expr) { Comment cmnt(masm_, "[ Assignment"); - ASSERT(expr->op() != Token::INIT_CONST); + // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' + // on the left-hand side. + if (!expr->target()->IsValidLeftHandSide()) { + VisitForEffect(expr->target()); + return; + } + // Left-hand side can only be a property, a global or a (parameter or local) // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; @@ -1047,8 +1062,15 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { } break; case KEYED_PROPERTY: - VisitForValue(prop->obj(), kStack); - VisitForValue(prop->key(), kStack); + if (expr->is_compound()) { + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kAccumulator); + __ movq(rdx, Operand(rsp, 0)); + __ push(rax); + } else { + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kStack); + } break; } @@ -1093,6 +1115,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { switch (assign_type) { case VARIABLE: EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + expr->op(), context_); break; case NAMED_PROPERTY: @@ -1135,60 +1158,78 @@ void FullCodeGenerator::EmitBinaryOp(Token::Value op, void FullCodeGenerator::EmitVariableAssignment(Variable* var, + Token::Value op, Expression::Context context) { - // Three main cases: non-this global variables, lookup slots, and - // all other types of slots. Left-hand-side parameters that rewrite - // to explicit property accesses do not reach here. + // Left-hand sides that rewrite to explicit property accesses do not reach + // here. ASSERT(var != NULL); ASSERT(var->is_global() || var->slot() != NULL); - Slot* slot = var->slot(); + if (var->is_global()) { ASSERT(!var->is_this()); // Assignment to a global variable. Use inline caching for the // assignment. Right-hand-side value is passed in rax, variable name in - // rcx, and the global object in rdx. + // rcx, and the global object on the stack. __ Move(rcx, var->name()); __ movq(rdx, CodeGenerator::GlobalObject()); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); - Apply(context, rax); - - } else if (slot != NULL && slot->type() == Slot::LOOKUP) { - __ push(result_register()); // Value. - __ push(rsi); // Context. - __ Push(var->name()); - __ CallRuntime(Runtime::kStoreContextSlot, 3); - Apply(context, rax); + __ nop(); - } else if (var->slot() != NULL) { + } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { + // Perform the assignment for non-const variables and for initialization + // of const variables. Const assignments are simply skipped. + Label done; + Slot* slot = var->slot(); switch (slot->type()) { - case Slot::LOCAL: case Slot::PARAMETER: - __ movq(Operand(rbp, SlotOffset(slot)), result_register()); + case Slot::LOCAL: + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ movq(rdx, Operand(rbp, SlotOffset(slot))); + __ Cmp(rdx, Factory::the_hole_value()); + __ j(not_equal, &done); + } + // Perform the assignment. + __ movq(Operand(rbp, SlotOffset(slot)), rax); break; case Slot::CONTEXT: { MemOperand target = EmitSlotSearch(slot, rcx); - __ movq(target, result_register()); - - // RecordWrite may destroy all its register arguments. - __ movq(rdx, result_register()); + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ movq(rdx, target); + __ Cmp(rdx, Factory::the_hole_value()); + __ j(not_equal, &done); + } + // Perform the assignment and issue the write barrier. + __ movq(target, rax); + // The value of the assignment is in rax. RecordWrite clobbers its + // register arguments. + __ movq(rdx, rax); int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; __ RecordWrite(rcx, offset, rdx, rbx); break; } case Slot::LOOKUP: - UNREACHABLE(); + // Call the runtime for the assignment. The runtime will ignore + // const reinitialization. + __ push(rax); // Value. + __ push(rsi); // Context. + __ Push(var->name()); + if (op == Token::INIT_CONST) { + // The runtime will ignore const redeclaration. + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); + } else { + __ CallRuntime(Runtime::kStoreContextSlot, 3); + } break; } - Apply(context, result_register()); - - } else { - // Variables rewritten as properties are not treated as variables in - // assignments. - UNREACHABLE(); + __ bind(&done); } + + Apply(context, rax); } @@ -1737,7 +1778,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { switch (assign_type) { case VARIABLE: if (expr->is_postfix()) { + // Perform the assignment as if via '='. EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, Expression::kEffect); // For all contexts except kEffect: We have the result on // top of the stack. @@ -1745,7 +1788,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { ApplyTOS(context_); } } else { + // Perform the assignment as if via '='. EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, context_); } break; -- 2.7.4