Revision: 3110 Author: [email protected] Date: Thu Oct 22 03:07:45 2009 Log: Added support for array literals to the toplevel compiler. They are currently compiled the same as with the optimizing compiler: they are cloned from a boilerplate object and the boilerplate objects are lazily constructed.
Also changed argument pushing on ARM to use stm (store multiple), which required changing the order of arguments to the runtime functions DeclareGlobals and NewClosure. They were only used from generated code. Finally, changed the toplevel code generator so that stack pops to discard a temporary became addition to the stack pointer on ia32 and x64. Review URL: http://codereview.chromium.org/303021 http://code.google.com/p/v8/source/detail?r=3110 Modified: /branches/bleeding_edge/src/arm/codegen-arm.cc /branches/bleeding_edge/src/arm/fast-codegen-arm.cc /branches/bleeding_edge/src/compiler.cc /branches/bleeding_edge/src/fast-codegen.cc /branches/bleeding_edge/src/ia32/codegen-ia32.cc /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc /branches/bleeding_edge/src/runtime.cc /branches/bleeding_edge/src/x64/codegen-x64.cc /branches/bleeding_edge/src/x64/fast-codegen-x64.cc /branches/bleeding_edge/test/mjsunit/compiler/literals.js ======================================= --- /branches/bleeding_edge/src/arm/codegen-arm.cc Mon Oct 12 06:14:06 2009 +++ /branches/bleeding_edge/src/arm/codegen-arm.cc Thu Oct 22 03:07:45 2009 @@ -1172,9 +1172,9 @@ void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { VirtualFrame::SpilledScope spilled_scope; + frame_->EmitPush(cp); __ mov(r0, Operand(pairs)); frame_->EmitPush(r0); - frame_->EmitPush(cp); __ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0))); frame_->EmitPush(r0); frame_->CallRuntime(Runtime::kDeclareGlobals, 3); @@ -2255,12 +2255,10 @@ VirtualFrame::SpilledScope spilled_scope; ASSERT(boilerplate->IsBoilerplate()); - // Push the boilerplate on the stack. - __ mov(r0, Operand(boilerplate)); - frame_->EmitPush(r0); - // Create a new closure. frame_->EmitPush(cp); + __ mov(r0, Operand(boilerplate)); + frame_->EmitPush(r0); frame_->CallRuntime(Runtime::kNewClosure, 2); frame_->EmitPush(r0); } ======================================= --- /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Thu Oct 22 02:29:03 2009 +++ /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Thu Oct 22 03:07:45 2009 @@ -29,6 +29,7 @@ #include "codegen-inl.h" #include "fast-codegen.h" +#include "parser.h" namespace v8 { namespace internal { @@ -110,11 +111,10 @@ void FastCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. - __ mov(r0, Operand(pairs)); - __ push(r0); - __ push(cp); // The context is the second argument. + // The context is the first argument. + __ mov(r1, Operand(pairs)); __ mov(r0, Operand(Smi::FromInt(is_eval_ ? 1 : 0))); - __ push(r0); + __ stm(db_w, sp, cp.bit() | r1.bit() | r0.bit()); __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } @@ -153,7 +153,7 @@ __ RecordJSReturn(); __ mov(sp, fp); __ ldm(ia_w, sp, fp.bit() | lr.bit()); - int num_parameters = function_->scope()->num_parameters(); + int num_parameters = function_->scope()->num_parameters(); __ add(sp, sp, Operand((num_parameters + 1) * kPointerSize)); __ Jump(lr); } @@ -170,8 +170,7 @@ // Create a new closure. __ mov(r0, Operand(boilerplate)); - __ push(r0); - __ push(cp); + __ stm(db_w, sp, cp.bit() | r0.bit()); __ CallRuntime(Runtime::kNewClosure, 2); if (expr->location().is_temporary()) { @@ -214,6 +213,7 @@ } } } + void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { Comment cmnt(masm_, "[ RegExp Literal"); @@ -244,6 +244,80 @@ ASSERT(expr->location().is_nowhere()); } } + + +void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { + Comment cmnt(masm_, "[ ArrayLiteral"); + Label make_clone; + + // Fetch the function's literals array. + __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); + // Check if the literal's boilerplate has been instantiated. + int offset = + FixedArray::kHeaderSize + (expr->literal_index() * kPointerSize); + __ ldr(r0, FieldMemOperand(r3, offset)); + __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); + __ cmp(r0, ip); + __ b(&make_clone, ne); + + // Instantiate the boilerplate. + __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); + __ mov(r1, Operand(expr->literals())); + __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit()); + __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); + + __ bind(&make_clone); + // Clone the boilerplate. + __ push(r0); + if (expr->depth() > 1) { + __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); + } else { + __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1); + } + + 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++) { + Expression* subexpr = subexprs->at(i); + // If the subexpression is a literal or a simple materialized literal it + // is already set in the cloned array. + if (subexpr->AsLiteral() != NULL || + CompileTimeValue::IsCompileTimeValue(subexpr)) { + continue; + } + + if (!result_saved) { + __ push(r0); + result_saved = true; + } + Visit(subexpr); + ASSERT(subexpr->location().is_temporary()); + + // Store the subexpression value in the array's elements. + __ pop(r0); // Subexpression value. + __ ldr(r1, MemOperand(sp)); // Copy of array literal. + __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); + int offset = FixedArray::kHeaderSize + (i * kPointerSize); + __ str(r0, FieldMemOperand(r1, offset)); + + // Update the write barrier for the array store with r0 as the scratch + // register. + __ mov(r2, Operand(offset)); + __ RecordWrite(r1, r2, r0); + } + + Location destination = expr->location(); + if (destination.is_nowhere() && result_saved) { + __ pop(); + } else if (destination.is_temporary() && !result_saved) { + __ push(r0); + } +} + void FastCodeGenerator::VisitAssignment(Assignment* expr) { Comment cmnt(masm_, "[ Assignment"); @@ -323,11 +397,10 @@ ASSERT(var != NULL && !var->is_this() && var->is_global()); ASSERT(!var->is_possibly_eval()); - __ mov(r0, Operand(var->name())); - __ push(r0); - // Push global object (receiver) + __ mov(r1, Operand(var->name())); + // Push global object as receiver. __ ldr(r0, CodeGenerator::GlobalObject()); - __ push(r0); + __ stm(db_w, sp, r1.bit() | r0.bit()); int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { Visit(args->at(i)); ======================================= --- /branches/bleeding_edge/src/compiler.cc Thu Oct 22 02:29:03 2009 +++ /branches/bleeding_edge/src/compiler.cc Thu Oct 22 03:07:45 2009 @@ -644,7 +644,14 @@ void CodeGenSelector::VisitArrayLiteral(ArrayLiteral* expr) { - BAILOUT("ArrayLiteral"); + ZoneList<Expression*>* subexprs = expr->values(); + for (int i = 0, len = subexprs->length(); i < len; i++) { + Expression* subexpr = subexprs->at(i); + if (subexpr->AsLiteral() != NULL) continue; + if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; + Visit(subexpr); + CHECK_BAILOUT; + } } ======================================= --- /branches/bleeding_edge/src/fast-codegen.cc Thu Oct 22 02:29:03 2009 +++ /branches/bleeding_edge/src/fast-codegen.cc Thu Oct 22 03:07:45 2009 @@ -287,11 +287,6 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { UNREACHABLE(); } - - -void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { - UNREACHABLE(); -} void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { ======================================= --- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Wed Oct 21 02:24:25 2009 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Thu Oct 22 03:07:45 2009 @@ -2275,8 +2275,8 @@ // allow us to push the arguments directly into place. frame_->SyncRange(0, frame_->element_count() - 1); + frame_->EmitPush(esi); // The context is the first argument. frame_->EmitPush(Immediate(pairs)); - frame_->EmitPush(esi); // The context is the second argument. frame_->EmitPush(Immediate(Smi::FromInt(is_eval() ? 1 : 0))); Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. @@ -3576,11 +3576,9 @@ ASSERT(boilerplate->IsBoilerplate()); frame_->SyncRange(0, frame_->element_count() - 1); - // Push the boilerplate on the stack. - frame_->EmitPush(Immediate(boilerplate)); - // Create a new closure. frame_->EmitPush(esi); + frame_->EmitPush(Immediate(boilerplate)); Result result = frame_->CallRuntime(Runtime::kNewClosure, 2); frame_->Push(&result); } ======================================= --- /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Thu Oct 22 02:29:03 2009 +++ /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Thu Oct 22 03:07:45 2009 @@ -29,6 +29,7 @@ #include "codegen-inl.h" #include "fast-codegen.h" +#include "parser.h" namespace v8 { namespace internal { @@ -100,8 +101,8 @@ void FastCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. + __ push(esi); // The context is the first argument. __ push(Immediate(pairs)); - __ push(esi); // The context is the second argument. __ push(Immediate(Smi::FromInt(is_eval_ ? 1 : 0))); __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. @@ -157,8 +158,8 @@ ASSERT(boilerplate->IsBoilerplate()); // Create a new closure. - __ push(Immediate(boilerplate)); __ push(esi); + __ push(Immediate(boilerplate)); __ CallRuntime(Runtime::kNewClosure, 2); if (expr->location().is_temporary()) { @@ -190,7 +191,7 @@ __ mov(Operand(esp, 0), eax); } else { ASSERT(expr->location().is_nowhere()); - __ pop(eax); + __ add(Operand(esp), Immediate(kPointerSize)); } } else { @@ -235,6 +236,76 @@ ASSERT(expr->location().is_nowhere()); } } + + +void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { + Comment cmnt(masm_, "[ ArrayLiteral"); + Label make_clone; + + // Fetch the function's literals array. + __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ mov(ebx, FieldOperand(ebx, JSFunction::kLiteralsOffset)); + // Check if the literal's boilerplate has been instantiated. + int offset = + FixedArray::kHeaderSize + (expr->literal_index() * kPointerSize); + __ mov(eax, FieldOperand(ebx, offset)); + __ cmp(eax, Factory::undefined_value()); + __ j(not_equal, &make_clone); + + // Instantiate the boilerplate. + __ push(ebx); + __ push(Immediate(Smi::FromInt(expr->literal_index()))); + __ push(Immediate(expr->literals())); + __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); + + __ bind(&make_clone); + // Clone the boilerplate. + __ push(eax); + if (expr->depth() > 1) { + __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); + } else { + __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1); + } + + 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++) { + Expression* subexpr = subexprs->at(i); + // If the subexpression is a literal or a simple materialized literal it + // is already set in the cloned array. + if (subexpr->AsLiteral() != NULL || + CompileTimeValue::IsCompileTimeValue(subexpr)) { + continue; + } + + if (!result_saved) { + __ push(eax); + result_saved = true; + } + Visit(subexpr); + ASSERT(subexpr->location().is_temporary()); + + // Store the subexpression value in the array's elements. + __ pop(eax); // Subexpression value. + __ mov(ebx, Operand(esp, 0)); // Copy of array literal. + __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); + int offset = FixedArray::kHeaderSize + (i * kPointerSize); + __ mov(FieldOperand(ebx, offset), eax); + + // Update the write barrier for the array store. + __ RecordWrite(ebx, offset, eax, ecx); + } + + Location destination = expr->location(); + if (destination.is_nowhere() && result_saved) { + __ add(Operand(esp), Immediate(kPointerSize)); + } else if (destination.is_temporary() && !result_saved) { + __ push(eax); + } +} void FastCodeGenerator::VisitAssignment(Assignment* expr) { @@ -275,7 +346,7 @@ __ mov(Operand(esp, 0), eax); } else { ASSERT(destination.is_nowhere()); - __ pop(eax); + __ add(Operand(esp), Immediate(kPointerSize)); } } else { @@ -339,7 +410,7 @@ __ mov(Operand(esp, 0), eax); } else { ASSERT(expr->location().is_nowhere()); - __ pop(eax); + __ add(Operand(esp), Immediate(kPointerSize)); } } ======================================= --- /branches/bleeding_edge/src/runtime.cc Wed Oct 21 10:07:43 2009 +++ /branches/bleeding_edge/src/runtime.cc Thu Oct 22 03:07:45 2009 @@ -577,8 +577,8 @@ HandleScope scope; Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global()); - CONVERT_ARG_CHECKED(FixedArray, pairs, 0); - Handle<Context> context = args.at<Context>(1); + Handle<Context> context = args.at<Context>(0); + CONVERT_ARG_CHECKED(FixedArray, pairs, 1); bool is_eval = Smi::cast(args[2])->value() == 1; // Compute the property attributes. According to ECMA-262, section @@ -4391,8 +4391,8 @@ static Object* Runtime_NewClosure(Arguments args) { HandleScope scope; ASSERT(args.length() == 2); - CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0); - CONVERT_ARG_CHECKED(Context, context, 1); + CONVERT_ARG_CHECKED(Context, context, 0); + CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1); Handle<JSFunction> result = Factory::NewFunctionFromBoilerplate(boilerplate, context); ======================================= --- /branches/bleeding_edge/src/x64/codegen-x64.cc Wed Oct 21 02:24:25 2009 +++ /branches/bleeding_edge/src/x64/codegen-x64.cc Thu Oct 22 03:07:45 2009 @@ -270,8 +270,8 @@ frame_->SyncRange(0, frame_->element_count() - 1); __ movq(kScratchRegister, pairs, RelocInfo::EMBEDDED_OBJECT); + frame_->EmitPush(rsi); // The context is the first argument. frame_->EmitPush(kScratchRegister); - frame_->EmitPush(rsi); // The context is the second argument. frame_->EmitPush(Smi::FromInt(is_eval() ? 1 : 0)); Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. @@ -2177,12 +2177,10 @@ ASSERT(boilerplate->IsBoilerplate()); frame_->SyncRange(0, frame_->element_count() - 1); - // Push the boilerplate on the stack. - __ movq(kScratchRegister, boilerplate, RelocInfo::EMBEDDED_OBJECT); - frame_->EmitPush(kScratchRegister); - // Create a new closure. frame_->EmitPush(rsi); + __ movq(kScratchRegister, boilerplate, RelocInfo::EMBEDDED_OBJECT); + frame_->EmitPush(kScratchRegister); Result result = frame_->CallRuntime(Runtime::kNewClosure, 2); frame_->Push(&result); } ======================================= --- /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Thu Oct 22 02:29:03 2009 +++ /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Thu Oct 22 03:07:45 2009 @@ -30,6 +30,7 @@ #include "codegen-inl.h" #include "debug.h" #include "fast-codegen.h" +#include "parser.h" namespace v8 { namespace internal { @@ -108,8 +109,8 @@ void FastCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. + __ push(rsi); // The context is the first argument. __ Push(pairs); - __ push(rsi); // The context is the second argument. __ Push(Smi::FromInt(is_eval_ ? 1 : 0)); __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. @@ -174,8 +175,8 @@ ASSERT(boilerplate->IsBoilerplate()); // Create a new closure. - __ Push(boilerplate); __ push(rsi); + __ Push(boilerplate); __ CallRuntime(Runtime::kNewClosure, 2); if (expr->location().is_temporary()) { @@ -206,7 +207,7 @@ __ movq(Operand(rsp, 0), rax); } else { ASSERT(expr->location().is_nowhere()); - __ pop(rax); + __ addq(rsp, Immediate(kPointerSize)); } } else { @@ -251,6 +252,76 @@ ASSERT(expr->location().is_nowhere()); } } + + +void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { + Comment cmnt(masm_, "[ ArrayLiteral"); + Label make_clone; + + // Fetch the function's literals array. + __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + __ movq(rbx, FieldOperand(rbx, JSFunction::kLiteralsOffset)); + // Check if the literal's boilerplate has been instantiated. + int offset = + FixedArray::kHeaderSize + (expr->literal_index() * kPointerSize); + __ movq(rax, FieldOperand(rbx, offset)); + __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); + __ j(not_equal, &make_clone); + + // Instantiate the boilerplate. + __ push(rbx); + __ Push(Smi::FromInt(expr->literal_index())); + __ Push(expr->literals()); + __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); + + __ bind(&make_clone); + // Clone the boilerplate. + __ push(rax); + if (expr->depth() > 1) { + __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); + } else { + __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1); + } + + 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++) { + Expression* subexpr = subexprs->at(i); + // If the subexpression is a literal or a simple materialized literal it + // is already set in the cloned array. + if (subexpr->AsLiteral() != NULL || + CompileTimeValue::IsCompileTimeValue(subexpr)) { + continue; + } + + if (!result_saved) { + __ push(rax); + result_saved = true; + } + Visit(subexpr); + ASSERT(subexpr->location().is_temporary()); + + // Store the subexpression value in the array's elements. + __ pop(rax); // Subexpression value. + __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. + __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); + int offset = FixedArray::kHeaderSize + (i * kPointerSize); + __ movq(FieldOperand(rbx, offset), rax); + + // Update the write barrier for the array store. + __ RecordWrite(rbx, offset, rax, rcx); + } + + Location destination = expr->location(); + if (destination.is_nowhere() && result_saved) { + __ addq(rsp, Immediate(kPointerSize)); + } else if (destination.is_temporary() && !result_saved) { + __ push(rax); + } +} void FastCodeGenerator::VisitAssignment(Assignment* expr) { @@ -290,7 +361,7 @@ if (destination.is_temporary()) { __ movq(Operand(rsp, 0), rax); } else { - __ pop(rax); + __ addq(rsp, Immediate(kPointerSize)); } } else { if (source.is_temporary()) { @@ -352,7 +423,7 @@ __ movq(Operand(rsp, 0), rax); } else { ASSERT(expr->location().is_nowhere()); - __ pop(rax); + __ addq(rsp, Immediate(kPointerSize)); } } ======================================= --- /branches/bleeding_edge/test/mjsunit/compiler/literals.js Wed Oct 14 03:24:50 2009 +++ /branches/bleeding_edge/test/mjsunit/compiler/literals.js Thu Oct 22 03:07:45 2009 @@ -33,3 +33,20 @@ assertEquals("abc", eval("'abc'")); assertEquals(8, eval("6;'abc';8")); + +// Test some materialized array literals. +assertEquals([1,2,3,4], eval('[1,2,3,4]')); +assertEquals([[1,2],3,4], eval('[[1,2],3,4]')); +assertEquals([1,[2,3,4]], eval('[1,[2,3,4]]')); + +assertEquals([1,2,3,4], eval('var a=1, b=2; [a,b,3,4]')) +assertEquals([1,2,3,4], eval('var a=1, b=2, c = [a,b,3,4]; c')); + +function double(x) { return x + x; } +var s = 'var a = 1, b = 2; [double(a), double(b), double(3), double(4)]'; +assertEquals([2,4,6,8], eval(s)); + +// Test array literals in effect context. +assertEquals(17, eval('[1,2,3,4]; 17')); +assertEquals(19, eval('var a=1, b=2; [a,b,3,4]; 19')); +assertEquals(23, eval('var a=1, b=2; c=23; [a,b,3,4]; c')); --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---
