Chromium Code Reviews| Index: src/x64/codegen-x64.cc |
| =================================================================== |
| --- src/x64/codegen-x64.cc (revision 2244) |
| +++ src/x64/codegen-x64.cc (working copy) |
| @@ -29,9 +29,12 @@ |
| #include "v8.h" |
| #include "bootstrapper.h" |
| -// #include "macro-assembler.h" |
| #include "codegen-inl.h" |
| +#include "debug.h" |
| +#include "ic-inl.h" |
| +#include "parser.h" |
| #include "register-allocator-inl.h" |
| +#include "scopes.h" |
| // TEST |
| #include "compiler.h" |
| @@ -167,6 +170,15 @@ |
| " // return test_recursion_with_base(0, 0, 0, 47);\n" |
| " var x_value = 42;" |
| " var o = { x: x_value };" |
| + " o.x = 43;" |
| + " o.x;" |
| + " var x_string = 'x';" |
| + " o[x_string] = 44;" |
| + " o[x_string];" |
| + " o.f = function() { return 45; };" |
| + " o.f();" |
| + " var f_string = 'f';" |
| + " o[f_string]();" |
|
William Hesse
2009/06/22 21:31:29
Now that if ... then is working, we can check all
|
| " var a = [ 1, 2, 3 ];" |
| " var x = true ? 42 : 32;" |
| " return test_if_then_else(0, 46, 47);" |
| @@ -1156,12 +1168,20 @@ |
| } |
| -void CodeGenerator::VisitThrow(Throw* a) { |
| - // UNIMPLEMENTED(); |
| +void CodeGenerator::VisitThrow(Throw* node) { |
| + Comment cmnt(masm_, "[ Throw"); |
| + CodeForStatementPosition(node); |
| + |
| + Load(node->exception()); |
| + Result result = frame_->CallRuntime(Runtime::kThrow, 1); |
| + frame_->Push(&result); |
| } |
| -void CodeGenerator::VisitProperty(Property* a) { |
| - UNIMPLEMENTED(); |
| + |
| +void CodeGenerator::VisitProperty(Property* node) { |
| + Comment cmnt(masm_, "[ Property"); |
| + Reference property(this, node); |
| + property.GetValue(typeof_state()); |
| } |
| @@ -1244,8 +1264,6 @@ |
| CallWithArguments(args, node->position()); |
| */ |
| } else if (property != NULL) { |
| - UNIMPLEMENTED(); |
| - /* |
| // Check if the key is a literal string. |
| Literal* literal = property->key()->AsLiteral(); |
| @@ -1293,7 +1311,7 @@ |
| // Call the function. |
| CallWithArguments(args, node->position()); |
| } |
| - */ |
| + |
| } else { |
| // ---------------------------------- |
| // JavaScript example: 'foo(1, 2, 3)' // foo is not global |
| @@ -1315,10 +1333,12 @@ |
| UNIMPLEMENTED(); |
| } |
| + |
| void CodeGenerator::VisitCallNew(CallNew* a) { |
| UNIMPLEMENTED(); |
| } |
| + |
| void CodeGenerator::VisitCallRuntime(CallRuntime* a) { |
| UNIMPLEMENTED(); |
| } |
| @@ -1632,136 +1652,6 @@ |
| } |
| -void Reference::SetValue(InitState init_state) { |
| - ASSERT(cgen_->HasValidEntryRegisters()); |
| - ASSERT(!is_illegal()); |
| - MacroAssembler* masm = cgen_->masm(); |
| - switch (type_) { |
| - case SLOT: { |
| - Comment cmnt(masm, "[ Store to Slot"); |
| - Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); |
| - ASSERT(slot != NULL); |
| - cgen_->StoreToSlot(slot, init_state); |
| - break; |
| - } |
| - // TODO(X64): Make cases other than SLOT work. |
| - /* |
| - case NAMED: { |
| - Comment cmnt(masm, "[ Store to named Property"); |
| - cgen_->frame()->Push(GetName()); |
| - Result answer = cgen_->frame()->CallStoreIC(); |
| - cgen_->frame()->Push(&answer); |
| - break; |
| - } |
| - |
| - case KEYED: { |
| - Comment cmnt(masm, "[ Store to keyed Property"); |
| - |
| - // Generate inlined version of the keyed store if the code is in |
| - // a loop and the key is likely to be a smi. |
| - Property* property = expression()->AsProperty(); |
| - ASSERT(property != NULL); |
| - SmiAnalysis* key_smi_analysis = property->key()->type(); |
| - |
| - if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) { |
| - Comment cmnt(masm, "[ Inlined store to keyed Property"); |
| - |
| - // Get the receiver, key and value into registers. |
| - Result value = cgen_->frame()->Pop(); |
| - Result key = cgen_->frame()->Pop(); |
| - Result receiver = cgen_->frame()->Pop(); |
| - |
| - Result tmp = cgen_->allocator_->Allocate(); |
| - ASSERT(tmp.is_valid()); |
| - |
| - // Determine whether the value is a constant before putting it |
| - // in a register. |
| - bool value_is_constant = value.is_constant(); |
| - |
| - // Make sure that value, key and receiver are in registers. |
| - value.ToRegister(); |
| - key.ToRegister(); |
| - receiver.ToRegister(); |
| - |
| - DeferredReferenceSetKeyedValue* deferred = |
| - new DeferredReferenceSetKeyedValue(value.reg(), |
| - key.reg(), |
| - receiver.reg()); |
| - |
| - // Check that the value is a smi if it is not a constant. We |
| - // can skip the write barrier for smis and constants. |
| - if (!value_is_constant) { |
| - __ test(value.reg(), Immediate(kSmiTagMask)); |
| - deferred->Branch(not_zero); |
| - } |
| - |
| - // Check that the key is a non-negative smi. |
| - __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000)); |
| - deferred->Branch(not_zero); |
| - |
| - // Check that the receiver is not a smi. |
| - __ test(receiver.reg(), Immediate(kSmiTagMask)); |
| - deferred->Branch(zero); |
| - |
| - // Check that the receiver is a JSArray. |
| - __ mov(tmp.reg(), |
| - FieldOperand(receiver.reg(), HeapObject::kMapOffset)); |
| - __ movzx_b(tmp.reg(), |
| - FieldOperand(tmp.reg(), Map::kInstanceTypeOffset)); |
| - __ cmp(tmp.reg(), JS_ARRAY_TYPE); |
| - deferred->Branch(not_equal); |
| - |
| - // Check that the key is within bounds. Both the key and the |
| - // length of the JSArray are smis. |
| - __ cmp(key.reg(), |
| - FieldOperand(receiver.reg(), JSArray::kLengthOffset)); |
| - deferred->Branch(greater_equal); |
| - |
| - // Get the elements array from the receiver and check that it |
| - // is not a dictionary. |
| - __ mov(tmp.reg(), |
| - FieldOperand(receiver.reg(), JSObject::kElementsOffset)); |
| - // Bind the deferred code patch site to be able to locate the |
| - // fixed array map comparison. When debugging, we patch this |
| - // comparison to always fail so that we will hit the IC call |
| - // in the deferred code which will allow the debugger to |
| - // break for fast case stores. |
| - __ bind(deferred->patch_site()); |
| - __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset), |
| - Immediate(Factory::fixed_array_map())); |
| - deferred->Branch(not_equal); |
| - |
| - // Store the value. |
| - __ mov(Operand(tmp.reg(), |
| - key.reg(), |
| - times_2, |
| - Array::kHeaderSize - kHeapObjectTag), |
| - value.reg()); |
| - __ IncrementCounter(&Counters::keyed_store_inline, 1); |
| - |
| - deferred->BindExit(); |
| - |
| - cgen_->frame()->Push(&receiver); |
| - cgen_->frame()->Push(&key); |
| - cgen_->frame()->Push(&value); |
| - } else { |
| - Result answer = cgen_->frame()->CallKeyedStoreIC(); |
| - // Make sure that we do not have a test instruction after the |
| - // call. A test instruction after the call is used to |
| - // indicate that we have generated an inline version of the |
| - // keyed store. |
| - __ nop(); |
| - cgen_->frame()->Push(&answer); |
| - } |
| - break; |
| - } |
| - */ |
| - default: |
| - UNREACHABLE(); |
| - } |
| -} |
| - |
| - |
| Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) { |
| // Currently, this assertion will fail if we try to assign to |
| // a constant variable that is constant because it is read-only |
| @@ -2057,23 +1947,244 @@ |
| frame_->Push(&temp); |
| } |
| +// Emit a LoadIC call to get the value from receiver and leave it in |
| +// dst. The receiver register is restored after the call. |
| +class DeferredReferenceGetNamedValue: public DeferredCode { |
| + public: |
| + DeferredReferenceGetNamedValue(Register dst, |
| + Register receiver, |
| + Handle<String> name) |
| + : dst_(dst), receiver_(receiver), name_(name) { |
| + set_comment("[ DeferredReferenceGetNamedValue"); |
| + } |
| -#undef __ |
| + virtual void Generate(); |
| -// End of CodeGenerator implementation. |
| + Label* patch_site() { return &patch_site_; } |
| -// ----------------------------------------------------------------------------- |
| -// Implementation of stubs. |
| + private: |
| + Label patch_site_; |
| + Register dst_; |
| + Register receiver_; |
| + Handle<String> name_; |
| +}; |
| -// Stub classes have public member named masm, not masm_. |
| + |
| +void DeferredReferenceGetNamedValue::Generate() { |
| + __ push(receiver_); |
| + __ Move(rcx, name_); |
| + Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
| + __ Call(ic, RelocInfo::CODE_TARGET); |
| + // The call must be followed by a test eax instruction to indicate |
| + // that the inobject property case was inlined. |
| + // |
| + // Store the delta to the map check instruction here in the test |
| + // instruction. Use masm_-> instead of the __ macro since the |
| + // latter can't return a value. |
| + int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site()); |
| + // Here we use masm_-> instead of the __ macro because this is the |
| + // instruction that gets patched and coverage code gets in the way. |
| + masm_->testq(rax, Immediate(-delta_to_patch_site)); |
| + __ IncrementCounter(&Counters::named_load_inline_miss, 1); |
| + |
| + if (!dst_.is(rax)) __ movq(dst_, rax); |
| + __ pop(receiver_); |
| +} |
| + |
| + |
| +#undef __ |
| #define __ ACCESS_MASM(masm) |
| +Handle<String> Reference::GetName() { |
| + ASSERT(type_ == NAMED); |
| + Property* property = expression_->AsProperty(); |
| + if (property == NULL) { |
| + // Global variable reference treated as a named property reference. |
| + VariableProxy* proxy = expression_->AsVariableProxy(); |
| + ASSERT(proxy->AsVariable() != NULL); |
| + ASSERT(proxy->AsVariable()->is_global()); |
| + return proxy->name(); |
| + } else { |
| + Literal* raw_name = property->key()->AsLiteral(); |
| + ASSERT(raw_name != NULL); |
| + return Handle<String>(String::cast(*raw_name->handle())); |
| + } |
| +} |
| + |
| + |
| void Reference::GetValue(TypeofState typeof_state) { |
| - UNIMPLEMENTED(); |
| + ASSERT(!cgen_->in_spilled_code()); |
| + ASSERT(cgen_->HasValidEntryRegisters()); |
| + ASSERT(!is_illegal()); |
| + MacroAssembler* masm = cgen_->masm(); |
| + switch (type_) { |
| + case SLOT: { |
| + Comment cmnt(masm, "[ Load from Slot"); |
| + Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); |
| + ASSERT(slot != NULL); |
| + cgen_->LoadFromSlot(slot, typeof_state); |
| + break; |
| + } |
| + |
| + case NAMED: { |
| + // TODO(1241834): Make sure that it is safe to ignore the |
| + // distinction between expressions in a typeof and not in a |
| + // typeof. If there is a chance that reference errors can be |
| + // thrown below, we must distinguish between the two kinds of |
| + // loads (typeof expression loads must not throw a reference |
| + // error). |
| + Variable* var = expression_->AsVariableProxy()->AsVariable(); |
| + bool is_global = var != NULL; |
| + ASSERT(!is_global || var->is_global()); |
| + |
| + // Do not inline the inobject property case for loads from the global |
| + // object. Also do not inline for unoptimized code. This saves time |
| + // in the code generator. Unoptimized code is toplevel code or code |
| + // that is not in a loop. |
| + if (is_global || |
| + cgen_->scope()->is_global_scope() || |
| + cgen_->loop_nesting() == 0) { |
| + Comment cmnt(masm, "[ Load from named Property"); |
| + cgen_->frame()->Push(GetName()); |
| + |
| + RelocInfo::Mode mode = is_global |
| + ? RelocInfo::CODE_TARGET_CONTEXT |
| + : RelocInfo::CODE_TARGET; |
| + Result answer = cgen_->frame()->CallLoadIC(mode); |
| + // A test eax instruction following the call signals that the |
| + // inobject property case was inlined. Ensure that there is not |
| + // a test eax instruction here. |
| + __ nop(); |
| + cgen_->frame()->Push(&answer); |
| + } else { |
| + // Inline the inobject property case. |
| + Comment cmnt(masm, "[ Inlined named property load"); |
| + Result receiver = cgen_->frame()->Pop(); |
| + receiver.ToRegister(); |
| + |
| + Result value = cgen_->allocator()->Allocate(); |
| + ASSERT(value.is_valid()); |
| + DeferredReferenceGetNamedValue* deferred = |
| + new DeferredReferenceGetNamedValue(value.reg(), |
| + receiver.reg(), |
| + GetName()); |
| + |
| + // Check that the receiver is a heap object. |
| + __ testq(receiver.reg(), Immediate(kSmiTagMask)); |
| + deferred->Branch(zero); |
| + |
| + __ bind(deferred->patch_site()); |
| + // This is the map check instruction that will be patched (so we can't |
| + // use the double underscore macro that may insert instructions). |
| + // Initially use an invalid map to force a failure. |
| + masm->Move(kScratchRegister, Factory::null_value()); |
| + masm->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset), |
| + kScratchRegister); |
| + // This branch is always a forwards branch so it's always a fixed |
| + // size which allows the assert below to succeed and patching to work. |
| + deferred->Branch(not_equal); |
| + |
| + // The delta from the patch label to the load offset must be |
| + // statically known. |
| + ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) == |
| + LoadIC::kOffsetToLoadInstruction); |
| + // The initial (invalid) offset has to be large enough to force |
| + // a 32-bit instruction encoding to allow patching with an |
| + // arbitrary offset. Use kMaxInt (minus kHeapObjectTag). |
| + int offset = kMaxInt; |
| + masm->movq(value.reg(), FieldOperand(receiver.reg(), offset)); |
| + |
| + __ IncrementCounter(&Counters::named_load_inline, 1); |
| + deferred->BindExit(); |
| + cgen_->frame()->Push(&receiver); |
| + cgen_->frame()->Push(&value); |
| + } |
| + break; |
| + } |
| + |
| + case KEYED: { |
| + // TODO(1241834): Make sure that this it is safe to ignore the |
| + // distinction between expressions in a typeof and not in a typeof. |
| + Comment cmnt(masm, "[ Load from keyed Property"); |
| + Variable* var = expression_->AsVariableProxy()->AsVariable(); |
| + bool is_global = var != NULL; |
| + ASSERT(!is_global || var->is_global()); |
| + // Inline array load code if inside of a loop. We do not know |
| + // the receiver map yet, so we initially generate the code with |
| + // a check against an invalid map. In the inline cache code, we |
| + // patch the map check if appropriate. |
| + |
| + // TODO(x64): Implement inlined loads for keyed properties. |
| + // Comment cmnt(masm, "[ Load from keyed Property"); |
| + |
| + RelocInfo::Mode mode = is_global |
| + ? RelocInfo::CODE_TARGET_CONTEXT |
| + : RelocInfo::CODE_TARGET; |
| + Result answer = cgen_->frame()->CallKeyedLoadIC(mode); |
| + // Make sure that we do not have a test instruction after the |
| + // call. A test instruction after the call is used to |
| + // indicate that we have generated an inline version of the |
| + // keyed load. The explicit nop instruction is here because |
| + // the push that follows might be peep-hole optimized away. |
| + __ nop(); |
| + cgen_->frame()->Push(&answer); |
| + break; |
| + } |
| + |
| + default: |
| + UNREACHABLE(); |
| + } |
| } |
| +void Reference::SetValue(InitState init_state) { |
| + ASSERT(cgen_->HasValidEntryRegisters()); |
| + ASSERT(!is_illegal()); |
| + MacroAssembler* masm = cgen_->masm(); |
| + switch (type_) { |
| + case SLOT: { |
| + Comment cmnt(masm, "[ Store to Slot"); |
| + Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); |
| + ASSERT(slot != NULL); |
| + cgen_->StoreToSlot(slot, init_state); |
| + break; |
| + } |
| + |
| + case NAMED: { |
| + Comment cmnt(masm, "[ Store to named Property"); |
| + cgen_->frame()->Push(GetName()); |
| + Result answer = cgen_->frame()->CallStoreIC(); |
| + cgen_->frame()->Push(&answer); |
| + break; |
| + } |
| + |
| + case KEYED: { |
| + Comment cmnt(masm, "[ Store to keyed Property"); |
| + |
| + // Generate inlined version of the keyed store if the code is in |
| + // a loop and the key is likely to be a smi. |
| + Property* property = expression()->AsProperty(); |
| + ASSERT(property != NULL); |
| + |
| + // TODO(x64): Implement inlined version of keyed stores. |
| + |
| + Result answer = cgen_->frame()->CallKeyedStoreIC(); |
| + // Make sure that we do not have a test instruction after the |
| + // call. A test instruction after the call is used to |
| + // indicate that we have generated an inline version of the |
| + // keyed store. |
| + __ nop(); |
| + cgen_->frame()->Push(&answer); |
| + break; |
| + } |
| + |
| + default: |
| + UNREACHABLE(); |
| + } |
| +} |
| + |
| + |
| void ToBooleanStub::Generate(MacroAssembler* masm) { |
| Label false_result, true_result, not_string; |
| __ movq(rax, Operand(rsp, 1 * kPointerSize)); |