-// 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:
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.
}
{ 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");
}
+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) {
}
+// 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<Object> 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
}
-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;
// 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,
__ 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());
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!
}
}
} 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);
}
+void FullCodeGenerator::VisitDeclaration(Declaration* decl) {
+ EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun());
+}
+
+
void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
// Call the runtime to declare the globals.
__ push(esi); // The context is the first argument.
}
-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<CaseClause*>* 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<SharedFunctionInfo> 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<SharedFunctionInfo> 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);
}
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");
void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
Comment cmnt(masm_, "[ ArrayLiteral");
+
+ ZoneList<Expression*>* 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<Expression*>* 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.
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 };
switch (assign_type) {
case VARIABLE:
EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ expr->op(),
context_);
break;
case NAMED_PROPERTY:
}
+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<Code> 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<Code> 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
Handle<Code> 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);
}
}
// 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));
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<Expression*>* 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();
}
+void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
+ Handle<String> 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<FixedArray> 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<String> name = expr->name();
+ if (name->length() > 0 && name->Get(0) == '_') {
+ Comment cmnt(masm_, "[ InlineRuntimeCall");
+ EmitInlineRuntimeCall(expr);
+ return;
+ }
+
Comment cmnt(masm_, "[ CallRuntime");
ZoneList<Expression*>* args = expr->arguments();
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());
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;
}
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.
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)));
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.
ApplyTOS(context_);
}
} else {
+ // Perform the assignment as if via '='.
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN,
context_);
}
break;
}
+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()) {
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);