From: fschneider@chromium.org Date: Mon, 26 Oct 2009 13:21:48 +0000 (+0000) Subject: Support for object literals in fast compiler. X-Git-Tag: upstream/4.7.83~23089 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9445079c510db2a7345c84f00fffca75d8f527e1;p=platform%2Fupstream%2Fv8.git 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 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3129 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc index 97feae5..0565862 100644 --- a/src/arm/fast-codegen-arm.cc +++ b/src/arm/fast-codegen-arm.cc @@ -234,6 +234,98 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } +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) { Comment cmnt(masm_, "[ RegExp Literal"); Label done; diff --git a/src/compiler.cc b/src/compiler.cc index e422bf7..fb32c39 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -639,7 +639,13 @@ void CodeGenSelector::VisitRegExpLiteral(RegExpLiteral* expr) { 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; + } } diff --git a/src/fast-codegen.cc b/src/fast-codegen.cc index d0c264a..3657a6b 100644 --- a/src/fast-codegen.cc +++ b/src/fast-codegen.cc @@ -284,11 +284,6 @@ void FastCodeGenerator::VisitLiteral(Literal* expr) { } -void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { - UNREACHABLE(); -} - - void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { UNREACHABLE(); } diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc index 663d136..ceeaff2 100644 --- a/src/ia32/fast-codegen-ia32.cc +++ b/src/ia32/fast-codegen-ia32.cc @@ -221,6 +221,108 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } +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 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) { Comment cmnt(masm_, "[ RegExp Literal"); Label done; diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc index 46d8dc4..7aa03e7 100644 --- a/src/x64/fast-codegen-x64.cc +++ b/src/x64/fast-codegen-x64.cc @@ -237,6 +237,104 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } +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 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) { Comment cmnt(masm_, "[ RegExp Literal"); Label done; diff --git a/test/mjsunit/compiler/literals-assignment.js b/test/mjsunit/compiler/literals-assignment.js index 932bfa7..d2996c7 100644 --- a/test/mjsunit/compiler/literals-assignment.js +++ b/test/mjsunit/compiler/literals-assignment.js @@ -69,3 +69,36 @@ code = "(function() {\ })()"; 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));