diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 95c113376e..a8c6186e47 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,15 @@ +2010-06-28: Version 2.2.20 + Fix bug with for-in on x64 platform (issue 748). + + Fix crash bug on x64 platform (issue 756). + + Fix bug in Object.getOwnPropertyNames. (chromium issue 41243). + + Fix a bug on ARM that caused the result of 1 << x to be + miscalculated for some inputs. + + Performance improvements on all platforms. + 2010-06-23: Version 2.2.19 Fix bug that causes the build to break when profillingsupport=off diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 464ca54d2d..0f64dd45ec 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -2606,6 +2606,8 @@ void v8::Object::SetIndexedPropertiesToPixelData(uint8_t* data, int length) { return; } i::Handle pixels = i::Factory::NewPixelArray(length, data); + self->set_map( + *i::Factory::GetSlowElementsMap(i::Handle(self->map()))); self->set_elements(*pixels); } @@ -2659,6 +2661,8 @@ void v8::Object::SetIndexedPropertiesToExternalArrayData( } i::Handle array = i::Factory::NewExternalArray(length, array_type, data); + self->set_map( + *i::Factory::GetSlowElementsMap(i::Handle(self->map()))); self->set_elements(*array); } diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index 869227a7a8..54b584ae46 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -1110,6 +1110,7 @@ class Assembler : public Malloced { void EndBlockConstPool() { const_pool_blocked_nesting_--; } + bool is_const_pool_blocked() const { return const_pool_blocked_nesting_ > 0; } private: // Code buffer: diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 8e87614c96..b6639ae670 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -157,6 +157,7 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm) state_(NULL), loop_nesting_(0), type_info_(NULL), + function_return_(JumpTarget::BIDIRECTIONAL), function_return_is_shadowed_(false) { } @@ -218,7 +219,7 @@ void CodeGenerator::Generate(CompilationInfo* info) { // for stack overflow. frame_->AllocateStackSlots(); - VirtualFrame::SpilledScope spilled_scope(frame_); + frame_->AssertIsSpilled(); int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; if (heap_slots > 0) { // Allocate local context. @@ -257,6 +258,7 @@ void CodeGenerator::Generate(CompilationInfo* info) { // order: such a parameter is copied repeatedly into the same // context location and thus the last value is what is seen inside // the function. + frame_->AssertIsSpilled(); for (int i = 0; i < scope()->num_parameters(); i++) { Variable* par = scope()->parameter(i); Slot* slot = par->slot(); @@ -282,8 +284,7 @@ void CodeGenerator::Generate(CompilationInfo* info) { // Initialize ThisFunction reference if present. if (scope()->is_function_scope() && scope()->function() != NULL) { - __ mov(ip, Operand(Factory::the_hole_value())); - frame_->EmitPush(ip); + frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex); StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT); } } else { @@ -510,7 +511,6 @@ void CodeGenerator::LoadCondition(Expression* x, has_valid_frame() && !has_cc() && frame_->height() == original_height) { - frame_->SpillAll(); true_target->Jump(); } } @@ -535,22 +535,18 @@ void CodeGenerator::Load(Expression* expr) { if (has_cc()) { // Convert cc_reg_ into a boolean value. - VirtualFrame::SpilledScope scope(frame_); JumpTarget loaded; JumpTarget materialize_true; materialize_true.Branch(cc_reg_); - __ LoadRoot(r0, Heap::kFalseValueRootIndex); - frame_->EmitPush(r0); + frame_->EmitPushRoot(Heap::kFalseValueRootIndex); loaded.Jump(); materialize_true.Bind(); - __ LoadRoot(r0, Heap::kTrueValueRootIndex); - frame_->EmitPush(r0); + frame_->EmitPushRoot(Heap::kTrueValueRootIndex); loaded.Bind(); cc_reg_ = al; } if (true_target.is_linked() || false_target.is_linked()) { - VirtualFrame::SpilledScope scope(frame_); // We have at least one condition value that has been "translated" // into a branch, thus it needs to be loaded explicitly. JumpTarget loaded; @@ -561,8 +557,7 @@ void CodeGenerator::Load(Expression* expr) { // Load "true" if necessary. if (true_target.is_linked()) { true_target.Bind(); - __ LoadRoot(r0, Heap::kTrueValueRootIndex); - frame_->EmitPush(r0); + frame_->EmitPushRoot(Heap::kTrueValueRootIndex); } // If both "true" and "false" need to be loaded jump across the code for // "false". @@ -572,8 +567,7 @@ void CodeGenerator::Load(Expression* expr) { // Load "false" if necessary. if (false_target.is_linked()) { false_target.Bind(); - __ LoadRoot(r0, Heap::kFalseValueRootIndex); - frame_->EmitPush(r0); + frame_->EmitPushRoot(Heap::kFalseValueRootIndex); } // A value is loaded on all paths reaching this point. loaded.Bind(); @@ -592,11 +586,11 @@ void CodeGenerator::LoadGlobal() { void CodeGenerator::LoadGlobalReceiver(Register scratch) { - VirtualFrame::SpilledScope spilled_scope(frame_); - __ ldr(scratch, ContextOperand(cp, Context::GLOBAL_INDEX)); - __ ldr(scratch, - FieldMemOperand(scratch, GlobalObject::kGlobalReceiverOffset)); - frame_->EmitPush(scratch); + Register reg = frame_->GetTOSRegister(); + __ ldr(reg, ContextOperand(cp, Context::GLOBAL_INDEX)); + __ ldr(reg, + FieldMemOperand(reg, GlobalObject::kGlobalReceiverOffset)); + frame_->EmitPush(reg); } @@ -613,8 +607,6 @@ ArgumentsAllocationMode CodeGenerator::ArgumentsMode() { void CodeGenerator::StoreArgumentsObject(bool initial) { - VirtualFrame::SpilledScope spilled_scope(frame_); - ArgumentsAllocationMode mode = ArgumentsMode(); ASSERT(mode != NO_ARGUMENTS_ALLOCATION); @@ -623,9 +615,9 @@ void CodeGenerator::StoreArgumentsObject(bool initial) { // When using lazy arguments allocation, we store the hole value // as a sentinel indicating that the arguments object hasn't been // allocated yet. - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - frame_->EmitPush(ip); + frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex); } else { + frame_->SpillAll(); ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); __ ldr(r2, frame_->Function()); // The receiver is below the arguments, the return address, and the @@ -649,9 +641,9 @@ void CodeGenerator::StoreArgumentsObject(bool initial) { // already been written to. This can happen if the a function // has a local variable named 'arguments'. LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF); - frame_->EmitPop(r0); + Register arguments = frame_->PopToRegister(); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r0, ip); + __ cmp(arguments, ip); done.Branch(ne); } StoreToSlot(arguments->slot(), NOT_CONST_INIT); @@ -754,36 +746,35 @@ void CodeGenerator::UnloadReference(Reference* ref) { // may jump to 'false_target' in case the register converts to 'false'. void CodeGenerator::ToBoolean(JumpTarget* true_target, JumpTarget* false_target) { - VirtualFrame::SpilledScope spilled_scope(frame_); // Note: The generated code snippet does not change stack variables. // Only the condition code should be set. - frame_->EmitPop(r0); + Register tos = frame_->PopToRegister(); // Fast case checks // Check if the value is 'false'. __ LoadRoot(ip, Heap::kFalseValueRootIndex); - __ cmp(r0, ip); + __ cmp(tos, ip); false_target->Branch(eq); // Check if the value is 'true'. __ LoadRoot(ip, Heap::kTrueValueRootIndex); - __ cmp(r0, ip); + __ cmp(tos, ip); true_target->Branch(eq); // Check if the value is 'undefined'. __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ cmp(r0, ip); + __ cmp(tos, ip); false_target->Branch(eq); // Check if the value is a smi. - __ cmp(r0, Operand(Smi::FromInt(0))); + __ cmp(tos, Operand(Smi::FromInt(0))); false_target->Branch(eq); - __ tst(r0, Operand(kSmiTagMask)); + __ tst(tos, Operand(kSmiTagMask)); true_target->Branch(eq); // Slow case: call the runtime. - frame_->EmitPush(r0); + frame_->EmitPush(tos); frame_->CallRuntime(Runtime::kToBool, 1); // Convert the result (r0) to a condition code. __ LoadRoot(ip, Heap::kFalseValueRootIndex); @@ -935,7 +926,15 @@ class DeferredInlineSmiOperation: public DeferredCode { }; + +// On entry the non-constant side of the binary operation is in tos_register_ +// and the constant smi side is nowhere. The tos_register_ is not used by the +// virtual frame. On exit the answer is in the tos_register_ and the virtual +// frame is unchanged. void DeferredInlineSmiOperation::Generate() { + VirtualFrame copied_frame(*frame_state()->frame()); + copied_frame.SpillAll(); + Register lhs = r1; Register rhs = r0; switch (op_) { @@ -969,45 +968,20 @@ void DeferredInlineSmiOperation::Generate() { case Token::MOD: case Token::BIT_OR: case Token::BIT_XOR: - case Token::BIT_AND: { - if (reversed_) { - if (tos_register_.is(r0)) { - __ mov(r1, Operand(Smi::FromInt(value_))); - } else { - ASSERT(tos_register_.is(r1)); - __ mov(r0, Operand(Smi::FromInt(value_))); - lhs = r0; - rhs = r1; - } - } else { - if (tos_register_.is(r1)) { - __ mov(r0, Operand(Smi::FromInt(value_))); - } else { - ASSERT(tos_register_.is(r0)); - __ mov(r1, Operand(Smi::FromInt(value_))); - lhs = r0; - rhs = r1; - } - } - break; - } - + case Token::BIT_AND: case Token::SHL: case Token::SHR: case Token::SAR: { - if (!reversed_) { - if (tos_register_.is(r1)) { - __ mov(r0, Operand(Smi::FromInt(value_))); - } else { - ASSERT(tos_register_.is(r0)); - __ mov(r1, Operand(Smi::FromInt(value_))); - lhs = r0; - rhs = r1; - } + if (tos_register_.is(r1)) { + __ mov(r0, Operand(Smi::FromInt(value_))); } else { - ASSERT(op_ == Token::SHL); + ASSERT(tos_register_.is(r0)); __ mov(r1, Operand(Smi::FromInt(value_))); } + if (reversed_ == tos_register_.is(r1)) { + lhs = r0; + rhs = r1; + } break; } @@ -1019,11 +993,17 @@ void DeferredInlineSmiOperation::Generate() { GenericBinaryOpStub stub(op_, overwrite_mode_, lhs, rhs, value_); __ CallStub(&stub); + // The generic stub returns its value in r0, but that's not // necessarily what we want. We want whatever the inlined code // expected, which is that the answer is in the same register as // the operand was. __ Move(tos_register_, r0); + + // The tos register was not in use for the virtual frame that we + // came into this function with, so we can merge back to that frame + // without trashing it. + copied_frame.MergeTo(frame_state()->frame()); } @@ -1124,12 +1104,6 @@ void CodeGenerator::SmiOperation(Token::Value op, // We move the top of stack to a register (normally no move is invoved). Register tos = frame_->PopToRegister(); - // All other registers are spilled. The deferred code expects one argument - // in a register and all other values are flushed to the stack. The - // answer is returned in the same register that the top of stack argument was - // in. - frame_->SpillAll(); - switch (op) { case Token::ADD: { DeferredCode* deferred = @@ -1448,8 +1422,6 @@ void CodeGenerator::Comparison(Condition cc, void CodeGenerator::CallWithArguments(ZoneList* args, CallFunctionFlags flags, int position) { - frame_->AssertIsSpilled(); - // Push the arguments ("left-to-right") on the stack. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { @@ -1482,7 +1454,6 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, // stack, as receiver and arguments, and calls x. // In the implementation comments, we call x the applicand // and y the receiver. - VirtualFrame::SpilledScope spilled_scope(frame_); ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION); ASSERT(arguments->IsArguments()); @@ -1500,6 +1471,15 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, Load(receiver); LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF); + // At this point the top two stack elements are probably in registers + // since they were just loaded. Ensure they are in regs and get the + // regs. + Register receiver_reg = frame_->Peek2(); + Register arguments_reg = frame_->Peek(); + + // From now on the frame is spilled. + frame_->SpillAll(); + // Emit the source position information after having loaded the // receiver and the arguments. CodeForSourcePosition(position); @@ -1513,32 +1493,30 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, // already. If so, just use that instead of copying the arguments // from the stack. This also deals with cases where a local variable // named 'arguments' has been introduced. - __ ldr(r0, MemOperand(sp, 0)); - - Label slow, done; + JumpTarget slow; + Label done; __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(ip, r0); - __ b(ne, &slow); + __ cmp(ip, arguments_reg); + slow.Branch(ne); Label build_args; // Get rid of the arguments object probe. frame_->Drop(); // Stack now has 3 elements on it. // Contents of stack at this point: - // sp[0]: receiver + // sp[0]: receiver - in the receiver_reg register. // sp[1]: applicand.apply // sp[2]: applicand. // Check that the receiver really is a JavaScript object. - __ ldr(r0, MemOperand(sp, 0)); - __ BranchOnSmi(r0, &build_args); + __ BranchOnSmi(receiver_reg, &build_args); // We allow all JSObjects including JSFunctions. As long as // JS_FUNCTION_TYPE is the last instance type and it is right // after LAST_JS_OBJECT_TYPE, we do not have to check the upper // bound. ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); - __ CompareObjectType(r0, r1, r2, FIRST_JS_OBJECT_TYPE); + __ CompareObjectType(receiver_reg, r2, r3, FIRST_JS_OBJECT_TYPE); __ b(lt, &build_args); // Check that applicand.apply is Function.prototype.apply. @@ -1627,7 +1605,7 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, StoreArgumentsObject(false); // Stack and frame now have 4 elements. - __ bind(&slow); + slow.Bind(); // Generic computation of x.apply(y, args) with no special optimization. // Flip applicand.apply and applicand on the stack, so @@ -1652,7 +1630,6 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, void CodeGenerator::Branch(bool if_true, JumpTarget* target) { - VirtualFrame::SpilledScope spilled_scope(frame_); ASSERT(has_cc()); Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_); target->Branch(cc); @@ -1661,7 +1638,7 @@ void CodeGenerator::Branch(bool if_true, JumpTarget* target) { void CodeGenerator::CheckStack() { - VirtualFrame::SpilledScope spilled_scope(frame_); + frame_->SpillAll(); Comment cmnt(masm_, "[ check stack"); __ LoadRoot(ip, Heap::kStackLimitRootIndex); // Put the lr setup instruction in the delay slot. kInstrSize is added to @@ -1683,7 +1660,6 @@ void CodeGenerator::VisitStatements(ZoneList* statements) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); for (int i = 0; frame_ != NULL && i < statements->length(); i++) { Visit(statements->at(i)); } @@ -1695,7 +1671,6 @@ void CodeGenerator::VisitBlock(Block* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ Block"); CodeForStatementPosition(node); node->break_target()->SetExpectedHeight(); @@ -1713,7 +1688,6 @@ void CodeGenerator::DeclareGlobals(Handle pairs) { frame_->EmitPush(Operand(pairs)); frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0))); - VirtualFrame::SpilledScope spilled_scope(frame_); frame_->CallRuntime(Runtime::kDeclareGlobals, 3); // The result is discarded. } @@ -1754,7 +1728,6 @@ void CodeGenerator::VisitDeclaration(Declaration* node) { frame_->EmitPush(Operand(0)); } - VirtualFrame::SpilledScope spilled_scope(frame_); frame_->CallRuntime(Runtime::kDeclareContextSlot, 4); // Ignore the return value (declarations are statements). @@ -1899,7 +1872,6 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { void CodeGenerator::VisitContinueStatement(ContinueStatement* node) { - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ ContinueStatement"); CodeForStatementPosition(node); node->target()->continue_target()->Jump(); @@ -1907,7 +1879,6 @@ void CodeGenerator::VisitContinueStatement(ContinueStatement* node) { void CodeGenerator::VisitBreakStatement(BreakStatement* node) { - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ BreakStatement"); CodeForStatementPosition(node); node->target()->break_target()->Jump(); @@ -1915,7 +1886,7 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) { void CodeGenerator::VisitReturnStatement(ReturnStatement* node) { - VirtualFrame::SpilledScope spilled_scope(frame_); + frame_->SpillAll(); Comment cmnt(masm_, "[ ReturnStatement"); CodeForStatementPosition(node); @@ -1926,7 +1897,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) { } else { // Pop the result from the frame and prepare the frame for // returning thus making it easier to merge. - frame_->EmitPop(r0); + frame_->PopToR0(); frame_->PrepareForReturn(); if (function_return_.is_bound()) { // If the function return label is already bound we reuse the @@ -1986,7 +1957,6 @@ void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ WithEnterStatement"); CodeForStatementPosition(node); Load(node->expression()); @@ -2012,7 +1982,6 @@ void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ WithExitStatement"); CodeForStatementPosition(node); // Pop context. @@ -2027,7 +1996,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ SwitchStatement"); CodeForStatementPosition(node); node->break_target()->SetExpectedHeight(); @@ -2055,8 +2023,7 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { next_test.Bind(); next_test.Unuse(); // Duplicate TOS. - __ ldr(r0, frame_->Top()); - frame_->EmitPush(r0); + frame_->Dup(); Comparison(eq, NULL, clause->label(), true); Branch(false, &next_test); @@ -2094,7 +2061,7 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { default_entry.Bind(); VisitStatements(default_clause->statements()); // If control flow can fall out of the default and there is a case after - // it, jup to that case's body. + // it, jump to that case's body. if (frame_ != NULL && default_exit.is_bound()) { default_exit.Jump(); } @@ -2116,7 +2083,6 @@ void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ DoWhileStatement"); CodeForStatementPosition(node); node->break_target()->SetExpectedHeight(); @@ -2191,7 +2157,6 @@ void CodeGenerator::VisitWhileStatement(WhileStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ WhileStatement"); CodeForStatementPosition(node); @@ -2209,7 +2174,7 @@ void CodeGenerator::VisitWhileStatement(WhileStatement* node) { node->continue_target()->Bind(); if (info == DONT_KNOW) { - JumpTarget body; + JumpTarget body(JumpTarget::BIDIRECTIONAL); LoadCondition(node->cond(), &body, node->break_target(), true); if (has_valid_frame()) { // A NULL frame indicates that control did not fall out of the @@ -2242,7 +2207,6 @@ void CodeGenerator::VisitForStatement(ForStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ ForStatement"); CodeForStatementPosition(node); if (node->init() != NULL) { @@ -2931,7 +2895,6 @@ void CodeGenerator::VisitConditional(Conditional* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ Conditional"); JumpTarget then; JumpTarget else_; @@ -2972,10 +2935,8 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { &done); slow.Bind(); - VirtualFrame::SpilledScope spilled_scope(frame_); frame_->EmitPush(cp); - __ mov(r0, Operand(slot->var()->name())); - frame_->EmitPush(r0); + frame_->EmitPush(Operand(slot->var()->name())); if (typeof_state == INSIDE_TYPEOF) { frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); @@ -2990,16 +2951,17 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { Register scratch = VirtualFrame::scratch0(); TypeInfo info = type_info(slot); frame_->EmitPush(SlotOperand(slot, scratch), info); + if (slot->var()->mode() == Variable::CONST) { // Const slots may contain 'the hole' value (the constant hasn't been // initialized yet) which needs to be converted into the 'undefined' // value. Comment cmnt(masm_, "[ Unhole const"); - frame_->EmitPop(scratch); + Register tos = frame_->PopToRegister(); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(scratch, ip); - __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex, eq); - frame_->EmitPush(scratch); + __ cmp(tos, ip); + __ LoadRoot(tos, Heap::kUndefinedValueRootIndex, eq); + frame_->EmitPush(tos); } } } @@ -3007,6 +2969,7 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot, TypeofState state) { + VirtualFrame::RegisterAllocationScope scope(this); LoadFromSlot(slot, state); // Bail out quickly if we're not using lazy arguments allocation. @@ -3015,17 +2978,15 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot, // ... or if the slot isn't a non-parameter arguments slot. if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return; - VirtualFrame::SpilledScope spilled_scope(frame_); - - // Load the loaded value from the stack into r0 but leave it on the + // Load the loaded value from the stack into a register but leave it on the // stack. - __ ldr(r0, MemOperand(sp, 0)); + Register tos = frame_->Peek(); // If the loaded value is the sentinel that indicates that we // haven't loaded the arguments object yet, we need to do it now. JumpTarget exit; __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r0, ip); + __ cmp(tos, ip); exit.Branch(ne); frame_->Drop(); StoreArgumentsObject(false); @@ -3035,14 +2996,13 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot, void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { ASSERT(slot != NULL); + VirtualFrame::RegisterAllocationScope scope(this); if (slot->type() == Slot::LOOKUP) { - VirtualFrame::SpilledScope spilled_scope(frame_); ASSERT(slot->var()->is_dynamic()); // For now, just do a runtime call. frame_->EmitPush(cp); - __ mov(r0, Operand(slot->var()->name())); - frame_->EmitPush(r0); + frame_->EmitPush(Operand(slot->var()->name())); if (init_state == CONST_INIT) { // Same as the case for a normal store, but ignores attribute @@ -3071,7 +3031,7 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { } else { ASSERT(!slot->var()->is_dynamic()); Register scratch = VirtualFrame::scratch0(); - VirtualFrame::RegisterAllocationScope scope(this); + Register scratch2 = VirtualFrame::scratch1(); // The frame must be spilled when branching to this target. JumpTarget exit; @@ -3085,7 +3045,6 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { __ ldr(scratch, SlotOperand(slot, scratch)); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ cmp(scratch, ip); - frame_->SpillAll(); exit.Branch(ne); } @@ -3104,18 +3063,18 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { // Skip write barrier if the written value is a smi. __ tst(tos, Operand(kSmiTagMask)); // We don't use tos any more after here. - VirtualFrame::SpilledScope spilled_scope(frame_); exit.Branch(eq); // scratch is loaded with context when calling SlotOperand above. int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; - // r1 could be identical with tos, but that doesn't matter. - __ RecordWrite(scratch, Operand(offset), r3, r1); + // We need an extra register. Until we have a way to do that in the + // virtual frame we will cheat and ask for a free TOS register. + Register scratch3 = frame_->GetTOSRegister(); + __ RecordWrite(scratch, Operand(offset), scratch2, scratch3); } // If we definitely did not jump over the assignment, we do not need // to bind the exit label. Doing so can defeat peephole // optimization. if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) { - frame_->SpillAll(); exit.Bind(); } } @@ -3289,42 +3248,51 @@ void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ RexExp Literal"); + Register tmp = VirtualFrame::scratch0(); + // Free up a TOS register that can be used to push the literal. + Register literal = frame_->GetTOSRegister(); + // Retrieve the literal array and check the allocated entry. // Load the function of this activation. - __ ldr(r1, frame_->Function()); + __ ldr(tmp, frame_->Function()); // Load the literals array of the function. - __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset)); + __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kLiteralsOffset)); // Load the literal at the ast saved index. int literal_offset = FixedArray::kHeaderSize + node->literal_index() * kPointerSize; - __ ldr(r2, FieldMemOperand(r1, literal_offset)); + __ ldr(literal, FieldMemOperand(tmp, literal_offset)); JumpTarget done; __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ cmp(r2, ip); + __ cmp(literal, ip); + // This branch locks the virtual frame at the done label to match the + // one we have here, where the literal register is not on the stack and + // nothing is spilled. done.Branch(ne); - // If the entry is undefined we call the runtime system to computed + // If the entry is undefined we call the runtime system to compute // the literal. - frame_->EmitPush(r1); // literal array (0) - __ mov(r0, Operand(Smi::FromInt(node->literal_index()))); - frame_->EmitPush(r0); // literal index (1) - __ mov(r0, Operand(node->pattern())); // RegExp pattern (2) - frame_->EmitPush(r0); - __ mov(r0, Operand(node->flags())); // RegExp flags (3) - frame_->EmitPush(r0); + // literal array (0) + frame_->EmitPush(tmp); + // literal index (1) + frame_->EmitPush(Operand(Smi::FromInt(node->literal_index()))); + // RegExp pattern (2) + frame_->EmitPush(Operand(node->pattern())); + // RegExp flags (3) + frame_->EmitPush(Operand(node->flags())); frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); - __ mov(r2, Operand(r0)); + __ Move(literal, r0); + // This call to bind will get us back to the virtual frame we had before + // where things are not spilled and the literal register is not on the stack. done.Bind(); // Push the literal. - frame_->EmitPush(r2); + frame_->EmitPush(literal); ASSERT_EQ(original_height + 1, frame_->height()); } @@ -3333,20 +3301,20 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ ObjectLiteral"); + Register literal = frame_->GetTOSRegister(); // Load the function of this activation. - __ ldr(r3, frame_->Function()); + __ ldr(literal, frame_->Function()); // Literal array. - __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); + __ ldr(literal, FieldMemOperand(literal, JSFunction::kLiteralsOffset)); + frame_->EmitPush(literal); // Literal index. - __ mov(r2, Operand(Smi::FromInt(node->literal_index()))); + frame_->EmitPush(Operand(Smi::FromInt(node->literal_index()))); // Constant properties. - __ mov(r1, Operand(node->constant_properties())); + frame_->EmitPush(Operand(node->constant_properties())); // Should the object literal have fast elements? - __ mov(r0, Operand(Smi::FromInt(node->fast_elements() ? 1 : 0))); - frame_->EmitPushMultiple(4, r3.bit() | r2.bit() | r1.bit() | r0.bit()); + frame_->EmitPush(Operand(Smi::FromInt(node->fast_elements() ? 1 : 0))); if (node->depth() > 1) { frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4); } else { @@ -3369,37 +3337,33 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { if (key->handle()->IsSymbol()) { Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); Load(value); - frame_->EmitPop(r0); + frame_->PopToR0(); + // Fetch the object literal. + frame_->SpillAllButCopyTOSToR1(); __ mov(r2, Operand(key->handle())); - __ ldr(r1, frame_->Top()); // Load the receiver. frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); break; } // else fall through case ObjectLiteral::Property::PROTOTYPE: { - __ ldr(r0, frame_->Top()); - frame_->EmitPush(r0); // dup the result + frame_->Dup(); Load(key); Load(value); frame_->CallRuntime(Runtime::kSetProperty, 3); break; } case ObjectLiteral::Property::SETTER: { - __ ldr(r0, frame_->Top()); - frame_->EmitPush(r0); + frame_->Dup(); Load(key); - __ mov(r0, Operand(Smi::FromInt(1))); - frame_->EmitPush(r0); + frame_->EmitPush(Operand(Smi::FromInt(1))); Load(value); frame_->CallRuntime(Runtime::kDefineAccessor, 4); break; } case ObjectLiteral::Property::GETTER: { - __ ldr(r0, frame_->Top()); - frame_->EmitPush(r0); + frame_->Dup(); Load(key); - __ mov(r0, Operand(Smi::FromInt(0))); - frame_->EmitPush(r0); + frame_->EmitPush(Operand(Smi::FromInt(0))); Load(value); frame_->CallRuntime(Runtime::kDefineAccessor, 4); break; @@ -3414,16 +3378,16 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ ArrayLiteral"); + Register tos = frame_->GetTOSRegister(); // Load the function of this activation. - __ ldr(r2, frame_->Function()); + __ ldr(tos, frame_->Function()); // Load the literals array of the function. - __ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset)); - __ mov(r1, Operand(Smi::FromInt(node->literal_index()))); - __ mov(r0, Operand(node->constant_elements())); - frame_->EmitPushMultiple(3, r2.bit() | r1.bit() | r0.bit()); + __ ldr(tos, FieldMemOperand(tos, JSFunction::kLiteralsOffset)); + frame_->EmitPush(tos); + frame_->EmitPush(Operand(Smi::FromInt(node->literal_index()))); + frame_->EmitPush(Operand(node->constant_elements())); int length = node->values()->length(); if (node->depth() > 1) { frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3); @@ -3450,10 +3414,10 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { // The property must be set by generated code. Load(value); - frame_->EmitPop(r0); - + frame_->PopToR0(); // Fetch the object literal. - __ ldr(r1, frame_->Top()); + frame_->SpillAllButCopyTOSToR1(); + // Get the elements array. __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); @@ -3863,7 +3827,6 @@ void CodeGenerator::VisitCall(Call* node) { // ------------------------------------------------------------------------ if (var != NULL && var->is_possibly_eval()) { - VirtualFrame::SpilledScope spilled_scope(frame_); // ---------------------------------- // JavaScript example: 'eval(arg)' // eval is not known to be shadowed // ---------------------------------- @@ -3877,8 +3840,7 @@ void CodeGenerator::VisitCall(Call* node) { Load(function); // Allocate a frame slot for the receiver. - __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - frame_->EmitPush(r2); + frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex); // Load the arguments. int arg_count = args->length(); @@ -3886,6 +3848,8 @@ void CodeGenerator::VisitCall(Call* node) { Load(args->at(i)); } + VirtualFrame::SpilledScope spilled_scope(frame_); + // If we know that eval can only be shadowed by eval-introduced // variables we attempt to load the global eval function directly // in generated code. If we succeed, there is no need to perform a @@ -5201,7 +5165,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ UnaryOperation"); Token::Value op = node->op(); @@ -5273,8 +5236,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { break; case Token::SUB: { - VirtualFrame::SpilledScope spilled(frame_); - frame_->EmitPop(r0); + frame_->PopToR0(); GenericUnaryOpStub stub(Token::SUB, overwrite); frame_->CallStub(&stub, 0); frame_->EmitPush(r0); // r0 has result @@ -5282,23 +5244,28 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { } case Token::BIT_NOT: { - // smi check - VirtualFrame::SpilledScope spilled(frame_); - frame_->EmitPop(r0); - JumpTarget smi_label; + Register tos = frame_->PopToRegister(); + JumpTarget not_smi_label; JumpTarget continue_label; - __ tst(r0, Operand(kSmiTagMask)); - smi_label.Branch(eq); + // Smi check. + __ tst(tos, Operand(kSmiTagMask)); + not_smi_label.Branch(ne); + + __ mvn(tos, Operand(tos)); + __ bic(tos, tos, Operand(kSmiTagMask)); // Bit-clear inverted smi-tag. + frame_->EmitPush(tos); + // The fast case is the first to jump to the continue label, so it gets + // to decide the virtual frame layout. + continue_label.Jump(); + not_smi_label.Bind(); + frame_->SpillAll(); + __ Move(r0, tos); GenericUnaryOpStub stub(Token::BIT_NOT, overwrite); frame_->CallStub(&stub, 0); - continue_label.Jump(); + frame_->EmitPush(r0); - smi_label.Bind(); - __ mvn(r0, Operand(r0)); - __ bic(r0, r0, Operand(kSmiTagMask)); // bit-clear inverted smi-tag continue_label.Bind(); - frame_->EmitPush(r0); // r0 has result break; } @@ -5308,16 +5275,16 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { break; case Token::ADD: { - VirtualFrame::SpilledScope spilled(frame_); - frame_->EmitPop(r0); + Register tos = frame_->Peek(); // Smi check. JumpTarget continue_label; - __ tst(r0, Operand(kSmiTagMask)); + __ tst(tos, Operand(kSmiTagMask)); continue_label.Branch(eq); - frame_->EmitPush(r0); + frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1); + frame_->EmitPush(r0); + continue_label.Bind(); - frame_->EmitPush(r0); // r0 has result break; } default: @@ -5335,6 +5302,7 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) { int original_height = frame_->height(); #endif Comment cmnt(masm_, "[ CountOperation"); + VirtualFrame::RegisterAllocationScope scope(this); bool is_postfix = node->is_postfix(); bool is_increment = node->op() == Token::INC; @@ -5478,7 +5446,6 @@ void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) { // after evaluating the left hand side (due to the shortcut // semantics), but the compiler must (statically) know if the result // of compiling the binary operation is materialized or not. - VirtualFrame::SpilledScope spilled_scope(frame_); if (node->op() == Token::AND) { JumpTarget is_true; LoadCondition(node->left(), &is_true, false_target(), false); @@ -5663,8 +5630,6 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { if (left_is_null || right_is_null) { Load(left_is_null ? right : left); Register tos = frame_->PopToRegister(); - // JumpTargets can't cope with register allocation yet. - frame_->SpillAll(); __ LoadRoot(ip, Heap::kNullValueRootIndex); __ cmp(tos, ip); @@ -5707,9 +5672,6 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { LoadTypeofExpression(operation->expression()); Register tos = frame_->PopToRegister(); - // JumpTargets can't cope with register allocation yet. - frame_->SpillAll(); - Register scratch = VirtualFrame::scratch0(); if (check->Equals(Heap::number_symbol())) { @@ -5830,7 +5792,6 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { break; case Token::IN: { - VirtualFrame::SpilledScope scope(frame_); Load(left); Load(right); frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2); @@ -5839,7 +5800,6 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { } case Token::INSTANCEOF: { - VirtualFrame::SpilledScope scope(frame_); Load(left); Load(right); InstanceofStub stub; @@ -5937,10 +5897,15 @@ class DeferredReferenceGetKeyedValue: public DeferredCode { }; +// Takes key and register in r0 and r1 or vice versa. Returns result +// in r0. void DeferredReferenceGetKeyedValue::Generate() { ASSERT((key_.is(r0) && receiver_.is(r1)) || (key_.is(r1) && receiver_.is(r0))); + VirtualFrame copied_frame(*frame_state()->frame()); + copied_frame.SpillAll(); + Register scratch1 = VirtualFrame::scratch0(); Register scratch2 = VirtualFrame::scratch1(); __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2); @@ -5961,6 +5926,13 @@ void DeferredReferenceGetKeyedValue::Generate() { // keyed load has been inlined. __ nop(PROPERTY_ACCESS_INLINED); + // Now go back to the frame that we entered with. This will not overwrite + // the receiver or key registers since they were not in use when we came + // in. The instructions emitted by this merge are skipped over by the + // inline load patching mechanism when looking for the branch instruction + // that tells it where the code to patch is. + copied_frame.MergeTo(frame_state()->frame()); + // Block the constant pool for one more instruction after leaving this // constant pool block scope to include the branch instruction ending the // deferred code. @@ -6114,7 +6086,6 @@ void CodeGenerator::EmitKeyedLoad() { bool key_is_known_smi = frame_->KnownSmiAt(0); Register key = frame_->PopToRegister(); Register receiver = frame_->PopToRegister(key); - VirtualFrame::SpilledScope spilled(frame_); // The deferred code expects key and receiver in registers. DeferredReferenceGetKeyedValue* deferred = @@ -6152,10 +6123,12 @@ void CodeGenerator::EmitKeyedLoad() { // Get the elements array from the receiver and check that it // is not a dictionary. __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset)); - __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); - __ cmp(scratch2, ip); - deferred->Branch(ne); + if (FLAG_debug_code) { + __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset)); + __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); + __ cmp(scratch2, ip); + __ Assert(eq, "JSObject with fast elements map has slow elements"); + } // Check that key is within bounds. Use unsigned comparison to handle // negative keys. @@ -6176,7 +6149,7 @@ void CodeGenerator::EmitKeyedLoad() { __ mov(r0, scratch1); // Make sure that the expected number of instructions are generated. - ASSERT_EQ(kInlinedKeyedLoadInstructionsAfterPatch, + ASSERT_EQ(GetInlinedKeyedLoadInstructionsAfterPatch(), masm_->InstructionsGeneratedSince(&check_inlined_codesize)); } @@ -6204,9 +6177,9 @@ void CodeGenerator::EmitKeyedStore(StaticType* key_type) { // Load the value, key and receiver from the stack. Register value = frame_->PopToRegister(); Register key = frame_->PopToRegister(value); + VirtualFrame::SpilledScope spilled(frame_); Register receiver = r2; frame_->EmitPop(receiver); - VirtualFrame::SpilledScope spilled(frame_); // The deferred code expects value, key and receiver in registers. DeferredReferenceSetKeyedValue* deferred = diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index be4d556196..925d2675e1 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -276,7 +276,9 @@ class CodeGenerator: public AstVisitor { static int InlineRuntimeCallArgumentsCount(Handle name); // Constants related to patching of inlined load/store. - static const int kInlinedKeyedLoadInstructionsAfterPatch = 17; + static int GetInlinedKeyedLoadInstructionsAfterPatch() { + return FLAG_debug_code ? 27 : 13; + } static const int kInlinedKeyedStoreInstructionsAfterPatch = 5; private: diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index c6de4d8ef4..134dfa3952 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -47,71 +47,97 @@ namespace internal { #define __ ACCESS_MASM(masm) + +static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, + Register type, + Label* global_object) { + // Register usage: + // type: holds the receiver instance type on entry. + __ cmp(type, Operand(JS_GLOBAL_OBJECT_TYPE)); + __ b(eq, global_object); + __ cmp(type, Operand(JS_BUILTINS_OBJECT_TYPE)); + __ b(eq, global_object); + __ cmp(type, Operand(JS_GLOBAL_PROXY_TYPE)); + __ b(eq, global_object); +} + + +// Generated code falls through if the receiver is a regular non-global +// JS object with slow properties and no interceptors. +static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm, + Register receiver, + Register elements, + Register t0, + Register t1, + Label* miss) { + // Register usage: + // receiver: holds the receiver on entry and is unchanged. + // elements: holds the property dictionary on fall through. + // Scratch registers: + // t0: used to holds the receiver map. + // t1: used to holds the receiver instance type, receiver bit mask and + // elements map. + + // Check that the receiver isn't a smi. + __ tst(receiver, Operand(kSmiTagMask)); + __ b(eq, miss); + + // Check that the receiver is a valid JS object. + __ CompareObjectType(receiver, t0, t1, FIRST_JS_OBJECT_TYPE); + __ b(lt, miss); + + // If this assert fails, we have to check upper bound too. + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + + GenerateGlobalInstanceTypeCheck(masm, t1, miss); + + // Check that the global object does not require access checks. + __ ldrb(t1, FieldMemOperand(t0, Map::kBitFieldOffset)); + __ tst(t1, Operand((1 << Map::kIsAccessCheckNeeded) | + (1 << Map::kHasNamedInterceptor))); + __ b(nz, miss); + + __ ldr(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); + __ ldr(t1, FieldMemOperand(elements, HeapObject::kMapOffset)); + __ LoadRoot(ip, Heap::kHashTableMapRootIndex); + __ cmp(t1, ip); + __ b(nz, miss); +} + + // Helper function used from LoadIC/CallIC GenerateNormal. -// receiver: Receiver. It is not clobbered if a jump to the miss label is -// done +// +// elements: Property dictionary. It is not clobbered if a jump to the miss +// label is done. // name: Property name. It is not clobbered if a jump to the miss label is // done // result: Register for the result. It is only updated if a jump to the miss -// label is not done. Can be the same as receiver or name clobbering +// label is not done. Can be the same as elements or name clobbering // one of these in the case of not jumping to the miss label. -// The three scratch registers need to be different from the receiver, name and +// The two scratch registers need to be different from elements, name and // result. +// The generated code assumes that the receiver has slow properties, +// is not a global object and does not have interceptors. static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss, - Register receiver, + Register elements, Register name, Register result, Register scratch1, - Register scratch2, - Register scratch3, - DictionaryCheck check_dictionary) { + Register scratch2) { // Main use of the scratch registers. - // scratch1: Used to hold the property dictionary. - // scratch2: Used as temporary and to hold the capacity of the property + // scratch1: Used as temporary and to hold the capacity of the property // dictionary. - // scratch3: Used as temporary. + // scratch2: Used as temporary. Label done; - // Check for the absence of an interceptor. - // Load the map into scratch1. - __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kMapOffset)); - - // Bail out if the receiver has a named interceptor. - __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset)); - __ tst(scratch2, Operand(1 << Map::kHasNamedInterceptor)); - __ b(nz, miss); - - // Bail out if we have a JS global proxy object. - __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); - __ cmp(scratch2, Operand(JS_GLOBAL_PROXY_TYPE)); - __ b(eq, miss); - - // Possible work-around for http://crbug.com/16276. - // See also: http://codereview.chromium.org/155418. - __ cmp(scratch2, Operand(JS_GLOBAL_OBJECT_TYPE)); - __ b(eq, miss); - __ cmp(scratch2, Operand(JS_BUILTINS_OBJECT_TYPE)); - __ b(eq, miss); - - // Load the properties array. - __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - - // Check that the properties array is a dictionary. - if (check_dictionary == CHECK_DICTIONARY) { - __ ldr(scratch2, FieldMemOperand(scratch1, HeapObject::kMapOffset)); - __ LoadRoot(ip, Heap::kHashTableMapRootIndex); - __ cmp(scratch2, ip); - __ b(ne, miss); - } - // Compute the capacity mask. const int kCapacityOffset = StringDictionary::kHeaderSize + StringDictionary::kCapacityIndex * kPointerSize; - __ ldr(scratch2, FieldMemOperand(scratch1, kCapacityOffset)); - __ mov(scratch2, Operand(scratch2, ASR, kSmiTagSize)); // convert smi to int - __ sub(scratch2, scratch2, Operand(1)); + __ ldr(scratch1, FieldMemOperand(elements, kCapacityOffset)); + __ mov(scratch1, Operand(scratch1, ASR, kSmiTagSize)); // convert smi to int + __ sub(scratch1, scratch1, Operand(1)); const int kElementsStartOffset = StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; @@ -122,26 +148,26 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, static const int kProbes = 4; for (int i = 0; i < kProbes; i++) { // Compute the masked index: (hash + i + i * i) & mask. - __ ldr(scratch3, FieldMemOperand(name, String::kHashFieldOffset)); + __ ldr(scratch2, FieldMemOperand(name, String::kHashFieldOffset)); if (i > 0) { // Add the probe offset (i + i * i) left shifted to avoid right shifting // the hash in a separate instruction. The value hash + i + i * i is right // shifted in the following and instruction. ASSERT(StringDictionary::GetProbeOffset(i) < 1 << (32 - String::kHashFieldOffset)); - __ add(scratch3, scratch3, Operand( + __ add(scratch2, scratch2, Operand( StringDictionary::GetProbeOffset(i) << String::kHashShift)); } - __ and_(scratch3, scratch2, Operand(scratch3, LSR, String::kHashShift)); + __ and_(scratch2, scratch1, Operand(scratch2, LSR, String::kHashShift)); // Scale the index by multiplying by the element size. ASSERT(StringDictionary::kEntrySize == 3); - // scratch3 = scratch3 * 3. - __ add(scratch3, scratch3, Operand(scratch3, LSL, 1)); + // scratch2 = scratch2 * 3. + __ add(scratch2, scratch2, Operand(scratch2, LSL, 1)); // Check if the key is identical to the name. - __ add(scratch3, scratch1, Operand(scratch3, LSL, 2)); - __ ldr(ip, FieldMemOperand(scratch3, kElementsStartOffset)); + __ add(scratch2, elements, Operand(scratch2, LSL, 2)); + __ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset)); __ cmp(name, Operand(ip)); if (i != kProbes - 1) { __ b(eq, &done); @@ -151,15 +177,15 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, } // Check that the value is a normal property. - __ bind(&done); // scratch3 == scratch1 + 4 * index - __ ldr(scratch2, - FieldMemOperand(scratch3, kElementsStartOffset + 2 * kPointerSize)); - __ tst(scratch2, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize)); + __ bind(&done); // scratch2 == elements + 4 * index + __ ldr(scratch1, + FieldMemOperand(scratch2, kElementsStartOffset + 2 * kPointerSize)); + __ tst(scratch1, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize)); __ b(ne, miss); // Get the value at the masked, scaled index and return. __ ldr(result, - FieldMemOperand(scratch3, kElementsStartOffset + 1 * kPointerSize)); + FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize)); } @@ -310,6 +336,7 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, Register receiver, Register scratch1, Register scratch2, + int interceptor_bit, Label* slow) { // Check that the object isn't a smi. __ BranchOnSmi(receiver, slow); @@ -317,8 +344,9 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); // Check bit field. __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset)); - __ tst(scratch2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask)); - __ b(ne, slow); + __ tst(scratch2, + Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit))); + __ b(nz, slow); // Check that the object is some kind of JS object EXCEPT JS Value type. // In the case that the object is a value-wrapper object, // we enter the runtime system to make sure that indexing into string @@ -502,13 +530,11 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, } -static void GenerateNormalHelper(MacroAssembler* masm, - int argc, - bool is_global_object, - Label* miss, - Register scratch) { - // Search dictionary - put result in register r1. - GenerateDictionaryLoad(masm, miss, r1, r2, r1, r0, r3, r4, CHECK_DICTIONARY); +static void GenerateFunctionTailCall(MacroAssembler* masm, + int argc, + Label* miss, + Register scratch) { + // r1: function // Check that the value isn't a smi. __ tst(r1, Operand(kSmiTagMask)); @@ -518,13 +544,6 @@ static void GenerateNormalHelper(MacroAssembler* masm, __ CompareObjectType(r1, scratch, scratch, JS_FUNCTION_TYPE); __ b(ne, miss); - // Patch the receiver with the global proxy if necessary. - if (is_global_object) { - __ ldr(r0, MemOperand(sp, argc * kPointerSize)); - __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset)); - __ str(r0, MemOperand(sp, argc * kPointerSize)); - } - // Invoke the function. ParameterCount actual(argc); __ InvokeFunction(r1, actual, JUMP_FUNCTION); @@ -536,53 +555,18 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { // -- r2 : name // -- lr : return address // ----------------------------------- - Label miss, global_object, non_global_object; + Label miss; // Get the receiver of the function from the stack into r1. __ ldr(r1, MemOperand(sp, argc * kPointerSize)); - // Check that the receiver isn't a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &miss); - - // Check that the receiver is a valid JS object. Put the map in r3. - __ CompareObjectType(r1, r3, r0, FIRST_JS_OBJECT_TYPE); - __ b(lt, &miss); - - // If this assert fails, we have to check upper bound too. - ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); - - // Check for access to global object. - __ cmp(r0, Operand(JS_GLOBAL_OBJECT_TYPE)); - __ b(eq, &global_object); - __ cmp(r0, Operand(JS_BUILTINS_OBJECT_TYPE)); - __ b(ne, &non_global_object); + GenerateDictionaryLoadReceiverCheck(masm, r1, r0, r3, r4, &miss); - // Accessing global object: Load and invoke. - __ bind(&global_object); - // Check that the global object does not require access checks. - __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset)); - __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded)); - __ b(ne, &miss); - GenerateNormalHelper(masm, argc, true, &miss, r4); - - // Accessing non-global object: Check for access to global proxy. - Label global_proxy, invoke; - __ bind(&non_global_object); - __ cmp(r0, Operand(JS_GLOBAL_PROXY_TYPE)); - __ b(eq, &global_proxy); - // Check that the non-global, non-global-proxy object does not - // require access checks. - __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset)); - __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded)); - __ b(ne, &miss); - __ bind(&invoke); - GenerateNormalHelper(masm, argc, false, &miss, r4); + // r0: elements + // Search the dictionary - put result in register r1. + GenerateDictionaryLoad(masm, &miss, r0, r2, r1, r3, r4); - // Global object access: Check access rights. - __ bind(&global_proxy); - __ CheckAccessGlobalProxy(r1, r0, &miss); - __ b(&invoke); + GenerateFunctionTailCall(masm, argc, &miss, r4); __ bind(&miss); } @@ -594,6 +578,12 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) { // -- lr : return address // ----------------------------------- + if (id == IC::kCallIC_Miss) { + __ IncrementCounter(&Counters::call_miss, 1, r3, r4); + } else { + __ IncrementCounter(&Counters::keyed_call_miss, 1, r3, r4); + } + // Get the receiver of the function from the stack. __ ldr(r3, MemOperand(sp, argc * kPointerSize)); @@ -614,23 +604,26 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) { __ LeaveInternalFrame(); // Check if the receiver is a global object of some sort. - Label invoke, global; - __ ldr(r2, MemOperand(sp, argc * kPointerSize)); // receiver - __ tst(r2, Operand(kSmiTagMask)); - __ b(eq, &invoke); - __ CompareObjectType(r2, r3, r3, JS_GLOBAL_OBJECT_TYPE); - __ b(eq, &global); - __ cmp(r3, Operand(JS_BUILTINS_OBJECT_TYPE)); - __ b(ne, &invoke); - - // Patch the receiver on the stack. - __ bind(&global); - __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset)); - __ str(r2, MemOperand(sp, argc * kPointerSize)); + // This can happen only for regular CallIC but not KeyedCallIC. + if (id == IC::kCallIC_Miss) { + Label invoke, global; + __ ldr(r2, MemOperand(sp, argc * kPointerSize)); // receiver + __ tst(r2, Operand(kSmiTagMask)); + __ b(eq, &invoke); + __ CompareObjectType(r2, r3, r3, JS_GLOBAL_OBJECT_TYPE); + __ b(eq, &global); + __ cmp(r3, Operand(JS_BUILTINS_OBJECT_TYPE)); + __ b(ne, &invoke); + + // Patch the receiver on the stack. + __ bind(&global); + __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset)); + __ str(r2, MemOperand(sp, argc * kPointerSize)); + __ bind(&invoke); + } // Invoke the function. ParameterCount actual(argc); - __ bind(&invoke); __ InvokeFunction(r1, actual, JUMP_FUNCTION); } @@ -698,7 +691,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // Now the key is known to be a smi. This place is also jumped to from below // where a numeric string is converted to a smi. - GenerateKeyedLoadReceiverCheck(masm, r1, r0, r3, &slow_call); + GenerateKeyedLoadReceiverCheck( + masm, r1, r0, r3, Map::kHasIndexedInterceptor, &slow_call); GenerateFastArrayLoad( masm, r1, r2, r4, r3, r0, r1, &check_number_dictionary, &slow_load); @@ -708,14 +702,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // receiver in r1 is not used after this point. // r2: key // r1: function - - // Check that the value in r1 is a JSFunction. - __ BranchOnSmi(r1, &slow_call); - __ CompareObjectType(r1, r0, r0, JS_FUNCTION_TYPE); - __ b(ne, &slow_call); - // Invoke the function. - ParameterCount actual(argc); - __ InvokeFunction(r1, actual, JUMP_FUNCTION); + GenerateFunctionTailCall(masm, argc, &slow_call, r0); __ bind(&check_number_dictionary); // r2: key @@ -751,16 +738,16 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // If the receiver is a regular JS object with slow properties then do // a quick inline probe of the receiver's dictionary. // Otherwise do the monomorphic cache probe. - GenerateKeyedLoadReceiverCheck(masm, r1, r0, r3, &lookup_monomorphic_cache); + GenerateKeyedLoadReceiverCheck( + masm, r1, r0, r3, Map::kHasNamedInterceptor, &lookup_monomorphic_cache); - __ ldr(r3, FieldMemOperand(r1, JSObject::kPropertiesOffset)); - __ ldr(r3, FieldMemOperand(r3, HeapObject::kMapOffset)); + __ ldr(r0, FieldMemOperand(r1, JSObject::kPropertiesOffset)); + __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset)); __ LoadRoot(ip, Heap::kHashTableMapRootIndex); __ cmp(r3, ip); __ b(ne, &lookup_monomorphic_cache); - GenerateDictionaryLoad( - masm, &slow_load, r1, r2, r1, r0, r3, r4, DICTIONARY_CHECK_DONE); + GenerateDictionaryLoad(masm, &slow_load, r0, r2, r1, r3, r4); __ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1, r0, r3); __ jmp(&do_call); @@ -826,36 +813,14 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // -- r0 : receiver // -- sp[0] : receiver // ----------------------------------- - Label miss, probe, global; - - // Check that the receiver isn't a smi. - __ tst(r0, Operand(kSmiTagMask)); - __ b(eq, &miss); - - // Check that the receiver is a valid JS object. Put the map in r3. - __ CompareObjectType(r0, r3, r1, FIRST_JS_OBJECT_TYPE); - __ b(lt, &miss); - // If this assert fails, we have to check upper bound too. - ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + Label miss; - // Check for access to global object (unlikely). - __ cmp(r1, Operand(JS_GLOBAL_PROXY_TYPE)); - __ b(eq, &global); + GenerateDictionaryLoadReceiverCheck(masm, r0, r1, r3, r4, &miss); - // Check for non-global object that requires access check. - __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset)); - __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded)); - __ b(ne, &miss); - - __ bind(&probe); - GenerateDictionaryLoad(masm, &miss, r0, r2, r0, r1, r3, r4, CHECK_DICTIONARY); + // r1: elements + GenerateDictionaryLoad(masm, &miss, r1, r2, r0, r3, r4); __ Ret(); - // Global object access: Check access rights. - __ bind(&global); - __ CheckAccessGlobalProxy(r0, r1, &miss); - __ b(&probe); - // Cache miss: Jump to runtime. __ bind(&miss); GenerateMiss(masm); @@ -870,6 +835,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) { // -- sp[0] : receiver // ----------------------------------- + __ IncrementCounter(&Counters::load_miss, 1, r3, r4); + __ mov(r3, r0); __ Push(r3, r2); @@ -963,7 +930,7 @@ bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { // Patch the map check. Address ldr_map_instr_address = inline_end_address - - (CodeGenerator::kInlinedKeyedLoadInstructionsAfterPatch * + (CodeGenerator::GetInlinedKeyedLoadInstructionsAfterPatch() * Assembler::kInstrSize); Assembler::set_target_address_at(ldr_map_instr_address, reinterpret_cast
(map)); @@ -1013,6 +980,8 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { // -- r1 : receiver // ----------------------------------- + __ IncrementCounter(&Counters::keyed_load_miss, 1, r3, r4); + __ Push(r1, r0); ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss)); @@ -1045,14 +1014,15 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { Register key = r0; Register receiver = r1; - GenerateKeyedLoadReceiverCheck(masm, receiver, r2, r3, &slow); - // Check that the key is a smi. __ BranchOnNotSmi(key, &check_string); __ bind(&index_smi); // Now the key is known to be a smi. This place is also jumped to from below // where a numeric string is converted to a smi. + GenerateKeyedLoadReceiverCheck( + masm, receiver, r2, r3, Map::kHasIndexedInterceptor, &slow); + GenerateFastArrayLoad( masm, receiver, key, r4, r3, r2, r0, &check_pixel_array, &slow); __ IncrementCounter(&Counters::keyed_load_generic_smi, 1, r2, r3); @@ -1095,12 +1065,15 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ bind(&check_string); GenerateKeyStringCheck(masm, key, r2, r3, &index_string, &slow); + GenerateKeyedLoadReceiverCheck( + masm, receiver, r2, r3, Map::kHasNamedInterceptor, &slow); + // If the receiver is a fast-case object, check the keyed lookup // cache. Otherwise probe the dictionary. __ ldr(r3, FieldMemOperand(r1, JSObject::kPropertiesOffset)); - __ ldr(r3, FieldMemOperand(r3, HeapObject::kMapOffset)); + __ ldr(r4, FieldMemOperand(r3, HeapObject::kMapOffset)); __ LoadRoot(ip, Heap::kHashTableMapRootIndex); - __ cmp(r3, ip); + __ cmp(r4, ip); __ b(eq, &probe_dictionary); // Load the map of the receiver, compute the keyed lookup cache hash @@ -1148,9 +1121,14 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Do a quick inline probe of the receiver's dictionary, if it // exists. __ bind(&probe_dictionary); + // r1: receiver + // r0: key + // r3: elements + __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); + GenerateGlobalInstanceTypeCheck(masm, r2, &slow); // Load the property to r0. - GenerateDictionaryLoad( - masm, &slow, r1, r0, r0, r2, r3, r4, DICTIONARY_CHECK_DONE); + GenerateDictionaryLoad(masm, &slow, r3, r0, r0, r2, r4); __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1, r2, r3); __ Ret(); diff --git a/deps/v8/src/arm/jump-target-arm.cc b/deps/v8/src/arm/jump-target-arm.cc index 86198fb7b3..c6eb62891f 100644 --- a/deps/v8/src/arm/jump-target-arm.cc +++ b/deps/v8/src/arm/jump-target-arm.cc @@ -61,9 +61,17 @@ void JumpTarget::DoJump() { } else { // Clone the current frame to use as the expected one at the target. set_entry_frame(cgen()->frame()); + // Zap the fall-through frame since the jump was unconditional. RegisterFile empty; cgen()->SetFrame(NULL, &empty); } + if (entry_label_.is_bound()) { + // You can't jump backwards to an already bound label unless you admitted + // up front that this was a bidirectional jump target. Bidirectional jump + // targets will zap their type info when bound in case some later virtual + // frame with less precise type info branches to them. + ASSERT(direction_ != FORWARD_ONLY); + } __ jmp(&entry_label_); } @@ -83,6 +91,13 @@ void JumpTarget::DoBranch(Condition cc, Hint ignored) { // Clone the current frame to use as the expected one at the target. set_entry_frame(cgen()->frame()); } + if (entry_label_.is_bound()) { + // You can't branch backwards to an already bound label unless you admitted + // up front that this was a bidirectional jump target. Bidirectional jump + // targets will zap their type info when bound in case some later virtual + // frame with less precise type info branches to them. + ASSERT(direction_ != FORWARD_ONLY); + } __ b(cc, &entry_label_); if (cc == al) { cgen()->DeleteFrame(); @@ -121,6 +136,7 @@ void JumpTarget::DoBind() { ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters()); if (cgen()->has_valid_frame()) { + if (direction_ != FORWARD_ONLY) cgen()->frame()->ForgetTypeInfo(); // If there is a current frame we can use it on the fall through. if (!entry_frame_set_) { entry_frame_ = *cgen()->frame(); diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 29e168c51e..df7565f135 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1548,6 +1548,8 @@ void MacroAssembler::Check(Condition cc, const char* msg) { void MacroAssembler::Abort(const char* msg) { + Label abort_start; + bind(&abort_start); // We want to pass the msg string like a smi to avoid GC // problems, however msg is not guaranteed to be aligned // properly. Instead, we pass an aligned pointer that is @@ -1571,6 +1573,17 @@ void MacroAssembler::Abort(const char* msg) { push(r0); CallRuntime(Runtime::kAbort, 2); // will not return here + if (is_const_pool_blocked()) { + // If the calling code cares about the exact number of + // instructions generated, we insert padding here to keep the size + // of the Abort macro constant. + static const int kExpectedAbortInstructions = 10; + int abort_instructions = InstructionsGeneratedSince(&abort_start); + ASSERT(abort_instructions <= kExpectedAbortInstructions); + while (abort_instructions++ < kExpectedAbortInstructions) { + nop(); + } + } } diff --git a/deps/v8/src/arm/virtual-frame-arm.cc b/deps/v8/src/arm/virtual-frame-arm.cc index 8b90f4244a..2ddfd0fd82 100644 --- a/deps/v8/src/arm/virtual-frame-arm.cc +++ b/deps/v8/src/arm/virtual-frame-arm.cc @@ -482,6 +482,32 @@ void VirtualFrame::SpillAllButCopyTOSToR0() { } +void VirtualFrame::SpillAllButCopyTOSToR1() { + switch (top_of_stack_state_) { + case NO_TOS_REGISTERS: + __ ldr(r1, MemOperand(sp, 0)); + break; + case R0_TOS: + __ push(r0); + __ mov(r1, r0); + break; + case R1_TOS: + __ push(r1); + break; + case R0_R1_TOS: + __ Push(r1, r0); + __ mov(r1, r0); + break; + case R1_R0_TOS: + __ Push(r0, r1); + break; + default: + UNREACHABLE(); + } + top_of_stack_state_ = NO_TOS_REGISTERS; +} + + void VirtualFrame::SpillAllButCopyTOSToR1R0() { switch (top_of_stack_state_) { case NO_TOS_REGISTERS: @@ -524,6 +550,24 @@ Register VirtualFrame::Peek() { } +Register VirtualFrame::Peek2() { + AssertIsNotSpilled(); + switch (top_of_stack_state_) { + case NO_TOS_REGISTERS: + case R0_TOS: + case R0_R1_TOS: + MergeTOSTo(R0_R1_TOS); + return r1; + case R1_TOS: + case R1_R0_TOS: + MergeTOSTo(R1_R0_TOS); + return r0; + } + UNREACHABLE(); + return no_reg; +} + + void VirtualFrame::Dup() { if (SpilledScope::is_spilled()) { __ ldr(ip, MemOperand(sp, 0)); diff --git a/deps/v8/src/arm/virtual-frame-arm.h b/deps/v8/src/arm/virtual-frame-arm.h index d8dc5c6c05..8eedf22364 100644 --- a/deps/v8/src/arm/virtual-frame-arm.h +++ b/deps/v8/src/arm/virtual-frame-arm.h @@ -189,12 +189,15 @@ class VirtualFrame : public ZoneObject { return (tos_known_smi_map_ & (~other->tos_known_smi_map_)) == 0; } + inline void ForgetTypeInfo() { + tos_known_smi_map_ = 0; + } + // Detach a frame from its code generator, perhaps temporarily. This // tells the register allocator that it is free to use frame-internal // registers. Used when the code generator's frame is switched from this // one to NULL by an unconditional jump. void DetachFromCodeGenerator() { - AssertIsSpilled(); } // (Re)attach a frame to its code generator. This informs the register @@ -202,7 +205,6 @@ class VirtualFrame : public ZoneObject { // Used when a code generator's frame is switched from NULL to this one by // binding a label. void AttachToCodeGenerator() { - AssertIsSpilled(); } // Emit code for the physical JS entry and exit frame sequences. After @@ -330,6 +332,10 @@ class VirtualFrame : public ZoneObject { // must be copied to a scratch register before modification. Register Peek(); + // Look at the value beneath the top of the stack. The register returned is + // aliased and must be copied to a scratch register before modification. + Register Peek2(); + // Duplicate the top of stack. void Dup(); @@ -339,6 +345,9 @@ class VirtualFrame : public ZoneObject { // Flushes all registers, but it puts a copy of the top-of-stack in r0. void SpillAllButCopyTOSToR0(); + // Flushes all registers, but it puts a copy of the top-of-stack in r1. + void SpillAllButCopyTOSToR1(); + // Flushes all registers, but it puts a copy of the top-of-stack in r1 // and the next value on the stack in r0. void SpillAllButCopyTOSToR1R0(); diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js index 216c03b633..f3c0697b99 100644 --- a/deps/v8/src/array.js +++ b/deps/v8/src/array.js @@ -954,7 +954,7 @@ function ArrayMap(f, receiver) { function ArrayIndexOf(element, index) { var length = this.length; - if (index == null) { + if (IS_UNDEFINED(index)) { index = 0; } else { index = TO_INTEGER(index); @@ -981,7 +981,7 @@ function ArrayIndexOf(element, index) { function ArrayLastIndexOf(element, index) { var length = this.length; - if (index == null) { + if (%_ArgumentsLength() < 2) { index = length - 1; } else { index = TO_INTEGER(index); diff --git a/deps/v8/src/ast-inl.h b/deps/v8/src/ast-inl.h index 2b5d7c472b..717f68d063 100644 --- a/deps/v8/src/ast-inl.h +++ b/deps/v8/src/ast-inl.h @@ -45,7 +45,9 @@ SwitchStatement::SwitchStatement(ZoneStringList* labels) IterationStatement::IterationStatement(ZoneStringList* labels) - : BreakableStatement(labels, TARGET_FOR_ANONYMOUS), body_(NULL) { + : BreakableStatement(labels, TARGET_FOR_ANONYMOUS), + body_(NULL), + continue_target_(JumpTarget::BIDIRECTIONAL) { } diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index 7116dc9042..c8d4e097a5 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -195,6 +195,7 @@ BUILTIN(ArrayCodeGeneric) { } // 'array' now contains the JSArray we should initialize. + ASSERT(array->HasFastElements()); // Optimize the case where there is one argument and the argument is a // small smi. diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 35d3c54e66..f6b93b0773 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -274,11 +274,22 @@ Handle Factory::CopyMap(Handle src, return copy; } + Handle Factory::CopyMapDropTransitions(Handle src) { CALL_HEAP_FUNCTION(src->CopyDropTransitions(), Map); } +Handle Factory::GetFastElementsMap(Handle src) { + CALL_HEAP_FUNCTION(src->GetFastElementsMap(), Map); +} + + +Handle Factory::GetSlowElementsMap(Handle src) { + CALL_HEAP_FUNCTION(src->GetSlowElementsMap(), Map); +} + + Handle Factory::CopyFixedArray(Handle array) { CALL_HEAP_FUNCTION(array->Copy(), FixedArray); } diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h index 8a190fa09a..b0a0571af0 100644 --- a/deps/v8/src/factory.h +++ b/deps/v8/src/factory.h @@ -180,6 +180,10 @@ class Factory : public AllStatic { static Handle CopyMapDropTransitions(Handle map); + static Handle GetFastElementsMap(Handle map); + + static Handle GetSlowElementsMap(Handle map); + static Handle CopyFixedArray(Handle array); // Numbers (eg, literals) are pretenured by the parser. diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index f1ec56ce5a..6ae46f2a6e 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -126,6 +126,12 @@ int Heap::always_allocate_scope_depth_ = 0; int Heap::linear_allocation_scope_depth_ = 0; int Heap::contexts_disposed_ = 0; +int Heap::young_survivors_after_last_gc_ = 0; +int Heap::high_survival_rate_period_length_ = 0; +double Heap::survival_rate_ = 0; +Heap::SurvivalRateTrend Heap::previous_survival_rate_trend_ = Heap::STABLE; +Heap::SurvivalRateTrend Heap::survival_rate_trend_ = Heap::STABLE; + #ifdef DEBUG bool Heap::allocation_allowed_ = true; @@ -582,6 +588,29 @@ static void VerifyPageWatermarkValidity(PagedSpace* space, } #endif +void Heap::UpdateSurvivalRateTrend(int start_new_space_size) { + double survival_rate = + (static_cast(young_survivors_after_last_gc_) * 100) / + start_new_space_size; + + if (survival_rate > kYoungSurvivalRateThreshold) { + high_survival_rate_period_length_++; + } else { + high_survival_rate_period_length_ = 0; + } + + double survival_rate_diff = survival_rate_ - survival_rate; + + if (survival_rate_diff > kYoungSurvivalRateAllowedDeviation) { + set_survival_rate_trend(DECREASING); + } else if (survival_rate_diff < -kYoungSurvivalRateAllowedDeviation) { + set_survival_rate_trend(INCREASING); + } else { + set_survival_rate_trend(STABLE); + } + + survival_rate_ = survival_rate; +} void Heap::PerformGarbageCollection(AllocationSpace space, GarbageCollector collector, @@ -604,6 +633,8 @@ void Heap::PerformGarbageCollection(AllocationSpace space, EnsureFromSpaceIsCommitted(); + int start_new_space_size = Heap::new_space()->Size(); + if (collector == MARK_COMPACTOR) { if (FLAG_flush_code) { // Flush all potentially unused code. @@ -613,16 +644,36 @@ void Heap::PerformGarbageCollection(AllocationSpace space, // Perform mark-sweep with optional compaction. MarkCompact(tracer); + bool high_survival_rate_during_scavenges = IsHighSurvivalRate() && + IsStableOrIncreasingSurvivalTrend(); + + UpdateSurvivalRateTrend(start_new_space_size); + int old_gen_size = PromotedSpaceSize(); old_gen_promotion_limit_ = old_gen_size + Max(kMinimumPromotionLimit, old_gen_size / 3); old_gen_allocation_limit_ = old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2); + + if (high_survival_rate_during_scavenges && + IsStableOrIncreasingSurvivalTrend()) { + // Stable high survival rates of young objects both during partial and + // full collection indicate that mutator is either building or modifying + // a structure with a long lifetime. + // In this case we aggressively raise old generation memory limits to + // postpone subsequent mark-sweep collection and thus trade memory + // space for the mutation speed. + old_gen_promotion_limit_ *= 2; + old_gen_allocation_limit_ *= 2; + } + old_gen_exhausted_ = false; } else { tracer_ = tracer; Scavenge(); tracer_ = NULL; + + UpdateSurvivalRateTrend(start_new_space_size); } Counters::objs_since_last_young.Set(0); @@ -1217,7 +1268,7 @@ Object* Heap::AllocateMap(InstanceType instance_type, int instance_size) { map->set_code_cache(empty_fixed_array()); map->set_unused_property_fields(0); map->set_bit_field(0); - map->set_bit_field2(1 << Map::kIsExtensible); + map->set_bit_field2((1 << Map::kIsExtensible) | (1 << Map::kHasFastElements)); // If the map object is aligned fill the padding area with Smi 0 objects. if (Map::kPadStart < Map::kSize) { @@ -2545,6 +2596,7 @@ Object* Heap::AllocateInitialMap(JSFunction* fun) { map->set_inobject_properties(in_object_properties); map->set_unused_property_fields(in_object_properties); map->set_prototype(prototype); + ASSERT(map->has_fast_elements()); // If the function has only simple this property assignments add // field descriptors for these to the initial map as the object @@ -2598,8 +2650,8 @@ Object* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { // properly initialized. ASSERT(map->instance_type() != JS_FUNCTION_TYPE); - // Both types of globla objects should be allocated using - // AllocateGloblaObject to be properly initialized. + // Both types of global objects should be allocated using + // AllocateGlobalObject to be properly initialized. ASSERT(map->instance_type() != JS_GLOBAL_OBJECT_TYPE); ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE); @@ -2623,6 +2675,7 @@ Object* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { InitializeJSObjectFromMap(JSObject::cast(obj), FixedArray::cast(properties), map); + ASSERT(JSObject::cast(obj)->HasFastElements()); return obj; } diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index a8f8c34395..df3ba0ea2a 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -1005,6 +1005,7 @@ class Heap : public AllStatic { static void CheckNewSpaceExpansionCriteria(); static inline void IncrementYoungSurvivorsCounter(int survived) { + young_survivors_after_last_gc_ = survived; survived_since_last_expansion_ += survived; } @@ -1272,6 +1273,55 @@ class Heap : public AllStatic { // be replaced with a lazy compilable version. static void FlushCode(); + static void UpdateSurvivalRateTrend(int start_new_space_size); + + enum SurvivalRateTrend { INCREASING, STABLE, DECREASING, FLUCTUATING }; + + static const int kYoungSurvivalRateThreshold = 90; + static const int kYoungSurvivalRateAllowedDeviation = 15; + + static int young_survivors_after_last_gc_; + static int high_survival_rate_period_length_; + static double survival_rate_; + static SurvivalRateTrend previous_survival_rate_trend_; + static SurvivalRateTrend survival_rate_trend_; + + static void set_survival_rate_trend(SurvivalRateTrend survival_rate_trend) { + ASSERT(survival_rate_trend != FLUCTUATING); + previous_survival_rate_trend_ = survival_rate_trend_; + survival_rate_trend_ = survival_rate_trend; + } + + static SurvivalRateTrend survival_rate_trend() { + if (survival_rate_trend_ == STABLE) { + return STABLE; + } else if (previous_survival_rate_trend_ == STABLE) { + return survival_rate_trend_; + } else if (survival_rate_trend_ != previous_survival_rate_trend_) { + return FLUCTUATING; + } else { + return survival_rate_trend_; + } + } + + static bool IsStableOrIncreasingSurvivalTrend() { + switch (survival_rate_trend()) { + case STABLE: + case INCREASING: + return true; + default: + return false; + } + } + + static bool IsIncreasingSurvivalTrend() { + return survival_rate_trend() == INCREASING; + } + + static bool IsHighSurvivalRate() { + return high_survival_rate_period_length_ > 0; + } + static const int kInitialSymbolTableSize = 2048; static const int kInitialEvalCacheSize = 64; diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index 6b0747238a..c0c54426a3 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -8853,7 +8853,7 @@ Result CodeGenerator::EmitKeyedLoad() { // Use masm-> here instead of the double underscore macro since extra // coverage code can interfere with the patching. masm_->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), - Immediate(Factory::null_value())); + Immediate(Factory::null_value())); deferred->Branch(not_equal); // Check that the key is a smi. @@ -8868,9 +8868,11 @@ Result CodeGenerator::EmitKeyedLoad() { // is not a dictionary. __ mov(elements.reg(), FieldOperand(receiver.reg(), JSObject::kElementsOffset)); - __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), - Immediate(Factory::fixed_array_map())); - deferred->Branch(not_equal); + if (FLAG_debug_code) { + __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), + Immediate(Factory::fixed_array_map())); + __ Assert(equal, "JSObject with fast elements map has slow elements"); + } // Check that the key is within bounds. __ cmp(key.reg(), @@ -13293,6 +13295,9 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ test(edx, Immediate(kSmiTagMask)); __ j(not_zero, &runtime); __ sub(ecx, Operand(edx)); + __ cmp(ecx, FieldOperand(eax, String::kLengthOffset)); + Label return_eax; + __ j(equal, &return_eax); // Special handling of sub-strings of length 1 and 2. One character strings // are handled in the runtime system (looked up in the single character // cache). Two character strings are looked for in the symbol cache. @@ -13397,6 +13402,8 @@ void SubStringStub::Generate(MacroAssembler* masm) { // esi: character of sub string start StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false); __ mov(esi, edx); // Restore esi. + + __ bind(&return_eax); __ IncrementCounter(&Counters::sub_string_native, 1); __ ret(3 * kPointerSize); diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index c75044402c..13173e2b92 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -2175,7 +2175,7 @@ void FullCodeGenerator::EmitClassOf(ZoneList* args) { // LAST_JS_OBJECT_TYPE. ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); - __ cmp(ebx, JS_FUNCTION_TYPE); + __ CmpInstanceType(eax, JS_FUNCTION_TYPE); __ j(equal, &function); // Check if the constructor in the map is a function. diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index b0c07b7b51..62f878c048 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -45,72 +45,96 @@ namespace internal { #define __ ACCESS_MASM(masm) +static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, + Register type, + Label* global_object) { + // Register usage: + // type: holds the receiver instance type on entry. + __ cmp(type, JS_GLOBAL_OBJECT_TYPE); + __ j(equal, global_object, not_taken); + __ cmp(type, JS_BUILTINS_OBJECT_TYPE); + __ j(equal, global_object, not_taken); + __ cmp(type, JS_GLOBAL_PROXY_TYPE); + __ j(equal, global_object, not_taken); +} + + +// Generated code falls through if the receiver is a regular non-global +// JS object with slow properties and no interceptors. +static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm, + Register receiver, + Register r0, + Register r1, + Label* miss) { + // Register usage: + // receiver: holds the receiver on entry and is unchanged. + // r0: used to hold receiver instance type. + // Holds the property dictionary on fall through. + // r1: used to hold receivers map. + + // Check that the receiver isn't a smi. + __ test(receiver, Immediate(kSmiTagMask)); + __ j(zero, miss, not_taken); + + // Check that the receiver is a valid JS object. + __ mov(r1, FieldOperand(receiver, HeapObject::kMapOffset)); + __ movzx_b(r0, FieldOperand(r1, Map::kInstanceTypeOffset)); + __ cmp(r0, FIRST_JS_OBJECT_TYPE); + __ j(below, miss, not_taken); + + // If this assert fails, we have to check upper bound too. + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + + GenerateGlobalInstanceTypeCheck(masm, r0, miss); + + // Check for non-global object that requires access check. + __ test_b(FieldOperand(r1, Map::kBitFieldOffset), + (1 << Map::kIsAccessCheckNeeded) | + (1 << Map::kHasNamedInterceptor)); + __ j(not_zero, miss, not_taken); + + __ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset)); + __ CheckMap(r0, Factory::hash_table_map(), miss, true); +} + + // Helper function used to load a property from a dictionary backing storage. // This function may return false negatives, so miss_label // must always call a backup property load that is complete. -// This function is safe to call if the receiver has fast properties, -// or if name is not a symbol, and will jump to the miss_label in that case. +// This function is safe to call if name is not a symbol, and will jump to +// the miss_label in that case. +// The generated code assumes that the receiver has slow properties, +// is not a global object and does not have interceptors. static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, - Register receiver, + Register elements, Register name, Register r0, Register r1, - Register r2, - Register result, - DictionaryCheck check_dictionary) { + Register result) { // Register use: // - // name - holds the name of the property and is unchanged. - // receiver - holds the receiver and is unchanged. + // elements - holds the property dictionary on entry and is unchanged. + // + // name - holds the name of the property on entry and is unchanged. + // // Scratch registers: - // r0 - used to hold the property dictionary. // - // r1 - used for the index into the property dictionary + // r0 - used for the index into the property dictionary // - // r2 - used to hold the capacity of the property dictionary. + // r1 - used to hold the capacity of the property dictionary. // // result - holds the result on exit. Label done; - // Check for the absence of an interceptor. - // Load the map into r0. - __ mov(r0, FieldOperand(receiver, JSObject::kMapOffset)); - - // Bail out if the receiver has a named interceptor. - __ test(FieldOperand(r0, Map::kBitFieldOffset), - Immediate(1 << Map::kHasNamedInterceptor)); - __ j(not_zero, miss_label, not_taken); - - // Bail out if we have a JS global proxy object. - __ movzx_b(r0, FieldOperand(r0, Map::kInstanceTypeOffset)); - __ cmp(r0, JS_GLOBAL_PROXY_TYPE); - __ j(equal, miss_label, not_taken); - - // Possible work-around for http://crbug.com/16276. - __ cmp(r0, JS_GLOBAL_OBJECT_TYPE); - __ j(equal, miss_label, not_taken); - __ cmp(r0, JS_BUILTINS_OBJECT_TYPE); - __ j(equal, miss_label, not_taken); - - // Load properties array. - __ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset)); - - // Check that the properties array is a dictionary. - if (check_dictionary == CHECK_DICTIONARY) { - __ cmp(FieldOperand(r0, HeapObject::kMapOffset), - Immediate(Factory::hash_table_map())); - __ j(not_equal, miss_label); - } - // Compute the capacity mask. const int kCapacityOffset = StringDictionary::kHeaderSize + StringDictionary::kCapacityIndex * kPointerSize; - __ mov(r2, FieldOperand(r0, kCapacityOffset)); - __ shr(r2, kSmiTagSize); // convert smi to int - __ dec(r2); + __ mov(r1, FieldOperand(elements, kCapacityOffset)); + __ shr(r1, kSmiTagSize); // convert smi to int + __ dec(r1); // Generate an unrolled loop that performs a few probes before // giving up. Measurements done on Gmail indicate that 2 probes @@ -121,20 +145,20 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, StringDictionary::kElementsStartIndex * kPointerSize; for (int i = 0; i < kProbes; i++) { // Compute the masked index: (hash + i + i * i) & mask. - __ mov(r1, FieldOperand(name, String::kHashFieldOffset)); - __ shr(r1, String::kHashShift); + __ mov(r0, FieldOperand(name, String::kHashFieldOffset)); + __ shr(r0, String::kHashShift); if (i > 0) { - __ add(Operand(r1), Immediate(StringDictionary::GetProbeOffset(i))); + __ add(Operand(r0), Immediate(StringDictionary::GetProbeOffset(i))); } - __ and_(r1, Operand(r2)); + __ and_(r0, Operand(r1)); // Scale the index by multiplying by the entry size. ASSERT(StringDictionary::kEntrySize == 3); - __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3 + __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3 // Check if the key is identical to the name. - __ cmp(name, - Operand(r0, r1, times_4, kElementsStartOffset - kHeapObjectTag)); + __ cmp(name, Operand(elements, r0, times_4, + kElementsStartOffset - kHeapObjectTag)); if (i != kProbes - 1) { __ j(equal, &done, taken); } else { @@ -145,13 +169,13 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, // Check that the value is a normal property. __ bind(&done); const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - __ test(Operand(r0, r1, times_4, kDetailsOffset - kHeapObjectTag), + __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag), Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize)); __ j(not_zero, miss_label, not_taken); // Get the value at the masked, scaled index. const int kValueOffset = kElementsStartOffset + kPointerSize; - __ mov(result, Operand(r0, r1, times_4, kValueOffset - kHeapObjectTag)); + __ mov(result, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag)); } @@ -307,6 +331,7 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, Register receiver, Register map, + int interceptor_bit, Label* slow) { // Register use: // receiver - holds the receiver and is unchanged. @@ -322,7 +347,7 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, // Check bit field. __ test_b(FieldOperand(map, Map::kBitFieldOffset), - KeyedLoadIC::kSlowCaseBitFieldMask); + (1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)); __ j(not_zero, slow, not_taken); // Check that the object is some kind of JS object EXCEPT JS Value type. // In the case that the object is a value-wrapper object, @@ -432,8 +457,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { Label slow, check_string, index_smi, index_string; Label check_pixel_array, probe_dictionary, check_number_dictionary; - GenerateKeyedLoadReceiverCheck(masm, edx, ecx, &slow); - // Check that the key is a smi. __ test(eax, Immediate(kSmiTagMask)); __ j(not_zero, &check_string, not_taken); @@ -441,6 +464,9 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Now the key is known to be a smi. This place is also jumped to from // where a numeric string is converted to a smi. + GenerateKeyedLoadReceiverCheck( + masm, edx, ecx, Map::kHasIndexedInterceptor, &slow); + GenerateFastArrayLoad(masm, edx, eax, @@ -503,6 +529,9 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ bind(&check_string); GenerateKeyStringCheck(masm, eax, ecx, ebx, &index_string, &slow); + GenerateKeyedLoadReceiverCheck( + masm, edx, ecx, Map::kHasNamedInterceptor, &slow); + // If the receiver is a fast-case object, check the keyed lookup // cache. Otherwise probe the dictionary. __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset)); @@ -555,15 +584,12 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Do a quick inline probe of the receiver's dictionary, if it // exists. __ bind(&probe_dictionary); - GenerateDictionaryLoad(masm, - &slow, - edx, - eax, - ebx, - ecx, - edi, - eax, - DICTIONARY_CHECK_DONE); + + __ mov(ecx, FieldOperand(edx, JSObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + GenerateGlobalInstanceTypeCheck(masm, ecx, &slow); + + GenerateDictionaryLoad(masm, &slow, ebx, eax, ecx, edi, eax); __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); __ ret(0); @@ -1173,24 +1199,18 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, } -static void GenerateNormalHelper(MacroAssembler* masm, - int argc, - bool is_global_object, - Label* miss) { +static void GenerateFunctionTailCall(MacroAssembler* masm, + int argc, + Label* miss) { // ----------- S t a t e ------------- // -- ecx : name - // -- edx : receiver + // -- edi : function // -- esp[0] : return address // -- esp[(argc - n) * 4] : arg[n] (zero-based) // -- ... // -- esp[(argc + 1) * 4] : receiver // ----------------------------------- - // Search dictionary - put result in register edi. - __ mov(edi, edx); - GenerateDictionaryLoad( - masm, miss, edx, ecx, eax, edi, ebx, edi, CHECK_DICTIONARY); - // Check that the result is not a smi. __ test(edi, Immediate(kSmiTagMask)); __ j(zero, miss, not_taken); @@ -1199,12 +1219,6 @@ static void GenerateNormalHelper(MacroAssembler* masm, __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax); __ j(not_equal, miss, not_taken); - // Patch the receiver on stack with the global proxy if necessary. - if (is_global_object) { - __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); - __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); - } - // Invoke the function. ParameterCount actual(argc); __ InvokeFunction(edi, actual, JUMP_FUNCTION); @@ -1219,55 +1233,17 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { // -- ... // -- esp[(argc + 1) * 4] : receiver // ----------------------------------- - Label miss, global_object, non_global_object; + Label miss; // Get the receiver of the function from the stack; 1 ~ return address. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); - // Check that the receiver isn't a smi. - __ test(edx, Immediate(kSmiTagMask)); - __ j(zero, &miss, not_taken); + GenerateDictionaryLoadReceiverCheck(masm, edx, eax, ebx, &miss); - // Check that the receiver is a valid JS object. - __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); - __ movzx_b(eax, FieldOperand(ebx, Map::kInstanceTypeOffset)); - __ cmp(eax, FIRST_JS_OBJECT_TYPE); - __ j(below, &miss, not_taken); - - // If this assert fails, we have to check upper bound too. - ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); - - // Check for access to global object. - __ cmp(eax, JS_GLOBAL_OBJECT_TYPE); - __ j(equal, &global_object); - __ cmp(eax, JS_BUILTINS_OBJECT_TYPE); - __ j(not_equal, &non_global_object); - - // Accessing global object: Load and invoke. - __ bind(&global_object); - // Check that the global object does not require access checks. - __ test_b(FieldOperand(ebx, Map::kBitFieldOffset), - 1 << Map::kIsAccessCheckNeeded); - __ j(not_equal, &miss, not_taken); - GenerateNormalHelper(masm, argc, true, &miss); - - // Accessing non-global object: Check for access to global proxy. - Label global_proxy, invoke; - __ bind(&non_global_object); - __ cmp(eax, JS_GLOBAL_PROXY_TYPE); - __ j(equal, &global_proxy, not_taken); - // Check that the non-global, non-global-proxy object does not - // require access checks. - __ test_b(FieldOperand(ebx, Map::kBitFieldOffset), - 1 << Map::kIsAccessCheckNeeded); - __ j(not_equal, &miss, not_taken); - __ bind(&invoke); - GenerateNormalHelper(masm, argc, false, &miss); - - // Global object proxy access: Check access rights. - __ bind(&global_proxy); - __ CheckAccessGlobalProxy(edx, eax, &miss); - __ jmp(&invoke); + // eax: elements + // Search the dictionary placing the result in edi. + GenerateDictionaryLoad(masm, &miss, eax, ecx, edi, ebx, edi); + GenerateFunctionTailCall(masm, argc, &miss); __ bind(&miss); } @@ -1282,6 +1258,12 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) { // -- esp[(argc + 1) * 4] : receiver // ----------------------------------- + if (id == IC::kCallIC_Miss) { + __ IncrementCounter(&Counters::call_miss, 1); + } else { + __ IncrementCounter(&Counters::keyed_call_miss, 1); + } + // Get the receiver of the function from the stack; 1 ~ return address. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); @@ -1303,25 +1285,28 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) { __ LeaveInternalFrame(); // Check if the receiver is a global object of some sort. - Label invoke, global; - __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver - __ test(edx, Immediate(kSmiTagMask)); - __ j(zero, &invoke, not_taken); - __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); - __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); - __ cmp(ebx, JS_GLOBAL_OBJECT_TYPE); - __ j(equal, &global); - __ cmp(ebx, JS_BUILTINS_OBJECT_TYPE); - __ j(not_equal, &invoke); - - // Patch the receiver on the stack. - __ bind(&global); - __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); - __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); + // This can happen only for regular CallIC but not KeyedCallIC. + if (id == IC::kCallIC_Miss) { + Label invoke, global; + __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver + __ test(edx, Immediate(kSmiTagMask)); + __ j(zero, &invoke, not_taken); + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); + __ cmp(ebx, JS_GLOBAL_OBJECT_TYPE); + __ j(equal, &global); + __ cmp(ebx, JS_BUILTINS_OBJECT_TYPE); + __ j(not_equal, &invoke); + + // Patch the receiver on the stack. + __ bind(&global); + __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); + __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); + __ bind(&invoke); + } // Invoke the function. ParameterCount actual(argc); - __ bind(&invoke); __ InvokeFunction(edi, actual, JUMP_FUNCTION); } @@ -1393,7 +1378,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // Now the key is known to be a smi. This place is also jumped to from // where a numeric string is converted to a smi. - GenerateKeyedLoadReceiverCheck(masm, edx, eax, &slow_call); + GenerateKeyedLoadReceiverCheck( + masm, edx, eax, Map::kHasIndexedInterceptor, &slow_call); GenerateFastArrayLoad( masm, edx, ecx, eax, edi, &check_number_dictionary, &slow_load); @@ -1403,15 +1389,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // receiver in edx is not used after this point. // ecx: key // edi: function - - // Check that the value in edi is a JavaScript function. - __ test(edi, Immediate(kSmiTagMask)); - __ j(zero, &slow_call, not_taken); - __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax); - __ j(not_equal, &slow_call, not_taken); - // Invoke the function. - ParameterCount actual(argc); - __ InvokeFunction(edi, actual, JUMP_FUNCTION); + GenerateFunctionTailCall(masm, argc, &slow_call); __ bind(&check_number_dictionary); // eax: elements @@ -1451,15 +1429,13 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // If the receiver is a regular JS object with slow properties then do // a quick inline probe of the receiver's dictionary. // Otherwise do the monomorphic cache probe. - GenerateKeyedLoadReceiverCheck(masm, edx, eax, &lookup_monomorphic_cache); + GenerateKeyedLoadReceiverCheck( + masm, edx, eax, Map::kHasNamedInterceptor, &lookup_monomorphic_cache); __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset)); - __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), - Immediate(Factory::hash_table_map())); - __ j(not_equal, &lookup_monomorphic_cache, not_taken); + __ CheckMap(ebx, Factory::hash_table_map(), &lookup_monomorphic_cache, true); - GenerateDictionaryLoad( - masm, &slow_load, edx, ecx, ebx, eax, edi, edi, DICTIONARY_CHECK_DONE); + GenerateDictionaryLoad(masm, &slow_load, ebx, ecx, eax, edi, edi); __ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1); __ jmp(&do_call); @@ -1539,49 +1515,15 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // -- ecx : name // -- esp[0] : return address // ----------------------------------- - Label miss, probe, global; - - // Check that the receiver isn't a smi. - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &miss, not_taken); - - // Check that the receiver is a valid JS object. - __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); - __ movzx_b(edx, FieldOperand(ebx, Map::kInstanceTypeOffset)); - __ cmp(edx, FIRST_JS_OBJECT_TYPE); - __ j(less, &miss, not_taken); - - // If this assert fails, we have to check upper bound too. - ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); - - // Check for access to global object (unlikely). - __ cmp(edx, JS_GLOBAL_PROXY_TYPE); - __ j(equal, &global, not_taken); + Label miss; - // Check for non-global object that requires access check. - __ test_b(FieldOperand(ebx, Map::kBitFieldOffset), - 1 << Map::kIsAccessCheckNeeded); - __ j(not_zero, &miss, not_taken); + GenerateDictionaryLoadReceiverCheck(masm, eax, edx, ebx, &miss); + // edx: elements // Search the dictionary placing the result in eax. - __ bind(&probe); - GenerateDictionaryLoad(masm, - &miss, - eax, - ecx, - edx, - edi, - ebx, - edi, - CHECK_DICTIONARY); - __ mov(eax, edi); + GenerateDictionaryLoad(masm, &miss, edx, ecx, edi, ebx, eax); __ ret(0); - // Global object access: Check access rights. - __ bind(&global); - __ CheckAccessGlobalProxy(eax, edx, &miss); - __ jmp(&probe); - // Cache miss: Jump to runtime. __ bind(&miss); GenerateMiss(masm); @@ -1595,6 +1537,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) { // -- esp[0] : return address // ----------------------------------- + __ IncrementCounter(&Counters::load_miss, 1); + __ pop(ebx); __ push(eax); // receiver __ push(ecx); // name @@ -1711,6 +1655,8 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { // -- esp[0] : return address // ----------------------------------- + __ IncrementCounter(&Counters::keyed_load_miss, 1); + __ pop(ebx); __ push(edx); // receiver __ push(eax); // name diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc index 475f1611c0..4b77d92507 100644 --- a/deps/v8/src/ic.cc +++ b/deps/v8/src/ic.cc @@ -992,12 +992,14 @@ Object* KeyedLoadIC::Load(State state, } } set_target(stub); - // For JSObjects that are not value wrappers and that do not have - // indexed interceptors, we initialize the inlined fast case (if - // present) by patching the inlined map check. + // For JSObjects with fast elements that are not value wrappers + // and that do not have indexed interceptors, we initialize the + // inlined fast case (if present) by patching the inlined map + // check. if (object->IsJSObject() && !object->IsJSValue() && - !JSObject::cast(*object)->HasIndexedInterceptor()) { + !JSObject::cast(*object)->HasIndexedInterceptor() && + JSObject::cast(*object)->HasFastElements()) { Map* map = JSObject::cast(*object)->map(); PatchInlinedLoad(address(), map); } diff --git a/deps/v8/src/ic.h b/deps/v8/src/ic.h index 5fd5078fed..738b6f4e68 100644 --- a/deps/v8/src/ic.h +++ b/deps/v8/src/ic.h @@ -33,10 +33,6 @@ namespace v8 { namespace internal { -// Flag indicating whether an IC stub needs to check that a backing -// store is in dictionary case. -enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE }; - // IC_UTIL_LIST defines all utility functions called from generated // inline caching code. The argument for the macro, ICU, is the function name. diff --git a/deps/v8/src/json.js b/deps/v8/src/json.js index 3e42d36609..e5ee03d31d 100644 --- a/deps/v8/src/json.js +++ b/deps/v8/src/json.js @@ -207,7 +207,7 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) { } else if (IS_STRING_WRAPPER(value)) { value = $String(value); } else if (IS_BOOLEAN_WRAPPER(value)) { - value = $Boolean(value); + value = %_ValueOf(value); } } switch (typeof value) { diff --git a/deps/v8/src/jump-target-heavy.h b/deps/v8/src/jump-target-heavy.h index b923fe57f3..b2113a5a4c 100644 --- a/deps/v8/src/jump-target-heavy.h +++ b/deps/v8/src/jump-target-heavy.h @@ -196,6 +196,8 @@ class BreakTarget : public JumpTarget { public: // Construct a break target. BreakTarget() {} + explicit BreakTarget(JumpTarget::Directionality direction) + : JumpTarget(direction) { } virtual ~BreakTarget() {} diff --git a/deps/v8/src/jump-target-light-inl.h b/deps/v8/src/jump-target-light-inl.h index 0b4eee40c8..e8f1a5fd41 100644 --- a/deps/v8/src/jump-target-light-inl.h +++ b/deps/v8/src/jump-target-light-inl.h @@ -36,16 +36,20 @@ namespace internal { // Construct a jump target. JumpTarget::JumpTarget(Directionality direction) : entry_frame_set_(false), + direction_(direction), entry_frame_(kInvalidVirtualFrameInitializer) { } JumpTarget::JumpTarget() : entry_frame_set_(false), + direction_(FORWARD_ONLY), entry_frame_(kInvalidVirtualFrameInitializer) { } BreakTarget::BreakTarget() { } +BreakTarget::BreakTarget(JumpTarget::Directionality direction) + : JumpTarget(direction) { } } } // namespace v8::internal diff --git a/deps/v8/src/jump-target-light.h b/deps/v8/src/jump-target-light.h index 084bd582c7..91b7266a4a 100644 --- a/deps/v8/src/jump-target-light.h +++ b/deps/v8/src/jump-target-light.h @@ -120,6 +120,9 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. // Has an entry frame been found? bool entry_frame_set_; + // Can we branch backwards to this label? + Directionality direction_; + // The frame used on entry to the block and expected at backward // jumps to the block. Set the first time something branches to this // jump target. @@ -150,6 +153,7 @@ class BreakTarget : public JumpTarget { public: // Construct a break target. inline BreakTarget(); + inline BreakTarget(JumpTarget::Directionality direction); virtual ~BreakTarget() {} diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index ada73cbe66..e083f01a44 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -309,10 +309,10 @@ void Profiler::Disengage() { void Profiler::Run() { TickSample sample; - bool overflow = Logger::profiler_->Remove(&sample); + bool overflow = Remove(&sample); while (running_) { LOG(TickEvent(&sample, overflow)); - overflow = Logger::profiler_->Remove(&sample); + overflow = Remove(&sample); } } @@ -1150,7 +1150,7 @@ void Logger::TickEvent(TickSample* sample, bool overflow) { int Logger::GetActiveProfilerModules() { int result = PROFILER_MODULE_NONE; - if (!profiler_->paused()) { + if (profiler_ != NULL && !profiler_->paused()) { result |= PROFILER_MODULE_CPU; } if (FLAG_log_gc) { @@ -1162,7 +1162,7 @@ int Logger::GetActiveProfilerModules() { void Logger::PauseProfiler(int flags, int tag) { if (!Log::IsEnabled()) return; - if (flags & PROFILER_MODULE_CPU) { + if (profiler_ != NULL && (flags & PROFILER_MODULE_CPU)) { // It is OK to have negative nesting. if (--cpu_profiler_nesting_ == 0) { profiler_->pause(); @@ -1193,7 +1193,7 @@ void Logger::ResumeProfiler(int flags, int tag) { if (tag != 0) { UncheckedIntEvent("open-tag", tag); } - if (flags & PROFILER_MODULE_CPU) { + if (profiler_ != NULL && (flags & PROFILER_MODULE_CPU)) { if (cpu_profiler_nesting_++ == 0) { ++logging_nesting_; if (FLAG_prof_lazy) { diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index b60e54d313..0b5ff9932d 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -539,6 +539,9 @@ void JSObject::JSObjectVerify() { (map()->inobject_properties() + properties()->length() - map()->NextFreePropertyIndex())); } + ASSERT(map()->has_fast_elements() == + (elements()->map() == Heap::fixed_array_map())); + ASSERT(map()->has_fast_elements() == HasFastElements()); } diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index d6571bff09..f9def82d23 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -1166,6 +1166,8 @@ HeapObject* JSObject::elements() { void JSObject::set_elements(HeapObject* value, WriteBarrierMode mode) { + ASSERT(map()->has_fast_elements() == + (value->map() == Heap::fixed_array_map())); // In the assert below Dictionary is covered under FixedArray. ASSERT(value->IsFixedArray() || value->IsPixelArray() || value->IsExternalArray()); @@ -1181,11 +1183,21 @@ void JSObject::initialize_properties() { void JSObject::initialize_elements() { + ASSERT(map()->has_fast_elements()); ASSERT(!Heap::InNewSpace(Heap::empty_fixed_array())); WRITE_FIELD(this, kElementsOffset, Heap::empty_fixed_array()); } +Object* JSObject::ResetElements() { + Object* obj = map()->GetFastElementsMap(); + if (obj->IsFailure()) return obj; + set_map(Map::cast(obj)); + initialize_elements(); + return this; +} + + ACCESSORS(Oddball, to_string, String, kToStringOffset) ACCESSORS(Oddball, to_number, Object, kToNumberOffset) @@ -2335,6 +2347,26 @@ void Map::set_prototype(Object* value, WriteBarrierMode mode) { } +Object* Map::GetFastElementsMap() { + if (has_fast_elements()) return this; + Object* obj = CopyDropTransitions(); + if (obj->IsFailure()) return obj; + Map* new_map = Map::cast(obj); + new_map->set_has_fast_elements(true); + return new_map; +} + + +Object* Map::GetSlowElementsMap() { + if (!has_fast_elements()) return this; + Object* obj = CopyDropTransitions(); + if (obj->IsFailure()) return obj; + Map* new_map = Map::cast(obj); + new_map->set_has_fast_elements(false); + return new_map; +} + + ACCESSORS(Map, instance_descriptors, DescriptorArray, kInstanceDescriptorsOffset) ACCESSORS(Map, code_cache, Object, kCodeCacheOffset) @@ -2838,11 +2870,14 @@ JSObject::ElementsKind JSObject::GetElementsKind() { if (array->IsFixedArray()) { // FAST_ELEMENTS or DICTIONARY_ELEMENTS are both stored in a FixedArray. if (array->map() == Heap::fixed_array_map()) { + ASSERT(map()->has_fast_elements()); return FAST_ELEMENTS; } ASSERT(array->IsDictionary()); + ASSERT(!map()->has_fast_elements()); return DICTIONARY_ELEMENTS; } + ASSERT(!map()->has_fast_elements()); if (array->IsExternalArray()) { switch (array->map()->instance_type()) { case EXTERNAL_BYTE_ARRAY_TYPE: diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 63b77b7994..5a057e1643 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -2222,6 +2222,11 @@ Object* JSObject::TransformToFastProperties(int unused_property_fields) { Object* JSObject::NormalizeElements() { ASSERT(!HasPixelElements() && !HasExternalArrayElements()); if (HasDictionaryElements()) return this; + ASSERT(map()->has_fast_elements()); + + Object* obj = map()->GetSlowElementsMap(); + if (obj->IsFailure()) return obj; + Map* new_map = Map::cast(obj); // Get number of entries. FixedArray* array = FixedArray::cast(elements()); @@ -2230,7 +2235,7 @@ Object* JSObject::NormalizeElements() { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : array->length(); - Object* obj = NumberDictionary::Allocate(length); + obj = NumberDictionary::Allocate(length); if (obj->IsFailure()) return obj; NumberDictionary* dictionary = NumberDictionary::cast(obj); // Copy entries. @@ -2243,7 +2248,10 @@ Object* JSObject::NormalizeElements() { dictionary = NumberDictionary::cast(result); } } - // Switch to using the dictionary as the backing storage for elements. + // Switch to using the dictionary as the backing storage for + // elements. Set the new map first to satify the elements type + // assert in set_elements(). + set_map(new_map); set_elements(dictionary); Counters::elements_to_dictionary.Increment(); @@ -5473,14 +5481,18 @@ void Code::Disassemble(const char* name) { #endif // ENABLE_DISASSEMBLER -void JSObject::SetFastElements(FixedArray* elems) { +Object* JSObject::SetFastElementsCapacityAndLength(int capacity, int length) { // We should never end in here with a pixel or external array. ASSERT(!HasPixelElements() && !HasExternalArrayElements()); -#ifdef DEBUG - // Check the provided array is filled with the_hole. - uint32_t len = static_cast(elems->length()); - for (uint32_t i = 0; i < len; i++) ASSERT(elems->get(i)->IsTheHole()); -#endif + + Object* obj = Heap::AllocateFixedArrayWithHoles(capacity); + if (obj->IsFailure()) return obj; + FixedArray* elems = FixedArray::cast(obj); + + obj = map()->GetFastElementsMap(); + if (obj->IsFailure()) return obj; + Map* new_map = Map::cast(obj); + AssertNoAllocation no_gc; WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc); switch (GetElementsKind()) { @@ -5508,7 +5520,15 @@ void JSObject::SetFastElements(FixedArray* elems) { UNREACHABLE(); break; } + + set_map(new_map); set_elements(elems); + + if (IsJSArray()) { + JSArray::cast(this)->set_length(Smi::FromInt(length)); + } + + return this; } @@ -5595,7 +5615,7 @@ Object* JSObject::SetElementsLength(Object* len) { Object* smi_length = len->ToSmi(); if (smi_length->IsSmi()) { - int value = Smi::cast(smi_length)->value(); + const int value = Smi::cast(smi_length)->value(); if (value < 0) return ArrayLengthRangeError(); switch (GetElementsKind()) { case FAST_ELEMENTS: { @@ -5617,12 +5637,8 @@ Object* JSObject::SetElementsLength(Object* len) { int new_capacity = value > min ? value : min; if (new_capacity <= kMaxFastElementsLength || !ShouldConvertToSlowElements(new_capacity)) { - Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity); + Object* obj = SetFastElementsCapacityAndLength(new_capacity, value); if (obj->IsFailure()) return obj; - if (IsJSArray()) { - JSArray::cast(this)->set_length(Smi::cast(smi_length)); - } - SetFastElements(FixedArray::cast(obj)); return this; } break; @@ -5633,7 +5649,8 @@ Object* JSObject::SetElementsLength(Object* len) { // If the length of a slow array is reset to zero, we clear // the array and flush backing storage. This has the added // benefit that the array returns to fast mode. - initialize_elements(); + Object* obj = ResetElements(); + if (obj->IsFailure()) return obj; } else { // Remove deleted elements. uint32_t old_length = @@ -6092,12 +6109,8 @@ Object* JSObject::SetFastElement(uint32_t index, Object* value) { if (new_capacity <= kMaxFastElementsLength || !ShouldConvertToSlowElements(new_capacity)) { ASSERT(static_cast(new_capacity) > index); - Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity); + Object* obj = SetFastElementsCapacityAndLength(new_capacity, index + 1); if (obj->IsFailure()) return obj; - SetFastElements(FixedArray::cast(obj)); - if (IsJSArray()) { - JSArray::cast(this)->set_length(Smi::FromInt(index + 1)); - } FixedArray::cast(elements())->set(index, value); return value; } @@ -6216,13 +6229,11 @@ Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) { uint32_t new_length = 0; if (IsJSArray()) { CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length)); - JSArray::cast(this)->set_length(Smi::FromInt(new_length)); } else { new_length = NumberDictionary::cast(elements())->max_number_key() + 1; } - Object* obj = Heap::AllocateFixedArrayWithHoles(new_length); + Object* obj = SetFastElementsCapacityAndLength(new_length, new_length); if (obj->IsFailure()) return obj; - SetFastElements(FixedArray::cast(obj)); #ifdef DEBUG if (FLAG_trace_normalization) { PrintF("Object elements are fast case again:\n"); @@ -7526,14 +7537,18 @@ Object* JSObject::PrepareElementsForSort(uint32_t limit) { } // Convert to fast elements. + Object* obj = map()->GetFastElementsMap(); + if (obj->IsFailure()) return obj; + Map* new_map = Map::cast(obj); + PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED; Object* new_array = Heap::AllocateFixedArray(dict->NumberOfElements(), tenure); - if (new_array->IsFailure()) { - return new_array; - } + if (new_array->IsFailure()) return new_array; FixedArray* fast_elements = FixedArray::cast(new_array); dict->CopyValuesTo(fast_elements); + + set_map(new_map); set_elements(fast_elements); } ASSERT(HasFastElements()); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 0c146656aa..0ad6f14cde 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1191,6 +1191,7 @@ class JSObject: public HeapObject { // case, and a PixelArray or ExternalArray in special cases. DECL_ACCESSORS(elements, HeapObject) inline void initialize_elements(); + inline Object* ResetElements(); inline ElementsKind GetElementsKind(); inline bool HasFastElements(); inline bool HasDictionaryElements(); @@ -1367,7 +1368,7 @@ class JSObject: public HeapObject { // The undefined object if index is out of bounds. Object* GetElementWithReceiver(JSObject* receiver, uint32_t index); - void SetFastElements(FixedArray* elements); + Object* SetFastElementsCapacityAndLength(int capacity, int length); Object* SetSlowElements(Object* length); // Lookup interceptors are used for handling properties controlled by host @@ -2987,6 +2988,19 @@ class Map: public HeapObject { return ((1 << kIsExtensible) & bit_field2()) != 0; } + // Tells whether the instance has fast elements. + void set_has_fast_elements(bool value) { + if (value) { + set_bit_field2(bit_field2() | (1 << kHasFastElements)); + } else { + set_bit_field2(bit_field2() & ~(1 << kHasFastElements)); + } + } + + bool has_fast_elements() { + return ((1 << kHasFastElements) & bit_field2()) != 0; + } + // Tells whether the instance needs security checks when accessing its // properties. inline void set_is_access_check_needed(bool access_check_needed); @@ -3010,6 +3024,16 @@ class Map: public HeapObject { // instance descriptors. Object* CopyDropTransitions(); + // Returns this map if it has the fast elements bit set, otherwise + // returns a copy of the map, with all transitions dropped from the + // descriptors and the fast elements bit set. + inline Object* GetFastElementsMap(); + + // Returns this map if it has the fast elements bit cleared, + // otherwise returns a copy of the map, with all transitions dropped + // from the descriptors and the fast elements bit cleared. + inline Object* GetSlowElementsMap(); + // Returns the property index for name (only valid for FAST MODE). int PropertyIndexFor(String* name); @@ -3111,6 +3135,7 @@ class Map: public HeapObject { // Bit positions for bit field 2 static const int kIsExtensible = 0; static const int kFunctionWithPrototype = 1; + static const int kHasFastElements = 2; // Layout of the default cache. It holds alternating name and code objects. static const int kCodeCacheEntrySize = 2; diff --git a/deps/v8/src/regexp.js b/deps/v8/src/regexp.js index 9367f15f91..f0945b3459 100644 --- a/deps/v8/src/regexp.js +++ b/deps/v8/src/regexp.js @@ -230,7 +230,10 @@ function RegExpExec(string) { var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); if (matchIndices == null) { - if (this.global) this.lastIndex = 0; + if (this.global) { + this.lastIndex = 0; + if (lastIndex != 0) return matchIndices; + } cache.lastIndex = lastIndex; cache.regExp = this; cache.subject = s; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 71148e6bd3..8625053894 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -7449,7 +7449,7 @@ class ArrayConcatVisitor { uint32_t index_limit_; // Index after last seen index. Always less than or equal to index_limit_. uint32_t index_offset_; - bool fast_elements_; + const bool fast_elements_; }; @@ -7766,13 +7766,14 @@ static Object* Runtime_ArrayConcat(Arguments args) { // The backing storage array must have non-existing elements to // preserve holes across concat operations. storage = Factory::NewFixedArrayWithHoles(result_length); - + result->set_map(*Factory::GetFastElementsMap(Handle(result->map()))); } else { // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate uint32_t at_least_space_for = estimate_nof_elements + (estimate_nof_elements >> 2); storage = Handle::cast( Factory::NewNumberDictionary(at_least_space_for)); + result->set_map(*Factory::GetSlowElementsMap(Handle(result->map()))); } Handle len = Factory::NewNumber(static_cast(result_length)); @@ -7822,9 +7823,19 @@ static Object* Runtime_MoveArrayContents(Arguments args) { ASSERT(args.length() == 2); CONVERT_CHECKED(JSArray, from, args[0]); CONVERT_CHECKED(JSArray, to, args[1]); - to->SetContent(FixedArray::cast(from->elements())); + HeapObject* new_elements = from->elements(); + Object* new_map; + if (new_elements->map() == Heap::fixed_array_map()) { + new_map = to->map()->GetFastElementsMap(); + } else { + new_map = to->map()->GetSlowElementsMap(); + } + if (new_map->IsFailure()) return new_map; + to->set_map(Map::cast(new_map)); + to->set_elements(new_elements); to->set_length(from->length()); - from->SetContent(Heap::empty_fixed_array()); + Object* obj = from->ResetElements(); + if (obj->IsFailure()) return obj; from->set_length(Smi::FromInt(0)); return to; } diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index d7c5b70f3b..236b85e64d 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -587,7 +587,7 @@ static inline void MemCopy(void* dest, const void* src, size_t size) { // Limit below which the extra overhead of the MemCopy function is likely // to outweigh the benefits of faster copying. // TODO(lrn): Try to find a more precise value. -static const int kMinComplexMemCopy = 256; +static const int kMinComplexMemCopy = 64; #else // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h index 10b8102460..93fecd1fe8 100644 --- a/deps/v8/src/v8-counters.h +++ b/deps/v8/src/v8-counters.h @@ -153,6 +153,10 @@ namespace internal { SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \ SC(named_store_global_inline, V8.NamedStoreGlobalInline) \ SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \ + SC(call_miss, V8.CallMiss) \ + SC(keyed_call_miss, V8.KeyedCallMiss) \ + SC(load_miss, V8.LoadMiss) \ + SC(keyed_load_miss, V8.KeyedLoadMiss) \ SC(call_const, V8.CallConst) \ SC(call_const_fast_api, V8.CallConstFastApi) \ SC(call_const_interceptor, V8.CallConstInterceptor) \ diff --git a/deps/v8/src/v8natives.js b/deps/v8/src/v8natives.js index 1d47eb75b9..24d5e7cd36 100644 --- a/deps/v8/src/v8natives.js +++ b/deps/v8/src/v8natives.js @@ -677,9 +677,20 @@ function ObjectGetOwnPropertyNames(obj) { } } - // Property names are expected to be strings. - for (var i = 0; i < propertyNames.length; ++i) - propertyNames[i] = ToString(propertyNames[i]); + // Property names are expected to be unique strings. + var propertySet = {}; + var j = 0; + for (var i = 0; i < propertyNames.length; ++i) { + var name = ToString(propertyNames[i]); + // We need to check for the exact property value since for intrinsic + // properties like toString if(propertySet["toString"]) will always + // succeed. + if (propertySet[name] === true) + continue; + propertySet[name] = true; + propertyNames[j++] = name; + } + propertyNames.length = j; return propertyNames; } diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index c9e8411933..4cc7e1be49 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 2 #define MINOR_VERSION 2 -#define BUILD_NUMBER 19 +#define BUILD_NUMBER 20 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc index e665385c69..2bb92d7e25 100644 --- a/deps/v8/src/x64/assembler-x64.cc +++ b/deps/v8/src/x64/assembler-x64.cc @@ -376,8 +376,13 @@ void Assembler::GetCode(CodeDesc* desc) { void Assembler::Align(int m) { ASSERT(IsPowerOf2(m)); - while ((pc_offset() & (m - 1)) != 0) { - nop(); + int delta = (m - (pc_offset() & (m - 1))) & (m - 1); + while (delta >= 9) { + nop(9); + delta -= 9; + } + if (delta > 0) { + nop(delta); } } @@ -837,9 +842,7 @@ void Assembler::call(Register adr) { EnsureSpace ensure_space(this); last_pc_ = pc_; // Opcode: FF /2 r64. - if (adr.high_bit()) { - emit_rex_64(adr); - } + emit_optional_rex_32(adr); emit(0xFF); emit_modrm(0x2, adr); } @@ -849,9 +852,9 @@ void Assembler::call(const Operand& op) { EnsureSpace ensure_space(this); last_pc_ = pc_; // Opcode: FF /2 m64. - emit_rex_64(op); + emit_optional_rex_32(op); emit(0xFF); - emit_operand(2, op); + emit_operand(0x2, op); } @@ -1270,9 +1273,7 @@ void Assembler::jmp(Register target) { EnsureSpace ensure_space(this); last_pc_ = pc_; // Opcode FF/4 r64. - if (target.high_bit()) { - emit_rex_64(target); - } + emit_optional_rex_32(target); emit(0xFF); emit_modrm(0x4, target); } @@ -1831,9 +1832,7 @@ void Assembler::nop(int n) { void Assembler::pop(Register dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; - if (dst.high_bit()) { - emit_rex_64(dst); - } + emit_optional_rex_32(dst); emit(0x58 | dst.low_bits()); } @@ -1841,7 +1840,7 @@ void Assembler::pop(Register dst) { void Assembler::pop(const Operand& dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; - emit_rex_64(dst); // Could be omitted in some cases. + emit_optional_rex_32(dst); emit(0x8F); emit_operand(0, dst); } @@ -1857,9 +1856,7 @@ void Assembler::popfq() { void Assembler::push(Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; - if (src.high_bit()) { - emit_rex_64(src); - } + emit_optional_rex_32(src); emit(0x50 | src.low_bits()); } @@ -1867,7 +1864,7 @@ void Assembler::push(Register src) { void Assembler::push(const Operand& src) { EnsureSpace ensure_space(this); last_pc_ = pc_; - emit_rex_64(src); // Could be omitted in some cases. + emit_optional_rex_32(src); emit(0xFF); emit_operand(6, src); } @@ -2609,6 +2606,28 @@ void Assembler::movsd(XMMRegister dst, const Operand& src) { } +void Assembler::movss(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF3); // single + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x10); // load + emit_sse_operand(dst, src); +} + + +void Assembler::movss(const Operand& src, XMMRegister dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF3); // single + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x11); // store + emit_sse_operand(dst, src); +} + + void Assembler::cvttss2si(Register dst, const Operand& src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -2664,6 +2683,17 @@ void Assembler::cvtlsi2sd(XMMRegister dst, Register src) { } +void Assembler::cvtlsi2ss(XMMRegister dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF3); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x2A); + emit_sse_operand(dst, src); +} + + void Assembler::cvtqsi2sd(XMMRegister dst, Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -2686,6 +2716,50 @@ void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) { } +void Assembler::cvtss2sd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF3); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5A); + emit_sse_operand(dst, src); +} + + +void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF2); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5A); + emit_sse_operand(dst, src); +} + + +void Assembler::cvtsd2si(Register dst, XMMRegister src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF2); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x2D); + emit_sse_operand(dst, src); +} + + +void Assembler::cvtsd2siq(Register dst, XMMRegister src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF2); + emit_rex_64(dst, src); + emit(0x0F); + emit(0x2D); + emit_sse_operand(dst, src); +} + + void Assembler::addsd(XMMRegister dst, XMMRegister src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -2763,6 +2837,18 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { } +void Assembler::ucomisd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0f); + emit(0x2e); + emit_sse_operand(dst, src); +} + + + void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) { Register ireg = { reg.code() }; emit_operand(ireg, adr); diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h index f195439eba..213db2cc8f 100644 --- a/deps/v8/src/x64/assembler-x64.h +++ b/deps/v8/src/x64/assembler-x64.h @@ -46,23 +46,23 @@ namespace internal { // Test whether a 64-bit value is in a specific range. static inline bool is_uint32(int64_t x) { - static const int64_t kUInt32Mask = V8_INT64_C(0xffffffff); - return x == (x & kUInt32Mask); + static const uint64_t kMaxUInt32 = V8_UINT64_C(0xffffffff); + return static_cast(x) <= kMaxUInt32; } static inline bool is_int32(int64_t x) { - static const int64_t kMinIntValue = V8_INT64_C(-0x80000000); - return is_uint32(x - kMinIntValue); + static const int64_t kMinInt32 = -V8_INT64_C(0x80000000); + return is_uint32(x - kMinInt32); } static inline bool uint_is_int32(uint64_t x) { - static const uint64_t kMaxIntValue = V8_UINT64_C(0x80000000); - return x < kMaxIntValue; + static const uint64_t kMaxInt32 = V8_UINT64_C(0x7fffffff); + return x <= kMaxInt32; } static inline bool is_uint32(uint64_t x) { - static const uint64_t kMaxUIntValue = V8_UINT64_C(0x100000000); - return x < kMaxUIntValue; + static const uint64_t kMaxUInt32 = V8_UINT64_C(0xffffffff); + return x <= kMaxUInt32; } // CPU Registers. @@ -1110,6 +1110,9 @@ class Assembler : public Malloced { void movsd(XMMRegister dst, XMMRegister src); void movsd(XMMRegister dst, const Operand& src); + void movss(XMMRegister dst, const Operand& src); + void movss(const Operand& dst, XMMRegister src); + void cvttss2si(Register dst, const Operand& src); void cvttsd2si(Register dst, const Operand& src); void cvttsd2siq(Register dst, XMMRegister src); @@ -1119,7 +1122,14 @@ class Assembler : public Malloced { void cvtqsi2sd(XMMRegister dst, const Operand& src); void cvtqsi2sd(XMMRegister dst, Register src); + void cvtlsi2ss(XMMRegister dst, Register src); + void cvtss2sd(XMMRegister dst, XMMRegister src); + void cvtss2sd(XMMRegister dst, const Operand& src); + void cvtsd2ss(XMMRegister dst, XMMRegister src); + + void cvtsd2si(Register dst, XMMRegister src); + void cvtsd2siq(Register dst, XMMRegister src); void addsd(XMMRegister dst, XMMRegister src); void subsd(XMMRegister dst, XMMRegister src); @@ -1130,6 +1140,7 @@ class Assembler : public Malloced { void sqrtsd(XMMRegister dst, XMMRegister src); void ucomisd(XMMRegister dst, XMMRegister src); + void ucomisd(XMMRegister dst, const Operand& src); // The first argument is the reg field, the second argument is the r/m field. void emit_sse_operand(XMMRegister dst, XMMRegister src); diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index 3ba890672b..0c4cd16cee 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -2641,7 +2641,7 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { // Generate code to set the elements in the array that are not // literals. - for (int i = 0; i < node->values()->length(); i++) { + for (int i = 0; i < length; i++) { Expression* value = node->values()->at(i); // If value is a literal the property value is already set in the @@ -3855,8 +3855,17 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { default: UNREACHABLE(); } - Load(left); - Load(right); + + if (left->IsTrivial()) { + Load(right); + Result right_result = frame_->Pop(); + frame_->Push(left); + frame_->Push(&right_result); + } else { + Load(left); + Load(right); + } + Comparison(node, cc, strict, destination()); } @@ -5336,9 +5345,8 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) { dest->false_target()->Branch(equal); Condition is_smi = masm_->CheckSmi(value.reg()); dest->true_target()->Branch(is_smi); - __ fldz(); - __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset)); - __ FCmp(); + __ xorpd(xmm0, xmm0); + __ ucomisd(xmm0, FieldOperand(value.reg(), HeapNumber::kValueOffset)); value.Unuse(); dest->Split(not_zero); } else { @@ -6511,7 +6519,7 @@ class DeferredInlineBinaryOperation: public DeferredCode { void DeferredInlineBinaryOperation::Generate() { Label done; if ((op_ == Token::ADD) - || (op_ ==Token::SUB) + || (op_ == Token::SUB) || (op_ == Token::MUL) || (op_ == Token::DIV)) { Label call_runtime; @@ -7530,9 +7538,11 @@ Result CodeGenerator::EmitKeyedLoad() { // is not a dictionary. __ movq(elements.reg(), FieldOperand(receiver.reg(), JSObject::kElementsOffset)); - __ Cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), - Factory::fixed_array_map()); - deferred->Branch(not_equal); + if (FLAG_debug_code) { + __ Cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), + Factory::fixed_array_map()); + __ Assert(equal, "JSObject with fast elements map has slow elements"); + } // Check that key is within bounds. __ SmiCompare(key.reg(), @@ -8000,14 +8010,12 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ jmp(&true_result); __ bind(¬_string); - // HeapNumber => false iff +0, -0, or NaN. - // These three cases set C3 when compared to zero in the FPU. __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex); __ j(not_equal, &true_result); - __ fldz(); // Load zero onto fp stack - // Load heap-number double value onto fp stack - __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset)); - __ FCmp(); + // HeapNumber => false iff +0, -0, or NaN. + // These three cases set the zero flag when compared to zero using ucomisd. + __ xorpd(xmm0, xmm0); + __ ucomisd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset)); __ j(zero, &false_result); // Fall through to |true_result|. @@ -8951,48 +8959,31 @@ void CompareStub::Generate(MacroAssembler* masm) { // Test for NaN. Sadly, we can't just compare to Factory::nan_value(), // so we do the second best thing - test it ourselves. // Note: if cc_ != equal, never_nan_nan_ is not used. + __ Set(rax, EQUAL); if (never_nan_nan_ && (cc_ == equal)) { - __ Set(rax, EQUAL); __ ret(0); } else { - Label return_equal; Label heap_number; // If it's not a heap number, then return equal. __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), Factory::heap_number_map()); __ j(equal, &heap_number); - __ bind(&return_equal); - __ Set(rax, EQUAL); __ ret(0); __ bind(&heap_number); - // It is a heap number, so return non-equal if it's NaN and equal if - // it's not NaN. - // The representation of NaN values has all exponent bits (52..62) set, - // and not all mantissa bits (0..51) clear. - // We only allow QNaNs, which have bit 51 set (which also rules out - // the value being Infinity). - - // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e., - // all bits in the mask are set. We only need to check the word - // that contains the exponent and high bit of the mantissa. - ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u); - __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset)); - __ xorl(rax, rax); - __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits. - __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1)); - if (cc_ == equal) { - __ setcc(above_equal, rax); - __ ret(0); - } else { - Label nan; - __ j(above_equal, &nan); - __ Set(rax, EQUAL); - __ ret(0); - __ bind(&nan); - __ Set(rax, NegativeComparisonResult(cc_)); - __ ret(0); + // It is a heap number, so return equal if it's not NaN. + // For NaN, return 1 for every condition except greater and + // greater-equal. Return -1 for them, so the comparison yields + // false for all conditions except not-equal. + + __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); + __ ucomisd(xmm0, xmm0); + __ setcc(parity_even, rax); + // rax is 0 for equal non-NaN heapnumbers, 1 for NaNs. + if (cc_ == greater_equal || cc_ == greater) { + __ neg(rax); } + __ ret(0); } __ bind(¬_identical); @@ -10040,20 +10031,15 @@ void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm, // Input: rdx, rax are the left and right objects of a bit op. // Output: rax, rcx are left and right integers for a bit op. void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm) { - if (FLAG_debug_code) { - // Both arguments can not be smis. That case is handled by smi-only code. - Label ok; - __ JumpIfNotBothSmi(rax, rdx, &ok); - __ Abort("Both arguments smi but not handled by smi-code."); - __ bind(&ok); - } // Check float operands. Label done; + Label rax_is_smi; Label rax_is_object; Label rdx_is_object; __ JumpIfNotSmi(rdx, &rdx_is_object); __ SmiToInteger32(rdx, rdx); + __ JumpIfSmi(rax, &rax_is_smi); __ bind(&rax_is_object); IntegerConvert(masm, rcx, rax); // Uses rdi, rcx and rbx. @@ -10062,6 +10048,7 @@ void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm) { __ bind(&rdx_is_object); IntegerConvert(masm, rdx, rdx); // Uses rdi, rcx and rbx. __ JumpIfNotSmi(rax, &rax_is_object); + __ bind(&rax_is_smi); __ SmiToInteger32(rcx, rax); __ bind(&done); @@ -10446,7 +10433,6 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { Label not_floats; // rax: y // rdx: x - ASSERT(!static_operands_type_.IsSmi()); if (static_operands_type_.IsNumber()) { if (FLAG_debug_code) { // Assert at runtime that inputs are only numbers. @@ -11583,7 +11569,9 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ JumpIfNotBothPositiveSmi(rcx, rdx, &runtime); __ SmiSub(rcx, rcx, rdx, NULL); // Overflow doesn't happen. - __ j(negative, &runtime); + __ cmpq(FieldOperand(rax, String::kLengthOffset), rcx); + Label return_rax; + __ j(equal, &return_rax); // Special handling of sub-strings of length 1 and 2. One character strings // are handled in the runtime system (looked up in the single character // cache). Two character strings are looked for in the symbol cache. @@ -11686,6 +11674,8 @@ void SubStringStub::Generate(MacroAssembler* masm) { // rsi: character of sub string start StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false); __ movq(rsi, rdx); // Restore esi. + + __ bind(&return_rax); __ IncrementCounter(&Counters::sub_string_native, 1); __ ret(kArgumentsSize); diff --git a/deps/v8/src/x64/disasm-x64.cc b/deps/v8/src/x64/disasm-x64.cc index 44ffe5fb5e..002a5ebc7b 100644 --- a/deps/v8/src/x64/disasm-x64.cc +++ b/deps/v8/src/x64/disasm-x64.cc @@ -1028,9 +1028,9 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { if (opcode == 0x57) { mnemonic = "xorpd"; } else if (opcode == 0x2E) { - mnemonic = "comisd"; - } else if (opcode == 0x2F) { mnemonic = "ucomisd"; + } else if (opcode == 0x2F) { + mnemonic = "comisd"; } else { UnimplementedInstruction(); } @@ -1057,7 +1057,7 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { // CVTSI2SD: integer to XMM double conversion. int mod, regop, rm; get_modrm(*current, &mod, ®op, &rm); - AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); + AppendToBuffer("%sd %s,", mnemonic, NameOfXMMRegister(regop)); current += PrintRightOperand(current); } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) { // XMM arithmetic. Mnemonic was retrieved at the start of this function. @@ -1070,7 +1070,25 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { } } else if (group_1_prefix_ == 0xF3) { // Instructions with prefix 0xF3. - if (opcode == 0x2C) { + if (opcode == 0x11 || opcode == 0x10) { + // MOVSS: Move scalar double-precision fp to/from/between XMM registers. + AppendToBuffer("movss "); + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + if (opcode == 0x11) { + current += PrintRightOperand(current); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else { + AppendToBuffer("%s,", NameOfXMMRegister(regop)); + current += PrintRightOperand(current); + } + } else if (opcode == 0x2A) { + // CVTSI2SS: integer to XMM single conversion. + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("%ss %s,", mnemonic, NameOfXMMRegister(regop)); + current += PrintRightOperand(current); + } else if (opcode == 0x2C) { // CVTTSS2SI: Convert scalar single-precision FP to dword integer. // Assert that mod is not 3, so source is memory, not an XMM register. ASSERT_NE(0xC0, *current & 0xC0); @@ -1146,8 +1164,8 @@ const char* DisassemblerX64::TwoByteMnemonic(byte opcode) { switch (opcode) { case 0x1F: return "nop"; - case 0x2A: // F2 prefix. - return "cvtsi2sd"; + case 0x2A: // F2/F3 prefix. + return "cvtsi2s"; case 0x31: return "rdtsc"; case 0x51: // F2 prefix. diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc index 1df1de3477..e3f74f6c05 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -1518,12 +1518,13 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) { case KEYED_PROPERTY: { __ push(rax); // Preserve value. VisitForValue(prop->obj(), kStack); - VisitForValue(prop->key(), kStack); - __ movq(rax, Operand(rsp, 2 * kPointerSize)); + VisitForValue(prop->key(), kAccumulator); + __ movq(rcx, rax); + __ pop(rdx); + __ pop(rax); Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); __ nop(); // Signal no inlined code. - __ Drop(3); // Receiver, key, and extra copy of value. break; } } diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc index 6e77c892d7..31a806a36b 100644 --- a/deps/v8/src/x64/ic-x64.cc +++ b/deps/v8/src/x64/ic-x64.cc @@ -45,71 +45,93 @@ namespace internal { #define __ ACCESS_MASM(masm) +static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, + Register type, + Label* global_object) { + // Register usage: + // type: holds the receiver instance type on entry. + __ cmpb(type, Immediate(JS_GLOBAL_OBJECT_TYPE)); + __ j(equal, global_object); + __ cmpb(type, Immediate(JS_BUILTINS_OBJECT_TYPE)); + __ j(equal, global_object); + __ cmpb(type, Immediate(JS_GLOBAL_PROXY_TYPE)); + __ j(equal, global_object); +} + + +// Generated code falls through if the receiver is a regular non-global +// JS object with slow properties and no interceptors. +static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm, + Register receiver, + Register r0, + Register r1, + Label* miss) { + // Register usage: + // receiver: holds the receiver on entry and is unchanged. + // r0: used to hold receiver instance type. + // Holds the property dictionary on fall through. + // r1: used to hold receivers map. + + __ JumpIfSmi(receiver, miss); + + // Check that the receiver is a valid JS object. + __ movq(r1, FieldOperand(receiver, HeapObject::kMapOffset)); + __ movb(r0, FieldOperand(r1, Map::kInstanceTypeOffset)); + __ cmpb(r0, Immediate(FIRST_JS_OBJECT_TYPE)); + __ j(below, miss); + + // If this assert fails, we have to check upper bound too. + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + + GenerateGlobalInstanceTypeCheck(masm, r0, miss); + + // Check for non-global object that requires access check. + __ testb(FieldOperand(r1, Map::kBitFieldOffset), + Immediate((1 << Map::kIsAccessCheckNeeded) | + (1 << Map::kHasNamedInterceptor))); + __ j(not_zero, miss); + + __ movq(r0, FieldOperand(receiver, JSObject::kPropertiesOffset)); + __ CompareRoot(FieldOperand(r0, HeapObject::kMapOffset), + Heap::kHashTableMapRootIndex); + __ j(not_equal, miss); +} + + // Helper function used to load a property from a dictionary backing storage. // This function may return false negatives, so miss_label // must always call a backup property load that is complete. -// This function is safe to call if the receiver has fast properties, -// or if name is not a symbol, and will jump to the miss_label in that case. +// This function is safe to call if name is not a symbol, and will jump to +// the miss_label in that case. +// The generated code assumes that the receiver has slow properties, +// is not a global object and does not have interceptors. static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, + Register elements, + Register name, Register r0, Register r1, - Register r2, - Register name, - Register r4, - Register result, - DictionaryCheck check_dictionary) { + Register result) { // Register use: // - // r0 - used to hold the property dictionary and is unchanged. + // elements - holds the property dictionary on entry and is unchanged. // - // r1 - used to hold the receiver and is unchanged. + // name - holds the name of the property on entry and is unchanged. // - // r2 - used to hold the capacity of the property dictionary. + // r0 - used to hold the capacity of the property dictionary. // - // name - holds the name of the property and is unchanged. - // - // r4 - used to hold the index into the property dictionary. + // r1 - used to hold the index into the property dictionary. // // result - holds the result on exit if the load succeeded. Label done; - // Check for the absence of an interceptor. - // Load the map into r0. - __ movq(r0, FieldOperand(r1, JSObject::kMapOffset)); - - // Bail out if the receiver has a named interceptor. - __ testl(FieldOperand(r0, Map::kBitFieldOffset), - Immediate(1 << Map::kHasNamedInterceptor)); - __ j(not_zero, miss_label); - - // Bail out if we have a JS global proxy object. - __ movzxbq(r0, FieldOperand(r0, Map::kInstanceTypeOffset)); - __ cmpb(r0, Immediate(JS_GLOBAL_PROXY_TYPE)); - __ j(equal, miss_label); - - // Possible work-around for http://crbug.com/16276. - __ cmpb(r0, Immediate(JS_GLOBAL_OBJECT_TYPE)); - __ j(equal, miss_label); - __ cmpb(r0, Immediate(JS_BUILTINS_OBJECT_TYPE)); - __ j(equal, miss_label); - - // Load properties array. - __ movq(r0, FieldOperand(r1, JSObject::kPropertiesOffset)); - - if (check_dictionary == CHECK_DICTIONARY) { - // Check that the properties array is a dictionary. - __ Cmp(FieldOperand(r0, HeapObject::kMapOffset), Factory::hash_table_map()); - __ j(not_equal, miss_label); - } - // Compute the capacity mask. const int kCapacityOffset = StringDictionary::kHeaderSize + StringDictionary::kCapacityIndex * kPointerSize; - __ SmiToInteger32(r2, FieldOperand(r0, kCapacityOffset)); - __ decl(r2); + __ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset)); + __ decl(r0); // Generate an unrolled loop that performs a few probes before // giving up. Measurements done on Gmail indicate that 2 probes @@ -120,19 +142,19 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, StringDictionary::kElementsStartIndex * kPointerSize; for (int i = 0; i < kProbes; i++) { // Compute the masked index: (hash + i + i * i) & mask. - __ movl(r4, FieldOperand(name, String::kHashFieldOffset)); - __ shrl(r4, Immediate(String::kHashShift)); + __ movl(r1, FieldOperand(name, String::kHashFieldOffset)); + __ shrl(r1, Immediate(String::kHashShift)); if (i > 0) { - __ addl(r4, Immediate(StringDictionary::GetProbeOffset(i))); + __ addl(r1, Immediate(StringDictionary::GetProbeOffset(i))); } - __ and_(r4, r2); + __ and_(r1, r0); // Scale the index by multiplying by the entry size. ASSERT(StringDictionary::kEntrySize == 3); - __ lea(r4, Operand(r4, r4, times_2, 0)); // r4 = r4 * 3 + __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3 // Check if the key is identical to the name. - __ cmpq(name, Operand(r0, r4, times_pointer_size, + __ cmpq(name, Operand(elements, r1, times_pointer_size, kElementsStartOffset - kHeapObjectTag)); if (i != kProbes - 1) { __ j(equal, &done); @@ -144,14 +166,16 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, // Check that the value is a normal property. __ bind(&done); const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - __ Test(Operand(r0, r4, times_pointer_size, kDetailsOffset - kHeapObjectTag), + __ Test(Operand(elements, r1, times_pointer_size, + kDetailsOffset - kHeapObjectTag), Smi::FromInt(PropertyDetails::TypeField::mask())); __ j(not_zero, miss_label); // Get the value at the masked, scaled index. const int kValueOffset = kElementsStartOffset + kPointerSize; __ movq(result, - Operand(r0, r4, times_pointer_size, kValueOffset - kHeapObjectTag)); + Operand(elements, r1, times_pointer_size, + kValueOffset - kHeapObjectTag)); } @@ -327,6 +351,8 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { // -- rsp[0] : return address // ----------------------------------- + __ IncrementCounter(&Counters::keyed_load_miss, 1); + __ pop(rbx); __ push(rdx); // receiver __ push(rax); // name @@ -360,6 +386,7 @@ void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, Register receiver, Register map, + int interceptor_bit, Label* slow) { // Register use: // receiver - holds the receiver and is unchanged. @@ -379,7 +406,8 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, // Check bit field. __ testb(FieldOperand(map, Map::kBitFieldOffset), - Immediate(KeyedLoadIC::kSlowCaseBitFieldMask)); + Immediate((1 << Map::kIsAccessCheckNeeded) | + (1 << interceptor_bit))); __ j(not_zero, slow); } @@ -500,14 +528,15 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { Label slow, check_string, index_smi, index_string; Label check_pixel_array, probe_dictionary, check_number_dictionary; - GenerateKeyedLoadReceiverCheck(masm, rdx, rcx, &slow); - // Check that the key is a smi. __ JumpIfNotSmi(rax, &check_string); __ bind(&index_smi); // Now the key is known to be a smi. This place is also jumped to from below // where a numeric string is converted to a smi. + GenerateKeyedLoadReceiverCheck( + masm, rdx, rcx, Map::kHasIndexedInterceptor, &slow); + GenerateFastArrayLoad(masm, rdx, rax, @@ -557,6 +586,9 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ bind(&check_string); GenerateKeyStringCheck(masm, rax, rcx, rbx, &index_string, &slow); + GenerateKeyedLoadReceiverCheck( + masm, rdx, rcx, Map::kHasNamedInterceptor, &slow); + // If the receiver is a fast-case object, check the keyed lookup // cache. Otherwise probe the dictionary leaving result in rcx. __ movq(rbx, FieldOperand(rdx, JSObject::kPropertiesOffset)); @@ -608,15 +640,13 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ bind(&probe_dictionary); // rdx: receiver // rax: key - GenerateDictionaryLoad(masm, - &slow, - rbx, - rdx, - rcx, - rax, - rdi, - rax, - DICTIONARY_CHECK_DONE); + // rbx: elements + + __ movq(rcx, FieldOperand(rdx, JSObject::kMapOffset)); + __ movb(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset)); + GenerateGlobalInstanceTypeCheck(masm, rcx, &slow); + + GenerateDictionaryLoad(masm, &slow, rbx, rax, rcx, rdi, rax); __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); __ ret(0); @@ -672,7 +702,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- - Label slow, failed_allocation; + Label slow; // Check that the object isn't a smi. __ JumpIfSmi(rdx, &slow); @@ -731,7 +761,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, __ movl(rcx, Operand(rbx, rcx, times_4, 0)); break; case kExternalFloatArray: - __ fld_s(Operand(rbx, rcx, times_4, 0)); + __ cvtss2sd(xmm0, Operand(rbx, rcx, times_4, 0)); break; default: UNREACHABLE(); @@ -743,20 +773,16 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, // For integer array types: // rcx: value // For floating-point array type: - // FP(0): value + // xmm0: value as double. - if (array_type == kExternalIntArray || - array_type == kExternalUnsignedIntArray) { - // For the Int and UnsignedInt array types, we need to see whether + ASSERT(kSmiValueSize == 32); + if (array_type == kExternalUnsignedIntArray) { + // For the UnsignedInt array type, we need to see whether // the value can be represented in a Smi. If not, we need to convert // it to a HeapNumber. Label box_int; - if (array_type == kExternalIntArray) { - __ JumpIfNotValidSmiValue(rcx, &box_int); - } else { - ASSERT_EQ(array_type, kExternalUnsignedIntArray); - __ JumpIfUIntNotValidSmiValue(rcx, &box_int); - } + + __ JumpIfUIntNotValidSmiValue(rcx, &box_int); __ Integer32ToSmi(rax, rcx); __ ret(0); @@ -765,42 +791,28 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, // Allocate a HeapNumber for the int and perform int-to-double // conversion. - __ push(rcx); - if (array_type == kExternalIntArray) { - __ fild_s(Operand(rsp, 0)); - } else { - ASSERT(array_type == kExternalUnsignedIntArray); - // The value is zero-extended on the stack, because all pushes are - // 64-bit and we loaded the value from memory with movl. - __ fild_d(Operand(rsp, 0)); - } - __ pop(rcx); - // FP(0): value - __ AllocateHeapNumber(rcx, rbx, &failed_allocation); + // The value is zero-extended since we loaded the value from memory + // with movl. + __ cvtqsi2sd(xmm0, rcx); + + __ AllocateHeapNumber(rcx, rbx, &slow); // Set the value. + __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0); __ movq(rax, rcx); - __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset)); __ ret(0); } else if (array_type == kExternalFloatArray) { // For the floating-point array type, we need to always allocate a // HeapNumber. - __ AllocateHeapNumber(rcx, rbx, &failed_allocation); + __ AllocateHeapNumber(rcx, rbx, &slow); // Set the value. + __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0); __ movq(rax, rcx); - __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset)); __ ret(0); } else { __ Integer32ToSmi(rax, rcx); __ ret(0); } - // If we fail allocation of the HeapNumber, we still have a value on - // top of the FPU stack. Remove it. - __ bind(&failed_allocation); - __ ffree(); - __ fincstp(); - // Fall through to slow case. - // Slow case: Jump to runtime. __ bind(&slow); __ IncrementCounter(&Counters::keyed_load_external_array_slow, 1); @@ -1086,10 +1098,8 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, break; case kExternalFloatArray: // Need to perform int-to-float conversion. - __ push(rdx); - __ fild_s(Operand(rsp, 0)); - __ pop(rdx); - __ fstp_s(Operand(rbx, rdi, times_4, 0)); + __ cvtlsi2ss(xmm0, rdx); + __ movss(Operand(rbx, rdi, times_4, 0), xmm0); break; default: UNREACHABLE(); @@ -1110,53 +1120,41 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, // The WebGL specification leaves the behavior of storing NaN and // +/-Infinity into integer arrays basically undefined. For more // reproducible behavior, convert these to zero. - __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset)); + __ movsd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset)); __ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset)); // rdi: untagged index // rbx: base pointer of external storage // top of FPU stack: value if (array_type == kExternalFloatArray) { - __ fstp_s(Operand(rbx, rdi, times_4, 0)); + __ cvtsd2ss(xmm0, xmm0); + __ movss(Operand(rbx, rdi, times_4, 0), xmm0); __ ret(0); } else { // Need to perform float-to-int conversion. - // Test the top of the FP stack for NaN. - Label is_nan; - __ fucomi(0); - __ j(parity_even, &is_nan); - - __ push(rdx); // Make room on the stack. Receiver is no longer needed. - __ fistp_d(Operand(rsp, 0)); - __ pop(rdx); + // Test the value for NaN. + + // Convert to int32 and store the low byte/word. + // If the value is NaN or +/-infinity, the result is 0x80000000, + // which is automatically zero when taken mod 2^n, n < 32. // rdx: value (converted to an untagged integer) // rdi: untagged index // rbx: base pointer of external storage switch (array_type) { case kExternalByteArray: case kExternalUnsignedByteArray: + __ cvtsd2si(rdx, xmm0); __ movb(Operand(rbx, rdi, times_1, 0), rdx); break; case kExternalShortArray: case kExternalUnsignedShortArray: + __ cvtsd2si(rdx, xmm0); __ movw(Operand(rbx, rdi, times_2, 0), rdx); break; case kExternalIntArray: case kExternalUnsignedIntArray: { - // We also need to explicitly check for +/-Infinity. These are - // converted to MIN_INT, but we need to be careful not to - // confuse with legal uses of MIN_INT. Since MIN_INT truncated - // to 8 or 16 bits is zero, we only perform this test when storing - // 32-bit ints. - Label not_infinity; - // This test would apparently detect both NaN and Infinity, - // but we've already checked for NaN using the FPU hardware - // above. - __ movzxwq(rcx, FieldOperand(rax, HeapNumber::kValueOffset + 6)); - __ and_(rcx, Immediate(0x7FF0)); - __ cmpw(rcx, Immediate(0x7FF0)); - __ j(not_equal, ¬_infinity); - __ movq(rdx, Immediate(0)); - __ bind(¬_infinity); + // Convert to int64, so that NaN and infinities become + // 0x8000000000000000, which is zero mod 2^32. + __ cvtsd2siq(rdx, xmm0); __ movl(Operand(rbx, rdi, times_4, 0), rdx); break; } @@ -1165,31 +1163,6 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, break; } __ ret(0); - - __ bind(&is_nan); - // rdi: untagged index - // rbx: base pointer of external storage - __ ffree(); - __ fincstp(); - __ movq(rdx, Immediate(0)); - switch (array_type) { - case kExternalByteArray: - case kExternalUnsignedByteArray: - __ movb(Operand(rbx, rdi, times_1, 0), rdx); - break; - case kExternalShortArray: - case kExternalUnsignedShortArray: - __ movw(Operand(rbx, rdi, times_2, 0), rdx); - break; - case kExternalIntArray: - case kExternalUnsignedIntArray: - __ movl(Operand(rbx, rdi, times_4, 0), rdx); - break; - default: - UNREACHABLE(); - break; - } - __ ret(0); } // Slow case: call runtime. @@ -1212,6 +1185,13 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) { // rsp[argc * 8] : argument 1 // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- + + if (id == IC::kCallIC_Miss) { + __ IncrementCounter(&Counters::call_miss, 1); + } else { + __ IncrementCounter(&Counters::keyed_call_miss, 1); + } + // Get the receiver of the function from the stack; 1 ~ return address. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); @@ -1233,22 +1213,25 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) { __ LeaveInternalFrame(); // Check if the receiver is a global object of some sort. - Label invoke, global; - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver - __ JumpIfSmi(rdx, &invoke); - __ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx); - __ j(equal, &global); - __ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE); - __ j(not_equal, &invoke); - - // Patch the receiver on the stack. - __ bind(&global); - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); - __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); + // This can happen only for regular CallIC but not KeyedCallIC. + if (id == IC::kCallIC_Miss) { + Label invoke, global; + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver + __ JumpIfSmi(rdx, &invoke); + __ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx); + __ j(equal, &global); + __ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE); + __ j(not_equal, &invoke); + + // Patch the receiver on the stack. + __ bind(&global); + __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); + __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); + __ bind(&invoke); + } // Invoke the function. ParameterCount actual(argc); - __ bind(&invoke); __ InvokeFunction(rdi, actual, JUMP_FUNCTION); } @@ -1309,13 +1292,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, } -static void GenerateNormalHelper(MacroAssembler* masm, - int argc, - bool is_global_object, - Label* miss) { +static void GenerateFunctionTailCall(MacroAssembler* masm, + int argc, + Label* miss) { // ----------- S t a t e ------------- // rcx : function name - // rdx : receiver + // rdi : function // rsp[0] : return address // rsp[8] : argument argc // rsp[16] : argument argc - 1 @@ -1323,21 +1305,11 @@ static void GenerateNormalHelper(MacroAssembler* masm, // rsp[argc * 8] : argument 1 // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- - // Search dictionary - put result in register rdx. - GenerateDictionaryLoad( - masm, miss, rax, rdx, rbx, rcx, rdi, rdi, CHECK_DICTIONARY); - __ JumpIfSmi(rdi, miss); // Check that the value is a JavaScript function. __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rdx); __ j(not_equal, miss); - // Patch the receiver with the global proxy if necessary. - if (is_global_object) { - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); - __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); - } - // Invoke the function. ParameterCount actual(argc); __ InvokeFunction(rdi, actual, JUMP_FUNCTION); @@ -1355,56 +1327,18 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { // rsp[argc * 8] : argument 1 // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- - Label miss, global_object, non_global_object; + Label miss; // Get the receiver of the function from the stack. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - // Check that the receiver isn't a smi. - __ JumpIfSmi(rdx, &miss); + GenerateDictionaryLoadReceiverCheck(masm, rdx, rax, rbx, &miss); - // Check that the receiver is a valid JS object. - // Because there are so many map checks and type checks, do not - // use CmpObjectType, but load map and type into registers. - __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); - __ movb(rax, FieldOperand(rbx, Map::kInstanceTypeOffset)); - __ cmpb(rax, Immediate(FIRST_JS_OBJECT_TYPE)); - __ j(below, &miss); - - // If this assert fails, we have to check upper bound too. - ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); - - // Check for access to global object. - __ cmpb(rax, Immediate(JS_GLOBAL_OBJECT_TYPE)); - __ j(equal, &global_object); - __ cmpb(rax, Immediate(JS_BUILTINS_OBJECT_TYPE)); - __ j(not_equal, &non_global_object); - - // Accessing global object: Load and invoke. - __ bind(&global_object); - // Check that the global object does not require access checks. - __ movb(rbx, FieldOperand(rbx, Map::kBitFieldOffset)); - __ testb(rbx, Immediate(1 << Map::kIsAccessCheckNeeded)); - __ j(not_equal, &miss); - GenerateNormalHelper(masm, argc, true, &miss); - - // Accessing non-global object: Check for access to global proxy. - Label global_proxy, invoke; - __ bind(&non_global_object); - __ cmpb(rax, Immediate(JS_GLOBAL_PROXY_TYPE)); - __ j(equal, &global_proxy); - // Check that the non-global, non-global-proxy object does not - // require access checks. - __ movb(rbx, FieldOperand(rbx, Map::kBitFieldOffset)); - __ testb(rbx, Immediate(1 << Map::kIsAccessCheckNeeded)); - __ j(not_equal, &miss); - __ bind(&invoke); - GenerateNormalHelper(masm, argc, false, &miss); + // rax: elements + // Search the dictionary placing the result in rdi. + GenerateDictionaryLoad(masm, &miss, rax, rcx, rbx, rdi, rdi); - // Global object proxy access: Check access rights. - __ bind(&global_proxy); - __ CheckAccessGlobalProxy(rdx, rax, &miss); - __ jmp(&invoke); + GenerateFunctionTailCall(masm, argc, &miss); __ bind(&miss); } @@ -1498,7 +1432,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // Now the key is known to be a smi. This place is also jumped to from below // where a numeric string is converted to a smi. - GenerateKeyedLoadReceiverCheck(masm, rdx, rax, &slow_call); + GenerateKeyedLoadReceiverCheck( + masm, rdx, rax, Map::kHasIndexedInterceptor, &slow_call); GenerateFastArrayLoad( masm, rdx, rcx, rax, rbx, rdi, &check_number_dictionary, &slow_load); @@ -1508,14 +1443,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // receiver in rdx is not used after this point. // rcx: key // rdi: function - - // Check that the value in edi is a JavaScript function. - __ JumpIfSmi(rdi, &slow_call); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax); - __ j(not_equal, &slow_call); - // Invoke the function. - ParameterCount actual(argc); - __ InvokeFunction(rdi, actual, JUMP_FUNCTION); + GenerateFunctionTailCall(masm, argc, &slow_call); __ bind(&check_number_dictionary); // eax: elements @@ -1523,6 +1451,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // Check whether the elements is a number dictionary. __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), Heap::kHashTableMapRootIndex); + __ j(not_equal, &slow_load); __ SmiToInteger32(rbx, rcx); // ebx: untagged index GenerateNumberDictionaryLoad(masm, &slow_load, rax, rcx, rbx, r9, rdi, rdi); @@ -1550,15 +1479,15 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // If the receiver is a regular JS object with slow properties then do // a quick inline probe of the receiver's dictionary. // Otherwise do the monomorphic cache probe. - GenerateKeyedLoadReceiverCheck(masm, rdx, rax, &lookup_monomorphic_cache); + GenerateKeyedLoadReceiverCheck( + masm, rdx, rax, Map::kHasNamedInterceptor, &lookup_monomorphic_cache); __ movq(rbx, FieldOperand(rdx, JSObject::kPropertiesOffset)); __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset), Heap::kHashTableMapRootIndex); __ j(not_equal, &lookup_monomorphic_cache); - GenerateDictionaryLoad( - masm, &slow_load, rbx, rdx, rax, rcx, rdi, rdi, DICTIONARY_CHECK_DONE); + GenerateDictionaryLoad(masm, &slow_load, rbx, rcx, rax, rdi, rdi); __ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1); __ jmp(&do_call); @@ -1620,6 +1549,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) { // -- rsp[0] : return address // ----------------------------------- + __ IncrementCounter(&Counters::load_miss, 1); + __ pop(rbx); __ push(rax); // receiver __ push(rcx); // name @@ -1683,38 +1614,15 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // -- rcx : name // -- rsp[0] : return address // ----------------------------------- - Label miss, probe, global; - - // Check that the receiver isn't a smi. - __ JumpIfSmi(rax, &miss); - - // Check that the receiver is a valid JS object. - __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx); - __ j(below, &miss); - - // If this assert fails, we have to check upper bound too. - ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); - - // Check for access to global object (unlikely). - __ CmpInstanceType(rbx, JS_GLOBAL_PROXY_TYPE); - __ j(equal, &global); + Label miss; - // Check for non-global object that requires access check. - __ testl(FieldOperand(rbx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsAccessCheckNeeded)); - __ j(not_zero, &miss); + GenerateDictionaryLoadReceiverCheck(masm, rax, rdx, rbx, &miss); + // rdx: elements // Search the dictionary placing the result in rax. - __ bind(&probe); - GenerateDictionaryLoad(masm, &miss, rdx, rax, rbx, - rcx, rdi, rax, CHECK_DICTIONARY); + GenerateDictionaryLoad(masm, &miss, rdx, rcx, rbx, rdi, rax); __ ret(0); - // Global object access: Check access rights. - __ bind(&global); - __ CheckAccessGlobalProxy(rax, rdx, &miss); - __ jmp(&probe); - // Cache miss: Jump to runtime. __ bind(&miss); GenerateMiss(masm); diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 24bac7d369..32cd2dbc13 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -652,8 +652,8 @@ Condition MacroAssembler::CheckBothPositiveSmi(Register first, if (first.is(second)) { return CheckPositiveSmi(first); } - movl(kScratchRegister, first); - orl(kScratchRegister, second); + movq(kScratchRegister, first); + or_(kScratchRegister, second); rol(kScratchRegister, Immediate(1)); testl(kScratchRegister, Immediate(0x03)); return zero; @@ -1678,8 +1678,7 @@ void MacroAssembler::Ret() { void MacroAssembler::FCmp() { fucomip(); - ffree(0); - fincstp(); + fstp(0); } diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index bb0b68105b..44573f34fa 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -546,7 +546,8 @@ class MacroAssembler: public Assembler { Register map, Register instance_type); - // FCmp is similar to integer cmp, but requires unsigned + // FCmp compares and pops the two values on top of the FPU stack. + // The flag results are similar to integer cmp, but requires unsigned // jcc instructions (je, ja, jae, jb, jbe, je, and jz). void FCmp(); diff --git a/deps/v8/src/x64/virtual-frame-x64.cc b/deps/v8/src/x64/virtual-frame-x64.cc index e65378dc07..f5e17fd18f 100644 --- a/deps/v8/src/x64/virtual-frame-x64.cc +++ b/deps/v8/src/x64/virtual-frame-x64.cc @@ -115,25 +115,45 @@ void VirtualFrame::AllocateStackSlots() { Handle undefined = Factory::undefined_value(); FrameElement initial_value = FrameElement::ConstantElement(undefined, FrameElement::SYNCED); - if (count == 1) { - __ Push(undefined); - } else if (count < kLocalVarBound) { - // For less locals the unrolled loop is more compact. - __ movq(kScratchRegister, undefined, RelocInfo::EMBEDDED_OBJECT); + if (count < kLocalVarBound) { + // For fewer locals the unrolled loop is more compact. + + // Hope for one of the first eight registers, where the push operation + // takes only one byte (kScratchRegister needs the REX.W bit). + Result tmp = cgen()->allocator()->Allocate(); + ASSERT(tmp.is_valid()); + __ movq(tmp.reg(), undefined, RelocInfo::EMBEDDED_OBJECT); for (int i = 0; i < count; i++) { - __ push(kScratchRegister); + __ push(tmp.reg()); } } else { // For more locals a loop in generated code is more compact. Label alloc_locals_loop; Result cnt = cgen()->allocator()->Allocate(); ASSERT(cnt.is_valid()); - __ movq(cnt.reg(), Immediate(count)); __ movq(kScratchRegister, undefined, RelocInfo::EMBEDDED_OBJECT); +#ifdef DEBUG + Label loop_size; + __ bind(&loop_size); +#endif + if (is_uint8(count)) { + // Loading imm8 is shorter than loading imm32. + // Loading only partial byte register, and using decb below. + __ movb(cnt.reg(), Immediate(count)); + } else { + __ movl(cnt.reg(), Immediate(count)); + } __ bind(&alloc_locals_loop); __ push(kScratchRegister); - __ decl(cnt.reg()); + if (is_uint8(count)) { + __ decb(cnt.reg()); + } else { + __ decl(cnt.reg()); + } __ j(not_zero, &alloc_locals_loop); +#ifdef DEBUG + CHECK(masm()->SizeOfCodeGeneratedSince(&loop_size) < kLocalVarBound); +#endif } for (int i = 0; i < count; i++) { elements_.Add(initial_value); diff --git a/deps/v8/src/x64/virtual-frame-x64.h b/deps/v8/src/x64/virtual-frame-x64.h index dc270fea0a..0549e3cc2a 100644 --- a/deps/v8/src/x64/virtual-frame-x64.h +++ b/deps/v8/src/x64/virtual-frame-x64.h @@ -200,7 +200,7 @@ class VirtualFrame : public ZoneObject { inline void PrepareForReturn(); // Number of local variables after when we use a loop for allocating. - static const int kLocalVarBound = 7; + static const int kLocalVarBound = 14; // Allocate and initialize the frame-allocated locals. void AllocateStackSlots(); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index c426db40b5..0cf3f7ba41 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -5035,6 +5035,31 @@ THREADED_TEST(AccessControlGetOwnPropertyNames) { } +static v8::Handle NamedPropertyEnumerator(const AccessorInfo& info) { + v8::Handle result = v8::Array::New(1); + result->Set(0, v8_str("x")); + return result; +} + + +THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { + v8::HandleScope handle_scope; + v8::Handle obj_template = v8::ObjectTemplate::New(); + + obj_template->Set(v8_str("x"), v8::Integer::New(42)); + obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, + NamedPropertyEnumerator); + + LocalContext context; + v8::Handle global = context->Global(); + global->Set(v8_str("object"), obj_template->NewInstance()); + + v8::Handle value = + CompileRun("Object.getOwnPropertyNames(object).join(',')"); + CHECK_EQ(v8_str("x"), value); +} + + static v8::Handle ConstTenGetter(Local name, const AccessorInfo& info) { return v8_num(10); diff --git a/deps/v8/test/cctest/test-profile-generator.cc b/deps/v8/test/cctest/test-profile-generator.cc index 308f764ab4..ea477de661 100644 --- a/deps/v8/test/cctest/test-profile-generator.cc +++ b/deps/v8/test/cctest/test-profile-generator.cc @@ -653,7 +653,7 @@ TEST(SampleRateCalculator) { time += SampleRateCalculator::kWallTimeQueryIntervalMs * 0.75; calc2.UpdateMeasurements(time); // (1.0 + 2.0 + 2.0) / 3 - CHECK_EQ(kSamplingIntervalMs * 1.66666, calc2.ticks_per_ms()); + CHECK_EQ(kSamplingIntervalMs * 5.0, floor(calc2.ticks_per_ms() * 3.0 + 0.5)); SampleRateCalculator calc3; time = 0.0; @@ -667,7 +667,7 @@ TEST(SampleRateCalculator) { time += SampleRateCalculator::kWallTimeQueryIntervalMs * 1.5; calc3.UpdateMeasurements(time); // (1.0 + 0.5 + 0.5) / 3 - CHECK_EQ(kSamplingIntervalMs * 0.66666, calc3.ticks_per_ms()); + CHECK_EQ(kSamplingIntervalMs * 2.0, floor(calc3.ticks_per_ms() * 3.0 + 0.5)); } diff --git a/deps/v8/test/mjsunit/for-in.js b/deps/v8/test/mjsunit/for-in.js index e3436ff3be..ab35e95ee3 100644 --- a/deps/v8/test/mjsunit/for-in.js +++ b/deps/v8/test/mjsunit/for-in.js @@ -84,3 +84,38 @@ var result = ''; for (var p in { get a() {}, set a(x) {}, b : 1}) { result += p; } assertEquals('ab', result, "abgetset"); + +// Test that for-in in the global scope works with a keyed property as "each". +// Test outside a loop and in a loop for multiple iterations. +a = [1,2,3,4]; +x = {foo:5, bar:6, zip:7, glep:9, 10:11}; +delete x.bar; +y = {} + +for (a[2] in x) { + y[a[2]] = x[a[2]]; +} + +assertEquals(5, y.foo, "y.foo"); +assertEquals("undefined", typeof y.bar, "y.bar"); +assertEquals(7, y.zip, "y.zip"); +assertEquals(9, y.glep, "y.glep"); +assertEquals(11, y[10], "y[10]"); +assertEquals("undefined", typeof y[2], "y[2]"); +assertEquals("undefined", typeof y[0], "y[0]"); + +for (i=0 ; i < 3; ++i) { + y = {} + + for (a[2] in x) { + y[a[2]] = x[a[2]]; + } + + assertEquals(5, y.foo, "y.foo"); + assertEquals("undefined", typeof y.bar, "y.bar"); + assertEquals(7, y.zip, "y.zip"); + assertEquals(9, y.glep, "y.glep"); + assertEquals(11, y[10], "y[10]"); + assertEquals("undefined", typeof y[2], "y[2]"); + assertEquals("undefined", typeof y[0], "y[0]"); +} diff --git a/deps/v8/test/mjsunit/regress/regress-45469.js b/deps/v8/test/mjsunit/regress/regress-45469.js new file mode 100644 index 0000000000..832a73f063 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-45469.js @@ -0,0 +1,46 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that global regexps capture and fail in the correct cyclic way. + +var re = /x/g; + +for (var i = 0; i < 15; i++) { + assertEquals(i % 3, re.lastIndex, "preindex" + i); + var res = re.exec("xx"); + assertEquals(i % 3 == 2 ? null : ["x"], res, "res" + i); +} + +re = /x/g; + +for (var i = 0; i < 15; i++) { + assertEquals(i % 3, re.lastIndex, "testpreindex" + i); + var res = re.test("xx"); + assertEquals(i % 3 != 2, res, "testres" + i); +} + + diff --git a/deps/v8/test/mjsunit/regress/regress-752.js b/deps/v8/test/mjsunit/regress/regress-752.js new file mode 100644 index 0000000000..1142a1fffa --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-752.js @@ -0,0 +1,36 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that JSON.stringify correctly unwraps Boolean objects. + +// See: http://code.google.com/p/v8/issues/detail?id=752 + +function replacer(key, value) { + return value === 42 ? new Boolean(false) : value; +} + +assertEquals(JSON.stringify([42], replacer), "[false]"); diff --git a/deps/v8/test/mjsunit/regress/regress-754.js b/deps/v8/test/mjsunit/regress/regress-754.js new file mode 100644 index 0000000000..0b8441628b --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-754.js @@ -0,0 +1,39 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that Array.prototype.lastIndexOf correctly handles null and undefined +// as fromIndex argument. + +// See: http://code.google.com/p/v8/issues/detail?id=754 + +var a = new Array(1,2,1); +assertEquals(1, a.lastIndexOf(2)); +assertEquals(2, a.lastIndexOf(1)); +assertEquals(0, a.lastIndexOf(1, undefined)); +assertEquals(0, a.lastIndexOf(1, null)); +assertEquals(-1, a.lastIndexOf(2, undefined)); +assertEquals(-1, a.lastIndexOf(2, null)); diff --git a/deps/v8/test/mjsunit/smi-ops.js b/deps/v8/test/mjsunit/smi-ops.js index d5bd21405b..499535c097 100644 --- a/deps/v8/test/mjsunit/smi-ops.js +++ b/deps/v8/test/mjsunit/smi-ops.js @@ -685,3 +685,8 @@ assertEquals(24, LeftShiftThreeBy(3)); assertEquals(24, LeftShiftThreeBy(35)); assertEquals(24, LeftShiftThreeBy(67)); assertEquals(24, LeftShiftThreeBy(-29)); + +// Regression test for a bug in the ARM code generator. For some register +// allocations we got the Smi overflow case wrong. +function f(x, y) { return y + ( 1 << (x & 31)); } +assertEquals(-2147483647, f(31, 1));