Commit 19d98493 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

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
parent 8fbac01b
......@@ -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<FixedArray> 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<SharedFunctionInfo> 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<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;
__ 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<Code> 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;
......
......@@ -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) \
......
......@@ -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<SharedFunctionInfo> 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.
......
// 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<Declaration*>* declarations);
void DeclareGlobals(Handle<FixedArray> 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<Object> name, RelocInfo::Mode mode);
// Platform-specific code for inline runtime calls.
void EmitInlineRuntimeCall(CallRuntime* expr);
void EmitIsSmi(ZoneList<Expression*>* arguments);
void EmitIsNonNegativeSmi(ZoneList<Expression*>* arguments);
void EmitIsObject(ZoneList<Expression*>* arguments);
void EmitIsUndetectableObject(ZoneList<Expression*>* arguments);
void EmitIsFunction(ZoneList<Expression*>* arguments);
void EmitIsArray(ZoneList<Expression*>* arguments);
void EmitIsRegExp(ZoneList<Expression*>* arguments);
void EmitIsConstructCall(ZoneList<Expression*>* arguments);
void EmitObjectEquals(ZoneList<Expression*>* arguments);
void EmitArguments(ZoneList<Expression*>* arguments);
void EmitArgumentsLength(ZoneList<Expression*>* arguments);
void EmitClassOf(ZoneList<Expression*>* arguments);
void EmitValueOf(ZoneList<Expression*>* arguments);
void EmitSetValueOf(ZoneList<Expression*>* arguments);
void EmitNumberToString(ZoneList<Expression*>* arguments);
void EmitCharFromCode(ZoneList<Expression*>* arguments);
void EmitFastCharCodeAt(ZoneList<Expression*>* arguments);
void EmitStringCompare(ZoneList<Expression*>* arguments);
void EmitStringAdd(ZoneList<Expression*>* arguments);
void EmitLog(ZoneList<Expression*>* arguments);
void EmitRandomHeapNumber(ZoneList<Expression*>* arguments);
void EmitSubString(ZoneList<Expression*>* arguments);
void EmitRegExpExec(ZoneList<Expression*>* arguments);
void EmitMathPow(ZoneList<Expression*>* arguments);
void EmitMathSin(ZoneList<Expression*>* arguments);
void EmitMathCos(ZoneList<Expression*>* arguments);
void EmitMathSqrt(ZoneList<Expression*>* arguments);
void EmitCallFunction(ZoneList<Expression*>* arguments);
void EmitRegExpConstructResult(ZoneList<Expression*>* arguments);
void EmitSwapElements(ZoneList<Expression*>* arguments);
void EmitGetFromCache(ZoneList<Expression*>* 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<SharedFunctionInfo> 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);
......
This diff is collapsed.
......@@ -779,19 +779,28 @@ void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
}
void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
Comment cmnt(masm_, "[ FunctionLiteral");
// Build the shared function info and instantiate the function based
// on it.
Handle<SharedFunctionInfo> 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<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(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<Code> 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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment