diff options
Diffstat (limited to 'deps/v8/src/ia32')
21 files changed, 1699 insertions, 336 deletions
diff --git a/deps/v8/src/ia32/assembler-ia32-inl.h b/deps/v8/src/ia32/assembler-ia32-inl.h index 6dc584e62b..1d88220469 100644 --- a/deps/v8/src/ia32/assembler-ia32-inl.h +++ b/deps/v8/src/ia32/assembler-ia32-inl.h @@ -159,11 +159,6 @@ Immediate::Immediate(const ExternalReference& ext) { rmode_ = RelocInfo::EXTERNAL_REFERENCE; } -Immediate::Immediate(const char* s) { - x_ = reinterpret_cast<int32_t>(s); - rmode_ = RelocInfo::EMBEDDED_STRING; -} - Immediate::Immediate(Label* internal_offset) { x_ = reinterpret_cast<int32_t>(internal_offset); diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 26e40b15bc..4690c67289 100644 --- a/deps/v8/src/ia32/assembler-ia32.cc +++ b/deps/v8/src/ia32/assembler-ia32.cc @@ -36,6 +36,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "disassembler.h" #include "macro-assembler.h" #include "serialize.h" @@ -160,6 +162,15 @@ const int RelocInfo::kApplyMask = 1 << RelocInfo::JS_RETURN | 1 << RelocInfo::INTERNAL_REFERENCE; +bool RelocInfo::IsCodedSpecially() { + // The deserializer needs to know whether a pointer is specially coded. Being + // specially coded on IA32 means that it is a relative address, as used by + // branch instructions. These are also the ones that need changing when a + // code object moves. + return (1 << rmode_) & kApplyMask; +} + + void RelocInfo::PatchCode(byte* instructions, int instruction_count) { // Patch the code at the current address with the supplied instructions. for (int i = 0; i < instruction_count; i++) { @@ -433,7 +444,7 @@ void Assembler::push(const Operand& src) { void Assembler::pop(Register dst) { ASSERT(reloc_info_writer.last_pc() != NULL); - if (FLAG_push_pop_elimination && (reloc_info_writer.last_pc() <= last_pc_)) { + if (FLAG_peephole_optimization && (reloc_info_writer.last_pc() <= last_pc_)) { // (last_pc_ != NULL) is rolled into the above check. // If a last_pc_ is set, we need to make sure that there has not been any // relocation information generated between the last instruction and this @@ -443,7 +454,7 @@ void Assembler::pop(Register dst) { int push_reg_code = instr & 0x7; if (push_reg_code == dst.code()) { pc_ = last_pc_; - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop (same reg) eliminated\n", pc_offset()); } } else { @@ -452,7 +463,7 @@ void Assembler::pop(Register dst) { Register src = { push_reg_code }; EnsureSpace ensure_space(this); emit_operand(dst, Operand(src)); - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop (reg->reg) eliminated\n", pc_offset()); } } @@ -466,7 +477,7 @@ void Assembler::pop(Register dst) { last_pc_[0] = 0x8b; last_pc_[1] = op1; last_pc_ = NULL; - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop (op->reg) eliminated\n", pc_offset()); } return; @@ -483,7 +494,7 @@ void Assembler::pop(Register dst) { last_pc_[1] = 0xc4; last_pc_[2] = 0x04; last_pc_ = NULL; - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop (mov-pop) eliminated\n", pc_offset()); } return; @@ -498,7 +509,7 @@ void Assembler::pop(Register dst) { // change to // 31c0 xor eax,eax last_pc_ = NULL; - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop (imm->reg) eliminated\n", pc_offset()); } return; @@ -521,7 +532,7 @@ void Assembler::pop(Register dst) { // b8XX000000 mov eax,0x000000XX } last_pc_ = NULL; - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop (imm->reg) eliminated\n", pc_offset()); } return; @@ -533,7 +544,7 @@ void Assembler::pop(Register dst) { last_pc_ = NULL; // change to // b8XXXXXXXX mov eax,0xXXXXXXXX - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop (imm->reg) eliminated\n", pc_offset()); } return; @@ -776,6 +787,13 @@ void Assembler::rep_stos() { } +void Assembler::stos() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0xAB); +} + + void Assembler::xchg(Register dst, Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -813,7 +831,7 @@ void Assembler::add(Register dst, const Operand& src) { void Assembler::add(const Operand& dst, const Immediate& x) { ASSERT(reloc_info_writer.last_pc() != NULL); - if (FLAG_push_pop_elimination && (reloc_info_writer.last_pc() <= last_pc_)) { + if (FLAG_peephole_optimization && (reloc_info_writer.last_pc() <= last_pc_)) { byte instr = last_pc_[0]; if ((instr & 0xf8) == 0x50) { // Last instruction was a push. Check whether this is a pop without a @@ -822,7 +840,7 @@ void Assembler::add(const Operand& dst, const Immediate& x) { (x.x_ == kPointerSize) && (x.rmode_ == RelocInfo::NONE)) { pc_ = last_pc_; last_pc_ = NULL; - if (FLAG_print_push_pop_elimination) { + if (FLAG_print_peephole_optimization) { PrintF("%d push/pop(noreg) eliminated\n", pc_offset()); } return; @@ -2528,3 +2546,5 @@ void LogGeneratedCodeCoverage(const char* file_line) { #endif } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index 6a7effd421..9ece74432b 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -194,7 +194,6 @@ inline Hint NegateHint(Hint hint) { class Immediate BASE_EMBEDDED { public: inline explicit Immediate(int x); - inline explicit Immediate(const char* s); inline explicit Immediate(const ExternalReference& ext); inline explicit Immediate(Handle<Object> handle); inline explicit Immediate(Smi* value); @@ -551,6 +550,7 @@ class Assembler : public Malloced { // Repetitive string instructions. void rep_movs(); void rep_stos(); + void stos(); // Exchange two registers void xchg(Register dst, Register src); diff --git a/deps/v8/src/ia32/builtins-ia32.cc b/deps/v8/src/ia32/builtins-ia32.cc index 80e421bccd..608625817a 100644 --- a/deps/v8/src/ia32/builtins-ia32.cc +++ b/deps/v8/src/ia32/builtins-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" namespace v8 { @@ -806,6 +808,7 @@ static void AllocateJSArray(MacroAssembler* masm, Label* gc_required) { ASSERT(scratch.is(edi)); // rep stos destination ASSERT(!fill_with_hole || array_size.is(ecx)); // rep stos count + ASSERT(!fill_with_hole || !result.is(eax)); // result is never eax // Load the initial map from the array function. __ mov(elements_array, @@ -863,15 +866,22 @@ static void AllocateJSArray(MacroAssembler* masm, if (fill_with_hole) { __ lea(edi, Operand(elements_array, FixedArray::kHeaderSize - kHeapObjectTag)); - - __ push(eax); __ mov(eax, Factory::the_hole_value()); - __ cld(); + // Do not use rep stos when filling less than kRepStosThreshold + // words. + const int kRepStosThreshold = 16; + Label loop, entry, done; + __ cmp(ecx, kRepStosThreshold); + __ j(below, &loop); // Note: ecx > 0. __ rep_stos(); - - // Restore saved registers. - __ pop(eax); + __ jmp(&done); + __ bind(&loop); + __ stos(); + __ bind(&entry); + __ cmp(edi, Operand(elements_array_end)); + __ j(below, &loop); + __ bind(&done); } } @@ -970,13 +980,14 @@ static void ArrayNativeCode(MacroAssembler* masm, AllocateJSArray(masm, edi, ecx, - eax, ebx, + eax, edx, edi, true, &prepare_generic_code_call); __ IncrementCounter(&Counters::array_function_native, 1); + __ mov(eax, ebx); __ pop(ebx); if (construct_call) { __ pop(edi); @@ -1067,7 +1078,7 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) { // -- esp[0] : return address // -- esp[4] : last argument // ----------------------------------- - Label generic_array_code, one_or_more_arguments, two_or_more_arguments; + Label generic_array_code; // Get the Array function. GenerateLoadArrayFunction(masm, edi); @@ -1247,3 +1258,5 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { #undef __ } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index 63286a762a..226a374bc4 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "bootstrapper.h" #include "codegen-inl.h" #include "compiler.h" @@ -2979,6 +2981,7 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { Load(args->at(i)); + frame_->SpillTop(); } // Record the position for debugging purposes. @@ -4227,8 +4230,7 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { // Get the i'th entry of the array. __ mov(edx, frame_->ElementAt(2)); - __ mov(ebx, Operand(edx, eax, times_2, - FixedArray::kHeaderSize - kHeapObjectTag)); + __ mov(ebx, FixedArrayElementOperand(edx, eax)); // Get the expected map from the stack or a zero map in the // permanent slow case eax: current iteration count ebx: i'th entry @@ -4724,43 +4726,14 @@ Result CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { JumpTarget slow; JumpTarget done; - // Generate fast-case code for variables that might be shadowed by - // eval-introduced variables. Eval is used a lot without - // introducing variables. In those cases, we do not want to - // perform a runtime call for all variables in the scope - // containing the eval. - if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { - result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow); - // If there was no control flow to slow, we can exit early. - if (!slow.is_linked()) return result; - done.Jump(&result); - - } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { - Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); - // Only generate the fast case for locals that rewrite to slots. - // This rules out argument loads because eval forces arguments - // access to be through the arguments object. - if (potential_slot != NULL) { - // Allocate a fresh register to use as a temp in - // ContextSlotOperandCheckExtensions and to hold the result - // value. - result = allocator()->Allocate(); - ASSERT(result.is_valid()); - __ mov(result.reg(), - ContextSlotOperandCheckExtensions(potential_slot, - result, - &slow)); - if (potential_slot->var()->mode() == Variable::CONST) { - __ cmp(result.reg(), Factory::the_hole_value()); - done.Branch(not_equal, &result); - __ mov(result.reg(), Factory::undefined_value()); - } - // There is always control flow to slow from - // ContextSlotOperandCheckExtensions so we have to jump around - // it. - done.Jump(&result); - } - } + // Generate fast case for loading from slots that correspond to + // local/global variables or arguments unless they are shadowed by + // eval-introduced bindings. + EmitDynamicLoadFromSlotFastCase(slot, + typeof_state, + &result, + &slow, + &done); slow.Bind(); // A runtime call is inevitable. We eagerly sync frame elements @@ -4929,6 +4902,68 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( } +void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot, + TypeofState typeof_state, + Result* result, + JumpTarget* slow, + JumpTarget* done) { + // Generate fast-case code for variables that might be shadowed by + // eval-introduced variables. Eval is used a lot without + // introducing variables. In those cases, we do not want to + // perform a runtime call for all variables in the scope + // containing the eval. + if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { + *result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow); + done->Jump(result); + + } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { + Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); + Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); + if (potential_slot != NULL) { + // Generate fast case for locals that rewrite to slots. + // Allocate a fresh register to use as a temp in + // ContextSlotOperandCheckExtensions and to hold the result + // value. + *result = allocator()->Allocate(); + ASSERT(result->is_valid()); + __ mov(result->reg(), + ContextSlotOperandCheckExtensions(potential_slot, *result, slow)); + if (potential_slot->var()->mode() == Variable::CONST) { + __ cmp(result->reg(), Factory::the_hole_value()); + done->Branch(not_equal, result); + __ mov(result->reg(), Factory::undefined_value()); + } + done->Jump(result); + } else if (rewrite != NULL) { + // Generate fast case for calls of an argument function. + Property* property = rewrite->AsProperty(); + if (property != NULL) { + VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); + Literal* key_literal = property->key()->AsLiteral(); + if (obj_proxy != NULL && + key_literal != NULL && + obj_proxy->IsArguments() && + key_literal->handle()->IsSmi()) { + // Load arguments object if there are no eval-introduced + // variables. Then load the argument from the arguments + // object using keyed load. + Result arguments = allocator()->Allocate(); + ASSERT(arguments.is_valid()); + __ mov(arguments.reg(), + ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(), + arguments, + slow)); + frame_->Push(&arguments); + frame_->Push(key_literal->handle()); + *result = EmitKeyedLoad(); + done->Jump(result); + } + } + } + } +} + + void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { if (slot->type() == Slot::LOOKUP) { ASSERT(slot->var()->is_dynamic()); @@ -5698,6 +5733,7 @@ void CodeGenerator::VisitCall(Call* node) { int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { Load(args->at(i)); + frame_->SpillTop(); } // Prepare the stack for the call to ResolvePossiblyDirectEval. @@ -5747,6 +5783,7 @@ void CodeGenerator::VisitCall(Call* node) { int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { Load(args->at(i)); + frame_->SpillTop(); } // Push the name of the function onto the frame. @@ -5765,59 +5802,26 @@ void CodeGenerator::VisitCall(Call* node) { // ---------------------------------- // JavaScript examples: // - // with (obj) foo(1, 2, 3) // foo is in obj + // with (obj) foo(1, 2, 3) // foo may be in obj. // // function f() {}; // function g() { // eval(...); - // f(); // f could be in extension object + // f(); // f could be in extension object. // } // ---------------------------------- - JumpTarget slow; - JumpTarget done; - - // Generate fast-case code for variables that might be shadowed by - // eval-introduced variables. Eval is used a lot without - // introducing variables. In those cases, we do not want to - // perform a runtime call for all variables in the scope - // containing the eval. + JumpTarget slow, done; Result function; - if (var->mode() == Variable::DYNAMIC_GLOBAL) { - function = LoadFromGlobalSlotCheckExtensions(var->slot(), - NOT_INSIDE_TYPEOF, - &slow); - frame_->Push(&function); - LoadGlobalReceiver(); - done.Jump(); - - } else if (var->mode() == Variable::DYNAMIC_LOCAL) { - Slot* potential_slot = var->local_if_not_shadowed()->slot(); - // Only generate the fast case for locals that rewrite to slots. - // This rules out argument loads because eval forces arguments - // access to be through the arguments object. - if (potential_slot != NULL) { - // Allocate a fresh register to use as a temp in - // ContextSlotOperandCheckExtensions and to hold the result - // value. - function = allocator()->Allocate(); - ASSERT(function.is_valid()); - __ mov(function.reg(), - ContextSlotOperandCheckExtensions(potential_slot, - function, - &slow)); - JumpTarget push_function_and_receiver; - if (potential_slot->var()->mode() == Variable::CONST) { - __ cmp(function.reg(), Factory::the_hole_value()); - push_function_and_receiver.Branch(not_equal, &function); - __ mov(function.reg(), Factory::undefined_value()); - } - push_function_and_receiver.Bind(&function); - frame_->Push(&function); - LoadGlobalReceiver(); - done.Jump(); - } - } + + // Generate fast case for loading functions from slots that + // correspond to local/global variables or arguments unless they + // are shadowed by eval-introduced bindings. + EmitDynamicLoadFromSlotFastCase(var->slot(), + NOT_INSIDE_TYPEOF, + &function, + &slow, + &done); slow.Bind(); // Enter the runtime system to load the function from the context. @@ -5839,7 +5843,18 @@ void CodeGenerator::VisitCall(Call* node) { ASSERT(!allocator()->is_used(edx)); frame_->EmitPush(edx); - done.Bind(); + // If fast case code has been generated, emit code to push the + // function and receiver and have the slow path jump around this + // code. + if (done.is_linked()) { + JumpTarget call; + call.Jump(); + done.Bind(&function); + frame_->Push(&function); + LoadGlobalReceiver(); + call.Bind(); + } + // Call the function. CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position()); @@ -5874,6 +5889,7 @@ void CodeGenerator::VisitCall(Call* node) { int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { Load(args->at(i)); + frame_->SpillTop(); } // Push the name of the function onto the frame. @@ -6149,11 +6165,11 @@ void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) { __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset)); __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset)); __ cmp(map.reg(), FIRST_JS_OBJECT_TYPE); - destination()->false_target()->Branch(less); + destination()->false_target()->Branch(below); __ cmp(map.reg(), LAST_JS_OBJECT_TYPE); obj.Unuse(); map.Unuse(); - destination()->Split(less_equal); + destination()->Split(below_equal); } @@ -6266,7 +6282,7 @@ void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) { __ mov(obj.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset)); __ movzx_b(tmp.reg(), FieldOperand(obj.reg(), Map::kInstanceTypeOffset)); __ cmp(tmp.reg(), FIRST_JS_OBJECT_TYPE); - null.Branch(less); + null.Branch(below); // As long as JS_FUNCTION_TYPE is the last instance type and it is // right after LAST_JS_OBJECT_TYPE, we can avoid checking for @@ -6634,16 +6650,6 @@ class DeferredSearchCache: public DeferredCode { }; -// Return a position of the element at |index_as_smi| + |additional_offset| -// in FixedArray pointer to which is held in |array|. |index_as_smi| is Smi. -static Operand ArrayElement(Register array, - Register index_as_smi, - int additional_offset = 0) { - int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize; - return FieldOperand(array, index_as_smi, times_half_pointer_size, offset); -} - - void DeferredSearchCache::Generate() { Label first_loop, search_further, second_loop, cache_miss; @@ -6660,11 +6666,11 @@ void DeferredSearchCache::Generate() { __ cmp(Operand(dst_), Immediate(kEntriesIndexSmi)); __ j(less, &search_further); - __ cmp(key_, ArrayElement(cache_, dst_)); + __ cmp(key_, CodeGenerator::FixedArrayElementOperand(cache_, dst_)); __ j(not_equal, &first_loop); __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_); - __ mov(dst_, ArrayElement(cache_, dst_, 1)); + __ mov(dst_, CodeGenerator::FixedArrayElementOperand(cache_, dst_, 1)); __ jmp(exit_label()); __ bind(&search_further); @@ -6678,11 +6684,11 @@ void DeferredSearchCache::Generate() { __ cmp(dst_, FieldOperand(cache_, JSFunctionResultCache::kFingerOffset)); __ j(less_equal, &cache_miss); - __ cmp(key_, ArrayElement(cache_, dst_)); + __ cmp(key_, CodeGenerator::FixedArrayElementOperand(cache_, dst_)); __ j(not_equal, &second_loop); __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_); - __ mov(dst_, ArrayElement(cache_, dst_, 1)); + __ mov(dst_, CodeGenerator::FixedArrayElementOperand(cache_, dst_, 1)); __ jmp(exit_label()); __ bind(&cache_miss); @@ -6730,7 +6736,7 @@ void DeferredSearchCache::Generate() { __ pop(ebx); // restore the key __ mov(FieldOperand(ecx, JSFunctionResultCache::kFingerOffset), edx); // Store key. - __ mov(ArrayElement(ecx, edx), ebx); + __ mov(CodeGenerator::FixedArrayElementOperand(ecx, edx), ebx); __ RecordWrite(ecx, 0, ebx, edx); // Store value. @@ -6738,7 +6744,7 @@ void DeferredSearchCache::Generate() { __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kFingerOffset)); __ add(Operand(edx), Immediate(Smi::FromInt(1))); __ mov(ebx, eax); - __ mov(ArrayElement(ecx, edx), ebx); + __ mov(CodeGenerator::FixedArrayElementOperand(ecx, edx), ebx); __ RecordWrite(ecx, 0, ebx, edx); if (!dst_.is(eax)) { @@ -6785,11 +6791,11 @@ void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) { // tmp.reg() now holds finger offset as a smi. ASSERT(kSmiTag == 0 && kSmiTagSize == 1); __ mov(tmp.reg(), FieldOperand(cache.reg(), - JSFunctionResultCache::kFingerOffset)); - __ cmp(key.reg(), ArrayElement(cache.reg(), tmp.reg())); + JSFunctionResultCache::kFingerOffset)); + __ cmp(key.reg(), FixedArrayElementOperand(cache.reg(), tmp.reg())); deferred->Branch(not_equal); - __ mov(tmp.reg(), ArrayElement(cache.reg(), tmp.reg(), 1)); + __ mov(tmp.reg(), FixedArrayElementOperand(cache.reg(), tmp.reg(), 1)); deferred->BindExit(); frame_->Push(&tmp); @@ -6866,7 +6872,7 @@ void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) { // Check that object doesn't require security checks and // has no indexed interceptor. __ CmpObjectType(object.reg(), FIRST_JS_OBJECT_TYPE, tmp1.reg()); - deferred->Branch(less); + deferred->Branch(below); __ movzx_b(tmp1.reg(), FieldOperand(tmp1.reg(), Map::kBitFieldOffset)); __ test(tmp1.reg(), Immediate(KeyedLoadIC::kSlowCaseBitFieldMask)); deferred->Branch(not_zero); @@ -6888,14 +6894,8 @@ void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) { deferred->Branch(not_zero); // Bring addresses into index1 and index2. - __ lea(index1.reg(), FieldOperand(tmp1.reg(), - index1.reg(), - times_half_pointer_size, // index1 is Smi - FixedArray::kHeaderSize)); - __ lea(index2.reg(), FieldOperand(tmp1.reg(), - index2.reg(), - times_half_pointer_size, // index2 is Smi - FixedArray::kHeaderSize)); + __ lea(index1.reg(), FixedArrayElementOperand(tmp1.reg(), index1.reg())); + __ lea(index2.reg(), FixedArrayElementOperand(tmp1.reg(), index2.reg())); // Swap elements. __ mov(object.reg(), Operand(index1.reg(), 0)); @@ -8192,11 +8192,11 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { __ mov(map.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset)); __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset)); __ cmp(map.reg(), FIRST_JS_OBJECT_TYPE); - destination()->false_target()->Branch(less); + destination()->false_target()->Branch(below); __ cmp(map.reg(), LAST_JS_OBJECT_TYPE); answer.Unuse(); map.Unuse(); - destination()->Split(less_equal); + destination()->Split(below_equal); } else { // Uncommon case: typeof testing against a string literal that is // never returned from the typeof operator. @@ -8768,11 +8768,7 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { deferred->Branch(not_equal); // Store the value. - __ mov(Operand(tmp.reg(), - key.reg(), - times_2, - FixedArray::kHeaderSize - kHeapObjectTag), - result.reg()); + __ mov(FixedArrayElementOperand(tmp.reg(), key.reg()), result.reg()); __ IncrementCounter(&Counters::keyed_store_inline, 1); deferred->BindExit(); @@ -9074,7 +9070,7 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { __ mov(ecx, Operand(esp, 3 * kPointerSize)); __ mov(eax, Operand(esp, 2 * kPointerSize)); ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0)); - __ mov(ecx, FieldOperand(ecx, eax, times_2, FixedArray::kHeaderSize)); + __ mov(ecx, CodeGenerator::FixedArrayElementOperand(ecx, eax)); __ cmp(ecx, Factory::undefined_value()); __ j(equal, &slow_case); @@ -10296,6 +10292,11 @@ void IntegerConvert(MacroAssembler* masm, Label done, right_exponent, normal_exponent; Register scratch = ebx; Register scratch2 = edi; + if (type_info.IsInteger32() && CpuFeatures::IsEnabled(SSE2)) { + CpuFeatures::Scope scope(SSE2); + __ cvttsd2si(ecx, FieldOperand(source, HeapNumber::kValueOffset)); + return; + } if (!type_info.IsInteger32() || !use_sse3) { // Get exponent word. __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset)); @@ -11601,7 +11602,7 @@ void CompareStub::Generate(MacroAssembler* masm) { ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); Label first_non_object; __ cmp(ecx, FIRST_JS_OBJECT_TYPE); - __ j(less, &first_non_object); + __ j(below, &first_non_object); // Return non-zero (eax is not zero) Label return_not_equal; @@ -11618,7 +11619,7 @@ void CompareStub::Generate(MacroAssembler* masm) { __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ cmp(ecx, FIRST_JS_OBJECT_TYPE); - __ j(greater_equal, &return_not_equal); + __ j(above_equal, &return_not_equal); // Check for oddballs: true, false, null, undefined. __ cmp(ecx, ODDBALL_TYPE); @@ -12266,9 +12267,9 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); // eax - object map __ movzx_b(ecx, FieldOperand(eax, Map::kInstanceTypeOffset)); // ecx - type __ cmp(ecx, FIRST_JS_OBJECT_TYPE); - __ j(less, &slow, not_taken); + __ j(below, &slow, not_taken); __ cmp(ecx, LAST_JS_OBJECT_TYPE); - __ j(greater, &slow, not_taken); + __ j(above, &slow, not_taken); // Get the prototype of the function. __ mov(edx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address @@ -12296,9 +12297,9 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ cmp(ecx, FIRST_JS_OBJECT_TYPE); - __ j(less, &slow, not_taken); + __ j(below, &slow, not_taken); __ cmp(ecx, LAST_JS_OBJECT_TYPE); - __ j(greater, &slow, not_taken); + __ j(above, &slow, not_taken); // Register mapping: // eax is object map. @@ -13296,3 +13297,5 @@ void StringCompareStub::Generate(MacroAssembler* masm) { #undef __ } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index 5967338da2..e00bec7131 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -28,7 +28,9 @@ #ifndef V8_IA32_CODEGEN_IA32_H_ #define V8_IA32_CODEGEN_IA32_H_ +#include "ast.h" #include "ic-inl.h" +#include "jump-target-heavy.h" namespace v8 { namespace internal { @@ -343,6 +345,15 @@ class CodeGenerator: public AstVisitor { // expected arguments. Otherwise return -1. static int InlineRuntimeCallArgumentsCount(Handle<String> name); + // Return a position of the element at |index_as_smi| + |additional_offset| + // in FixedArray pointer to which is held in |array|. |index_as_smi| is Smi. + static Operand FixedArrayElementOperand(Register array, + Register index_as_smi, + int additional_offset = 0) { + int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize; + return FieldOperand(array, index_as_smi, times_half_pointer_size, offset); + } + private: // Construction/Destruction explicit CodeGenerator(MacroAssembler* masm); @@ -454,6 +465,16 @@ class CodeGenerator: public AstVisitor { TypeofState typeof_state, JumpTarget* slow); + // Support for loading from local/global variables and arguments + // whose location is known unless they are shadowed by + // eval-introduced bindings. Generates no code for unsupported slot + // types and therefore expects to fall through to the slow jump target. + void EmitDynamicLoadFromSlotFastCase(Slot* slot, + TypeofState typeof_state, + Result* result, + JumpTarget* slow, + JumpTarget* done); + // Store the value on top of the expression stack into a slot, leaving the // value in place. void StoreToSlot(Slot* slot, InitState init_state); diff --git a/deps/v8/src/ia32/cpu-ia32.cc b/deps/v8/src/ia32/cpu-ia32.cc index 2107ad96f4..b15140f04c 100644 --- a/deps/v8/src/ia32/cpu-ia32.cc +++ b/deps/v8/src/ia32/cpu-ia32.cc @@ -33,6 +33,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "cpu.h" #include "macro-assembler.h" @@ -77,3 +79,5 @@ void CPU::DebugBreak() { } } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/debug-ia32.cc b/deps/v8/src/ia32/debug-ia32.cc index d142b11cf7..9780f3b09e 100644 --- a/deps/v8/src/ia32/debug-ia32.cc +++ b/deps/v8/src/ia32/debug-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" #include "debug.h" @@ -261,3 +263,5 @@ const int Debug::kFrameDropperFrameSize = 5; #endif // ENABLE_DEBUGGER_SUPPORT } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/disasm-ia32.cc b/deps/v8/src/ia32/disasm-ia32.cc index 8d342e087c..58c22afcd3 100644 --- a/deps/v8/src/ia32/disasm-ia32.cc +++ b/deps/v8/src/ia32/disasm-ia32.cc @@ -30,6 +30,9 @@ #include <stdarg.h> #include "v8.h" + +#if defined(V8_TARGET_ARCH_IA32) + #include "disasm.h" namespace disasm { @@ -90,6 +93,7 @@ static ByteMnemonic zero_operands_instr[] = { {0x99, "cdq", UNSET_OP_ORDER}, {0x9B, "fwait", UNSET_OP_ORDER}, {0xFC, "cld", UNSET_OP_ORDER}, + {0xAB, "stos", UNSET_OP_ORDER}, {-1, "", UNSET_OP_ORDER} }; @@ -1438,3 +1442,5 @@ int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; } } // namespace disasm + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/fast-codegen-ia32.cc b/deps/v8/src/ia32/fast-codegen-ia32.cc index 61e2b5edfc..b749e594bc 100644 --- a/deps/v8/src/ia32/fast-codegen-ia32.cc +++ b/deps/v8/src/ia32/fast-codegen-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" #include "fast-codegen.h" #include "data-flow.h" @@ -948,3 +950,5 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) { } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/frames-ia32.cc b/deps/v8/src/ia32/frames-ia32.cc index 5c900bedd7..212cfdeaa0 100644 --- a/deps/v8/src/ia32/frames-ia32.cc +++ b/deps/v8/src/ia32/frames-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "frames-inl.h" namespace v8 { @@ -109,3 +111,5 @@ Address InternalFrame::GetCallerStackPointer() const { } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index e9838ada77..368a8eeb0b 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" #include "compiler.h" #include "debug.h" @@ -79,11 +81,17 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) { bool function_in_register = true; // Possibly allocate a local context. - if (scope()->num_heap_slots() > 0) { + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; + if (heap_slots > 0) { Comment cmnt(masm_, "[ Allocate local context"); // Argument to NewContext is the function, which is still in edi. __ push(edi); - __ CallRuntime(Runtime::kNewContext, 1); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { + FastNewContextStub stub(heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); + } function_in_register = false; // Context is returned in both eax and esi. It replaces the context // passed to us. It's saved in the stack and kept live in esi. @@ -140,7 +148,18 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) { } { Comment cmnt(masm_, "[ Declarations"); - VisitDeclarations(scope()->declarations()); + // For named function expressions, declare the function name as a + // constant. + if (scope()->is_function_scope() && scope()->function() != NULL) { + EmitDeclaration(scope()->function(), Variable::CONST, NULL); + } + // Visit all the explicit declarations unless there is an illegal + // redeclaration. + if (scope()->HasIllegalRedeclaration()) { + scope()->VisitIllegalRedeclaration(this); + } else { + VisitDeclarations(scope()->declarations()); + } } { Comment cmnt(masm_, "[ Stack check"); @@ -425,6 +444,39 @@ void FullCodeGenerator::DropAndApply(int count, } +void FullCodeGenerator::PrepareTest(Label* materialize_true, + Label* materialize_false, + Label** if_true, + Label** if_false) { + switch (context_) { + case Expression::kUninitialized: + UNREACHABLE(); + break; + case Expression::kEffect: + // In an effect context, the true and the false case branch to the + // same label. + *if_true = *if_false = materialize_true; + break; + case Expression::kValue: + *if_true = materialize_true; + *if_false = materialize_false; + break; + case Expression::kTest: + *if_true = true_label_; + *if_false = false_label_; + break; + case Expression::kValueTest: + *if_true = materialize_true; + *if_false = false_label_; + break; + case Expression::kTestValue: + *if_true = true_label_; + *if_false = materialize_false; + break; + } +} + + void FullCodeGenerator::Apply(Expression::Context context, Label* materialize_true, Label* materialize_false) { @@ -490,6 +542,61 @@ void FullCodeGenerator::Apply(Expression::Context context, } +// Convert constant control flow (true or false) to the result expected for +// a given expression context. +void FullCodeGenerator::Apply(Expression::Context context, bool flag) { + switch (context) { + case Expression::kUninitialized: + UNREACHABLE(); + break; + case Expression::kEffect: + break; + case Expression::kValue: { + Handle<Object> value = + flag ? Factory::true_value() : Factory::false_value(); + switch (location_) { + case kAccumulator: + __ mov(result_register(), value); + break; + case kStack: + __ push(Immediate(value)); + break; + } + break; + } + case Expression::kTest: + __ jmp(flag ? true_label_ : false_label_); + break; + case Expression::kTestValue: + switch (location_) { + case kAccumulator: + // If value is false it's needed. + if (!flag) __ mov(result_register(), Factory::false_value()); + break; + case kStack: + // If value is false it's needed. + if (!flag) __ push(Immediate(Factory::false_value())); + break; + } + __ jmp(flag ? true_label_ : false_label_); + break; + case Expression::kValueTest: + switch (location_) { + case kAccumulator: + // If value is true it's needed. + if (flag) __ mov(result_register(), Factory::true_value()); + break; + case kStack: + // If value is true it's needed. + if (flag) __ push(Immediate(Factory::true_value())); + break; + } + __ jmp(flag ? true_label_ : false_label_); + break; + } +} + + void FullCodeGenerator::DoTest(Expression::Context context) { // The value to test is in the accumulator. If the value might be needed // on the stack (value/test and test/value contexts with a stack location @@ -665,22 +772,22 @@ void FullCodeGenerator::Move(Slot* dst, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { +void FullCodeGenerator::EmitDeclaration(Variable* variable, + Variable::Mode mode, + FunctionLiteral* function) { Comment cmnt(masm_, "[ Declaration"); - Variable* var = decl->proxy()->var(); - ASSERT(var != NULL); // Must have been resolved. - Slot* slot = var->slot(); - Property* prop = var->AsProperty(); - + ASSERT(variable != NULL); // Must have been resolved. + Slot* slot = variable->slot(); + Property* prop = variable->AsProperty(); if (slot != NULL) { switch (slot->type()) { case Slot::PARAMETER: case Slot::LOCAL: - if (decl->mode() == Variable::CONST) { + if (mode == Variable::CONST) { __ mov(Operand(ebp, SlotOffset(slot)), Immediate(Factory::the_hole_value())); - } else if (decl->fun() != NULL) { - VisitForValue(decl->fun(), kAccumulator); + } else if (function != NULL) { + VisitForValue(function, kAccumulator); __ mov(Operand(ebp, SlotOffset(slot)), result_register()); } break; @@ -690,7 +797,7 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { // this specific context. // The variable in the decl always resides in the current context. - ASSERT_EQ(0, scope()->ContextChainLength(var->scope())); + ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); if (FLAG_debug_code) { // Check if we have the correct context pointer. __ mov(ebx, @@ -698,12 +805,12 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { __ cmp(ebx, Operand(esi)); __ Check(equal, "Unexpected declaration in current context."); } - if (decl->mode() == Variable::CONST) { - __ mov(eax, Immediate(Factory::the_hole_value())); - __ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax); + if (mode == Variable::CONST) { + __ mov(CodeGenerator::ContextOperand(esi, slot->index()), + Immediate(Factory::the_hole_value())); // No write barrier since the hole value is in old space. - } else if (decl->fun() != NULL) { - VisitForValue(decl->fun(), kAccumulator); + } else if (function != NULL) { + VisitForValue(function, kAccumulator); __ mov(CodeGenerator::ContextOperand(esi, slot->index()), result_register()); int offset = Context::SlotOffset(slot->index()); @@ -714,21 +821,19 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { case Slot::LOOKUP: { __ push(esi); - __ push(Immediate(var->name())); + __ push(Immediate(variable->name())); // Declaration nodes are always introduced in one of two modes. - ASSERT(decl->mode() == Variable::VAR || - decl->mode() == Variable::CONST); - PropertyAttributes attr = - (decl->mode() == Variable::VAR) ? NONE : READ_ONLY; + ASSERT(mode == Variable::VAR || mode == Variable::CONST); + PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY; __ push(Immediate(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as // 'undefined') because we may have a (legal) redeclaration and we // must not destroy the current value. - if (decl->mode() == Variable::CONST) { + if (mode == Variable::CONST) { __ push(Immediate(Factory::the_hole_value())); - } else if (decl->fun() != NULL) { - VisitForValue(decl->fun(), kStack); + } else if (function != NULL) { + VisitForValue(function, kStack); } else { __ push(Immediate(Smi::FromInt(0))); // No initial value! } @@ -738,13 +843,13 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } } else if (prop != NULL) { - if (decl->fun() != NULL || decl->mode() == Variable::CONST) { + if (function != NULL || mode == Variable::CONST) { // We are declaring a function or constant that rewrites to a // property. Use (keyed) IC to set the initial value. VisitForValue(prop->obj(), kStack); - if (decl->fun() != NULL) { + if (function != NULL) { VisitForValue(prop->key(), kStack); - VisitForValue(decl->fun(), kAccumulator); + VisitForValue(function, kAccumulator); __ pop(ecx); } else { VisitForValue(prop->key(), kAccumulator); @@ -763,6 +868,11 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } +void FullCodeGenerator::VisitDeclaration(Declaration* decl) { + EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); +} + + void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. __ push(esi); // The context is the first argument. @@ -773,19 +883,225 @@ void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { } -void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { - Comment cmnt(masm_, "[ FunctionLiteral"); +void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { + Comment cmnt(masm_, "[ SwitchStatement"); + Breakable nested_statement(this, stmt); + SetStatementPosition(stmt); + // Keep the switch value on the stack until a case matches. + VisitForValue(stmt->tag(), kStack); + + ZoneList<CaseClause*>* clauses = stmt->cases(); + CaseClause* default_clause = NULL; // Can occur anywhere in the list. + + Label next_test; // Recycled for each test. + // Compile all the tests with branches to their bodies. + for (int i = 0; i < clauses->length(); i++) { + CaseClause* clause = clauses->at(i); + // The default is not a test, but remember it as final fall through. + if (clause->is_default()) { + default_clause = clause; + continue; + } + + Comment cmnt(masm_, "[ Case comparison"); + __ bind(&next_test); + next_test.Unuse(); + + // Compile the label expression. + VisitForValue(clause->label(), kAccumulator); + + // Perform the comparison as if via '==='. The comparison stub expects + // the smi vs. smi case to be handled before it is called. + Label slow_case; + __ mov(edx, Operand(esp, 0)); // Switch value. + __ mov(ecx, edx); + __ or_(ecx, Operand(eax)); + __ test(ecx, Immediate(kSmiTagMask)); + __ j(not_zero, &slow_case, not_taken); + __ cmp(edx, Operand(eax)); + __ j(not_equal, &next_test); + __ Drop(1); // Switch value is no longer needed. + __ jmp(clause->body_target()->entry_label()); + + __ bind(&slow_case); + CompareStub stub(equal, true); + __ CallStub(&stub); + __ test(eax, Operand(eax)); + __ j(not_equal, &next_test); + __ Drop(1); // Switch value is no longer needed. + __ jmp(clause->body_target()->entry_label()); + } - // Build the shared function info and instantiate the function based - // on it. - Handle<SharedFunctionInfo> function_info = - Compiler::BuildFunctionInfo(expr, script(), this); - if (HasStackOverflow()) return; + // Discard the test value and jump to the default if present, otherwise to + // the end of the statement. + __ bind(&next_test); + __ Drop(1); // Switch value is no longer needed. + if (default_clause == NULL) { + __ jmp(nested_statement.break_target()); + } else { + __ jmp(default_clause->body_target()->entry_label()); + } + + // Compile all the case bodies. + for (int i = 0; i < clauses->length(); i++) { + Comment cmnt(masm_, "[ Case body"); + CaseClause* clause = clauses->at(i); + __ bind(clause->body_target()->entry_label()); + VisitStatements(clause->statements()); + } + + __ bind(nested_statement.break_target()); +} + + +void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { + Comment cmnt(masm_, "[ ForInStatement"); + SetStatementPosition(stmt); + + Label loop, exit; + ForIn loop_statement(this, stmt); + increment_loop_depth(); + + // Get the object to enumerate over. Both SpiderMonkey and JSC + // ignore null and undefined in contrast to the specification; see + // ECMA-262 section 12.6.4. + VisitForValue(stmt->enumerable(), kAccumulator); + __ cmp(eax, Factory::undefined_value()); + __ j(equal, &exit); + __ cmp(eax, Factory::null_value()); + __ j(equal, &exit); + + // Convert the object to a JS object. + Label convert, done_convert; + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &convert); + __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx); + __ j(above_equal, &done_convert); + __ bind(&convert); + __ push(eax); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ bind(&done_convert); + __ push(eax); - // Create a new closure. - __ push(esi); - __ push(Immediate(function_info)); - __ CallRuntime(Runtime::kNewClosure, 2); + // TODO(kasperl): Check cache validity in generated code. This is a + // fast case for the JSObject::IsSimpleEnum cache validity + // checks. If we cannot guarantee cache validity, call the runtime + // system to check cache validity or get the property names in a + // fixed array. + + // Get the set of properties to enumerate. + __ push(eax); // Duplicate the enumerable object on the stack. + __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); + + // If we got a map from the runtime call, we can do a fast + // modification check. Otherwise, we got a fixed array, and we have + // to do a slow check. + Label fixed_array; + __ cmp(FieldOperand(eax, HeapObject::kMapOffset), Factory::meta_map()); + __ j(not_equal, &fixed_array); + + // We got a map in register eax. Get the enumeration cache from it. + __ mov(ecx, FieldOperand(eax, Map::kInstanceDescriptorsOffset)); + __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset)); + __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + + // Setup the four remaining stack slots. + __ push(eax); // Map. + __ push(edx); // Enumeration cache. + __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset)); + __ SmiTag(eax); + __ push(eax); // Enumeration cache length (as smi). + __ push(Immediate(Smi::FromInt(0))); // Initial index. + __ jmp(&loop); + + // We got a fixed array in register eax. Iterate through that. + __ bind(&fixed_array); + __ push(Immediate(Smi::FromInt(0))); // Map (0) - force slow check. + __ push(eax); + __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset)); + __ SmiTag(eax); + __ push(eax); // Fixed array length (as smi). + __ push(Immediate(Smi::FromInt(0))); // Initial index. + + // Generate code for doing the condition check. + __ bind(&loop); + __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index. + __ cmp(eax, Operand(esp, 1 * kPointerSize)); // Compare to the array length. + __ j(above_equal, loop_statement.break_target()); + + // Get the current entry of the array into register ebx. + __ mov(ebx, Operand(esp, 2 * kPointerSize)); + __ mov(ebx, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize)); + + // Get the expected map from the stack or a zero map in the + // permanent slow case into register edx. + __ mov(edx, Operand(esp, 3 * kPointerSize)); + + // Check if the expected map still matches that of the enumerable. + // If not, we have to filter the key. + Label update_each; + __ mov(ecx, Operand(esp, 4 * kPointerSize)); + __ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset)); + __ j(equal, &update_each); + + // Convert the entry to a string or null if it isn't a property + // anymore. If the property has been removed while iterating, we + // just skip it. + __ push(ecx); // Enumerable. + __ push(ebx); // Current entry. + __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION); + __ cmp(eax, Factory::null_value()); + __ j(equal, loop_statement.continue_target()); + __ mov(ebx, Operand(eax)); + + // Update the 'each' property or variable from the possibly filtered + // entry in register ebx. + __ bind(&update_each); + __ mov(result_register(), ebx); + // Perform the assignment as if via '='. + EmitAssignment(stmt->each()); + + // Generate code for the body of the loop. + Label stack_limit_hit, stack_check_done; + Visit(stmt->body()); + + __ StackLimitCheck(&stack_limit_hit); + __ bind(&stack_check_done); + + // Generate code for going to the next element by incrementing the + // index (smi) stored on top of the stack. + __ bind(loop_statement.continue_target()); + __ add(Operand(esp, 0 * kPointerSize), Immediate(Smi::FromInt(1))); + __ jmp(&loop); + + // Slow case for the stack limit check. + StackCheckStub stack_check_stub; + __ bind(&stack_limit_hit); + __ CallStub(&stack_check_stub); + __ jmp(&stack_check_done); + + // Remove the pointers stored on the stack. + __ bind(loop_statement.break_target()); + __ add(Operand(esp), Immediate(5 * kPointerSize)); + + // Exit and decrement the loop depth. + __ bind(&exit); + decrement_loop_depth(); +} + + +void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { + // Use the fast case closure allocation code that allocates in new + // space for nested functions that don't need literals cloning. + if (scope()->is_function_scope() && info->num_literals() == 0) { + FastNewClosureStub stub; + __ push(Immediate(info)); + __ CallStub(&stub); + } else { + __ push(esi); + __ push(Immediate(info)); + __ CallRuntime(Runtime::kNewClosure, 2); + } Apply(context_, eax); } @@ -830,7 +1146,20 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) ? "Context slot" : "Stack slot"); - Apply(context, slot); + if (var->mode() == Variable::CONST) { + // Constants may be the hole value if they have not been initialized. + // Unhole them. + Label done; + MemOperand slot_operand = EmitSlotSearch(slot, eax); + __ mov(eax, slot_operand); + __ cmp(eax, Factory::the_hole_value()); + __ j(not_equal, &done); + __ mov(eax, Factory::undefined_value()); + __ bind(&done); + Apply(context, eax); + } else { + Apply(context, slot); + } } else { Comment cmnt(masm_, "Rewritten parameter"); @@ -966,22 +1295,28 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { Comment cmnt(masm_, "[ ArrayLiteral"); + + ZoneList<Expression*>* subexprs = expr->values(); + int length = subexprs->length(); + __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(expr->literal_index()))); __ push(Immediate(expr->constant_elements())); if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateArrayLiteral, 3); - } else { + } else if (length > FastCloneShallowArrayStub::kMaximumLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); + } else { + FastCloneShallowArrayStub stub(length); + __ CallStub(&stub); } 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++) { + for (int i = 0; i < length; 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. @@ -1016,7 +1351,13 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { void FullCodeGenerator::VisitAssignment(Assignment* expr) { Comment cmnt(masm_, "[ Assignment"); - ASSERT(expr->op() != Token::INIT_CONST); + // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' + // on the left-hand side. + if (!expr->target()->IsValidLeftHandSide()) { + VisitForEffect(expr->target()); + return; + } + // Left-hand side can only be a property, a global or a (parameter or local) // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; @@ -1095,6 +1436,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { switch (assign_type) { case VARIABLE: EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + expr->op(), context_); break; case NAMED_PROPERTY: @@ -1137,15 +1479,66 @@ void FullCodeGenerator::EmitBinaryOp(Token::Value op, } +void FullCodeGenerator::EmitAssignment(Expression* expr) { + // Invalid left-hand sides are rewritten to have a 'throw + // ReferenceError' on the left-hand side. + if (!expr->IsValidLeftHandSide()) { + VisitForEffect(expr); + return; + } + + // Left-hand side can only be a property, a global or a (parameter or local) + // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. + enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; + LhsKind assign_type = VARIABLE; + Property* prop = expr->AsProperty(); + if (prop != NULL) { + assign_type = (prop->key()->IsPropertyName()) + ? NAMED_PROPERTY + : KEYED_PROPERTY; + } + + switch (assign_type) { + case VARIABLE: { + Variable* var = expr->AsVariableProxy()->var(); + EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect); + break; + } + case NAMED_PROPERTY: { + __ push(eax); // Preserve value. + VisitForValue(prop->obj(), kAccumulator); + __ mov(edx, eax); + __ pop(eax); // Restore value. + __ mov(ecx, prop->key()->AsLiteral()->handle()); + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + __ nop(); // Signal no inlined code. + break; + } + case KEYED_PROPERTY: { + __ push(eax); // Preserve value. + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kAccumulator); + __ mov(ecx, eax); + __ pop(edx); + __ pop(eax); // Restore value. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + __ nop(); // Signal no inlined code. + break; + } + } +} + + void FullCodeGenerator::EmitVariableAssignment(Variable* var, + Token::Value op, Expression::Context context) { - // Three main cases: global variables, lookup slots, and all other - // types of slots. Left-hand-side parameters that rewrite to - // explicit property accesses do not reach here. + // Left-hand sides that rewrite to explicit property accesses do not reach + // here. ASSERT(var != NULL); ASSERT(var->is_global() || var->slot() != NULL); - Slot* slot = var->slot(); if (var->is_global()) { ASSERT(!var->is_this()); // Assignment to a global variable. Use inline caching for the @@ -1156,44 +1549,61 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); __ nop(); - Apply(context, eax); - } else if (slot != NULL && slot->type() == Slot::LOOKUP) { - __ push(result_register()); // Value. - __ push(esi); // Context. - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kStoreContextSlot, 3); - Apply(context, eax); - - } else if (slot != NULL) { + } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { + // Perform the assignment for non-const variables and for initialization + // of const variables. Const assignments are simply skipped. + Label done; + Slot* slot = var->slot(); switch (slot->type()) { - case Slot::LOCAL: case Slot::PARAMETER: - __ mov(Operand(ebp, SlotOffset(slot)), result_register()); + case Slot::LOCAL: + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ mov(edx, Operand(ebp, SlotOffset(slot))); + __ cmp(edx, Factory::the_hole_value()); + __ j(not_equal, &done); + } + // Perform the assignment. + __ mov(Operand(ebp, SlotOffset(slot)), eax); break; case Slot::CONTEXT: { MemOperand target = EmitSlotSearch(slot, ecx); - __ mov(target, result_register()); - - // RecordWrite may destroy all its register arguments. - __ mov(edx, result_register()); + if (op == Token::INIT_CONST) { + // Detect const reinitialization by checking for the hole value. + __ mov(edx, target); + __ cmp(edx, Factory::the_hole_value()); + __ j(not_equal, &done); + } + // Perform the assignment and issue the write barrier. + __ mov(target, eax); + // The value of the assignment is in eax. RecordWrite clobbers its + // register arguments. + __ mov(edx, eax); int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; __ RecordWrite(ecx, offset, edx, ebx); break; } case Slot::LOOKUP: - UNREACHABLE(); + // Call the runtime for the assignment. The runtime will ignore + // const reinitialization. + __ push(eax); // Value. + __ push(esi); // Context. + __ push(Immediate(var->name())); + if (op == Token::INIT_CONST) { + // The runtime will ignore const redeclaration. + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); + } else { + __ CallRuntime(Runtime::kStoreContextSlot, 3); + } break; } - Apply(context, result_register()); - - } else { - // Variables rewritten as properties are not treated as variables in - // assignments. - UNREACHABLE(); + __ bind(&done); } + + Apply(context, eax); } @@ -1327,7 +1737,8 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { } // Record source position for debugger. SetSourcePosition(expr->position()); - CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE); + InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); @@ -1341,16 +1752,62 @@ void FullCodeGenerator::VisitCall(Call* expr) { Variable* var = fun->AsVariableProxy()->AsVariable(); if (var != NULL && var->is_possibly_eval()) { - // Call to the identifier 'eval'. - UNREACHABLE(); + // In a call to eval, we first call %ResolvePossiblyDirectEval to + // resolve the function we need to call and the receiver of the + // call. Then we call the resolved function using the given + // arguments. + VisitForValue(fun, kStack); + __ push(Immediate(Factory::undefined_value())); // Reserved receiver slot. + + // Push the arguments. + ZoneList<Expression*>* args = expr->arguments(); + int arg_count = args->length(); + for (int i = 0; i < arg_count; i++) { + VisitForValue(args->at(i), kStack); + } + + // Push copy of the function - found below the arguments. + __ push(Operand(esp, (arg_count + 1) * kPointerSize)); + + // Push copy of the first argument or undefined if it doesn't exist. + if (arg_count > 0) { + __ push(Operand(esp, arg_count * kPointerSize)); + } else { + __ push(Immediate(Factory::undefined_value())); + } + + // Push the receiver of the enclosing function and do runtime call. + __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize)); + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + + // The runtime call returns a pair of values in eax (function) and + // edx (receiver). Touch up the stack with the right values. + __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx); + __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax); + + // Record source position for debugger. + SetSourcePosition(expr->position()); + InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); + __ CallStub(&stub); + // Restore context register. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + DropAndApply(1, context_, eax); } else if (var != NULL && !var->is_this() && var->is_global()) { // Push global object as receiver for the call IC. __ push(CodeGenerator::GlobalObject()); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { - // Call to a lookup slot. - UNREACHABLE(); + // Call to a lookup slot (dynamically introduced variable). Call the + // runtime to find the function to call (returned in eax) and the object + // holding it (returned in edx). + __ push(context_register()); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kLoadContextSlot, 2); + __ push(eax); // Function. + __ push(edx); // Receiver. + EmitCallWithStub(expr); } else if (fun->AsProperty() != NULL) { // Call to an object property. Property* prop = fun->AsProperty(); @@ -1447,7 +1904,730 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { } +void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) { + Handle<String> name = expr->name(); + if (strcmp("_IsSmi", *name->ToCString()) == 0) { + EmitIsSmi(expr->arguments()); + } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) { + EmitIsNonNegativeSmi(expr->arguments()); + } else if (strcmp("_IsObject", *name->ToCString()) == 0) { + EmitIsObject(expr->arguments()); + } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) { + EmitIsUndetectableObject(expr->arguments()); + } else if (strcmp("_IsFunction", *name->ToCString()) == 0) { + EmitIsFunction(expr->arguments()); + } else if (strcmp("_IsArray", *name->ToCString()) == 0) { + EmitIsArray(expr->arguments()); + } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) { + EmitIsRegExp(expr->arguments()); + } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) { + EmitIsConstructCall(expr->arguments()); + } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) { + EmitObjectEquals(expr->arguments()); + } else if (strcmp("_Arguments", *name->ToCString()) == 0) { + EmitArguments(expr->arguments()); + } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) { + EmitArgumentsLength(expr->arguments()); + } else if (strcmp("_ClassOf", *name->ToCString()) == 0) { + EmitClassOf(expr->arguments()); + } else if (strcmp("_Log", *name->ToCString()) == 0) { + EmitLog(expr->arguments()); + } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) { + EmitRandomHeapNumber(expr->arguments()); + } else if (strcmp("_SubString", *name->ToCString()) == 0) { + EmitSubString(expr->arguments()); + } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) { + EmitRegExpExec(expr->arguments()); + } else if (strcmp("_ValueOf", *name->ToCString()) == 0) { + EmitValueOf(expr->arguments()); + } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) { + EmitSetValueOf(expr->arguments()); + } else if (strcmp("_NumberToString", *name->ToCString()) == 0) { + EmitNumberToString(expr->arguments()); + } else if (strcmp("_CharFromCode", *name->ToCString()) == 0) { + EmitCharFromCode(expr->arguments()); + } else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) { + EmitFastCharCodeAt(expr->arguments()); + } else if (strcmp("_StringAdd", *name->ToCString()) == 0) { + EmitStringAdd(expr->arguments()); + } else if (strcmp("_StringCompare", *name->ToCString()) == 0) { + EmitStringCompare(expr->arguments()); + } else if (strcmp("_MathPow", *name->ToCString()) == 0) { + EmitMathPow(expr->arguments()); + } else if (strcmp("_MathSin", *name->ToCString()) == 0) { + EmitMathSin(expr->arguments()); + } else if (strcmp("_MathCos", *name->ToCString()) == 0) { + EmitMathCos(expr->arguments()); + } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) { + EmitMathSqrt(expr->arguments()); + } else if (strcmp("_CallFunction", *name->ToCString()) == 0) { + EmitCallFunction(expr->arguments()); + } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) { + EmitRegExpConstructResult(expr->arguments()); + } else if (strcmp("_SwapElements", *name->ToCString()) == 0) { + EmitSwapElements(expr->arguments()); + } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) { + EmitGetFromCache(expr->arguments()); + } else { + UNREACHABLE(); + } +} + + +void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask | 0x80000000)); + __ j(zero, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_false); + __ cmp(eax, Factory::null_value()); + __ j(equal, if_true); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + // Undetectable objects behave like undefined when tested with typeof. + __ movzx_b(ecx, FieldOperand(ebx, Map::kBitFieldOffset)); + __ test(ecx, Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, if_false); + __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceTypeOffset)); + __ cmp(ecx, FIRST_JS_OBJECT_TYPE); + __ j(below, if_false); + __ cmp(ecx, LAST_JS_OBJECT_TYPE); + __ j(below_equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_false); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset)); + __ test(ebx, Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, if_false); + __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(equal, if_false); + __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ test(eax, Immediate(kSmiTagMask)); + __ j(equal, if_false); + __ CmpObjectType(eax, JS_REGEXP_TYPE, ebx); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + + +void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { + ASSERT(args->length() == 0); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + // Get the frame pointer for the calling frame. + __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + + // Skip the arguments adaptor frame if it exists. + Label check_frame_marker; + __ cmp(Operand(eax, StandardFrameConstants::kContextOffset), + Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ j(not_equal, &check_frame_marker); + __ mov(eax, Operand(eax, StandardFrameConstants::kCallerFPOffset)); + + // Check the marker in the calling frame. + __ bind(&check_frame_marker); + __ cmp(Operand(eax, StandardFrameConstants::kMarkerOffset), + Immediate(Smi::FromInt(StackFrame::CONSTRUCT))); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { + ASSERT(args->length() == 2); + + // Load the two objects into registers and perform the comparison. + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + + __ pop(ebx); + __ cmp(eax, Operand(ebx)); + __ j(equal, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + // ArgumentsAccessStub expects the key in edx and the formal + // parameter count in eax. + VisitForValue(args->at(0), kAccumulator); + __ mov(edx, eax); + __ mov(eax, Immediate(Smi::FromInt(scope()->num_parameters()))); + ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { + ASSERT(args->length() == 0); + + Label exit; + // Get the number of formal parameters. + __ Set(eax, Immediate(Smi::FromInt(scope()->num_parameters()))); + + // Check if the calling frame is an arguments adaptor frame. + __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + __ cmp(Operand(ebx, StandardFrameConstants::kContextOffset), + Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ j(not_equal, &exit); + + // Arguments adaptor case: Read the arguments length from the + // adaptor frame. + __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); + + __ bind(&exit); + if (FLAG_debug_code) __ AbortIfNotSmi(eax); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + Label done, null, function, non_function_constructor; + + VisitForValue(args->at(0), kAccumulator); + + // If the object is a smi, we return null. + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &null); + + // Check that the object is a JS object but take special care of JS + // functions to make sure they have 'Function' as their class. + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(eax, Map::kInstanceTypeOffset)); + __ cmp(ebx, FIRST_JS_OBJECT_TYPE); + __ j(below, &null); + + // As long as JS_FUNCTION_TYPE is the last instance type and it is + // right after LAST_JS_OBJECT_TYPE, we can avoid checking for + // LAST_JS_OBJECT_TYPE. + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); + __ cmp(ebx, JS_FUNCTION_TYPE); + __ j(equal, &function); + + // Check if the constructor in the map is a function. + __ mov(eax, FieldOperand(eax, Map::kConstructorOffset)); + __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); + __ j(not_equal, &non_function_constructor); + + // eax now contains the constructor function. Grab the + // instance class name from there. + __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); + __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset)); + __ jmp(&done); + + // Functions have class 'Function'. + __ bind(&function); + __ mov(eax, Factory::function_class_symbol()); + __ jmp(&done); + + // Objects with a non-function constructor have class 'Object'. + __ bind(&non_function_constructor); + __ mov(eax, Factory::Object_symbol()); + __ jmp(&done); + + // Non-JS objects have class null. + __ bind(&null); + __ mov(eax, Factory::null_value()); + + // All done. + __ bind(&done); + + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { + // Conditionally generate a log call. + // Args: + // 0 (literal string): The type of logging (corresponds to the flags). + // This is used to determine whether or not to generate the log call. + // 1 (string): Format string. Access the string at argument index 2 + // with '%2s' (see Logger::LogRuntime for all the formats). + // 2 (array): Arguments to the format string. + ASSERT_EQ(args->length(), 3); +#ifdef ENABLE_LOGGING_AND_PROFILING + if (CodeGenerator::ShouldGenerateLog(args->at(0))) { + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallRuntime(Runtime::kLog, 2); + } +#endif + // Finally, we're expected to leave a value on the top of the stack. + __ mov(eax, Factory::undefined_value()); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { + ASSERT(args->length() == 0); + + Label slow_allocate_heapnumber; + Label heapnumber_allocated; + + __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber); + __ jmp(&heapnumber_allocated); + + __ bind(&slow_allocate_heapnumber); + // To allocate a heap number, and ensure that it is not a smi, we + // call the runtime function FUnaryMinus on 0, returning the double + // -0.0. A new, distinct heap number is returned each time. + __ push(Immediate(Smi::FromInt(0))); + __ CallRuntime(Runtime::kNumberUnaryMinus, 1); + __ mov(edi, eax); + + __ bind(&heapnumber_allocated); + + __ PrepareCallCFunction(0, ebx); + __ CallCFunction(ExternalReference::random_uint32_function(), 0); + + // Convert 32 random bits in eax to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + // This is implemented on both SSE2 and FPU. + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope fscope(SSE2); + __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single. + __ movd(xmm1, Operand(ebx)); + __ movd(xmm0, Operand(eax)); + __ cvtss2sd(xmm1, xmm1); + __ pxor(xmm0, xmm1); + __ subsd(xmm0, xmm1); + __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0); + } else { + // 0x4130000000000000 is 1.0 x 2^20 as a double. + __ mov(FieldOperand(edi, HeapNumber::kExponentOffset), + Immediate(0x41300000)); + __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax); + __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset)); + __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0)); + __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset)); + __ fsubp(1); + __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset)); + } + __ mov(eax, edi); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { + // Load the arguments on the stack and call the stub. + SubStringStub stub; + ASSERT(args->length() == 3); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { + // Load the arguments on the stack and call the stub. + RegExpExecStub stub; + ASSERT(args->length() == 4); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + VisitForValue(args->at(3), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); // Load the object. + + Label done; + // If the object is a smi return the object. + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &done); + // If the object is not a value type, return the object. + __ CmpObjectType(eax, JS_VALUE_TYPE, ebx); + __ j(not_equal, &done); + __ mov(eax, FieldOperand(eax, JSValue::kValueOffset)); + + __ bind(&done); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { + // Load the arguments on the stack and call the runtime function. + ASSERT(args->length() == 2); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + __ CallRuntime(Runtime::kMath_pow, 2); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { + ASSERT(args->length() == 2); + + VisitForValue(args->at(0), kStack); // Load the object. + VisitForValue(args->at(1), kAccumulator); // Load the value. + __ pop(ebx); // eax = value. ebx = object. + + Label done; + // If the object is a smi, return the value. + __ test(ebx, Immediate(kSmiTagMask)); + __ j(zero, &done); + + // If the object is not a value type, return the value. + __ CmpObjectType(ebx, JS_VALUE_TYPE, ecx); + __ j(not_equal, &done); + + // Store the value. + __ mov(FieldOperand(ebx, JSValue::kValueOffset), eax); + // Update the write barrier. Save the value as it will be + // overwritten by the write barrier code and is needed afterward. + __ mov(edx, eax); + __ RecordWrite(ebx, JSValue::kValueOffset, edx, ecx); + + __ bind(&done); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + + // Load the argument on the stack and call the stub. + VisitForValue(args->at(0), kStack); + + NumberToStringStub stub; + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label slow_case, done; + // Fast case of Heap::LookupSingleCharacterStringFromCode. + ASSERT(kSmiTag == 0); + ASSERT(kSmiShiftSize == 0); + ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1)); + __ test(eax, + Immediate(kSmiTagMask | + ((~String::kMaxAsciiCharCode) << kSmiTagSize))); + __ j(not_zero, &slow_case); + __ Set(ebx, Immediate(Factory::single_character_string_cache())); + ASSERT(kSmiTag == 0); + ASSERT(kSmiTagSize == 1); + ASSERT(kSmiShiftSize == 0); + // At this point code register contains smi tagged ascii char code. + __ mov(ebx, FieldOperand(ebx, + eax, times_half_pointer_size, + FixedArray::kHeaderSize)); + __ cmp(ebx, Factory::undefined_value()); + __ j(equal, &slow_case); + __ mov(eax, ebx); + __ jmp(&done); + + __ bind(&slow_case); + __ push(eax); + __ CallRuntime(Runtime::kCharFromCode, 1); + + __ bind(&done); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) { + // TODO(fsc): Port the complete implementation from the classic back-end. + // Move the undefined value into the result register, which will + // trigger the slow case. + __ Set(eax, Immediate(Factory::undefined_value())); + Apply(context_, eax); +} + +void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + + StringAddStub stub(NO_STRING_ADD_FLAGS); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + + StringCompareStub stub; + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::SIN); + ASSERT(args->length() == 1); + VisitForValue(args->at(0), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::COS); + ASSERT(args->length() == 1); + VisitForValue(args->at(0), kStack); + __ CallStub(&stub); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { + // Load the argument on the stack and call the runtime function. + ASSERT(args->length() == 1); + VisitForValue(args->at(0), kStack); + __ CallRuntime(Runtime::kMath_sqrt, 1); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { + ASSERT(args->length() >= 2); + + int arg_count = args->length() - 2; // For receiver and function. + VisitForValue(args->at(0), kStack); // Receiver. + for (int i = 0; i < arg_count; i++) { + VisitForValue(args->at(i + 1), kStack); + } + VisitForValue(args->at(arg_count + 1), kAccumulator); // Function. + + // InvokeFunction requires function in edi. Move it in there. + if (!result_register().is(edi)) __ mov(edi, result_register()); + ParameterCount count(arg_count); + __ InvokeFunction(edi, count, CALL_FUNCTION); + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { + ASSERT(args->length() == 3); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallRuntime(Runtime::kRegExpConstructResult, 3); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { + ASSERT(args->length() == 3); + VisitForValue(args->at(0), kStack); + VisitForValue(args->at(1), kStack); + VisitForValue(args->at(2), kStack); + __ CallRuntime(Runtime::kSwapElements, 3); + Apply(context_, eax); +} + + +void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + ASSERT_NE(NULL, args->at(0)->AsLiteral()); + int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); + + Handle<FixedArray> jsfunction_result_caches( + Top::global_context()->jsfunction_result_caches()); + if (jsfunction_result_caches->length() <= cache_id) { + __ Abort("Attempt to use undefined cache."); + __ mov(eax, Factory::undefined_value()); + Apply(context_, eax); + return; + } + + VisitForValue(args->at(1), kAccumulator); + + Register key = eax; + Register cache = ebx; + Register tmp = ecx; + __ mov(cache, CodeGenerator::ContextOperand(esi, Context::GLOBAL_INDEX)); + __ mov(cache, + FieldOperand(cache, GlobalObject::kGlobalContextOffset)); + __ mov(cache, + CodeGenerator::ContextOperand( + cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); + __ mov(cache, + FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); + + Label done, not_found; + // tmp now holds finger offset as a smi. + ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset)); + __ cmp(key, CodeGenerator::FixedArrayElementOperand(cache, tmp)); + __ j(not_equal, ¬_found); + + __ mov(eax, CodeGenerator::FixedArrayElementOperand(cache, tmp, 1)); + __ jmp(&done); + + __ bind(¬_found); + // Call runtime to perform the lookup. + __ push(cache); + __ push(key); + __ CallRuntime(Runtime::kGetFromCache, 2); + + __ bind(&done); + Apply(context_, eax); +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { + Handle<String> name = expr->name(); + if (name->length() > 0 && name->Get(0) == '_') { + Comment cmnt(masm_, "[ InlineRuntimeCall"); + EmitInlineRuntimeCall(expr); + return; + } + Comment cmnt(masm_, "[ CallRuntime"); ZoneList<Expression*>* args = expr->arguments(); @@ -1481,6 +2661,46 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { + case Token::DELETE: { + Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); + Property* prop = expr->expression()->AsProperty(); + Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); + if (prop == NULL && var == NULL) { + // Result of deleting non-property, non-variable reference is true. + // The subexpression may have side effects. + VisitForEffect(expr->expression()); + Apply(context_, true); + } else if (var != NULL && + !var->is_global() && + var->slot() != NULL && + var->slot()->type() != Slot::LOOKUP) { + // Result of deleting non-global, non-dynamic variables is false. + // The subexpression does not have side effects. + Apply(context_, false); + } else { + // Property or variable reference. Call the delete builtin with + // object and property name as arguments. + if (prop != NULL) { + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kStack); + } else if (var->is_global()) { + __ push(CodeGenerator::GlobalObject()); + __ push(Immediate(var->name())); + } else { + // Non-global variable. Call the runtime to look up the context + // where the variable was introduced. + __ push(context_register()); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kLookupContext, 2); + __ push(eax); + __ push(Immediate(var->name())); + } + __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); + Apply(context_, eax); + } + break; + } + case Token::VOID: { Comment cmnt(masm_, "[ UnaryOperation (VOID)"); VisitForEffect(expr->expression()); @@ -1521,33 +2741,15 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { case Token::NOT: { Comment cmnt(masm_, "[ UnaryOperation (NOT)"); - Label materialize_true, materialize_false, done; - // Initially assume a pure test context. Notice that the labels are - // swapped. - Label* if_true = false_label_; - Label* if_false = true_label_; - switch (context_) { - case Expression::kUninitialized: - UNREACHABLE(); - break; - case Expression::kEffect: - if_true = &done; - if_false = &done; - break; - case Expression::kValue: - if_true = &materialize_false; - if_false = &materialize_true; - break; - case Expression::kTest: - break; - case Expression::kValueTest: - if_false = &materialize_true; - break; - case Expression::kTestValue: - if_true = &materialize_false; - break; - } + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + + // Notice that the labels are swapped. + PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true); + VisitForControl(expr->expression(), if_true, if_false); + Apply(context_, if_false, if_true); // Labels swapped. break; } @@ -1643,6 +2845,12 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { Comment cmnt(masm_, "[ CountOperation"); + // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' + // as the left-hand side. + if (!expr->expression()->IsValidLeftHandSide()) { + VisitForEffect(expr->expression()); + return; + } // Expression can only be a property, a global or a (parameter or local) // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. @@ -1664,7 +2872,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), Expression::kValue); location_ = saved_location; - } else { + } else { // Reserve space for result of postfix operation. if (expr->is_postfix() && context_ != Expression::kEffect) { __ push(Immediate(Smi::FromInt(0))); @@ -1754,7 +2962,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { switch (assign_type) { case VARIABLE: if (expr->is_postfix()) { + // Perform the assignment as if via '='. EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, Expression::kEffect); // For all contexts except kEffect: We have the result on // top of the stack. @@ -1762,7 +2972,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { ApplyTOS(context_); } } else { + // Perform the assignment as if via '='. EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), + Token::ASSIGN, context_); } break; @@ -1840,36 +3052,41 @@ void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { } +void FullCodeGenerator::EmitNullCompare(bool strict, + Register obj, + Register null_const, + Label* if_true, + Label* if_false, + Register scratch) { + __ cmp(obj, Operand(null_const)); + if (strict) { + __ j(equal, if_true); + } else { + __ j(equal, if_true); + __ cmp(obj, Factory::undefined_value()); + __ j(equal, if_true); + __ test(obj, Immediate(kSmiTagMask)); + __ j(zero, if_false); + // It can be an undetectable object. + __ mov(scratch, FieldOperand(obj, HeapObject::kMapOffset)); + __ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset)); + __ test(scratch, Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, if_true); + } + __ jmp(if_false); +} + + void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. - Label materialize_true, materialize_false, done; - // Initially assume we are in a test context. - Label* if_true = true_label_; - Label* if_false = false_label_; - switch (context_) { - case Expression::kUninitialized: - UNREACHABLE(); - break; - case Expression::kEffect: - if_true = &done; - if_false = &done; - break; - case Expression::kValue: - if_true = &materialize_true; - if_false = &materialize_false; - break; - case Expression::kTest: - break; - case Expression::kValueTest: - if_true = &materialize_true; - break; - case Expression::kTestValue: - if_false = &materialize_false; - break; - } + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); VisitForValue(expr->left(), kStack); switch (expr->op()) { @@ -1899,10 +3116,24 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::EQ_STRICT: strict = true; // Fall through - case Token::EQ: + case Token::EQ: { cc = equal; __ pop(edx); + // If either operand is constant null we do a fast compare + // against null. + Literal* right_literal = expr->right()->AsLiteral(); + Literal* left_literal = expr->left()->AsLiteral(); + if (right_literal != NULL && right_literal->handle()->IsNull()) { + EmitNullCompare(strict, edx, eax, if_true, if_false, ecx); + Apply(context_, if_true, if_false); + return; + } else if (left_literal != NULL && left_literal->handle()->IsNull()) { + EmitNullCompare(strict, eax, edx, if_true, if_false, ecx); + Apply(context_, if_true, if_false); + return; + } break; + } case Token::LT: cc = less; __ pop(edx); @@ -2012,3 +3243,5 @@ void FullCodeGenerator::ExitFinallyBlock() { #undef __ } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index bc7a33c6cc..644d20072e 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" #include "ic-inl.h" #include "runtime.h" @@ -868,7 +870,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // ecx: key (a smi) // edx: receiver // edi: FixedArray receiver->elements - __ mov(FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize), eax); + __ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax); // Update write barrier for the elements array address. __ mov(edx, Operand(eax)); __ RecordWrite(edi, 0, edx, ecx); @@ -1643,3 +1645,5 @@ void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/jump-target-ia32.cc b/deps/v8/src/ia32/jump-target-ia32.cc index cba6508031..76c0d02d4f 100644 --- a/deps/v8/src/ia32/jump-target-ia32.cc +++ b/deps/v8/src/ia32/jump-target-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" #include "jump-target-inl.h" #include "register-allocator-inl.h" @@ -431,3 +433,5 @@ void BreakTarget::Bind(Result* arg) { } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index a7d2834520..ba2fe2dd4e 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "bootstrapper.h" #include "codegen-inl.h" #include "debug.h" @@ -1706,3 +1708,5 @@ CodePatcher::~CodePatcher() { } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc index fdf3b9febb..b0de82752b 100644 --- a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc @@ -26,6 +26,9 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" + +#if defined(V8_TARGET_ARCH_IA32) + #include "unicode.h" #include "log.h" #include "ast.h" @@ -51,7 +54,7 @@ namespace internal { * - esp : points to tip of C stack. * - ecx : points to tip of backtrack stack * - * The registers eax, ebx and ecx are free to use for computations. + * The registers eax and ebx are free to use for computations. * * Each call to a public method should retain this convention. * The stack will have the following structure: @@ -72,8 +75,6 @@ namespace internal { * - backup of caller ebx * - Offset of location before start of input (effectively character * position -1). Used to initialize capture registers to a non-position. - * - Boolean at start (if 1, we are starting at the start of the string, - * otherwise 0) * - register 0 ebp[-4] (Only positions must be stored in the first * - register 1 ebp[-8] num_saved_registers_ registers) * - ... @@ -178,8 +179,8 @@ void RegExpMacroAssemblerIA32::CheckCharacterGT(uc16 limit, Label* on_greater) { void RegExpMacroAssemblerIA32::CheckAtStart(Label* on_at_start) { Label not_at_start; // Did we start the match at the start of the string at all? - __ cmp(Operand(ebp, kAtStart), Immediate(0)); - BranchOrBacktrack(equal, ¬_at_start); + __ cmp(Operand(ebp, kStartIndex), Immediate(0)); + BranchOrBacktrack(not_equal, ¬_at_start); // If we did, are we still at the start of the input? __ lea(eax, Operand(esi, edi, times_1, 0)); __ cmp(eax, Operand(ebp, kInputStart)); @@ -190,8 +191,8 @@ void RegExpMacroAssemblerIA32::CheckAtStart(Label* on_at_start) { void RegExpMacroAssemblerIA32::CheckNotAtStart(Label* on_not_at_start) { // Did we start the match at the start of the string at all? - __ cmp(Operand(ebp, kAtStart), Immediate(0)); - BranchOrBacktrack(equal, on_not_at_start); + __ cmp(Operand(ebp, kStartIndex), Immediate(0)); + BranchOrBacktrack(not_equal, on_not_at_start); // If we did, are we still at the start of the input? __ lea(eax, Operand(esi, edi, times_1, 0)); __ cmp(eax, Operand(ebp, kInputStart)); @@ -209,6 +210,15 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str, int cp_offset, Label* on_failure, bool check_end_of_string) { +#ifdef DEBUG + // If input is ASCII, don't even bother calling here if the string to + // match contains a non-ascii character. + if (mode_ == ASCII) { + for (int i = 0; i < str.length(); i++) { + ASSERT(str[i] <= String::kMaxAsciiCharCodeU); + } + } +#endif int byte_length = str.length() * char_size(); int byte_offset = cp_offset * char_size(); if (check_end_of_string) { @@ -222,14 +232,56 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str, on_failure = &backtrack_label_; } - for (int i = 0; i < str.length(); i++) { + // Do one character test first to minimize loading for the case that + // we don't match at all (loading more than one character introduces that + // chance of reading unaligned and reading across cache boundaries). + // If the first character matches, expect a larger chance of matching the + // string, and start loading more characters at a time. + if (mode_ == ASCII) { + __ cmpb(Operand(esi, edi, times_1, byte_offset), + static_cast<int8_t>(str[0])); + } else { + // Don't use 16-bit immediate. The size changing prefix throws off + // pre-decoding. + __ movzx_w(eax, + Operand(esi, edi, times_1, byte_offset)); + __ cmp(eax, static_cast<int32_t>(str[0])); + } + BranchOrBacktrack(not_equal, on_failure); + + __ lea(ebx, Operand(esi, edi, times_1, 0)); + for (int i = 1, n = str.length(); i < n;) { if (mode_ == ASCII) { - __ cmpb(Operand(esi, edi, times_1, byte_offset + i), - static_cast<int8_t>(str[i])); + if (i <= n - 4) { + int combined_chars = + (static_cast<uint32_t>(str[i + 0]) << 0) | + (static_cast<uint32_t>(str[i + 1]) << 8) | + (static_cast<uint32_t>(str[i + 2]) << 16) | + (static_cast<uint32_t>(str[i + 3]) << 24); + __ cmp(Operand(ebx, byte_offset + i), Immediate(combined_chars)); + i += 4; + } else { + __ cmpb(Operand(ebx, byte_offset + i), + static_cast<int8_t>(str[i])); + i += 1; + } } else { ASSERT(mode_ == UC16); - __ cmpw(Operand(esi, edi, times_1, byte_offset + i * sizeof(uc16)), - Immediate(str[i])); + if (i <= n - 2) { + __ cmp(Operand(ebx, byte_offset + i * sizeof(uc16)), + Immediate(*reinterpret_cast<const int*>(&str[i]))); + i += 2; + } else { + // Avoid a 16-bit immediate operation. It uses the length-changing + // 0x66 prefix which causes pre-decoder misprediction and pipeline + // stalls. See + // "Intel(R) 64 and IA-32 Architectures Optimization Reference Manual" + // (248966.pdf) section 3.4.2.3 "Length-Changing Prefixes (LCP)" + __ movzx_w(eax, + Operand(ebx, byte_offset + i * sizeof(uc16))); + __ cmp(eax, static_cast<int32_t>(str[i])); + i += 1; + } } BranchOrBacktrack(not_equal, on_failure); } @@ -625,7 +677,6 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { __ push(edi); __ push(ebx); // Callee-save on MacOS. __ push(Immediate(0)); // Make room for "input start - 1" constant. - __ push(Immediate(0)); // Make room for "at start" constant. // Check if we have space on the stack for registers. Label stack_limit_hit; @@ -677,14 +728,6 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { // position registers. __ mov(Operand(ebp, kInputStartMinusOne), eax); - // Determine whether the start index is zero, that is at the start of the - // string, and store that value in a local variable. - __ xor_(Operand(ecx), ecx); // setcc only operates on cl (lower byte of ecx). - // Register ebx still holds -stringIndex. - __ test(ebx, Operand(ebx)); - __ setcc(zero, ecx); // 1 if 0 (start of string), 0 if positive. - __ mov(Operand(ebp, kAtStart), ecx); - if (num_saved_registers_ > 0) { // Always is, if generated from a regexp. // Fill saved registers with initial value = start offset - 1 // Fill in stack push order, to avoid accessing across an unwritten @@ -712,8 +755,8 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { __ mov(backtrack_stackpointer(), Operand(ebp, kStackHighEnd)); // Load previous char as initial value of current-character. Label at_start; - __ cmp(Operand(ebp, kAtStart), Immediate(0)); - __ j(not_equal, &at_start); + __ cmp(Operand(ebp, kStartIndex), Immediate(0)); + __ j(equal, &at_start); LoadCurrentCharacterUnchecked(-1, 1); // Load previous char. __ jmp(&start_label_); __ bind(&at_start); @@ -1201,3 +1244,5 @@ void RegExpMacroAssemblerIA32::LoadCurrentCharacterUnchecked(int cp_offset, #endif // V8_INTERPRETED_REGEXP }} // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/regexp-macro-assembler-ia32.h b/deps/v8/src/ia32/regexp-macro-assembler-ia32.h index 823bc03312..8b8eeed6ce 100644 --- a/deps/v8/src/ia32/regexp-macro-assembler-ia32.h +++ b/deps/v8/src/ia32/regexp-macro-assembler-ia32.h @@ -132,9 +132,8 @@ class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler { static const int kBackup_edi = kBackup_esi - kPointerSize; static const int kBackup_ebx = kBackup_edi - kPointerSize; static const int kInputStartMinusOne = kBackup_ebx - kPointerSize; - static const int kAtStart = kInputStartMinusOne - kPointerSize; // First register address. Following registers are below it on the stack. - static const int kRegisterZero = kAtStart - kPointerSize; + static const int kRegisterZero = kInputStartMinusOne - kPointerSize; // Initial size of code buffer. static const size_t kRegExpCodeSize = 1024; diff --git a/deps/v8/src/ia32/register-allocator-ia32.cc b/deps/v8/src/ia32/register-allocator-ia32.cc index 73fefb3bbf..d840c0cc5c 100644 --- a/deps/v8/src/ia32/register-allocator-ia32.cc +++ b/deps/v8/src/ia32/register-allocator-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" #include "register-allocator-inl.h" #include "virtual-frame-inl.h" @@ -151,3 +153,5 @@ Result RegisterAllocator::AllocateByteRegisterWithoutSpilling() { } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index 189c0e4d16..d7b05cf40b 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "ic-inl.h" #include "codegen-inl.h" #include "stub-cache.h" @@ -2387,3 +2389,5 @@ Object* ConstructStubCompiler::CompileConstructStub( #undef __ } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/virtual-frame-ia32.cc b/deps/v8/src/ia32/virtual-frame-ia32.cc index 10aaa52b83..e22df6ec28 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.cc +++ b/deps/v8/src/ia32/virtual-frame-ia32.cc @@ -27,6 +27,8 @@ #include "v8.h" +#if defined(V8_TARGET_ARCH_IA32) + #include "codegen-inl.h" #include "register-allocator-inl.h" #include "scopes.h" @@ -1310,3 +1312,5 @@ void VirtualFrame::Push(Expression* expr) { #undef __ } } // namespace v8::internal + +#endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/virtual-frame-ia32.h b/deps/v8/src/ia32/virtual-frame-ia32.h index 14fe4662dc..a8f23b0cc8 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.h +++ b/deps/v8/src/ia32/virtual-frame-ia32.h @@ -28,9 +28,10 @@ #ifndef V8_IA32_VIRTUAL_FRAME_IA32_H_ #define V8_IA32_VIRTUAL_FRAME_IA32_H_ -#include "type-info.h" +#include "codegen.h" #include "register-allocator.h" #include "scopes.h" +#include "type-info.h" namespace v8 { namespace internal { @@ -97,23 +98,16 @@ class VirtualFrame: public ZoneObject { return register_locations_[num]; } - int register_location(Register reg) { - return register_locations_[RegisterAllocator::ToNumber(reg)]; - } + inline int register_location(Register reg); - void set_register_location(Register reg, int index) { - register_locations_[RegisterAllocator::ToNumber(reg)] = index; - } + inline void set_register_location(Register reg, int index); bool is_used(int num) { ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); return register_locations_[num] != kIllegalIndex; } - bool is_used(Register reg) { - return register_locations_[RegisterAllocator::ToNumber(reg)] - != kIllegalIndex; - } + inline bool is_used(Register reg); // Add extra in-memory elements to the top of the frame to match an actual // frame (eg, the frame after an exception handler is pushed). No code is @@ -150,6 +144,9 @@ class VirtualFrame: public ZoneObject { // (ie, they all have frame-external references). Register SpillAnyRegister(); + // Spill the top element of the frame. + void SpillTop() { SpillElementAt(element_count() - 1); } + // Sync the range of elements in [begin, end] with memory. void SyncRange(int begin, int end); @@ -217,10 +214,7 @@ class VirtualFrame: public ZoneObject { void SetElementAt(int index, Result* value); // Set a frame element to a constant. The index is frame-top relative. - void SetElementAt(int index, Handle<Object> value) { - Result temp(value); - SetElementAt(index, &temp); - } + inline void SetElementAt(int index, Handle<Object> value); void PushElementAt(int index) { PushFrameSlotAt(element_count() - index - 1); @@ -315,10 +309,7 @@ class VirtualFrame: public ZoneObject { // Call stub given the number of arguments it expects on (and // removes from) the stack. - Result CallStub(CodeStub* stub, int arg_count) { - PrepareForCall(arg_count, arg_count); - return RawCallStub(stub); - } + inline Result CallStub(CodeStub* stub, int arg_count); // Call stub that takes a single argument passed in eax. The // argument is given as a result which does not have to be eax or @@ -361,7 +352,7 @@ class VirtualFrame: public ZoneObject { Result CallStoreIC(Handle<String> name, bool is_contextual); // Call keyed store IC. Value, key, and receiver are found on top - // of the frame. Key and receiver are not dropped. + // of the frame. All three are dropped. Result CallKeyedStoreIC(); // Call call IC. Function name, arguments, and receiver are found on top @@ -473,12 +464,9 @@ class VirtualFrame: public ZoneObject { int register_locations_[RegisterAllocator::kNumRegisters]; // The number of frame-allocated locals and parameters respectively. - int parameter_count() { - return cgen()->scope()->num_parameters(); - } - int local_count() { - return cgen()->scope()->num_stack_slots(); - } + inline int parameter_count(); + + inline int local_count(); // The index of the element that is at the processor's frame pointer // (the ebp register). The parameters, receiver, and return address |