Author: [email protected]
Date: Thu Jun 18 04:46:38 2009
New Revision: 2217
Modified:
branches/bleeding_edge/src/ia32/assembler-ia32.cc
branches/bleeding_edge/src/ia32/codegen-ia32.cc
branches/bleeding_edge/src/x64/codegen-x64.cc
branches/bleeding_edge/src/x64/macro-assembler-x64.cc
branches/bleeding_edge/src/x64/macro-assembler-x64.h
branches/bleeding_edge/src/x64/virtual-frame-x64.cc
Log:
X64 implementation: Add function literals and function calls.
Review URL: http://codereview.chromium.org/131029
Modified: branches/bleeding_edge/src/ia32/assembler-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/ia32/assembler-ia32.cc (original)
+++ branches/bleeding_edge/src/ia32/assembler-ia32.cc Thu Jun 18 04:46:38
2009
@@ -1417,7 +1417,7 @@
}
-void Assembler::call(Handle<Code> code, RelocInfo::Mode rmode) {
+void Assembler::call(Handle<Code> code, RelocInfo::Mode rmode) {
WriteRecordedPositions();
EnsureSpace ensure_space(this);
last_pc_ = pc_;
Modified: branches/bleeding_edge/src/ia32/codegen-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/ia32/codegen-ia32.cc (original)
+++ branches/bleeding_edge/src/ia32/codegen-ia32.cc Thu Jun 18 04:46:38 2009
@@ -7163,7 +7163,6 @@
}
-
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
// eax holds the exception.
Modified: branches/bleeding_edge/src/x64/codegen-x64.cc
==============================================================================
--- branches/bleeding_edge/src/x64/codegen-x64.cc (original)
+++ branches/bleeding_edge/src/x64/codegen-x64.cc Thu Jun 18 04:46:38 2009
@@ -103,7 +103,15 @@
void CodeGenerator::TestCodeGenerator() {
// Compile a function from a string, and run it.
Handle<JSFunction> test_function = Compiler::Compile(
- Factory::NewStringFromAscii(CStrVector("39; 42;")),
+ Factory::NewStringFromAscii(CStrVector(
+ "39;"
+ "(function(){return 43})();"
+ "42;"
+ // "function foo(x, y){return x;};"
+ "43;"
+ // "foo(2,3);"
+ "44;"
+ "(function(){return (function(){return 47})()})();")),
Factory::NewStringFromAscii(CStrVector("CodeGeneratorTestScript")),
0,
0,
@@ -132,7 +140,7 @@
&pending_exceptions);
// Function compiles and runs, but returns a JSFunction object.
CHECK(result->IsSmi());
- CHECK_EQ(42, Smi::cast(*result)->value());
+ CHECK_EQ(47, Smi::cast(*result)->value());
}
@@ -370,15 +378,44 @@
UNIMPLEMENTED();
}
-void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* a) {
- UNIMPLEMENTED();
+
+void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate)
{
+ // Call the runtime to instantiate the function boilerplate object.
+ // The inevitable call will sync frame elements to memory anyway, so
+ // we do it eagerly to allow us to push the arguments directly into
+ // place.
+ 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);
+ Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
+ frame_->Push(&result);
+}
+
+
+void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
+ Comment cmnt(masm_, "[ FunctionLiteral");
+
+ // Build the function boilerplate and instantiate it.
+ Handle<JSFunction> boilerplate = BuildBoilerplate(node);
+ // Check for stack-overflow exception.
+ if (HasStackOverflow()) return;
+ InstantiateBoilerplate(boilerplate);
}
+
void CodeGenerator::VisitFunctionBoilerplateLiteral(
- FunctionBoilerplateLiteral* a) {
- UNIMPLEMENTED();
+ FunctionBoilerplateLiteral* node) {
+ Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
+ InstantiateBoilerplate(node->boilerplate());
}
+
void CodeGenerator::VisitConditional(Conditional* a) {
UNIMPLEMENTED();
}
@@ -521,10 +558,153 @@
UNIMPLEMENTED();
}
-void CodeGenerator::VisitCall(Call* a) {
- UNIMPLEMENTED();
+
+void CodeGenerator::VisitCall(Call* node) {
+ Comment cmnt(masm_, "[ Call");
+
+ ZoneList<Expression*>* args = node->arguments();
+
+ CodeForStatementPosition(node);
+
+ // Check if the function is a variable or a property.
+ Expression* function = node->expression();
+ Variable* var = function->AsVariableProxy()->AsVariable();
+ Property* property = function->AsProperty();
+
+ //
------------------------------------------------------------------------
+ // Fast-case: Use inline caching.
+ // ---
+ // According to ECMA-262, section 11.2.3, page 44, the function to call
+ // must be resolved after the arguments have been evaluated. The IC code
+ // automatically handles this by loading the arguments before the
function
+ // is resolved in cache misses (this also holds for megamorphic calls).
+ //
------------------------------------------------------------------------
+
+ if (var != NULL && !var->is_this() && var->is_global()) {
+ // ----------------------------------
+ // JavaScript example: 'foo(1, 2, 3)' // foo is global
+ // ----------------------------------
+
+ // Push the name of the function and the receiver onto the stack.
+ frame_->Push(var->name());
+
+ // Pass the global object as the receiver and let the IC stub
+ // patch the stack to use the global proxy as 'this' in the
+ // invoked function.
+ LoadGlobal();
+
+ // Load the arguments.
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
+
+ // Call the IC initialization code.
+ CodeForSourcePosition(node->position());
+ Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT,
+ arg_count,
+ loop_nesting());
+ frame_->RestoreContextRegister();
+ // Replace the function on the stack with the result.
+ frame_->SetElementAt(0, &result);
+ } else if (var != NULL && var->slot() != NULL &&
+ var->slot()->type() == Slot::LOOKUP) {
+ // TODO(X64): Enable calls of non-global functions.
+ UNIMPLEMENTED();
+ /*
+ // ----------------------------------
+ // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
+ // ----------------------------------
+
+ // Load the function from the context. Sync the frame so we can
+ // push the arguments directly into place.
+ frame_->SyncRange(0, frame_->element_count() - 1);
+ frame_->EmitPush(esi);
+ frame_->EmitPush(Immediate(var->name()));
+ frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
+ // The runtime call returns a pair of values in eax and edx. The
+ // looked-up function is in eax and the receiver is in edx. These
+ // register references are not ref counted here. We spill them
+ // eagerly since they are arguments to an inevitable call (and are
+ // not sharable by the arguments).
+ ASSERT(!allocator()->is_used(eax));
+ frame_->EmitPush(eax);
+
+ // Load the receiver.
+ ASSERT(!allocator()->is_used(edx));
+ frame_->EmitPush(edx);
+
+ // Call the function.
+ CallWithArguments(args, node->position());
+ */
+ } else if (property != NULL) {
+ UNIMPLEMENTED();
+ /*
+ // Check if the key is a literal string.
+ Literal* literal = property->key()->AsLiteral();
+
+ if (literal != NULL && literal->handle()->IsSymbol()) {
+ // ------------------------------------------------------------------
+ // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
+ // ------------------------------------------------------------------
+
+ // Push the name of the function and the receiver onto the stack.
+ frame_->Push(literal->handle());
+ Load(property->obj());
+
+ // Load the arguments.
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
+
+ // Call the IC initialization code.
+ CodeForSourcePosition(node->position());
+ Result result =
+ frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count,
loop_nesting());
+ frame_->RestoreContextRegister();
+ // Replace the function on the stack with the result.
+ frame_->SetElementAt(0, &result);
+
+ } else {
+ // -------------------------------------------
+ // JavaScript example: 'array[index](1, 2, 3)'
+ // -------------------------------------------
+
+ // Load the function to call from the property through a reference.
+ Reference ref(this, property);
+ ref.GetValue(NOT_INSIDE_TYPEOF);
+
+ // Pass receiver to called function.
+ if (property->is_synthetic()) {
+ // Use global object as receiver.
+ LoadGlobalReceiver();
+ } else {
+ // The reference's size is non-negative.
+ frame_->PushElementAt(ref.size());
+ }
+
+ // Call the function.
+ CallWithArguments(args, node->position());
+ }
+ */
+ } else {
+ // ----------------------------------
+ // JavaScript example: 'foo(1, 2, 3)' // foo is not global
+ // ----------------------------------
+
+ // Load the function.
+ Load(function);
+
+ // Pass the global proxy as the receiver.
+ LoadGlobalReceiver();
+
+ // Call the function.
+ CallWithArguments(args, node->position());
+ }
}
+
void CodeGenerator::VisitCallEval(CallEval* a) {
UNIMPLEMENTED();
}
@@ -537,6 +717,7 @@
UNIMPLEMENTED();
}
+
void CodeGenerator::VisitUnaryOperation(UnaryOperation* a) {
UNIMPLEMENTED();
}
@@ -1216,6 +1397,16 @@
}
}
+
+void CodeGenerator::LoadGlobalReceiver() {
+ Result temp = allocator_->Allocate();
+ Register reg = temp.reg();
+ __ movq(reg, GlobalObject());
+ __ movq(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset));
+ frame_->Push(&temp);
+}
+
+
#undef __
// End of CodeGenerator implementation.
@@ -1588,12 +1779,59 @@
void CallFunctionStub::Generate(MacroAssembler* masm) {
+ Label slow;
+
+ // Get the function to call from the stack.
+ // +2 ~ receiver, return address
+ __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
+
+ // Check that the function really is a JavaScript function.
+ __ testq(rdi, Immediate(kSmiTagMask));
+ __ j(zero, &slow);
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &slow);
+
+ // Fast-case: Just invoke the function.
+ ParameterCount actual(argc_);
+ __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
+
+ // Slow-case: Non-function called.
+ __ bind(&slow);
+ __ Set(rax, argc_);
+ __ Set(rbx, 0);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
+ Handle<Code>
adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
+ __ Jump(adaptor, RelocInfo::CODE_TARGET);
}
-void InstanceofStub::Generate(MacroAssembler* masm) {
+// Call the function just below TOS on the stack with the given
+// arguments. The receiver is the TOS.
+void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
+ int position) {
+ // Push the arguments ("left-to-right") on the stack.
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
+
+ // Record the position for debugging purposes.
+ CodeForSourcePosition(position);
+
+ // Use the shared code stub to call the function.
+ InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
+ CallFunctionStub call_function(arg_count, in_loop);
+ Result answer = frame_->CallStub(&call_function, arg_count + 1);
+ // Restore context and replace function on the stack with the
+ // result of the stub invocation.
+ frame_->RestoreContextRegister();
+ frame_->SetElementAt(0, &answer);
}
+
+void InstanceofStub::Generate(MacroAssembler* masm) {
+}
void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
Modified: branches/bleeding_edge/src/x64/macro-assembler-x64.cc
==============================================================================
--- branches/bleeding_edge/src/x64/macro-assembler-x64.cc (original)
+++ branches/bleeding_edge/src/x64/macro-assembler-x64.cc Thu Jun 18
04:46:38 2009
@@ -206,6 +206,44 @@
}
+void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript
id) {
+ bool resolved;
+ Handle<Code> code = ResolveBuiltin(id, &resolved);
+
+ const char* name = Builtins::GetName(id);
+ int argc = Builtins::GetArgumentsCount(id);
+
+ movq(target, code, RelocInfo::EXTERNAL_REFERENCE); // Is external
reference?
+ if (!resolved) {
+ uint32_t flags =
+ Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
+ Bootstrapper::FixupFlagsIsPCRelative::encode(false) |
+ Bootstrapper::FixupFlagsUseCodeObject::encode(true);
+ Unresolved entry = { pc_offset() - sizeof(intptr_t), flags, name };
+ unresolved_.Add(entry);
+ }
+ addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag));
+}
+
+
+Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
+ bool* resolved) {
+ // Move the builtin function into the temporary function slot by
+ // reading it from the builtins object. NOTE: We should be able to
+ // reduce this to two instructions by putting the function table in
+ // the global object instead of the "builtins" object and by using a
+ // real register for the function.
+ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ movq(rdx, FieldOperand(rdx, GlobalObject::kBuiltinsOffset));
+ int builtins_offset =
+ JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
+ movq(rdi, FieldOperand(rdx, builtins_offset));
+
+
+ return Builtins::GetCode(id, resolved);
+}
+
+
void MacroAssembler::Set(Register dst, int64_t x) {
if (is_int32(x)) {
movq(dst, Immediate(x));
@@ -241,6 +279,14 @@
}
+void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode)
{
+ WriteRecordedPositions();
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ movq(kScratchRegister, code_object, rmode);
+ jmp(kScratchRegister);
+}
+
+
void MacroAssembler::Call(ExternalReference ext) {
movq(kScratchRegister, ext);
call(kScratchRegister);
@@ -249,6 +295,14 @@
void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) {
movq(kScratchRegister, destination, rmode);
+ call(kScratchRegister);
+}
+
+
+void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode)
{
+ WriteRecordedPositions();
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ movq(kScratchRegister, code_object, rmode);
call(kScratchRegister);
}
Modified: branches/bleeding_edge/src/x64/macro-assembler-x64.h
==============================================================================
--- branches/bleeding_edge/src/x64/macro-assembler-x64.h (original)
+++ branches/bleeding_edge/src/x64/macro-assembler-x64.h Thu Jun 18
04:46:38 2009
@@ -161,8 +161,11 @@
// Control Flow
void Jump(Address destination, RelocInfo::Mode rmode);
void Jump(ExternalReference ext);
+ void Jump(Handle<Code> code_object, RelocInfo::Mode rmode);
+
void Call(Address destination, RelocInfo::Mode rmode);
void Call(ExternalReference ext);
+ void Call(Handle<Code> code_object, RelocInfo::Mode rmode);
// Compare object type for heap object.
// Incoming register is heap_object and outgoing register is map.
Modified: branches/bleeding_edge/src/x64/virtual-frame-x64.cc
==============================================================================
--- branches/bleeding_edge/src/x64/virtual-frame-x64.cc (original)
+++ branches/bleeding_edge/src/x64/virtual-frame-x64.cc Thu Jun 18 04:46:38
2009
@@ -137,6 +137,26 @@
}
+void VirtualFrame::SaveContextRegister() {
+ ASSERT(elements_[context_index()].is_memory());
+ __ movq(Operand(rbp, fp_relative(context_index())), rsi);
+}
+
+
+void VirtualFrame::RestoreContextRegister() {
+ ASSERT(elements_[context_index()].is_memory());
+ __ movq(rsi, Operand(rbp, fp_relative(context_index())));
+}
+
+
+void VirtualFrame::PushReceiverSlotAddress() {
+ Result temp = cgen()->allocator()->Allocate();
+ ASSERT(temp.is_valid());
+ __ lea(temp.reg(), ParameterAt(-1));
+ Push(&temp);
+}
+
+
void VirtualFrame::EmitPop(Register reg) {
ASSERT(stack_pointer_ == element_count() - 1);
stack_pointer_--;
@@ -433,13 +453,65 @@
}
-Result VirtualFrame::RawCallStub(CodeStub* a) {
- UNIMPLEMENTED();
- return Result(NULL);
+Result VirtualFrame::RawCallStub(CodeStub* stub) {
+ ASSERT(cgen()->HasValidEntryRegisters());
+ __ CallStub(stub);
+ Result result = cgen()->allocator()->Allocate(rax);
+ ASSERT(result.is_valid());
+ return result;
}
-void VirtualFrame::SyncElementBelowStackPointer(int a) {
- UNIMPLEMENTED();
+
+void VirtualFrame::SyncElementBelowStackPointer(int index) {
+ // Emit code to write elements below the stack pointer to their
+ // (already allocated) stack address.
+ ASSERT(index <= stack_pointer_);
+ FrameElement element = elements_[index];
+ ASSERT(!element.is_synced());
+ switch (element.type()) {
+ case FrameElement::INVALID:
+ break;
+
+ case FrameElement::MEMORY:
+ // This function should not be called with synced elements.
+ // (memory elements are always synced).
+ UNREACHABLE();
+ break;
+
+ case FrameElement::REGISTER:
+ __ movq(Operand(rbp, fp_relative(index)), element.reg());
+ break;
+
+ case FrameElement::CONSTANT:
+ if (element.handle()->IsSmi()) {
+ if (CodeGeneratorScope::Current()->IsUnsafeSmi(element.handle())) {
+ CodeGeneratorScope::Current()->LoadUnsafeSmi(kScratchRegister,
+ element.handle());
+ } else {
+ __ movq(kScratchRegister, element.handle(), RelocInfo::NONE);
+ }
+ } else {
+ __ movq(kScratchRegister,
+ element.handle(),
+ RelocInfo::EMBEDDED_OBJECT);
+ }
+ __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
+ break;
+
+ case FrameElement::COPY: {
+ int backing_index = element.index();
+ FrameElement backing_element = elements_[backing_index];
+ if (backing_element.is_memory()) {
+ __ movq(kScratchRegister, Operand(rbp,
fp_relative(backing_index)));
+ __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
+ } else {
+ ASSERT(backing_element.is_register());
+ __ movq(Operand(rbp, fp_relative(index)), backing_element.reg());
+ }
+ break;
+ }
+ }
+ elements_[index].set_sync();
}
@@ -518,6 +590,55 @@
if (!elements_[i].is_synced()) SyncElementBelowStackPointer(i);
}
}
+
+//------------------------------------------------------------------------------
+// Virtual frame stub and IC calling functions.
+
+Result VirtualFrame::RawCallCodeObject(Handle<Code> code,
+ RelocInfo::Mode rmode) {
+ ASSERT(cgen()->HasValidEntryRegisters());
+ __ Call(code, rmode);
+ Result result = cgen()->allocator()->Allocate(rax);
+ ASSERT(result.is_valid());
+ return result;
+}
+
+
+Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) {
+ PrepareForCall(arg_count, arg_count);
+ ASSERT(cgen()->HasValidEntryRegisters());
+ __ CallRuntime(f, arg_count);
+ Result result = cgen()->allocator()->Allocate(rax);
+ ASSERT(result.is_valid());
+ return result;
+}
+
+
+Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
+ PrepareForCall(arg_count, arg_count);
+ ASSERT(cgen()->HasValidEntryRegisters());
+ __ CallRuntime(id, arg_count);
+ Result result = cgen()->allocator()->Allocate(rax);
+ ASSERT(result.is_valid());
+ return result;
+}
+
+
+Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
+ int arg_count,
+ int loop_nesting) {
+ // Arguments, receiver, and function name are on top of the frame.
+ // The IC expects them on the stack. It does not drop the function
+ // name slot (but it does drop the rest).
+ InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop);
+ // Spill args, receiver, and function. The call will drop args and
+ // receiver.
+ PrepareForCall(arg_count + 2, arg_count + 1);
+ return RawCallCodeObject(ic, mode);
+}
+
+
#undef __
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---