Revision: 3129 Author: [email protected] Date: Mon Oct 26 06:21:48 2009 Log: Support for object literals in fast compiler.
I also added more unit tests for literals. Right now, the fast compiler produces code very similar to the existing code generator. We may consider different ways to further compact the generated code for top-level code. ARM always goes through a runtime function to initialize computed properties in an object literal whereas IA32 and x64 use StoreIC. Review URL: http://codereview.chromium.org/316009 http://code.google.com/p/v8/source/detail?r=3129 Modified: /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/fast-codegen-ia32.cc /branches/bleeding_edge/src/x64/fast-codegen-x64.cc /branches/bleeding_edge/test/mjsunit/compiler/literals-assignment.js ======================================= --- /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Fri Oct 23 03:42:14 2009 +++ /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Mon Oct 26 06:21:48 2009 @@ -232,6 +232,98 @@ } } } + + +void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { + Comment cmnt(masm_, "[ ObjectLiteral"); + Label boilerplate_exists; + __ ldr(r2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + // r2 = literal array (0). + __ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset)); + int literal_offset = + FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; + __ ldr(r0, FieldMemOperand(r2, literal_offset)); + // Check whether we need to materialize the object literal boilerplate. + __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); + __ cmp(r0, Operand(ip)); + __ b(ne, &boilerplate_exists); + // Create boilerplate if it does not exist. + // r1 = literal index (1). + __ mov(r1, Operand(Smi::FromInt(expr->literal_index()))); + // r0 = constant properties (2). + __ mov(r0, Operand(expr->constant_properties())); + __ stm(db_w, sp, r2.bit() | r1.bit() | r0.bit()); + __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); + __ bind(&boilerplate_exists); + // r0 contains boilerplate. + // Clone boilerplate. + __ push(r0); + if (expr->depth() > 1) { + __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); + } else { + __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1); + } + + // If result_saved == true: the result is saved on top of the stack. + // If result_saved == false: the result is in eax. + bool result_saved = false; + + for (int i = 0; i < expr->properties()->length(); i++) { + ObjectLiteral::Property* property = expr->properties()->at(i); + Literal* key = property->key(); + Expression* value = property->value(); + if (property->kind() == ObjectLiteral::Property::CONSTANT) continue; + if (property->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL && + CompileTimeValue::IsCompileTimeValue(value)) { + continue; + } + if (!result_saved) { + __ push(r0); // Save result on stack + result_saved = true; + } + switch (property->kind()) { + case ObjectLiteral::Property::MATERIALIZED_LITERAL: // fall through + ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value())); + case ObjectLiteral::Property::COMPUTED: // fall through + case ObjectLiteral::Property::PROTOTYPE: + __ push(r0); + Visit(key); + if (key->location().is_constant()) { + __ mov(r1, Operand(key->handle())); + __ push(r1); + } + Visit(value); + ASSERT(value->location().is_temporary()); + __ CallRuntime(Runtime::kSetProperty, 3); + __ ldr(r0, MemOperand(sp)); // Restore result into r0 + break; + case ObjectLiteral::Property::SETTER: // fall through + case ObjectLiteral::Property::GETTER: + __ push(r0); + Visit(key); + if (key->location().is_constant()) { + __ mov(r1, Operand(key->handle())); + __ push(r1); + } + __ mov(r1, Operand(property->kind() == ObjectLiteral::Property::SETTER ? + Smi::FromInt(1) : + Smi::FromInt(0))); + __ push(r1); + Visit(value); + ASSERT(value->location().is_temporary()); + __ CallRuntime(Runtime::kDefineAccessor, 4); + __ ldr(r0, MemOperand(sp)); // Restore result into r0 + break; + default: UNREACHABLE(); + } + } + if (expr->location().is_nowhere() && result_saved) { + __ pop(); + } else if (expr->location().is_temporary() && !result_saved) { + ASSERT(expr->location().is_temporary()); + __ push(r0); + } +} void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { ======================================= --- /branches/bleeding_edge/src/compiler.cc Fri Oct 23 03:42:14 2009 +++ /branches/bleeding_edge/src/compiler.cc Mon Oct 26 06:21:48 2009 @@ -639,7 +639,13 @@ void CodeGenSelector::VisitObjectLiteral(ObjectLiteral* expr) { - BAILOUT("ObjectLiteral"); + for (int i = 0; i < expr->properties()->length(); i++) { + ObjectLiteral::Property* property = expr->properties()->at(i); + Visit(property->key()); + CHECK_BAILOUT; + Visit(property->value()); + CHECK_BAILOUT; + } } ======================================= --- /branches/bleeding_edge/src/fast-codegen.cc Fri Oct 23 03:42:14 2009 +++ /branches/bleeding_edge/src/fast-codegen.cc Mon Oct 26 06:21:48 2009 @@ -282,11 +282,6 @@ void FastCodeGenerator::VisitLiteral(Literal* expr) { // No code is emitted (here) for simple literals. } - - -void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { - UNREACHABLE(); -} void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { ======================================= --- /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Fri Oct 23 03:42:14 2009 +++ /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Mon Oct 26 06:21:48 2009 @@ -219,6 +219,108 @@ } } } + + +void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { + Comment cmnt(masm_, "[ ObjectLiteral"); + Label exists; + // Registers will be used as follows: + // edi = JS function. + // ebx = literals array. + // eax = boilerplate + + __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ mov(ebx, FieldOperand(edi, JSFunction::kLiteralsOffset)); + int literal_offset = + FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; + __ mov(eax, FieldOperand(ebx, literal_offset)); + __ cmp(eax, Factory::undefined_value()); + __ j(not_equal, &exists); + // Create boilerplate if it does not exist. + // Literal array (0). + __ push(ebx); + // Literal index (1). + __ push(Immediate(Smi::FromInt(expr->literal_index()))); + // Constant properties (2). + __ push(Immediate(expr->constant_properties())); + __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); + __ bind(&exists); + // eax contains boilerplate. + // Clone boilerplate. + __ push(eax); + if (expr->depth() == 1) { + __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1); + } else { + __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); + } + + // If result_saved == true: the result is saved on top of the stack. + // If result_saved == false: the result not on the stack, just is in eax. + bool result_saved = false; + + for (int i = 0; i < expr->properties()->length(); i++) { + ObjectLiteral::Property* property = expr->properties()->at(i); + Literal* key = property->key(); + Expression* value = property->value(); + if (property->kind() == ObjectLiteral::Property::CONSTANT) continue; + if (property->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL && + CompileTimeValue::IsCompileTimeValue(value)) { + continue; + } + if (!result_saved) { + __ push(eax); // Save result on the stack + result_saved = true; + } + switch (property->kind()) { + case ObjectLiteral::Property::MATERIALIZED_LITERAL: // fall through + ASSERT(!CompileTimeValue::IsCompileTimeValue(value)); + case ObjectLiteral::Property::COMPUTED: + if (key->handle()->IsSymbol()) { + Visit(value); + ASSERT(value->location().is_temporary()); + __ pop(eax); + __ mov(ecx, Immediate(key->handle())); + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + // StoreIC leaves the receiver on the stack. + break; + } + // fall through + case ObjectLiteral::Property::PROTOTYPE: + __ push(eax); + Visit(key); + if (key->location().is_constant()) { + __ push(Immediate(key->handle())); + } + Visit(value); + ASSERT(value->location().is_temporary()); + __ CallRuntime(Runtime::kSetProperty, 3); + __ mov(eax, Operand(esp, 0)); // Restore result into eax. + break; + case ObjectLiteral::Property::SETTER: // fall through + case ObjectLiteral::Property::GETTER: + __ push(eax); + Visit(key); + if (key->location().is_constant()) { + __ push(Immediate(key->handle())); + } + __ push(Immediate(property->kind() == ObjectLiteral::Property::SETTER ? + Smi::FromInt(1) : + Smi::FromInt(0))); + Visit(value); + ASSERT(value->location().is_temporary()); + __ CallRuntime(Runtime::kDefineAccessor, 4); + __ mov(eax, Operand(esp, 0)); // Restore result into eax. + break; + default: UNREACHABLE(); + } + } + if (expr->location().is_nowhere() && result_saved) { + __ add(Operand(esp), Immediate(kPointerSize)); + } else if (expr->location().is_temporary() && !result_saved) { + __ push(eax); + } +} void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { ======================================= --- /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Fri Oct 23 03:42:14 2009 +++ /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Mon Oct 26 06:21:48 2009 @@ -235,6 +235,104 @@ } } } + + +void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { + Comment cmnt(masm_, "[ ObjectLiteral"); + Label boilerplate_exists; + + __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + __ movq(rbx, FieldOperand(rdi, JSFunction::kLiteralsOffset)); + int literal_offset = + FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; + __ movq(rax, FieldOperand(rbx, literal_offset)); + __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); + __ j(not_equal, &boilerplate_exists); + // Create boilerplate if it does not exist. + // Literal array (0). + __ push(rbx); + // Literal index (1). + __ Push(Smi::FromInt(expr->literal_index())); + // Constant properties (2). + __ Push(expr->constant_properties()); + __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); + __ bind(&boilerplate_exists); + // rax contains boilerplate. + // Clone boilerplate. + __ push(rax); + if (expr->depth() == 1) { + __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1); + } else { + __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1); + } + + // If result_saved == true: the result is saved on top of the stack. + // If result_saved == false: the result is not on the stack, just in rax. + bool result_saved = false; + + for (int i = 0; i < expr->properties()->length(); i++) { + ObjectLiteral::Property* property = expr->properties()->at(i); + Literal* key = property->key(); + Expression* value = property->value(); + if (property->kind() == ObjectLiteral::Property::CONSTANT) continue; + if (property->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL && + CompileTimeValue::IsCompileTimeValue(value)) { + continue; + } + if (!result_saved) { + __ push(rax); // Save result on the stack + result_saved = true; + } + switch (property->kind()) { + case ObjectLiteral::Property::MATERIALIZED_LITERAL: // fall through + ASSERT(!CompileTimeValue::IsCompileTimeValue(value)); + case ObjectLiteral::Property::COMPUTED: + if (key->handle()->IsSymbol()) { + Visit(value); + ASSERT(value->location().is_temporary()); + __ pop(rax); + __ Move(rcx, key->handle()); + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + // StoreIC leaves the receiver on the stack. + break; + } + // fall through + case ObjectLiteral::Property::PROTOTYPE: + __ push(rax); + Visit(key); + if (key->location().is_constant()) { + __ Push(key->handle()); + } + Visit(value); + ASSERT(value->location().is_temporary()); + __ CallRuntime(Runtime::kSetProperty, 3); + __ movq(rax, Operand(rsp, 0)); // Restore result into rax. + break; + case ObjectLiteral::Property::SETTER: // fall through + case ObjectLiteral::Property::GETTER: + __ push(rax); + Visit(key); + if (key->location().is_constant()) { + __ Push(key->handle()); + } + __ Push(property->kind() == ObjectLiteral::Property::SETTER ? + Smi::FromInt(1) : + Smi::FromInt(0)); + Visit(value); + ASSERT(value->location().is_temporary()); + __ CallRuntime(Runtime::kDefineAccessor, 4); + __ movq(rax, Operand(rsp, 0)); // Restore result into rax. + break; + default: UNREACHABLE(); + } + } + if (expr->location().is_nowhere() && result_saved) { + __ addq(rsp, Immediate(kPointerSize)); + } else if (expr->location().is_temporary() && !result_saved) { + __ push(rax); + } +} void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { ======================================= --- /branches/bleeding_edge/test/mjsunit/compiler/literals-assignment.js Wed Oct 14 03:24:50 2009 +++ /branches/bleeding_edge/test/mjsunit/compiler/literals-assignment.js Mon Oct 26 06:21:48 2009 @@ -69,3 +69,36 @@ })()"; assertEquals(8, eval(code)); +// Test object literals. +var a, b; +code = "a = {x:8}"; +eval(code); +assertEquals(8, a.x); + +code = "b = {x:a, y:'abc'}"; +eval(code); +assertEquals(a, b.x); +assertEquals(8, b.x.x); +assertEquals("abc", b.y); + +code = "({x:8, y:9}); 10"; +assertEquals(10, eval(code)); + +code = "({x:8, y:9})"; +eval(code); +assertEquals(9, eval(code+".y")); + +code = "a = {2:8, x:9}"; +eval(code); +assertEquals(8, a[2]); +assertEquals(8, a["2"]); +assertEquals(9, a["x"]); + +// Test regexp literals. + +a = /abc/; + +assertEquals(/abc/, a); + +code = "/abc/; 8"; +assertEquals(8, eval(code)); --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---
