From 84f9178e664e7a779cff10656848f89eb6795a73 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 17 Jun 2009 18:15:28 +0200 Subject: [PATCH] upgrade v8 to 1.2.8 --- deps/v8/ChangeLog | 14 + deps/v8/include/v8.h | 16 +- deps/v8/src/SConscript | 3 +- deps/v8/src/api.cc | 5 +- deps/v8/src/arm/builtins-arm.cc | 20 +- deps/v8/src/arm/codegen-arm-inl.h | 10 + deps/v8/src/arm/codegen-arm.cc | 937 +++++++++++++----- deps/v8/src/arm/codegen-arm.h | 9 + deps/v8/src/arm/constants-arm.h | 20 +- deps/v8/src/arm/cpu-arm.cc | 4 +- deps/v8/src/arm/disasm-arm.cc | 18 +- deps/v8/src/arm/frames-arm.h | 242 +---- deps/v8/src/arm/ic-arm.cc | 30 +- deps/v8/src/arm/jump-target-arm.cc | 4 +- deps/v8/src/arm/macro-assembler-arm.cc | 126 ++- deps/v8/src/arm/macro-assembler-arm.h | 35 +- deps/v8/src/arm/simulator-arm.cc | 181 +++- deps/v8/src/arm/simulator-arm.h | 10 +- deps/v8/src/arm/stub-cache-arm.cc | 16 +- deps/v8/src/arm/virtual-frame-arm.cc | 8 +- deps/v8/src/array.js | 63 ++ deps/v8/src/assembler.cc | 31 +- deps/v8/src/assembler.h | 42 +- deps/v8/src/bootstrapper.cc | 3 + deps/v8/src/builtins.cc | 3 +- deps/v8/src/code-stubs.cc | 2 +- deps/v8/src/code-stubs.h | 2 + deps/v8/src/codegen.cc | 11 +- deps/v8/src/codegen.h | 18 +- deps/v8/src/compiler.cc | 28 +- deps/v8/src/d8-debug.h | 2 +- deps/v8/src/d8.cc | 20 +- deps/v8/src/d8.js | 125 ++- deps/v8/src/date-delay.js | 77 +- deps/v8/src/debug-delay.js | 67 +- deps/v8/src/debug.cc | 14 + deps/v8/src/flag-definitions.h | 2 + deps/v8/src/frames-inl.h | 25 +- deps/v8/src/frames.cc | 14 +- deps/v8/src/frames.h | 8 +- deps/v8/src/heap.cc | 35 +- deps/v8/src/heap.h | 5 +- deps/v8/src/ia32/assembler-ia32.cc | 19 +- deps/v8/src/ia32/assembler-ia32.h | 2 + deps/v8/src/ia32/codegen-ia32-inl.h | 10 + deps/v8/src/ia32/codegen-ia32.cc | 716 +++++++++---- deps/v8/src/ia32/codegen-ia32.h | 9 + deps/v8/src/ia32/frames-ia32.h | 173 +--- deps/v8/src/ia32/ic-ia32.cc | 27 +- deps/v8/src/ia32/jump-target-ia32.cc | 6 +- deps/v8/src/ia32/macro-assembler-ia32.cc | 24 +- deps/v8/src/ia32/macro-assembler-ia32.h | 7 +- deps/v8/src/ia32/virtual-frame-ia32.cc | 20 +- deps/v8/src/ia32/virtual-frame-ia32.h | 7 +- deps/v8/src/ic.cc | 35 +- deps/v8/src/ic.h | 11 + deps/v8/src/jump-target.cc | 173 +--- deps/v8/src/jump-target.h | 51 +- deps/v8/src/log-utils.cc | 187 +++- deps/v8/src/log-utils.h | 67 +- deps/v8/src/log.cc | 171 +++- deps/v8/src/log.h | 57 +- deps/v8/src/macros.py | 1 + deps/v8/src/math.js | 92 +- deps/v8/src/messages.js | 63 +- deps/v8/src/mirror-delay.js | 174 +++- deps/v8/src/objects.cc | 60 +- deps/v8/src/objects.h | 33 +- deps/v8/src/parser.cc | 15 + .../v8/src/regexp-macro-assembler-irregexp.cc | 1 + deps/v8/src/runtime.cc | 488 +++++++-- deps/v8/src/runtime.h | 4 +- deps/v8/src/runtime.js | 114 ++- deps/v8/src/serialize.cc | 32 +- deps/v8/src/stub-cache.cc | 60 +- deps/v8/src/utils.h | 5 + deps/v8/src/v8-counters.h | 2 + deps/v8/src/v8.cc | 37 +- deps/v8/src/v8.h | 13 +- deps/v8/src/version.cc | 4 +- deps/v8/src/x64/assembler-x64-inl.h | 28 +- deps/v8/src/x64/assembler-x64.cc | 314 +++--- deps/v8/src/x64/assembler-x64.h | 129 +-- deps/v8/src/x64/builtins-x64.cc | 240 ++++- deps/v8/src/x64/codegen-x64-inl.h | 11 + deps/v8/src/x64/codegen-x64.cc | 466 ++++++++- deps/v8/src/x64/codegen-x64.h | 17 + deps/v8/src/x64/frames-x64.cc | 45 + deps/v8/src/x64/frames-x64.h | 58 +- deps/v8/src/x64/ic-x64.cc | 13 + deps/v8/src/x64/macro-assembler-x64.cc | 599 ++++++++++- deps/v8/src/x64/macro-assembler-x64.h | 30 +- deps/v8/src/x64/register-allocator-x64-inl.h | 37 +- deps/v8/src/x64/register-allocator-x64.cc | 63 ++ deps/v8/src/x64/register-allocator-x64.h | 2 +- deps/v8/src/x64/simulator-x64.h | 1 + deps/v8/src/x64/virtual-frame-x64.cc | 169 ++++ deps/v8/src/x64/virtual-frame-x64.h | 4 +- deps/v8/test/cctest/test-api.cc | 75 +- deps/v8/test/cctest/test-assembler-x64.cc | 15 +- deps/v8/test/cctest/test-debug.cc | 140 ++- deps/v8/test/cctest/test-log-utils.cc | 183 +++- deps/v8/test/mjsunit/array-sort.js | 38 +- deps/v8/test/mjsunit/big-object-literal.js | 2 +- deps/v8/test/mjsunit/debug-scopes.js | 660 ++++++++++++ deps/v8/test/mjsunit/debug-sourceinfo.js | 92 +- deps/v8/test/mjsunit/sin-cos.js | 45 + deps/v8/test/mjsunit/smi-ops.js | 7 + deps/v8/tools/tickprocessor.js | 24 +- 109 files changed, 6736 insertions(+), 2046 deletions(-) create mode 100644 deps/v8/test/mjsunit/debug-scopes.js create mode 100644 deps/v8/test/mjsunit/sin-cos.js diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 3df6885a21..41b3234581 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,17 @@ +2009-06-16: Version 1.2.8 + + Optimized math on ARM platforms. + + Fixed two crash bugs in the handling of getters and setters. + + Improved the debugger support by adding scope chain information. + + Improved the profiler support by compressing log data transmitted + to clients. + + Improved overall performance. + + 2009-06-08: Version 1.2.7 Improved debugger and profiler support. diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 87ce2a20cf..e7b26778a9 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -212,9 +212,9 @@ template class V8EXPORT_INLINE Handle { */ bool IsEmpty() const { return val_ == 0; } - T* operator->() const; + T* operator->() const { return val_; } - T* operator*() const; + T* operator*() const { return val_; } /** * Sets the handle to be empty. IsEmpty() will then return true. @@ -2509,18 +2509,6 @@ void Persistent::ClearWeak() { V8::ClearWeak(reinterpret_cast(**this)); } -template -T* Handle::operator->() const { - return val_; -} - - -template -T* Handle::operator*() const { - return val_; -} - - Local Arguments::operator[](int i) const { if (i < 0 || length_ <= i) return Local(*Undefined()); return Local(reinterpret_cast(values_ - i)); diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index 64d20631ca..f1ca8753c2 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -77,7 +77,8 @@ SOURCES = { 'x64/debug-x64.cc', 'x64/frames-x64.cc', 'x64/ic-x64.cc', 'x64/jump-target-x64.cc', 'x64/macro-assembler-x64.cc', # 'x64/regexp-macro-assembler-x64.cc', - 'x64/stub-cache-x64.cc' + 'x64/register-allocator-x64.cc', + 'x64/stub-cache-x64.cc', 'x64/virtual-frame-x64.cc' ], 'simulator:arm': ['arm/simulator-arm.cc'], 'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'], diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 7b7f290816..1001847904 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -2124,7 +2124,9 @@ int v8::Object::GetIdentityHash() { } else { int attempts = 0; do { - hash_value = random() & i::Smi::kMaxValue; // Limit range to fit a smi. + // Generate a random 32-bit hash value but limit range to fit + // within a smi. + hash_value = i::V8::Random() & i::Smi::kMaxValue; attempts++; } while (hash_value == 0 && attempts < 30); hash_value = hash_value != 0 ? hash_value : 1; // never return 0 @@ -3382,6 +3384,7 @@ void Debug::SetMessageHandler(v8::Debug::MessageHandler handler, void Debug::SetMessageHandler2(v8::Debug::MessageHandler2 handler) { EnsureInitialized("v8::Debug::SetMessageHandler"); ENTER_V8; + HandleScope scope; i::Debugger::SetMessageHandler(handler); } diff --git a/deps/v8/src/arm/builtins-arm.cc b/deps/v8/src/arm/builtins-arm.cc index 588798bdea..6d23a19bb2 100644 --- a/deps/v8/src/arm/builtins-arm.cc +++ b/deps/v8/src/arm/builtins-arm.cc @@ -64,9 +64,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &non_function_call); // Check that the function is a JSFunction. - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(JS_FUNCTION_TYPE)); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); __ b(ne, &non_function_call); // Enter a construct frame. @@ -159,9 +157,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // If the type of the result (stored in its map) is less than // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense. - __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset)); - __ cmp(r3, Operand(FIRST_JS_OBJECT_TYPE)); + __ CompareObjectType(r0, r3, r3, FIRST_JS_OBJECT_TYPE); __ b(ge, &exit); // Throw away the result of the constructor invocation and use the @@ -290,9 +286,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &non_function); - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(JS_FUNCTION_TYPE)); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); __ b(eq, &function); // Non-function called: Clear the function to force exception. @@ -328,9 +322,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ cmp(r2, r3); __ b(eq, &use_global_receiver); - __ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset)); - __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset)); - __ cmp(r3, Operand(FIRST_JS_OBJECT_TYPE)); + __ CompareObjectType(r2, r3, r3, FIRST_JS_OBJECT_TYPE); __ b(lt, &call_to_object); __ cmp(r3, Operand(LAST_JS_OBJECT_TYPE)); __ b(le, &done); @@ -501,9 +493,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // Check if the receiver is already a JavaScript object. // r0: receiver - __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); - __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE)); + __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE); __ b(lt, &call_to_object); __ cmp(r1, Operand(LAST_JS_OBJECT_TYPE)); __ b(le, &push_receiver); diff --git a/deps/v8/src/arm/codegen-arm-inl.h b/deps/v8/src/arm/codegen-arm-inl.h index 544331a522..5a29a45002 100644 --- a/deps/v8/src/arm/codegen-arm-inl.h +++ b/deps/v8/src/arm/codegen-arm-inl.h @@ -39,6 +39,16 @@ namespace internal { void DeferredCode::Jump() { __ jmp(&entry_label_); } void DeferredCode::Branch(Condition cc) { __ b(cc, &entry_label_); } +void CodeGenerator::GenerateMathSin(ZoneList* args) { + GenerateFastMathOp(SIN, args); +} + + +void CodeGenerator::GenerateMathCos(ZoneList* args) { + GenerateFastMathOp(COS, args); +} + + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 7428d3b597..8c28b24347 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -289,7 +289,6 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { // r0: result // sp: stack pointer // fp: frame pointer - // pp: parameter pointer // cp: callee's context __ mov(r0, Operand(Factory::undefined_value())); @@ -703,6 +702,7 @@ class GenericBinaryOpStub : public CodeStub { } void Generate(MacroAssembler* masm); + void HandleNonSmiBitwiseOp(MacroAssembler* masm); const char* GetName() { switch (op_) { @@ -1503,9 +1503,7 @@ void CodeGenerator::GenerateFastCaseSwitchJumpTable( // Test for a Smi value in a HeapNumber. __ tst(r0, Operand(kSmiTagMask)); is_smi.Branch(eq); - __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); - __ cmp(r1, Operand(HEAP_NUMBER_TYPE)); + __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE); default_target->Branch(ne); frame_->EmitPush(r0); frame_->CallRuntime(Runtime::kNumberToSmi, 1); @@ -1872,9 +1870,7 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { // Check if enumerable is already a JSObject __ tst(r0, Operand(kSmiTagMask)); primitive.Branch(eq); - __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); - __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE)); + __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE); jsobject.Branch(hs); primitive.Bind(); @@ -2107,14 +2103,16 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { // Get an external reference to the handler address. ExternalReference handler_address(Top::k_handler_address); - // The next handler address is at kNextIndex in the stack. - const int kNextIndex = StackHandlerConstants::kNextOffset / kPointerSize; // If we can fall off the end of the try block, unlink from try chain. if (has_valid_frame()) { - __ ldr(r1, frame_->ElementAt(kNextIndex)); + // The next handler address is on top of the frame. Unlink from + // the handler list and drop the rest of this handler from the + // frame. + ASSERT(StackHandlerConstants::kNextOffset == 0); + frame_->EmitPop(r1); __ mov(r3, Operand(handler_address)); __ str(r1, MemOperand(r3)); - frame_->Drop(StackHandlerConstants::kSize / kPointerSize); + frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); if (has_unlinks) { exit.Jump(); } @@ -2134,15 +2132,11 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { // break from (eg, for...in) may have left stuff on the stack. __ mov(r3, Operand(handler_address)); __ ldr(sp, MemOperand(r3)); - // The stack pointer was restored to just below the code slot - // (the topmost slot) in the handler. - frame_->Forget(frame_->height() - handler_height + 1); + frame_->Forget(frame_->height() - handler_height); - // kNextIndex is off by one because the code slot has already - // been dropped. - __ ldr(r1, frame_->ElementAt(kNextIndex - 1)); + ASSERT(StackHandlerConstants::kNextOffset == 0); + frame_->EmitPop(r1); __ str(r1, MemOperand(r3)); - // The code slot has already been dropped from the handler. frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); if (!function_return_is_shadowed_ && i == kReturnShadowIndex) { @@ -2223,15 +2217,15 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { // Get an external reference to the handler address. ExternalReference handler_address(Top::k_handler_address); - // The next handler address is at kNextIndex in the stack. - const int kNextIndex = StackHandlerConstants::kNextOffset / kPointerSize; // If we can fall off the end of the try block, unlink from the try // chain and set the state on the frame to FALLING. if (has_valid_frame()) { - __ ldr(r1, frame_->ElementAt(kNextIndex)); + // The next handler address is on top of the frame. + ASSERT(StackHandlerConstants::kNextOffset == 0); + frame_->EmitPop(r1); __ mov(r3, Operand(handler_address)); __ str(r1, MemOperand(r3)); - frame_->Drop(StackHandlerConstants::kSize / kPointerSize); + frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); // Fake a top of stack value (unneeded when FALLING) and set the // state in r2, then jump around the unlink blocks if any. @@ -2262,17 +2256,14 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { // stack. __ mov(r3, Operand(handler_address)); __ ldr(sp, MemOperand(r3)); - // The stack pointer was restored to the address slot in the handler. - ASSERT(StackHandlerConstants::kNextOffset == 1 * kPointerSize); - frame_->Forget(frame_->height() - handler_height + 1); + frame_->Forget(frame_->height() - handler_height); // Unlink this handler and drop it from the frame. The next - // handler address is now on top of the frame. + // handler address is currently on top of the frame. + ASSERT(StackHandlerConstants::kNextOffset == 0); frame_->EmitPop(r1); __ str(r1, MemOperand(r3)); - // The top (code) and the second (handler) slot have both been - // dropped already. - frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 2); + frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); if (i == kReturnShadowIndex) { // If this label shadowed the function return, materialize the @@ -3281,11 +3272,8 @@ void CodeGenerator::GenerateValueOf(ZoneList* args) { // if (object->IsSmi()) return the object. __ tst(r0, Operand(kSmiTagMask)); leave.Branch(eq); - // It is a heap object - get map. - __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); - // if (!object->IsJSValue()) return the object. - __ cmp(r1, Operand(JS_VALUE_TYPE)); + // It is a heap object - get map. If (!object->IsJSValue()) return the object. + __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE); leave.Branch(ne); // Load the value. __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset)); @@ -3305,11 +3293,8 @@ void CodeGenerator::GenerateSetValueOf(ZoneList* args) { // if (object->IsSmi()) return object. __ tst(r1, Operand(kSmiTagMask)); leave.Branch(eq); - // It is a heap object - get map. - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - // if (!object->IsJSValue()) return object. - __ cmp(r2, Operand(JS_VALUE_TYPE)); + // It is a heap object - get map. If (!object->IsJSValue()) return the object. + __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE); leave.Branch(ne); // Store the value. __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset)); @@ -3381,11 +3366,8 @@ void CodeGenerator::GenerateIsArray(ZoneList* args) { __ and_(r1, r0, Operand(kSmiTagMask)); __ eor(r1, r1, Operand(kSmiTagMask), SetCC); answer.Branch(ne); - // It is a heap object - get the map. - __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); - // Check if the object is a JS array or not. - __ cmp(r1, Operand(JS_ARRAY_TYPE)); + // It is a heap object - get the map. Check if the object is a JS array. + __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE); answer.Bind(); cc_reg_ = eq; } @@ -3423,6 +3405,30 @@ void CodeGenerator::GenerateArgumentsAccess(ZoneList* args) { } +void CodeGenerator::GenerateRandomPositiveSmi(ZoneList* args) { + VirtualFrame::SpilledScope spilled_scope; + ASSERT(args->length() == 0); + __ Call(ExternalReference::random_positive_smi_function().address(), + RelocInfo::RUNTIME_ENTRY); + frame_->EmitPush(r0); +} + + +void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* args) { + VirtualFrame::SpilledScope spilled_scope; + LoadAndSpill(args->at(0)); + switch (op) { + case SIN: + frame_->CallRuntime(Runtime::kMath_sin, 1); + break; + case COS: + frame_->CallRuntime(Runtime::kMath_cos, 1); + break; + } + frame_->EmitPush(r0); +} + + void CodeGenerator::GenerateObjectEquals(ZoneList* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); @@ -3571,7 +3577,10 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { break; case Token::SUB: { - UnarySubStub stub; + bool overwrite = + (node->AsBinaryOperation() != NULL && + node->AsBinaryOperation()->ResultOverwriteAllowed()); + UnarySubStub stub(overwrite); frame_->CallStub(&stub, 0); break; } @@ -4001,9 +4010,7 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { } else if (check->Equals(Heap::function_symbol())) { __ tst(r1, Operand(kSmiTagMask)); false_target()->Branch(eq); - __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); - __ cmp(r1, Operand(JS_FUNCTION_TYPE)); + __ CompareObjectType(r1, r1, r1, JS_FUNCTION_TYPE); cc_reg_ = eq; } else if (check->Equals(Heap::object_symbol())) { @@ -4076,13 +4083,9 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { } case Token::INSTANCEOF: { - Result arg_count = allocator_->Allocate(r0); - ASSERT(arg_count.is_valid()); - __ mov(arg_count.reg(), Operand(1)); // not counting receiver - Result result = frame_->InvokeBuiltin(Builtins::INSTANCE_OF, - CALL_JS, - &arg_count, - 2); + InstanceofStub stub; + Result result = frame_->CallStub(&stub, 2); + // At this point if instanceof succeeded then r0 == 0. __ tst(result.reg(), Operand(result.reg())); cc_reg_ = eq; break; @@ -4341,6 +4344,229 @@ void Reference::SetValue(InitState init_state) { } +// Count leading zeros in a 32 bit word. On ARM5 and later it uses the clz +// instruction. On pre-ARM5 hardware this routine gives the wrong answer for 0 +// (31 instead of 32). +static void CountLeadingZeros( + MacroAssembler* masm, + Register source, + Register scratch, + Register zeros) { +#ifdef __ARM_ARCH_5__ + __ clz(zeros, source); // This instruction is only supported after ARM5. +#else + __ mov(zeros, Operand(0)); + __ mov(scratch, source); + // Top 16. + __ tst(scratch, Operand(0xffff0000)); + __ add(zeros, zeros, Operand(16), LeaveCC, eq); + __ mov(scratch, Operand(scratch, LSL, 16), LeaveCC, eq); + // Top 8. + __ tst(scratch, Operand(0xff000000)); + __ add(zeros, zeros, Operand(8), LeaveCC, eq); + __ mov(scratch, Operand(scratch, LSL, 8), LeaveCC, eq); + // Top 4. + __ tst(scratch, Operand(0xf0000000)); + __ add(zeros, zeros, Operand(4), LeaveCC, eq); + __ mov(scratch, Operand(scratch, LSL, 4), LeaveCC, eq); + // Top 2. + __ tst(scratch, Operand(0xc0000000)); + __ add(zeros, zeros, Operand(2), LeaveCC, eq); + __ mov(scratch, Operand(scratch, LSL, 2), LeaveCC, eq); + // Top bit. + __ tst(scratch, Operand(0x80000000)); + __ add(zeros, zeros, Operand(1), LeaveCC, eq); +#endif +} + + +// Takes a Smi and converts to an IEEE 64 bit floating point value in two +// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and +// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a +// scratch register. Destroys the source register. No GC occurs during this +// stub so you don't have to set up the frame. +class ConvertToDoubleStub : public CodeStub { + public: + ConvertToDoubleStub(Register result_reg_1, + Register result_reg_2, + Register source_reg, + Register scratch_reg) + : result1_(result_reg_1), + result2_(result_reg_2), + source_(source_reg), + zeros_(scratch_reg) { } + + private: + Register result1_; + Register result2_; + Register source_; + Register zeros_; + + // Minor key encoding in 16 bits. + class ModeBits: public BitField {}; + class OpBits: public BitField {}; + + Major MajorKey() { return ConvertToDouble; } + int MinorKey() { + // Encode the parameters in a unique 16 bit value. + return result1_.code() + + (result2_.code() << 4) + + (source_.code() << 8) + + (zeros_.code() << 12); + } + + void Generate(MacroAssembler* masm); + + const char* GetName() { return "ConvertToDoubleStub"; } + +#ifdef DEBUG + void Print() { PrintF("ConvertToDoubleStub\n"); } +#endif +}; + + +void ConvertToDoubleStub::Generate(MacroAssembler* masm) { +#ifndef BIG_ENDIAN_FLOATING_POINT + Register exponent = result1_; + Register mantissa = result2_; +#else + Register exponent = result2_; + Register mantissa = result1_; +#endif + Label not_special; + // Convert from Smi to integer. + __ mov(source_, Operand(source_, ASR, kSmiTagSize)); + // Move sign bit from source to destination. This works because the sign bit + // in the exponent word of the double has the same position and polarity as + // the 2's complement sign bit in a Smi. + ASSERT(HeapNumber::kSignMask == 0x80000000u); + __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC); + // Subtract from 0 if source was negative. + __ rsb(source_, source_, Operand(0), LeaveCC, ne); + __ cmp(source_, Operand(1)); + __ b(gt, ¬_special); + + // We have -1, 0 or 1, which we treat specially. + __ cmp(source_, Operand(0)); + // For 1 or -1 we need to or in the 0 exponent (biased to 1023). + static const uint32_t exponent_word_for_1 = + HeapNumber::kExponentBias << HeapNumber::kExponentShift; + __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, ne); + // 1, 0 and -1 all have 0 for the second word. + __ mov(mantissa, Operand(0)); + __ Ret(); + + __ bind(¬_special); + // Count leading zeros. Uses result2 for a scratch register on pre-ARM5. + // Gets the wrong answer for 0, but we already checked for that case above. + CountLeadingZeros(masm, source_, mantissa, zeros_); + // Compute exponent and or it into the exponent register. + // We use result2 as a scratch register here. + __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias)); + __ orr(exponent, + exponent, + Operand(mantissa, LSL, HeapNumber::kExponentShift)); + // Shift up the source chopping the top bit off. + __ add(zeros_, zeros_, Operand(1)); + // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0. + __ mov(source_, Operand(source_, LSL, zeros_)); + // Compute lower part of fraction (last 12 bits). + __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord)); + // And the top (top 20 bits). + __ orr(exponent, + exponent, + Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord)); + __ Ret(); +} + + +// This stub can convert a signed int32 to a heap number (double). It does +// not work for int32s that are in Smi range! No GC occurs during this stub +// so you don't have to set up the frame. +class WriteInt32ToHeapNumberStub : public CodeStub { + public: + WriteInt32ToHeapNumberStub(Register the_int, + Register the_heap_number, + Register scratch) + : the_int_(the_int), + the_heap_number_(the_heap_number), + scratch_(scratch) { } + + private: + Register the_int_; + Register the_heap_number_; + Register scratch_; + + // Minor key encoding in 16 bits. + class ModeBits: public BitField {}; + class OpBits: public BitField {}; + + Major MajorKey() { return WriteInt32ToHeapNumber; } + int MinorKey() { + // Encode the parameters in a unique 16 bit value. + return the_int_.code() + + (the_heap_number_.code() << 4) + + (scratch_.code() << 8); + } + + void Generate(MacroAssembler* masm); + + const char* GetName() { return "WriteInt32ToHeapNumberStub"; } + +#ifdef DEBUG + void Print() { PrintF("WriteInt32ToHeapNumberStub\n"); } +#endif +}; + + +// See comment for class. +void WriteInt32ToHeapNumberStub::Generate(MacroAssembler *masm) { + Label max_negative_int; + // the_int_ has the answer which is a signed int32 but not a Smi. + // We test for the special value that has a different exponent. This test + // has the neat side effect of setting the flags according to the sign. + ASSERT(HeapNumber::kSignMask == 0x80000000u); + __ cmp(the_int_, Operand(0x80000000)); + __ b(eq, &max_negative_int); + // Set up the correct exponent in scratch_. All non-Smi int32s have the same. + // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). + uint32_t non_smi_exponent = + (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift; + __ mov(scratch_, Operand(non_smi_exponent)); + // Set the sign bit in scratch_ if the value was negative. + __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs); + // Subtract from 0 if the value was negative. + __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs); + // We should be masking the implict first digit of the mantissa away here, + // but it just ends up combining harmlessly with the last digit of the + // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get + // the most significant 1 to hit the last bit of the 12 bit sign and exponent. + ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0); + const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2; + __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance)); + __ str(scratch_, FieldMemOperand(the_heap_number_, + HeapNumber::kExponentOffset)); + __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance)); + __ str(scratch_, FieldMemOperand(the_heap_number_, + HeapNumber::kMantissaOffset)); + __ Ret(); + + __ bind(&max_negative_int); + // The max negative int32 is stored as a positive number in the mantissa of + // a double because it uses a sign bit instead of using two's complement. + // The actual mantissa bits stored are all 0 because the implicit most + // significant 1 bit is not stored. + non_smi_exponent += 1 << HeapNumber::kExponentShift; + __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent)); + __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset)); + __ mov(ip, Operand(0)); + __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset)); + __ Ret(); +} + + +// Allocates a heap number or jumps to the label if the young space is full and +// a scavenge is needed. static void AllocateHeapNumber( MacroAssembler* masm, Label* need_gc, // Jump here if young space is full. @@ -4379,78 +4605,121 @@ static void AllocateHeapNumber( // We fall into this code if the operands were Smis, but the result was // not (eg. overflow). We branch into this code (to the not_smi label) if -// the operands were not both Smi. +// the operands were not both Smi. The operands are in r0 and r1. In order +// to call the C-implemented binary fp operation routines we need to end up +// with the double precision floating point operands in r0 and r1 (for the +// value in r1) and r2 and r3 (for the value in r0). static void HandleBinaryOpSlowCases(MacroAssembler* masm, Label* not_smi, const Builtins::JavaScript& builtin, Token::Value operation, - int swi_number, OverwriteMode mode) { - Label slow; + Label slow, slow_pop_2_first, do_the_call; + Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1; + // Smi-smi case (overflow). + // Since both are Smis there is no heap number to overwrite, so allocate. + // The new heap number is in r5. r6 and r7 are scratch. + AllocateHeapNumber(masm, &slow, r5, r6, r7); + // Write Smi from r0 to r3 and r2 in double format. r6 is scratch. + ConvertToDoubleStub stub1(r3, r2, r0, r6); + __ push(lr); + __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET); + // Write Smi from r1 to r1 and r0 in double format. r6 is scratch. + __ mov(r7, Operand(r1)); + ConvertToDoubleStub stub2(r1, r0, r7, r6); + __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET); + __ pop(lr); + __ jmp(&do_the_call); // Tail call. No return. + + // We jump to here if something goes wrong (one param is not a number of any + // sort or new-space allocation fails). __ bind(&slow); __ push(r1); __ push(r0); __ mov(r0, Operand(1)); // Set number of arguments. - __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. + __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return. + // We branch here if at least one of r0 and r1 is not a Smi. __ bind(not_smi); + if (mode == NO_OVERWRITE) { + // In the case where there is no chance of an overwritable float we may as + // well do the allocation immediately while r0 and r1 are untouched. + AllocateHeapNumber(masm, &slow, r5, r6, r7); + } + + // Move r0 to a double in r2-r3. __ tst(r0, Operand(kSmiTagMask)); - __ b(eq, &slow); // We can't handle a Smi-double combination yet. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &slow); // We can't handle a Smi-double combination yet. - // Get map of r0 into r2. - __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); - // Get type of r0 into r3. - __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r3, Operand(HEAP_NUMBER_TYPE)); - __ b(ne, &slow); - // Get type of r1 into r3. - __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); - // Check they are both the same map (heap number map). - __ cmp(r2, r3); + __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number. + __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE); __ b(ne, &slow); - // Both are doubles. + if (mode == OVERWRITE_RIGHT) { + __ mov(r5, Operand(r0)); // Overwrite this heap number. + } // Calling convention says that second double is in r2 and r3. __ ldr(r2, FieldMemOperand(r0, HeapNumber::kValueOffset)); - __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset + kPointerSize)); - - if (mode == NO_OVERWRITE) { - // Get address of new heap number into r5. + __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset + 4)); + __ jmp(&finished_loading_r0); + __ bind(&r0_is_smi); + if (mode == OVERWRITE_RIGHT) { + // We can't overwrite a Smi so get address of new heap number into r5. AllocateHeapNumber(masm, &slow, r5, r6, r7); - __ push(lr); - __ push(r5); - } else if (mode == OVERWRITE_LEFT) { - __ push(lr); - __ push(r1); - } else { - ASSERT(mode == OVERWRITE_RIGHT); - __ push(lr); - __ push(r0); + } + // Write Smi from r0 to r3 and r2 in double format. + __ mov(r7, Operand(r0)); + ConvertToDoubleStub stub3(r3, r2, r7, r6); + __ push(lr); + __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET); + __ pop(lr); + __ bind(&finished_loading_r0); + + // Move r1 to a double in r0-r1. + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number. + __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE); + __ b(ne, &slow); + if (mode == OVERWRITE_LEFT) { + __ mov(r5, Operand(r1)); // Overwrite this heap number. } // Calling convention says that first double is in r0 and r1. __ ldr(r0, FieldMemOperand(r1, HeapNumber::kValueOffset)); - __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset + kPointerSize)); + __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset + 4)); + __ jmp(&finished_loading_r1); + __ bind(&r1_is_smi); + if (mode == OVERWRITE_LEFT) { + // We can't overwrite a Smi so get address of new heap number into r5. + AllocateHeapNumber(masm, &slow, r5, r6, r7); + } + // Write Smi from r1 to r1 and r0 in double format. + __ mov(r7, Operand(r1)); + ConvertToDoubleStub stub4(r1, r0, r7, r6); + __ push(lr); + __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET); + __ pop(lr); + __ bind(&finished_loading_r1); + + __ bind(&do_the_call); + // r0: Left value (least significant part of mantissa). + // r1: Left value (sign, exponent, top of mantissa). + // r2: Right value (least significant part of mantissa). + // r3: Right value (sign, exponent, top of mantissa). + // r5: Address of heap number for result. + __ push(lr); // For later. + __ push(r5); // Address of heap number that is answer. // Call C routine that may not cause GC or other trouble. __ mov(r5, Operand(ExternalReference::double_fp_operation(operation))); -#if !defined(__arm__) - // Notify the simulator that we are calling an add routine in C. - __ swi(swi_number); -#else - // Actually call the add routine written in C. __ Call(r5); -#endif // Store answer in the overwritable heap number. __ pop(r4); -#if !defined(__ARM_EABI__) && defined(__arm__) +#if !defined(USE_ARM_EABI) // Double returned in fp coprocessor register 0 and 1, encoded as register // cr8. Offsets must be divisible by 4 for coprocessor so we need to // substract the tag from r4. __ sub(r5, r4, Operand(kHeapObjectTag)); __ stc(p1, cr8, MemOperand(r5, HeapNumber::kValueOffset)); #else - // Double returned in fp coprocessor register 0 and 1. + // Double returned in registers 0 and 1. __ str(r0, FieldMemOperand(r4, HeapNumber::kValueOffset)); - __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset + kPointerSize)); + __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset + 4)); #endif __ mov(r0, Operand(r4)); // And we are done. @@ -4458,6 +4727,185 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm, } +// Tries to get a signed int32 out of a double precision floating point heap +// number. Rounds towards 0. Only succeeds for doubles that are in the ranges +// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds +// almost to the range of signed int32 values that are not Smis. Jumps to the +// label if the double isn't in the range it can cope with. +static void GetInt32(MacroAssembler* masm, + Register source, + Register dest, + Register scratch, + Label* slow) { + Register scratch2 = dest; + // Get exponent word. + __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset)); + // Get exponent alone in scratch2. + __ and_(scratch2, scratch, Operand(HeapNumber::kExponentMask)); + // Check whether the exponent matches a 32 bit signed int that is not a Smi. + // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). + const uint32_t non_smi_exponent = + (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift; + __ cmp(scratch2, Operand(non_smi_exponent)); + // If not, then we go slow. + __ b(ne, slow); + // Get the top bits of the mantissa. + __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask)); + // Put back the implicit 1. + __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift)); + // Shift up the mantissa bits to take up the space the exponent used to take. + // We just orred in the implicit bit so that took care of one and we want to + // leave the sign bit 0 so we subtract 2 bits from the shift distance. + const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2; + __ mov(scratch2, Operand(scratch2, LSL, shift_distance)); + // Put sign in zero flag. + __ tst(scratch, Operand(HeapNumber::kSignMask)); + // Get the second half of the double. + __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset)); + // Shift down 22 bits to get the last 10 bits. + __ orr(dest, scratch2, Operand(scratch, LSR, 32 - shift_distance)); + // Fix sign if sign bit was set. + __ rsb(dest, dest, Operand(0), LeaveCC, ne); +} + + +// For bitwise ops where the inputs are not both Smis we here try to determine +// whether both inputs are either Smis or at least heap numbers that can be +// represented by a 32 bit signed value. We truncate towards zero as required +// by the ES spec. If this is the case we do the bitwise op and see if the +// result is a Smi. If so, great, otherwise we try to find a heap number to +// write the answer into (either by allocating or by overwriting). +// On entry the operands are in r0 and r1. On exit the answer is in r0. +void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm) { + Label slow, result_not_a_smi; + Label r0_is_smi, r1_is_smi; + Label done_checking_r0, done_checking_r1; + + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number. + __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE); + __ b(ne, &slow); + GetInt32(masm, r1, r3, r4, &slow); + __ jmp(&done_checking_r1); + __ bind(&r1_is_smi); + __ mov(r3, Operand(r1, ASR, 1)); + __ bind(&done_checking_r1); + + __ tst(r0, Operand(kSmiTagMask)); + __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number. + __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE); + __ b(ne, &slow); + GetInt32(masm, r0, r2, r4, &slow); + __ jmp(&done_checking_r0); + __ bind(&r0_is_smi); + __ mov(r2, Operand(r0, ASR, 1)); + __ bind(&done_checking_r0); + + // r0 and r1: Original operands (Smi or heap numbers). + // r2 and r3: Signed int32 operands. + switch (op_) { + case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break; + case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break; + case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break; + case Token::SAR: + // Use only the 5 least significant bits of the shift count. + __ and_(r2, r2, Operand(0x1f)); + __ mov(r2, Operand(r3, ASR, r2)); + break; + case Token::SHR: + // Use only the 5 least significant bits of the shift count. + __ and_(r2, r2, Operand(0x1f)); + __ mov(r2, Operand(r3, LSR, r2), SetCC); + // SHR is special because it is required to produce a positive answer. + // The code below for writing into heap numbers isn't capable of writing + // the register as an unsigned int so we go to slow case if we hit this + // case. + __ b(mi, &slow); + break; + case Token::SHL: + // Use only the 5 least significant bits of the shift count. + __ and_(r2, r2, Operand(0x1f)); + __ mov(r2, Operand(r3, LSL, r2)); + break; + default: UNREACHABLE(); + } + // check that the *signed* result fits in a smi + __ add(r3, r2, Operand(0x40000000), SetCC); + __ b(mi, &result_not_a_smi); + __ mov(r0, Operand(r2, LSL, kSmiTagSize)); + __ Ret(); + + Label have_to_allocate, got_a_heap_number; + __ bind(&result_not_a_smi); + switch (mode_) { + case OVERWRITE_RIGHT: { + __ tst(r0, Operand(kSmiTagMask)); + __ b(eq, &have_to_allocate); + __ mov(r5, Operand(r0)); + break; + } + case OVERWRITE_LEFT: { + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &have_to_allocate); + __ mov(r5, Operand(r1)); + break; + } + case NO_OVERWRITE: { + // Get a new heap number in r5. r6 and r7 are scratch. + AllocateHeapNumber(masm, &slow, r5, r6, r7); + } + default: break; + } + __ bind(&got_a_heap_number); + // r2: Answer as signed int32. + // r5: Heap number to write answer into. + + // Nothing can go wrong now, so move the heap number to r0, which is the + // result. + __ mov(r0, Operand(r5)); + + // Tail call that writes the int32 in r2 to the heap number in r0, using + // r3 as scratch. r0 is preserved and returned. + WriteInt32ToHeapNumberStub stub(r2, r0, r3); + __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); + + if (mode_ != NO_OVERWRITE) { + __ bind(&have_to_allocate); + // Get a new heap number in r5. r6 and r7 are scratch. + AllocateHeapNumber(masm, &slow, r5, r6, r7); + __ jmp(&got_a_heap_number); + } + + // If all else failed then we go to the runtime system. + __ bind(&slow); + __ push(r1); // restore stack + __ push(r0); + __ mov(r0, Operand(1)); // 1 argument (not counting receiver). + switch (op_) { + case Token::BIT_OR: + __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); + break; + case Token::BIT_AND: + __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); + break; + case Token::BIT_XOR: + __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); + break; + case Token::SAR: + __ InvokeBuiltin(Builtins::SAR, JUMP_JS); + break; + case Token::SHR: + __ InvokeBuiltin(Builtins::SHR, JUMP_JS); + break; + case Token::SHL: + __ InvokeBuiltin(Builtins::SHL, JUMP_JS); + break; + default: + UNREACHABLE(); + } +} + + void GenericBinaryOpStub::Generate(MacroAssembler* masm) { // r1 : x // r0 : y @@ -4483,7 +4931,6 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ¬_smi, Builtins::ADD, Token::ADD, - assembler::arm::simulator_fp_add, mode_); break; } @@ -4503,7 +4950,6 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ¬_smi, Builtins::SUB, Token::SUB, - assembler::arm::simulator_fp_sub, mode_); break; } @@ -4532,14 +4978,16 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ¬_smi, Builtins::MUL, Token::MUL, - assembler::arm::simulator_fp_mul, - mode_); + mode_); break; } case Token::BIT_OR: case Token::BIT_AND: - case Token::BIT_XOR: { + case Token::BIT_XOR: + case Token::SAR: + case Token::SHR: + case Token::SHL: { Label slow; ASSERT(kSmiTag == 0); // adjust code below __ tst(r2, Operand(kSmiTagMask)); @@ -4548,84 +4996,47 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { case Token::BIT_OR: __ orr(r0, r0, Operand(r1)); break; case Token::BIT_AND: __ and_(r0, r0, Operand(r1)); break; case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break; - default: UNREACHABLE(); - } - __ Ret(); - __ bind(&slow); - __ push(r1); // restore stack - __ push(r0); - __ mov(r0, Operand(1)); // 1 argument (not counting receiver). - switch (op_) { - case Token::BIT_OR: - __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); - break; - case Token::BIT_AND: - __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); - break; - case Token::BIT_XOR: - __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); - break; - default: - UNREACHABLE(); - } - break; - } - - case Token::SHL: - case Token::SHR: - case Token::SAR: { - Label slow; - ASSERT(kSmiTag == 0); // adjust code below - __ tst(r2, Operand(kSmiTagMask)); - __ b(ne, &slow); - // remove tags from operands (but keep sign) - __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x - __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y - // use only the 5 least significant bits of the shift count - __ and_(r2, r2, Operand(0x1f)); - // perform operation - switch (op_) { case Token::SAR: - __ mov(r3, Operand(r3, ASR, r2)); - // no checks of result necessary + // Remove tags from right operand. + __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y + // Use only the 5 least significant bits of the shift count. + __ and_(r2, r2, Operand(0x1f)); + __ mov(r0, Operand(r1, ASR, r2)); + // Smi tag result. + __ and_(r0, r0, Operand(~kSmiTagMask)); break; - case Token::SHR: + // Remove tags from operands. We can't do this on a 31 bit number + // because then the 0s get shifted into bit 30 instead of bit 31. + __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x + __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y + // Use only the 5 least significant bits of the shift count. + __ and_(r2, r2, Operand(0x1f)); __ mov(r3, Operand(r3, LSR, r2)); - // check that the *unsigned* result fits in a smi - // neither of the two high-order bits can be set: - // - 0x80000000: high bit would be lost when smi tagging - // - 0x40000000: this number would convert to negative when - // smi tagging these two cases can only happen with shifts - // by 0 or 1 when handed a valid smi - __ and_(r2, r3, Operand(0xc0000000), SetCC); + // Unsigned shift is not allowed to produce a negative number, so + // check the sign bit and the sign bit after Smi tagging. + __ tst(r3, Operand(0xc0000000)); __ b(ne, &slow); + // Smi tag result. + __ mov(r0, Operand(r3, LSL, kSmiTagSize)); break; - case Token::SHL: + // Remove tags from operands. + __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x + __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y + // Use only the 5 least significant bits of the shift count. + __ and_(r2, r2, Operand(0x1f)); __ mov(r3, Operand(r3, LSL, r2)); - // check that the *signed* result fits in a smi + // Check that the signed result fits in a Smi. __ add(r2, r3, Operand(0x40000000), SetCC); __ b(mi, &slow); + __ mov(r0, Operand(r3, LSL, kSmiTagSize)); break; - default: UNREACHABLE(); } - // tag result and store it in r0 - ASSERT(kSmiTag == 0); // adjust code below - __ mov(r0, Operand(r3, LSL, kSmiTagSize)); __ Ret(); - // slow case __ bind(&slow); - __ push(r1); // restore stack - __ push(r0); - __ mov(r0, Operand(1)); // 1 argument (not counting receiver). - switch (op_) { - case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break; - case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break; - case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break; - default: UNREACHABLE(); - } + HandleNonSmiBitwiseOp(masm); break; } @@ -4657,10 +5068,11 @@ void UnarySubStub::Generate(MacroAssembler* masm) { Label undo; Label slow; Label done; + Label not_smi; // Enter runtime system if the value is not a smi. __ tst(r0, Operand(kSmiTagMask)); - __ b(ne, &slow); + __ b(ne, ¬_smi); // Enter runtime system if the value of the expression is zero // to make sure that we switch between 0 and -0. @@ -4672,33 +5084,59 @@ void UnarySubStub::Generate(MacroAssembler* masm) { __ rsb(r1, r0, Operand(0), SetCC); __ b(vs, &slow); - // If result is a smi we are done. - __ tst(r1, Operand(kSmiTagMask)); - __ mov(r0, Operand(r1), LeaveCC, eq); // conditionally set r0 to result - __ b(eq, &done); + __ mov(r0, Operand(r1)); // Set r0 to result. + __ StubReturn(1); // Enter runtime system. __ bind(&slow); __ push(r0); - __ mov(r0, Operand(0)); // set number of arguments + __ mov(r0, Operand(0)); // Set number of arguments. __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS); __ bind(&done); __ StubReturn(1); + + __ bind(¬_smi); + __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE); + __ b(ne, &slow); + // r0 is a heap number. Get a new heap number in r1. + if (overwrite_) { + __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); + __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign. + __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); + } else { + AllocateHeapNumber(masm, &slow, r1, r2, r3); + __ ldr(r2, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); + __ str(r2, FieldMemOperand(r1, HeapNumber::kMantissaOffset)); + __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); + __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign. + __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset)); + __ mov(r0, Operand(r1)); + } + __ StubReturn(1); } void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { - // r0 holds exception - ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code + // r0 holds the exception. + + // Adjust this code if not the case. + ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // Drop the sp to the top of the handler. __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); __ ldr(sp, MemOperand(r3)); - __ pop(r2); // pop next in chain + + // Restore the next handler and frame pointer, discard handler state. + ASSERT(StackHandlerConstants::kNextOffset == 0); + __ pop(r2); __ str(r2, MemOperand(r3)); - // restore parameter- and frame-pointer and pop state. - __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); - // Before returning we restore the context from the frame pointer if not NULL. - // The frame pointer is NULL in the exception handler of a JS entry frame. + ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state. + + // Before returning we restore the context from the frame pointer if + // not NULL. The frame pointer is NULL in the exception handler of a + // JS entry frame. __ cmp(fp, Operand(0)); // Set cp to NULL if fp is NULL. __ mov(cp, Operand(0), LeaveCC, eq); @@ -4709,39 +5147,41 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { __ mov(lr, Operand(pc)); } #endif + ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); __ pop(pc); } void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { - // Fetch top stack handler. + // Adjust this code if not the case. + ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // Drop sp to the top stack handler. __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); - __ ldr(r3, MemOperand(r3)); + __ ldr(sp, MemOperand(r3)); // Unwind the handlers until the ENTRY handler is found. Label loop, done; __ bind(&loop); // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kAddressDisplacement + - StackHandlerConstants::kStateOffset; - __ ldr(r2, MemOperand(r3, kStateOffset)); + const int kStateOffset = StackHandlerConstants::kStateOffset; + __ ldr(r2, MemOperand(sp, kStateOffset)); __ cmp(r2, Operand(StackHandler::ENTRY)); __ b(eq, &done); // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kAddressDisplacement + - StackHandlerConstants::kNextOffset; - __ ldr(r3, MemOperand(r3, kNextOffset)); + const int kNextOffset = StackHandlerConstants::kNextOffset; + __ ldr(sp, MemOperand(sp, kNextOffset)); __ jmp(&loop); __ bind(&done); // Set the top handler address to next handler past the current ENTRY handler. - __ ldr(r0, MemOperand(r3, kNextOffset)); - __ mov(r2, Operand(ExternalReference(Top::k_handler_address))); - __ str(r0, MemOperand(r2)); + ASSERT(StackHandlerConstants::kNextOffset == 0); + __ pop(r0); + __ str(r0, MemOperand(r3)); // Set external caught exception to false. - __ mov(r0, Operand(false)); ExternalReference external_caught(Top::k_external_caught_exception_address); + __ mov(r0, Operand(false)); __ mov(r2, Operand(external_caught)); __ str(r0, MemOperand(r2)); @@ -4751,21 +5191,17 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); __ str(r0, MemOperand(r2)); - // Restore the stack to the address of the ENTRY handler - __ mov(sp, Operand(r3)); - - // Stack layout at this point. See also PushTryHandler - // r3, sp -> next handler - // state (ENTRY) - // pp - // fp - // lr - - // Discard ENTRY state (r2 is not used), and restore parameter- - // and frame-pointer and pop state. - __ ldm(ia_w, sp, r2.bit() | r3.bit() | pp.bit() | fp.bit()); - // Before returning we restore the context from the frame pointer if not NULL. - // The frame pointer is NULL in the exception handler of a JS entry frame. + // Stack layout at this point. See also StackHandlerConstants. + // sp -> state (ENTRY) + // fp + // lr + + // Discard handler state (r2 is not used) and restore frame pointer. + ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state. + // Before returning we restore the context from the frame pointer if + // not NULL. The frame pointer is NULL in the exception handler of a + // JS entry frame. __ cmp(fp, Operand(0)); // Set cp to NULL if fp is NULL. __ mov(cp, Operand(0), LeaveCC, eq); @@ -4776,6 +5212,7 @@ void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { __ mov(lr, Operand(pc)); } #endif + ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); __ pop(pc); } @@ -4793,7 +5230,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, if (do_gc) { // Passing r0. - __ Call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY); + ExternalReference gc_reference = ExternalReference::perform_gc_function(); + __ Call(gc_reference.address(), RelocInfo::RUNTIME_ENTRY); } ExternalReference scope_depth = @@ -4819,12 +5257,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // sequence that it is not moving ever. __ add(lr, pc, Operand(4)); // compute return address: (pc + 8) + 4 __ push(lr); -#if !defined(__arm__) - // Notify the simulator of the transition to C code. - __ swi(assembler::arm::call_rt_r5); -#else /* !defined(__arm__) */ __ Jump(r5); -#endif /* !defined(__arm__) */ if (always_allocate) { // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1 @@ -4847,7 +5280,6 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // r0:r1: result // sp: stack pointer // fp: frame pointer - // pp: caller's parameter pointer pp (restored as C callee-saved) __ LeaveExitFrame(frame_type); // check if we should retry or throw exception @@ -4887,9 +5319,8 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // r0: number of arguments including receiver // r1: pointer to builtin function // fp: frame pointer (restored after C call) - // sp: stack pointer (restored as callee's pp after C call) + // sp: stack pointer (restored as callee's sp after C call) // cp: current context (C callee-saved) - // pp: caller's parameter pointer pp (C callee-saved) // NOTE: Invocations of builtins may return failure objects // instead of a proper result. The builtin entry handles @@ -4960,7 +5391,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Called from C, so do not pop argc and args on exit (preserve sp) // No need to save register-passed args - // Save callee-saved registers (incl. cp, pp, and fp), sp, and lr + // Save callee-saved registers (incl. cp and fp), sp, and lr __ stm(db_w, sp, kCalleeSaved | lr.bit()); // Get address of argv, see stm above. @@ -5004,10 +5435,10 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ bind(&invoke); // Must preserve r0-r4, r5-r7 are available. __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); - // If an exception not caught by another handler occurs, this handler returns - // control to the code after the bl(&invoke) above, which restores all - // kCalleeSaved registers (including cp, pp and fp) to their saved values - // before returning a failure to C. + // If an exception not caught by another handler occurs, this handler + // returns control to the code after the bl(&invoke) above, which + // restores all kCalleeSaved registers (including cp and fp) to their + // saved values before returning a failure to C. // Clear any pending exceptions. __ mov(ip, Operand(ExternalReference::the_hole_value_location())); @@ -5070,6 +5501,66 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { } +// This stub performs an instanceof, calling the builtin function if +// necessary. Uses r1 for the object, r0 for the function that it may +// be an instance of (these are fetched from the stack). +void InstanceofStub::Generate(MacroAssembler* masm) { + // Get the object - slow case for smis (we may need to throw an exception + // depending on the rhs). + Label slow, loop, is_instance, is_not_instance; + __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); + __ BranchOnSmi(r0, &slow); + + // Check that the left hand is a JS object and put map in r3. + __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE); + __ b(lt, &slow); + __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE)); + __ b(gt, &slow); + + // Get the prototype of the function (r4 is result, r2 is scratch). + __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); + __ TryGetFunctionPrototype(r1, r4, r2, &slow); + + // Check that the function prototype is a JS object. + __ BranchOnSmi(r4, &slow); + __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE); + __ b(lt, &slow); + __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE)); + __ b(gt, &slow); + + // Register mapping: r3 is object map and r4 is function prototype. + // Get prototype of object into r2. + __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset)); + + // Loop through the prototype chain looking for the function prototype. + __ bind(&loop); + __ cmp(r2, Operand(r4)); + __ b(eq, &is_instance); + __ cmp(r2, Operand(Factory::null_value())); + __ b(eq, &is_not_instance); + __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset)); + __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset)); + __ jmp(&loop); + + __ bind(&is_instance); + __ mov(r0, Operand(Smi::FromInt(0))); + __ pop(); + __ pop(); + __ mov(pc, Operand(lr)); // Return. + + __ bind(&is_not_instance); + __ mov(r0, Operand(Smi::FromInt(1))); + __ pop(); + __ pop(); + __ mov(pc, Operand(lr)); // Return. + + // Slow-case. Tail call builtin. + __ bind(&slow); + __ mov(r0, Operand(1)); // Arg count without receiver. + __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS); +} + + void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) { // Check if the calling frame is an arguments adaptor frame. Label adaptor; @@ -5098,8 +5589,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { // Check that the key is a smi. Label slow; - __ tst(r1, Operand(kSmiTagMask)); - __ b(ne, &slow); + __ BranchOnNotSmi(r1, &slow); // Check if the calling frame is an arguments adaptor frame. Label adaptor; @@ -5171,12 +5661,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // Check that the function is really a JavaScript function. // r1: pushed function (to be verified) - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &slow); + __ BranchOnSmi(r1, &slow); // Get the map of the function object. - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(JS_FUNCTION_TYPE)); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); __ b(ne, &slow); // Fast-case: Invoke the function now. diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index a8cb777d79..0df793a4ae 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -349,6 +349,15 @@ class CodeGenerator: public AstVisitor { void GenerateLog(ZoneList* args); + // Fast support for Math.random(). + void GenerateRandomPositiveSmi(ZoneList* args); + + // Fast support for Math.sin and Math.cos. + enum MathOp { SIN, COS }; + void GenerateFastMathOp(MathOp op, ZoneList* args); + inline void GenerateMathSin(ZoneList* args); + inline void GenerateMathCos(ZoneList* args); + // Methods and constants for fast case switch statement support. // // Only allow fast-case switch if the range of labels is at most diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h index 99eab238c7..d5f967fcb2 100644 --- a/deps/v8/src/arm/constants-arm.h +++ b/deps/v8/src/arm/constants-arm.h @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2009 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: @@ -28,6 +28,14 @@ #ifndef V8_ARM_CONSTANTS_ARM_H_ #define V8_ARM_CONSTANTS_ARM_H_ +// The simulator emulates the EABI so we define the USE_ARM_EABI macro if we +// are not running on real ARM hardware. One reason for this is that the +// old ABI uses fp registers in the calling convention and the simulator does +// not simulate fp registers or coroutine instructions. +#if defined(__ARM_EABI__) || !defined(__arm__) +# define USE_ARM_EABI 1 +#endif + namespace assembler { namespace arm { @@ -104,15 +112,9 @@ enum Shift { // simulator. enum SoftwareInterruptCodes { // transition to C code - call_rt_r5 = 0x10, - call_rt_r2 = 0x11, + call_rt_redirected = 0x10, // break point - break_point = 0x20, - // FP operations. These simulate calling into C for a moment to do fp ops. - // They should trash all caller-save registers. - simulator_fp_add = 0x21, - simulator_fp_sub = 0x22, - simulator_fp_mul = 0x23 + break_point = 0x20 }; diff --git a/deps/v8/src/arm/cpu-arm.cc b/deps/v8/src/arm/cpu-arm.cc index 71da1ecc90..cafefce45d 100644 --- a/deps/v8/src/arm/cpu-arm.cc +++ b/deps/v8/src/arm/cpu-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-2009 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: @@ -46,6 +46,8 @@ void CPU::FlushICache(void* start, size_t size) { #if !defined (__arm__) // Not generating ARM instructions for C-code. This means that we are // building an ARM emulator based target. No I$ flushes are necessary. + // None of this code ends up in the snapshot so there are no issues + // around whether or not to generate the code when building snapshots. #else // Ideally, we would call // syscall(__ARM_NR_cacheflush, start, diff --git a/deps/v8/src/arm/disasm-arm.cc b/deps/v8/src/arm/disasm-arm.cc index f56a599f81..8083ce30af 100644 --- a/deps/v8/src/arm/disasm-arm.cc +++ b/deps/v8/src/arm/disasm-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2007-2008 the V8 project authors. All rights reserved. +// Copyright 2007-2009 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: @@ -253,24 +253,12 @@ void Decoder::PrintPU(Instr* instr) { // the FormatOption method. void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) { switch (swi) { - case call_rt_r5: - Print("call_rt_r5"); - return; - case call_rt_r2: - Print("call_rt_r2"); + case call_rt_redirected: + Print("call_rt_redirected"); return; case break_point: Print("break_point"); return; - case simulator_fp_add: - Print("simulator_fp_add"); - return; - case simulator_fp_mul: - Print("simulator_fp_mul"); - return; - case simulator_fp_sub: - Print("simulator_fp_sub"); - return; default: out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", diff --git a/deps/v8/src/arm/frames-arm.h b/deps/v8/src/arm/frames-arm.h index a67b18a2b6..0874c09274 100644 --- a/deps/v8/src/arm/frames-arm.h +++ b/deps/v8/src/arm/frames-arm.h @@ -68,7 +68,7 @@ static const RegList kCalleeSaved = 1 << 8 | // r8 v5 (cp in JavaScript code) kR9Available << 9 | // r9 v6 - 1 << 10 | // r10 v7 (pp in JavaScript code) + 1 << 10 | // r10 v7 1 << 11; // r11 v8 (fp in JavaScript code) static const int kNumCalleeSaved = 7 + kR9Available; @@ -79,15 +79,11 @@ static const int kNumCalleeSaved = 7 + kR9Available; class StackHandlerConstants : public AllStatic { public: - // TODO(1233780): Get rid of the code slot in stack handlers. - static const int kCodeOffset = 0 * kPointerSize; - static const int kNextOffset = 1 * kPointerSize; - static const int kStateOffset = 2 * kPointerSize; - static const int kPPOffset = 3 * kPointerSize; - static const int kFPOffset = 4 * kPointerSize; - static const int kPCOffset = 5 * kPointerSize; - - static const int kAddressDisplacement = -1 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kStateOffset = 1 * kPointerSize; + static const int kFPOffset = 2 * kPointerSize; + static const int kPCOffset = 3 * kPointerSize; + static const int kSize = kPCOffset + kPointerSize; }; @@ -108,14 +104,14 @@ class ExitFrameConstants : public AllStatic { static const int kSavedRegistersOffset = 0 * kPointerSize; - // Let the parameters pointer for exit frames point just below the - // frame structure on the stack. - static const int kPPDisplacement = 3 * kPointerSize; - // The caller fields are below the frame pointer on the stack. static const int kCallerFPOffset = +0 * kPointerSize; - static const int kCallerPPOffset = +1 * kPointerSize; + // The calling JS function is between FP and PC. static const int kCallerPCOffset = +2 * kPointerSize; + + // FP-relative displacement of the caller's SP. It points just + // below the saved PC. + static const int kCallerSPDisplacement = +3 * kPointerSize; }; @@ -137,7 +133,7 @@ class JavaScriptFrameConstants : public AllStatic { static const int kSavedRegistersOffset = +2 * kPointerSize; static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset; - // PP-relative. + // Caller SP-relative. static const int kParam0Offset = -2 * kPointerSize; static const int kReceiverOffset = -1 * kPointerSize; }; @@ -161,220 +157,6 @@ inline Object* JavaScriptFrame::function_slot_object() const { } -// ---------------------------------------------------- - - - - - // lower | Stack | - // addresses | ^ | - // | | | - // | | - // | JS frame | - // | | - // | | - // ----------- +=============+ <--- sp (stack pointer) - // | function | - // +-------------+ - // +-------------+ - // | | - // | expressions | - // | | - // +-------------+ - // | | - // a | locals | - // c | | - // t +- - - - - - -+ <--- - // i -4 | local0 | ^ - // v +-------------+ | - // a -3 | code | | - // t +-------------+ | kLocal0Offset - // i -2 | context | | - // o +-------------+ | - // n -1 | args_length | v - // +-------------+ <--- fp (frame pointer) - // 0 | caller_pp | - // f +-------------+ - // r 1 | caller_fp | - // a +-------------+ - // m 2 | sp_on_exit | (pp if return, caller_sp if no return) - // e +-------------+ - // 3 | caller_pc | - // +-------------+ <--- caller_sp (incl. parameters) - // | | - // | parameters | - // | | - // +- - - - - - -+ <--- - // -2 | parameter0 | ^ - // +-------------+ | kParam0Offset - // -1 | receiver | v - // ----------- +=============+ <--- pp (parameter pointer, r10) - // 0 | function | - // +-------------+ - // | | - // |caller-saved | (must be valid JS values, traversed during GC) - // | regs | - // | | - // +-------------+ - // | | - // | caller | - // higher | expressions | - // addresses | | - // | | - // | JS frame | - - - - // Handler frames (part of expressions of JS frames): - - // lower | Stack | - // addresses | ^ | - // | | | - // | | - // h | expressions | - // a | | - // n +-------------+ - // d -1 | code | - // l +-------------+ <--- handler sp - // e 0 | next_sp | link to next handler (next handler's sp) - // r +-------------+ - // 1 | state | - // f +-------------+ - // r 2 | pp | - // a +-------------+ - // m 3 | fp | - // e +-------------+ - // 4 | pc | - // +-------------+ - // | | - // higher | expressions | - // addresses | | - - - - // JS entry frames: When calling from C to JS, we construct two extra - // frames: An entry frame (C) and a trampoline frame (JS). The - // following pictures shows the two frames: - - // lower | Stack | - // addresses | ^ | - // | | | - // | | - // | JS frame | - // | | - // | | - // ----------- +=============+ <--- sp (stack pointer) - // | | - // | parameters | - // t | | - // r +- - - - - - -+ - // a | parameter0 | - // m +-------------+ - // p | receiver | - // o +-------------+ - // l | function | - // i +-------------+ - // n -3 | code | - // e +-------------+ - // -2 | NULL | context is always NULL - // +-------------+ - // f -1 | 0 | args_length is always zero - // r +-------------+ <--- fp (frame pointer) - // a 0 | NULL | caller pp is always NULL for entries - // m +-------------+ - // e 1 | caller_fp | - // +-------------+ - // 2 | sp_on_exit | (caller_sp) - // +-------------+ - // 3 | caller_pc | - // ----------- +=============+ <--- caller_sp == pp - // . ^ - // . | try-handler, fake, not GC'ed - // . v - // +-------------+ <--- - // -2 | next top pp | - // +-------------+ - // -1 | next top fp | - // +-------------+ <--- fp - // | r4 | r4-r9 holding non-JS values must be preserved - // +-------------+ - // J | r5 | before being initialized not to confuse GC - // S +-------------+ - // | r6 | - // +-------------+ - // e | r7 | - // n +-------------+ - // t | r8 | - // r +-------------+ - // y [ | r9 | ] only if r9 available - // +-------------+ - // | r10 | - // f +-------------+ - // r | r11 | - // a +-------------+ - // m | caller_sp | - // e +-------------+ - // | caller_pc | - // +-------------+ <--- caller_sp - // | argv | passed on stack from C code - // +-------------+ - // | | - // higher | | - // addresses | C frame | - - - // The first 4 args are passed from C in r0-r3 and are not spilled on entry: - // r0: code entry - // r1: function - // r2: receiver - // r3: argc - // [sp+0]: argv - - - // C entry frames: When calling from JS to C, we construct one extra - // frame: - - // lower | Stack | - // addresses | ^ | - // | | | - // | | - // | C frame | - // | | - // | | - // ----------- +=============+ <--- sp (stack pointer) - // | | - // | parameters | (first 4 args are passed in r0-r3) - // | | - // +-------------+ <--- fp (frame pointer) - // f 4/5 | caller_fp | - // r +-------------+ - // a 5/6 | sp_on_exit | (pp) - // m +-------------+ - // e 6/7 | caller_pc | - // +-------------+ <--- caller_sp (incl. parameters) - // 7/8 | | - // | parameters | - // | | - // +- - - - - - -+ <--- - // -2 | parameter0 | ^ - // +-------------+ | kParam0Offset - // -1 | receiver | v - // ----------- +=============+ <--- pp (parameter pointer, r10) - // 0 | function | - // +-------------+ - // | | - // |caller-saved | - // | regs | - // | | - // +-------------+ - // | | - // | caller | - // | expressions | - // | | - // higher | | - // addresses | JS frame | - - } } // namespace v8::internal #endif // V8_ARM_FRAMES_ARM_H_ diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index 9b45c46a8c..8b4e087e32 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -223,9 +223,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // Check for number. __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &number); - __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset)); - __ cmp(r3, Operand(HEAP_NUMBER_TYPE)); + __ CompareObjectType(r1, r3, r3, HEAP_NUMBER_TYPE); __ b(ne, &non_number); __ bind(&number); StubCompiler::GenerateLoadGlobalFunctionPrototype( @@ -272,9 +270,7 @@ static void GenerateNormalHelper(MacroAssembler* masm, __ b(eq, miss); // Check that the value is a JSFunction. - __ ldr(r0, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset)); - __ cmp(r0, Operand(JS_FUNCTION_TYPE)); + __ CompareObjectType(r1, r0, r0, JS_FUNCTION_TYPE); __ b(ne, miss); // Check that the function has been loaded. @@ -312,10 +308,8 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &miss); - // Check that the receiver is a valid JS object. - __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r0, FieldMemOperand(r3, Map::kInstanceTypeOffset)); - __ cmp(r0, Operand(FIRST_JS_OBJECT_TYPE)); + // 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. @@ -392,9 +386,7 @@ void CallIC::Generate(MacroAssembler* masm, __ ldr(r2, MemOperand(sp, argc * kPointerSize)); // receiver __ tst(r2, Operand(kSmiTagMask)); __ b(eq, &invoke); - __ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset)); - __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset)); - __ cmp(r3, Operand(JS_GLOBAL_OBJECT_TYPE)); + __ CompareObjectType(r2, r3, r3, JS_GLOBAL_OBJECT_TYPE); __ b(eq, &global); __ cmp(r3, Operand(JS_BUILTINS_OBJECT_TYPE)); __ b(ne, &invoke); @@ -447,10 +439,8 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { __ tst(r0, Operand(kSmiTagMask)); __ b(eq, &miss); - // Check that the receiver is a valid JS object. - __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r3, Map::kInstanceTypeOffset)); - __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE)); + // 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); @@ -513,6 +503,12 @@ bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { return false; } +void KeyedStoreIC::ClearInlinedVersion(Address address) {} +void KeyedStoreIC::RestoreInlinedVersion(Address address) {} +bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) { + return false; +} + Object* KeyedLoadIC_Miss(Arguments args); diff --git a/deps/v8/src/arm/jump-target-arm.cc b/deps/v8/src/arm/jump-target-arm.cc index 65e7eafa6f..a925c51bb4 100644 --- a/deps/v8/src/arm/jump-target-arm.cc +++ b/deps/v8/src/arm/jump-target-arm.cc @@ -149,7 +149,7 @@ void JumpTarget::Call() { } -void JumpTarget::DoBind(int mergable_elements) { +void JumpTarget::DoBind() { ASSERT(!is_bound()); // Live non-frame registers are not allowed at the start of a basic @@ -207,7 +207,7 @@ void JumpTarget::DoBind(int mergable_elements) { // Compute the frame to use for entry to the block. if (entry_frame_ == NULL) { - ComputeEntryFrame(mergable_elements); + ComputeEntryFrame(); } // Some moves required to merge to an expected frame require purely diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 4e24063c94..897b5a7c17 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-2009 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: @@ -35,11 +35,6 @@ namespace v8 { namespace internal { -// Give alias names to registers -Register cp = { 8 }; // JavaScript context pointer -Register pp = { 10 }; // parameter pointer - - MacroAssembler::MacroAssembler(void* buffer, int size) : Assembler(buffer, size), unresolved_(0), @@ -128,26 +123,10 @@ void MacroAssembler::Call(Register target, Condition cond) { void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode, Condition cond) { -#if !defined(__arm__) - if (rmode == RelocInfo::RUNTIME_ENTRY) { - mov(r2, Operand(target, rmode), LeaveCC, cond); - // Set lr for return at current pc + 8. - mov(lr, Operand(pc), LeaveCC, cond); - // Emit a ldr pc, [pc + offset of target in constant pool]. - // Notify the simulator of the transition to C code. - swi(assembler::arm::call_rt_r2); - } else { - // set lr for return at current pc + 8 - mov(lr, Operand(pc), LeaveCC, cond); - // emit a ldr pc, [pc + offset of target in constant pool] - mov(pc, Operand(target, rmode), LeaveCC, cond); - } -#else // Set lr for return at current pc + 8. mov(lr, Operand(pc), LeaveCC, cond); // Emit a ldr pc, [pc + offset of target in constant pool]. mov(pc, Operand(target, rmode), LeaveCC, cond); -#endif // !defined(__arm__) // If USE_BLX is defined, we could emit a 'mov ip, target', followed by a // 'blx ip'; however, the code would not be shorter than the above sequence // and the target address of the call would be referenced by the first @@ -301,8 +280,8 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { add(r6, sp, Operand(r0, LSL, kPointerSizeLog2)); sub(r6, r6, Operand(kPointerSize)); - // Compute parameter pointer before making changes and save it as ip - // register so that it is restored as sp register on exit, thereby + // Compute callee's stack pointer before making changes and save it as + // ip register so that it is restored as sp register on exit, thereby // popping the args. // ip = sp + kPointerSize * #args; @@ -573,41 +552,48 @@ void MacroAssembler::CopyRegistersFromStackToMemory(Register base, } #endif + void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { - ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code + // Adjust this code if not the case. + ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); // The pc (return address) is passed in register lr. if (try_location == IN_JAVASCRIPT) { - stm(db_w, sp, pp.bit() | fp.bit() | lr.bit()); if (type == TRY_CATCH_HANDLER) { mov(r3, Operand(StackHandler::TRY_CATCH)); } else { mov(r3, Operand(StackHandler::TRY_FINALLY)); } - push(r3); // state + ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize + && StackHandlerConstants::kFPOffset == 2 * kPointerSize + && StackHandlerConstants::kPCOffset == 3 * kPointerSize); + stm(db_w, sp, r3.bit() | fp.bit() | lr.bit()); + // Save the current handler as the next handler. mov(r3, Operand(ExternalReference(Top::k_handler_address))); ldr(r1, MemOperand(r3)); - push(r1); // next sp - str(sp, MemOperand(r3)); // chain handler - mov(r0, Operand(Smi::FromInt(StackHandler::kCodeNotPresent))); // new TOS - push(r0); + ASSERT(StackHandlerConstants::kNextOffset == 0); + push(r1); + // Link this handler as the new current one. + str(sp, MemOperand(r3)); } else { // Must preserve r0-r4, r5-r7 are available. ASSERT(try_location == IN_JS_ENTRY); - // The parameter pointer is meaningless here and fp does not point to a JS - // frame. So we save NULL for both pp and fp. We expect the code throwing an - // exception to check fp before dereferencing it to restore the context. - mov(pp, Operand(0)); // set pp to NULL - mov(ip, Operand(0)); // to save a NULL fp - stm(db_w, sp, pp.bit() | ip.bit() | lr.bit()); + // The frame pointer does not point to a JS frame so we save NULL + // for fp. We expect the code throwing an exception to check fp + // before dereferencing it to restore the context. + mov(ip, Operand(0)); // To save a NULL frame pointer. mov(r6, Operand(StackHandler::ENTRY)); - push(r6); // state + ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize + && StackHandlerConstants::kFPOffset == 2 * kPointerSize + && StackHandlerConstants::kPCOffset == 3 * kPointerSize); + stm(db_w, sp, r6.bit() | ip.bit() | lr.bit()); + // Save the current handler as the next handler. mov(r7, Operand(ExternalReference(Top::k_handler_address))); ldr(r6, MemOperand(r7)); - push(r6); // next sp - str(sp, MemOperand(r7)); // chain handler - mov(r5, Operand(Smi::FromInt(StackHandler::kCodeNotPresent))); // new TOS - push(r5); // flush TOS + ASSERT(StackHandlerConstants::kNextOffset == 0); + push(r6); + // Link this handler as the new current one. + str(sp, MemOperand(r7)); } } @@ -759,6 +745,62 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::CompareObjectType(Register function, + Register map, + Register type_reg, + InstanceType type) { + ldr(map, FieldMemOperand(function, HeapObject::kMapOffset)); + ldrb(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset)); + cmp(type_reg, Operand(type)); +} + + +void MacroAssembler::TryGetFunctionPrototype(Register function, + Register result, + Register scratch, + Label* miss) { + // Check that the receiver isn't a smi. + BranchOnSmi(function, miss); + + // Check that the function really is a function. Load map into result reg. + CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE); + b(ne, miss); + + // Make sure that the function has an instance prototype. + Label non_instance; + ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset)); + tst(scratch, Operand(1 << Map::kHasNonInstancePrototype)); + b(ne, &non_instance); + + // Get the prototype or initial map from the function. + ldr(result, + FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); + + // If the prototype or initial map is the hole, don't return it and + // simply miss the cache instead. This will allow us to allocate a + // prototype object on-demand in the runtime system. + cmp(result, Operand(Factory::the_hole_value())); + b(eq, miss); + + // If the function does not have an initial map, we're done. + Label done; + CompareObjectType(result, scratch, scratch, MAP_TYPE); + b(ne, &done); + + // Get the prototype from the initial map. + ldr(result, FieldMemOperand(result, Map::kPrototypeOffset)); + jmp(&done); + + // Non-instance prototype: Fetch prototype from constructor field + // in initial map. + bind(&non_instance); + ldr(result, FieldMemOperand(result, Map::kConstructorOffset)); + + // All done. + bind(&done); +} + + void MacroAssembler::CallStub(CodeStub* stub) { ASSERT(allow_stub_calls()); // stub calls are not allowed in some stubs Call(stub->GetCode(), RelocInfo::CODE_TARGET); diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 27eeab2e9a..ab74805941 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -35,8 +35,7 @@ namespace internal { // Give alias names to registers -extern Register cp; // JavaScript context pointer -extern Register pp; // parameter pointer +const Register cp = { 8 }; // JavaScript context pointer // Helper types to make boolean flag easier to read at call-site. @@ -187,6 +186,38 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // Support functions. + // Try to get function prototype of a function and puts the value in + // the result register. Checks that the function really is a + // function and jumps to the miss label if the fast checks fail. The + // function register will be untouched; the other registers may be + // clobbered. + void TryGetFunctionPrototype(Register function, + Register result, + Register scratch, + Label* miss); + + // Compare object type for heap object. heap_object contains a non-Smi + // whose object type should be compared with the given type. This both + // sets the flags and leaves the object type in the type_reg register. + // It leaves the map in the map register (unless the type_reg and map register + // are the same register). It leaves the heap object in the heap_object + // register unless the heap_object register is the same register as one of the + // other // registers. + void CompareObjectType(Register heap_object, + Register map, + Register type_reg, + InstanceType type); + + inline void BranchOnSmi(Register value, Label* smi_label) { + tst(value, Operand(kSmiTagMask)); + b(eq, smi_label); + } + + inline void BranchOnNotSmi(Register value, Label* not_smi_label) { + tst(value, Operand(kSmiTagMask)); + b(ne, not_smi_label); + } + // Generates code for reporting that an illegal operation has // occurred. void IllegalOperation(int num_arguments); diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc index b8b66636c4..af4f28efe6 100644 --- a/deps/v8/src/arm/simulator-arm.cc +++ b/deps/v8/src/arm/simulator-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2009 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: @@ -30,6 +30,7 @@ #include "v8.h" #include "disasm.h" +#include "assembler.h" #include "arm/constants-arm.h" #include "arm/simulator-arm.h" @@ -380,7 +381,23 @@ void Debugger::Debug() { } +// Create one simulator per thread and keep it in thread local storage. +static v8::internal::Thread::LocalStorageKey simulator_key; + + +bool Simulator::initialized_ = false; + + +void Simulator::Initialize() { + if (initialized_) return; + simulator_key = v8::internal::Thread::CreateThreadLocalKey(); + initialized_ = true; + ::v8::internal::ExternalReference::set_redirector(&RedirectExternalReference); +} + + Simulator::Simulator() { + ASSERT(initialized_); // Setup simulator support first. Some of this information is needed to // setup the architecture state. size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack @@ -412,9 +429,63 @@ Simulator::Simulator() { } -// Create one simulator per thread and keep it in thread local storage. -static v8::internal::Thread::LocalStorageKey simulator_key = - v8::internal::Thread::CreateThreadLocalKey(); +// When the generated code calls an external reference we need to catch that in +// the simulator. The external reference will be a function compiled for the +// host architecture. We need to call that function instead of trying to +// execute it with the simulator. We do that by redirecting the external +// reference to a swi (software-interrupt) instruction that is handled by +// the simulator. We write the original destination of the jump just at a known +// offset from the swi instruction so the simulator knows what to call. +class Redirection { + public: + Redirection(void* external_function, bool fp_return) + : external_function_(external_function), + swi_instruction_((AL << 28) | (0xf << 24) | call_rt_redirected), + fp_return_(fp_return), + next_(list_) { + list_ = this; + } + + void* address_of_swi_instruction() { + return reinterpret_cast(&swi_instruction_); + } + + void* external_function() { return external_function_; } + bool fp_return() { return fp_return_; } + + static Redirection* Get(void* external_function, bool fp_return) { + Redirection* current; + for (current = list_; current != NULL; current = current->next_) { + if (current->external_function_ == external_function) return current; + } + return new Redirection(external_function, fp_return); + } + + static Redirection* FromSwiInstruction(Instr* swi_instruction) { + char* addr_of_swi = reinterpret_cast(swi_instruction); + char* addr_of_redirection = + addr_of_swi - OFFSET_OF(Redirection, swi_instruction_); + return reinterpret_cast(addr_of_redirection); + } + + private: + void* external_function_; + uint32_t swi_instruction_; + bool fp_return_; + Redirection* next_; + static Redirection* list_; +}; + + +Redirection* Redirection::list_ = NULL; + + +void* Simulator::RedirectExternalReference(void* external_function, + bool fp_return) { + Redirection* redirection = Redirection::Get(external_function, fp_return); + return redirection->address_of_swi_instruction(); +} + // Get the active Simulator for the current thread. Simulator* Simulator::current() { @@ -921,7 +992,14 @@ void Simulator::HandleRList(Instr* instr, bool load) { // 64-bit value. With the code below we assume that all runtime calls return // 64 bits of result. If they don't, the r1 result register contains a bogus // value, which is fine because it is caller-saved. -typedef int64_t (*SimulatorRuntimeCall)(intptr_t arg0, intptr_t arg1); +typedef int64_t (*SimulatorRuntimeCall)(int32_t arg0, + int32_t arg1, + int32_t arg2, + int32_t arg3); +typedef double (*SimulatorRuntimeFPCall)(int32_t arg0, + int32_t arg1, + int32_t arg2, + int32_t arg3); // Software interrupt instructions are used by the simulator to call into the @@ -929,30 +1007,51 @@ typedef int64_t (*SimulatorRuntimeCall)(intptr_t arg0, intptr_t arg1); void Simulator::SoftwareInterrupt(Instr* instr) { int swi = instr->SwiField(); switch (swi) { - case call_rt_r5: { - SimulatorRuntimeCall target = - reinterpret_cast(get_register(r5)); - intptr_t arg0 = get_register(r0); - intptr_t arg1 = get_register(r1); - int64_t result = target(arg0, arg1); - int32_t lo_res = static_cast(result); - int32_t hi_res = static_cast(result >> 32); - set_register(r0, lo_res); - set_register(r1, hi_res); - set_pc(reinterpret_cast(instr) + Instr::kInstrSize); - break; - } - case call_rt_r2: { - SimulatorRuntimeCall target = - reinterpret_cast(get_register(r2)); - intptr_t arg0 = get_register(r0); - intptr_t arg1 = get_register(r1); - int64_t result = target(arg0, arg1); - int32_t lo_res = static_cast(result); - int32_t hi_res = static_cast(result >> 32); - set_register(r0, lo_res); - set_register(r1, hi_res); - set_pc(reinterpret_cast(instr) + Instr::kInstrSize); + case call_rt_redirected: { + Redirection* redirection = Redirection::FromSwiInstruction(instr); + int32_t arg0 = get_register(r0); + int32_t arg1 = get_register(r1); + int32_t arg2 = get_register(r2); + int32_t arg3 = get_register(r3); + // This is dodgy but it works because the C entry stubs are never moved. + // See comment in codegen-arm.cc and bug 1242173. + int32_t saved_lr = get_register(lr); + if (redirection->fp_return()) { + intptr_t external = + reinterpret_cast(redirection->external_function()); + SimulatorRuntimeFPCall target = + reinterpret_cast(external); + if (::v8::internal::FLAG_trace_sim) { + double x, y; + GetFpArgs(&x, &y); + PrintF("Call to host function at %p with args %f, %f\n", + FUNCTION_ADDR(target), x, y); + } + double result = target(arg0, arg1, arg2, arg3); + SetFpResult(result); + } else { + intptr_t external = + reinterpret_cast(redirection->external_function()); + SimulatorRuntimeCall target = + reinterpret_cast(external); + if (::v8::internal::FLAG_trace_sim) { + PrintF( + "Call to host function at %p with args %08x, %08x, %08x, %08x\n", + FUNCTION_ADDR(target), + arg0, + arg1, + arg2, + arg3); + } + int64_t result = target(arg0, arg1, arg2, arg3); + int32_t lo_res = static_cast(result); + int32_t hi_res = static_cast(result >> 32); + set_register(r0, lo_res); + set_register(r1, hi_res); + set_register(r0, result); + } + set_register(lr, saved_lr); + set_pc(get_register(lr)); break; } case break_point: { @@ -960,30 +1059,6 @@ void Simulator::SoftwareInterrupt(Instr* instr) { dbg.Debug(); break; } - { - double x, y, z; - case simulator_fp_add: - GetFpArgs(&x, &y); - z = x + y; - SetFpResult(z); - TrashCallerSaveRegisters(); - set_pc(reinterpret_cast(instr) + Instr::kInstrSize); - break; - case simulator_fp_sub: - GetFpArgs(&x, &y); - z = x - y; - SetFpResult(z); - TrashCallerSaveRegisters(); - set_pc(reinterpret_cast(instr) + Instr::kInstrSize); - break; - case simulator_fp_mul: - GetFpArgs(&x, &y); - z = x * y; - SetFpResult(z); - TrashCallerSaveRegisters(); - set_pc(reinterpret_cast(instr) + Instr::kInstrSize); - break; - } default: { UNREACHABLE(); break; diff --git a/deps/v8/src/arm/simulator-arm.h b/deps/v8/src/arm/simulator-arm.h index d4a395acaf..15b92a5f82 100644 --- a/deps/v8/src/arm/simulator-arm.h +++ b/deps/v8/src/arm/simulator-arm.h @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2009 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: @@ -106,6 +106,9 @@ class Simulator { // Executes ARM instructions until the PC reaches end_sim_pc. void Execute(); + // Call on program start. + static void Initialize(); + // V8 generally calls into generated code with 5 parameters. This is a // convenience function, which sets up the simulator state and grabs the // result on return. @@ -175,6 +178,10 @@ class Simulator { // Executes one instruction. void InstructionDecode(Instr* instr); + // Runtime call support. + static void* RedirectExternalReference(void* external_function, + bool fp_return); + // For use in calls that take two double values, constructed from r0, r1, r2 // and r3. void GetFpArgs(double* x, double* y); @@ -192,6 +199,7 @@ class Simulator { char* stack_; bool pc_modified_; int icount_; + static bool initialized_; // registered breakpoints Instr* break_pc_; diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index c09f9e3b60..782455792e 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -283,9 +283,7 @@ void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, __ b(eq, miss_label); // Check that the object is a JS array. - __ ldr(scratch, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); - __ cmp(scratch, Operand(JS_ARRAY_TYPE)); + __ CompareObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE); __ b(ne, miss_label); // Load length directly from the JS array. @@ -523,9 +521,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &miss); // Get the map. - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(JS_FUNCTION_TYPE)); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); __ b(ne, &miss); // Patch the receiver on the stack with the global proxy if @@ -588,9 +584,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, case STRING_CHECK: // Check that the object is a two-byte string or a symbol. - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(FIRST_NONSTRING_TYPE)); + __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE); __ b(hs, &miss); // Check that the maps starting from the prototype haven't changed. GenerateLoadGlobalFunctionPrototype(masm(), @@ -605,9 +599,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, // Check that the object is a smi or a heap number. __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &fast); - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(HEAP_NUMBER_TYPE)); + __ CompareObjectType(r1, r2, r2, HEAP_NUMBER_TYPE); __ b(ne, &miss); __ bind(&fast); // Check that the maps starting from the prototype haven't changed. diff --git a/deps/v8/src/arm/virtual-frame-arm.cc b/deps/v8/src/arm/virtual-frame-arm.cc index 952738329b..3d0ada7b66 100644 --- a/deps/v8/src/arm/virtual-frame-arm.cc +++ b/deps/v8/src/arm/virtual-frame-arm.cc @@ -156,9 +156,7 @@ void VirtualFrame::Enter() { __ b(ne, &map_check); __ stop("VirtualFrame::Enter - r1 is not a function (smi check)."); __ bind(&map_check); - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r2, Operand(JS_FUNCTION_TYPE)); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); __ b(eq, &done); __ stop("VirtualFrame::Enter - r1 is not a function (map check)."); __ bind(&done); @@ -230,8 +228,8 @@ void VirtualFrame::StoreToFrameSlotAt(int index) { void VirtualFrame::PushTryHandler(HandlerType type) { - // Grow the expression stack by handler size less one (the return address - // is already pushed by a call instruction). + // Grow the expression stack by handler size less one (the return + // address in lr is already counted by a call instruction). Adjust(kHandlerSize - 1); __ PushTryHandler(IN_JAVASCRIPT, type); } diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js index ed84b5f72d..eb69f97c18 100644 --- a/deps/v8/src/array.js +++ b/deps/v8/src/array.js @@ -769,6 +769,63 @@ function ArraySort(comparefn) { } } + function SafeRemoveArrayHoles(obj) { + // Copy defined elements from the end to fill in all holes and undefineds + // in the beginning of the array. Write undefineds and holes at the end + // after loop is finished. + var first_undefined = 0; + var last_defined = length - 1; + var num_holes = 0; + while (first_undefined < last_defined) { + // Find first undefined element. + while (first_undefined < last_defined && + !IS_UNDEFINED(obj[first_undefined])) { + first_undefined++; + } + // Maintain the invariant num_holes = the number of holes in the original + // array with indices <= first_undefined or > last_defined. + if (!obj.hasOwnProperty(first_undefined)) { + num_holes++; + } + + // Find last defined element. + while (first_undefined < last_defined && + IS_UNDEFINED(obj[last_defined])) { + if (!obj.hasOwnProperty(last_defined)) { + num_holes++; + } + last_defined--; + } + if (first_undefined < last_defined) { + // Fill in hole or undefined. + obj[first_undefined] = obj[last_defined]; + obj[last_defined] = void 0; + } + } + // If there were any undefineds in the entire array, first_undefined + // points to one past the last defined element. Make this true if + // there were no undefineds, as well, so that first_undefined == number + // of defined elements. + if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++; + // Fill in the undefineds and the holes. There may be a hole where + // an undefined should be and vice versa. + var i; + for (i = first_undefined; i < length - num_holes; i++) { + obj[i] = void 0; + } + for (i = length - num_holes; i < length; i++) { + // For compatability with Webkit, do not expose elements in the prototype. + if (i in obj.__proto__) { + obj[i] = void 0; + } else { + delete obj[i]; + } + } + + // Return the number of defined elements. + return first_undefined; + } + var length = ToUint32(this.length); if (length < 2) return this; @@ -787,6 +844,12 @@ function ArraySort(comparefn) { } var num_non_undefined = %RemoveArrayHoles(this, length); + if (num_non_undefined == -1) { + // There were indexed accessors in the array. Move array holes and + // undefineds to the end using a Javascript function that is safe + // in the presence of accessors. + num_non_undefined = SafeRemoveArrayHoles(this); + } QuickSort(this, 0, num_non_undefined); diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc index 5dba75d2d1..7b7778c045 100644 --- a/deps/v8/src/assembler.cc +++ b/deps/v8/src/assembler.cc @@ -30,7 +30,7 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-2009 the V8 project authors. All rights reserved. #include "v8.h" @@ -363,7 +363,7 @@ void RelocIterator::next() { if (SetMode(DebugInfoModeFromTag(top_tag))) return; } else { // Otherwise, just skip over the data. - Advance(kIntSize); + Advance(kIntptrSize); } } else { AdvanceReadPC(); @@ -508,7 +508,7 @@ void RelocInfo::Verify() { // Implementation of ExternalReference ExternalReference::ExternalReference(Builtins::CFunctionId id) - : address_(Builtins::c_function_address(id)) {} + : address_(Redirect(Builtins::c_function_address(id))) {} ExternalReference::ExternalReference(Builtins::Name name) @@ -516,15 +516,15 @@ ExternalReference::ExternalReference(Builtins::Name name) ExternalReference::ExternalReference(Runtime::FunctionId id) - : address_(Runtime::FunctionForId(id)->entry) {} + : address_(Redirect(Runtime::FunctionForId(id)->entry)) {} ExternalReference::ExternalReference(Runtime::Function* f) - : address_(f->entry) {} + : address_(Redirect(f->entry)) {} ExternalReference::ExternalReference(const IC_Utility& ic_utility) - : address_(ic_utility.address()) {} + : address_(Redirect(ic_utility.address())) {} #ifdef ENABLE_DEBUGGER_SUPPORT ExternalReference::ExternalReference(const Debug_Address& debug_address) @@ -543,10 +543,21 @@ ExternalReference::ExternalReference(const SCTableReference& table_ref) : address_(table_ref.address()) {} +ExternalReference ExternalReference::perform_gc_function() { + return ExternalReference(Redirect(FUNCTION_ADDR(Runtime::PerformGC))); +} + + ExternalReference ExternalReference::builtin_passed_function() { return ExternalReference(&Builtins::builtin_passed_function); } + +ExternalReference ExternalReference::random_positive_smi_function() { + return ExternalReference(Redirect(FUNCTION_ADDR(V8::RandomPositiveSmi))); +} + + ExternalReference ExternalReference::the_hole_value_location() { return ExternalReference(Factory::the_hole_value().location()); } @@ -614,13 +625,17 @@ ExternalReference ExternalReference::double_fp_operation( default: UNREACHABLE(); } - return ExternalReference(FUNCTION_ADDR(function)); + // Passing true as 2nd parameter indicates that they return an fp value. + return ExternalReference(Redirect(FUNCTION_ADDR(function), true)); } +ExternalReferenceRedirector* ExternalReference::redirector_ = NULL; + + #ifdef ENABLE_DEBUGGER_SUPPORT ExternalReference ExternalReference::debug_break() { - return ExternalReference(FUNCTION_ADDR(Debug::Break)); + return ExternalReference(Redirect(FUNCTION_ADDR(Debug::Break))); } diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h index 66f952adbd..0abd852a16 100644 --- a/deps/v8/src/assembler.h +++ b/deps/v8/src/assembler.h @@ -30,7 +30,7 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-2009 the V8 project authors. All rights reserved. #ifndef V8_ASSEMBLER_H_ #define V8_ASSEMBLER_H_ @@ -352,10 +352,15 @@ class SCTableReference; class Debug_Address; #endif -// An ExternalReference represents a C++ address called from the generated -// code. All references to C++ functions and must be encapsulated in an -// ExternalReference instance. This is done in order to track the origin of -// all external references in the code. + +typedef void* ExternalReferenceRedirector(void* original, bool fp_return); + + +// An ExternalReference represents a C++ address used in the generated +// code. All references to C++ functions and variables must be encapsulated in +// an ExternalReference instance. This is done in order to track the origin of +// all external references in the code so that they can be bound to the correct +// addresses when deserializing a heap. class ExternalReference BASE_EMBEDDED { public: explicit ExternalReference(Builtins::CFunctionId id); @@ -382,7 +387,9 @@ class ExternalReference BASE_EMBEDDED { // pattern. This means that they have to be added to the // ExternalReferenceTable in serialize.cc manually. + static ExternalReference perform_gc_function(); static ExternalReference builtin_passed_function(); + static ExternalReference random_positive_smi_function(); // Static variable Factory::the_hole_value.location() static ExternalReference the_hole_value_location(); @@ -403,7 +410,7 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference double_fp_operation(Token::Value operation); - Address address() const {return address_;} + Address address() const {return reinterpret_cast
(address_);} #ifdef ENABLE_DEBUGGER_SUPPORT // Function Debug::Break() @@ -413,11 +420,30 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference debug_step_in_fp_address(); #endif + // This lets you register a function that rewrites all external references. + // Used by the ARM simulator to catch calls to external references. + static void set_redirector(ExternalReferenceRedirector* redirector) { + ASSERT(redirector_ == NULL); // We can't stack them. + redirector_ = redirector; + } + private: explicit ExternalReference(void* address) - : address_(reinterpret_cast
(address)) {} + : address_(address) {} + + static ExternalReferenceRedirector* redirector_; + + static void* Redirect(void* address, bool fp_return = false) { + if (redirector_ == NULL) return address; + return (*redirector_)(address, fp_return); + } + + static void* Redirect(Address address_arg, bool fp_return = false) { + void* address = reinterpret_cast(address_arg); + return redirector_ == NULL ? address : (*redirector_)(address, fp_return); + } - Address address_; + void* address_; }; diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index 89c92b0697..2dbc030327 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -1113,6 +1113,9 @@ bool Genesis::InstallNatives() { } #ifdef V8_HOST_ARCH_64_BIT + // TODO(X64): Remove these tests when code generation works and is stable. + MacroAssembler::ConstructAndTestJSFunction(); + CodeGenerator::TestCodeGenerator(); // TODO(X64): Reenable remaining initialization when code generation works. return true; #endif // V8_HOST_ARCH_64_BIT diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index 1c43f7a4ba..0648e54dc2 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -720,7 +720,8 @@ void Builtins::Setup(bool create_heap_objects) { // bootstrapper. Bootstrapper::AddFixup(Code::cast(code), &masm); // Log the event and add the code to the builtins array. - LOG(CodeCreateEvent("Builtin", Code::cast(code), functions[i].s_name)); + LOG(CodeCreateEvent(Logger::BUILTIN_TAG, + Code::cast(code), functions[i].s_name)); builtins_[i] = code; #ifdef ENABLE_DISASSEMBLER if (FLAG_print_builtin_code) { diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index b14ede18a4..f4d8ce8ae4 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -66,7 +66,7 @@ Handle CodeStub::GetCode() { // Add unresolved entries in the code to the fixup list. Bootstrapper::AddFixup(*code, &masm); - LOG(CodeCreateEvent("Stub", *code, GetName())); + LOG(CodeCreateEvent(Logger::STUB_TAG, *code, GetName())); Counters::total_stubs_code_size.Increment(code->instruction_size()); #ifdef ENABLE_DISASSEMBLER diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index 183a64abe7..76ec7870f3 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -41,6 +41,8 @@ class CodeStub BASE_EMBEDDED { SmiOp, Compare, RecordWrite, // Last stub that allows stub calls inside. + ConvertToDouble, + WriteInt32ToHeapNumber, StackCheck, UnarySub, RevertToNumber, diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc index f46269fe97..e359c348ae 100644 --- a/deps/v8/src/codegen.cc +++ b/deps/v8/src/codegen.cc @@ -302,12 +302,12 @@ Handle CodeGenerator::BuildBoilerplate(FunctionLiteral* node) { } // Function compilation complete. - LOG(CodeCreateEvent("Function", *code, *node->name())); + LOG(CodeCreateEvent(Logger::FUNCTION_TAG, *code, *node->name())); #ifdef ENABLE_OPROFILE_AGENT OProfileAgent::CreateNativeCodeRegion(*node->name(), - code->address(), - code->ExecutableSize()); + code->instruction_start(), + code->instruction_size()); #endif } @@ -422,7 +422,10 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { {&CodeGenerator::GenerateSetValueOf, "_SetValueOf"}, {&CodeGenerator::GenerateFastCharCodeAt, "_FastCharCodeAt"}, {&CodeGenerator::GenerateObjectEquals, "_ObjectEquals"}, - {&CodeGenerator::GenerateLog, "_Log"} + {&CodeGenerator::GenerateLog, "_Log"}, + {&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"}, + {&CodeGenerator::GenerateMathSin, "_Math_sin"}, + {&CodeGenerator::GenerateMathCos, "_Math_cos"} }; diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h index e1758e1a9f..0b42935f5b 100644 --- a/deps/v8/src/codegen.h +++ b/deps/v8/src/codegen.h @@ -228,13 +228,27 @@ class StackCheckStub : public CodeStub { }; +class InstanceofStub: public CodeStub { + public: + InstanceofStub() { } + + void Generate(MacroAssembler* masm); + + private: + Major MajorKey() { return Instanceof; } + int MinorKey() { return 0; } +}; + + class UnarySubStub : public CodeStub { public: - UnarySubStub() { } + explicit UnarySubStub(bool overwrite) + : overwrite_(overwrite) { } private: + bool overwrite_; Major MajorKey() { return UnarySub; } - int MinorKey() { return 0; } + int MinorKey() { return overwrite_ ? 1 : 0; } void Generate(MacroAssembler* masm); const char* GetName() { return "UnarySubStub"; } diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index ea7c134daa..73d200226e 100644 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -179,13 +179,17 @@ static Handle MakeFunction(bool is_global, if (script->name()->IsString()) { SmartPointer data = String::cast(script->name())->ToCString(DISALLOW_NULLS); - LOG(CodeCreateEvent(is_eval ? "Eval" : "Script", *code, *data)); - OProfileAgent::CreateNativeCodeRegion(*data, code->address(), - code->ExecutableSize()); + LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG, + *code, *data)); + OProfileAgent::CreateNativeCodeRegion(*data, + code->instruction_start(), + code->instruction_size()); } else { - LOG(CodeCreateEvent(is_eval ? "Eval" : "Script", *code, "")); + LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG, + *code, "")); OProfileAgent::CreateNativeCodeRegion(is_eval ? "Eval" : "Script", - code->address(), code->ExecutableSize()); + code->instruction_start(), + code->instruction_size()); } } #endif @@ -380,16 +384,18 @@ bool Compiler::CompileLazy(Handle shared, if (line_num > 0) { line_num += script->line_offset()->value() + 1; } - LOG(CodeCreateEvent("LazyCompile", *code, *func_name, + LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, *code, *func_name, String::cast(script->name()), line_num)); OProfileAgent::CreateNativeCodeRegion(*func_name, String::cast(script->name()), - line_num, code->address(), - code->ExecutableSize()); + line_num, + code->instruction_start(), + code->instruction_size()); } else { - LOG(CodeCreateEvent("LazyCompile", *code, *func_name)); - OProfileAgent::CreateNativeCodeRegion(*func_name, code->address(), - code->ExecutableSize()); + LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, *code, *func_name)); + OProfileAgent::CreateNativeCodeRegion(*func_name, + code->instruction_start(), + code->instruction_size()); } } #endif diff --git a/deps/v8/src/d8-debug.h b/deps/v8/src/d8-debug.h index c7acc2f79f..d541fda1b1 100644 --- a/deps/v8/src/d8-debug.h +++ b/deps/v8/src/d8-debug.h @@ -41,7 +41,7 @@ void HandleDebugEvent(DebugEvent event, Handle event_data, Handle data); -// Start the remove debugger connecting to a V8 debugger agent on the specified +// Start the remote debugger connecting to a V8 debugger agent on the specified // port. void RunRemoteDebugger(int port); diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index ee845ee2cd..e02c80aa65 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -460,6 +460,16 @@ void Shell::Initialize() { #ifdef ENABLE_DEBUGGER_SUPPORT // Set the security token of the debug context to allow access. i::Debug::debug_context()->set_security_token(i::Heap::undefined_value()); + + // Start the debugger agent if requested. + if (i::FLAG_debugger_agent) { + v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port); + } + + // Start the in-process debugger if requested. + if (i::FLAG_debugger && !i::FLAG_debugger_agent) { + v8::Debug::SetDebugEventListener(HandleDebugEvent); + } #endif } @@ -721,16 +731,6 @@ int Shell::Main(int argc, char* argv[]) { RunRemoteDebugger(i::FLAG_debugger_port); return 0; } - - // Start the debugger agent if requested. - if (i::FLAG_debugger_agent) { - v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port); - } - - // Start the in-process debugger if requested. - if (i::FLAG_debugger && !i::FLAG_debugger_agent) { - v8::Debug::SetDebugEventListener(HandleDebugEvent); - } #endif } if (run_shell) diff --git a/deps/v8/src/d8.js b/deps/v8/src/d8.js index a8db9e1d9e..3a0eedbf1d 100644 --- a/deps/v8/src/d8.js +++ b/deps/v8/src/d8.js @@ -25,8 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// How crappy is it that I have to implement completely basic stuff -// like this myself? Answer: very. String.prototype.startsWith = function (str) { if (str.length > this.length) return false; @@ -100,6 +98,13 @@ Debug.ScriptCompilationType = { Host: 0, JSON: 2 }; +// The different types of scopes matching constants runtime.cc. +Debug.ScopeType = { Global: 0, + Local: 1, + With: 2, + Closure: 3 }; + + // Current debug state. const kNoFrame = -1; Debug.State = { @@ -124,7 +129,7 @@ function DebugMessageDetails(message) { } function DebugEventDetails(response) { - details = {text:'', running:false} + details = {text:'', running:false}; // Get the running state. details.running = response.running(); @@ -297,6 +302,14 @@ function DebugRequest(cmd_line) { this.request_ = this.frameCommandToJSONRequest_(args); break; + case 'scopes': + this.request_ = this.scopesCommandToJSONRequest_(args); + break; + + case 'scope': + this.request_ = this.scopeCommandToJSONRequest_(args); + break; + case 'print': case 'p': this.request_ = this.printCommandToJSONRequest_(args); @@ -396,13 +409,17 @@ DebugRequest.prototype.createRequest = function(command) { // Create a JSON request for the evaluation command. DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) { + // Global varaible used to store whether a handle was requested. + lookup_handle = null; // Check if the expression is a handle id in the form ##. var handle_match = expression.match(/^#([0-9]*)#$/); if (handle_match) { + // Remember the handle requested in a global variable. + lookup_handle = parseInt(handle_match[1]); // Build a lookup request. var request = this.createRequest('lookup'); request.arguments = {}; - request.arguments.handle = parseInt(handle_match[1]); + request.arguments.handles = [ lookup_handle ]; return request.toJSONProtocol(); } else { // Build an evaluate request. @@ -561,6 +578,27 @@ DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) { }; +// Create a JSON request for the scopes command. +DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) { + // Build a scopes request from the text command. + var request = this.createRequest('scopes'); + return request.toJSONProtocol(); +}; + + +// Create a JSON request for the scope command. +DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) { + // Build a scope request from the text command. + var request = this.createRequest('scope'); + args = args.split(/\s*[ ]+\s*/g); + if (args.length > 0 && args[0].length > 0) { + request.arguments = {}; + request.arguments.number = args[0]; + } + return request.toJSONProtocol(); +}; + + // Create a JSON request for the print command. DebugRequest.prototype.printCommandToJSONRequest_ = function(args) { // Build an evaluate request from the text command. @@ -785,8 +823,11 @@ DebugRequest.prototype.helpCommand_ = function(args) { print('clear '); print('backtrace [n] | [-n] | [from to]'); print('frame '); + print('scopes'); + print('scope '); print('step [in | next | out| min [step count]]'); print('print '); + print('dir '); print('source [from line [num lines]]'); print('scripts'); print('continue'); @@ -796,7 +837,11 @@ DebugRequest.prototype.helpCommand_ = function(args) { function formatHandleReference_(value) { - return '#' + value.handle() + '#'; + if (value.handle() >= 0) { + return '#' + value.handle() + '#'; + } else { + return '#Transient#'; + } } @@ -820,10 +865,14 @@ function formatObject_(value, include_properties) { result += value.propertyName(i); result += ': '; var property_value = value.propertyValue(i); - if (property_value && property_value.type()) { - result += property_value.type(); - } else { + if (property_value instanceof ProtocolReference) { result += ''; + } else { + if (property_value && property_value.type()) { + result += property_value.type(); + } else { + result += ''; + } } result += ' '; result += formatHandleReference_(property_value); @@ -834,6 +883,33 @@ function formatObject_(value, include_properties) { } +function formatScope_(scope) { + var result = ''; + var index = scope.index; + result += '#' + (index <= 9 ? '0' : '') + index; + result += ' '; + switch (scope.type) { + case Debug.ScopeType.Global: + result += 'Global, '; + result += '#' + scope.object.ref + '#'; + break; + case Debug.ScopeType.Local: + result += 'Local'; + break; + case Debug.ScopeType.With: + result += 'With, '; + result += '#' + scope.object.ref + '#'; + break; + case Debug.ScopeType.Closure: + result += 'Closure'; + break; + default: + result += 'UNKNOWN'; + } + return result; +} + + // Convert a JSON response to text for display in a text based debugger. function DebugResponseDetails(response) { details = {text:'', running:false} @@ -883,12 +959,41 @@ function DebugResponseDetails(response) { Debug.State.currentFrame = body.index; break; + case 'scopes': + if (body.totalScopes == 0) { + result = '(no scopes)'; + } else { + result = 'Scopes #' + body.fromScope + ' to #' + + (body.toScope - 1) + ' of ' + body.totalScopes + '\n'; + for (i = 0; i < body.scopes.length; i++) { + if (i != 0) { + result += '\n'; + } + result += formatScope_(body.scopes[i]); + } + } + details.text = result; + break; + + case 'scope': + result += formatScope_(body); + result += '\n'; + var scope_object_value = response.lookup(body.object.ref); + result += formatObject_(scope_object_value, true); + details.text = result; + break; + case 'evaluate': case 'lookup': if (last_cmd == 'p' || last_cmd == 'print') { result = body.text; } else { - var value = response.bodyValue(); + var value; + if (lookup_handle) { + value = response.bodyValue(lookup_handle); + } else { + value = response.bodyValue(); + } if (value.isObject()) { result += formatObject_(value, true); } else { @@ -1105,7 +1210,7 @@ ProtocolPackage.prototype.body = function() { ProtocolPackage.prototype.bodyValue = function(index) { - if (index) { + if (index != null) { return new ProtocolValue(this.packet_.body[index], this); } else { return new ProtocolValue(this.packet_.body, this); diff --git a/deps/v8/src/date-delay.js b/deps/v8/src/date-delay.js index f06e8b75bb..9aecadbec6 100644 --- a/deps/v8/src/date-delay.js +++ b/deps/v8/src/date-delay.js @@ -115,7 +115,7 @@ function EquivalentYear(year) { // - leap year. // - week day of first day. var time = TimeFromYear(year); - var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) + + var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) + (WeekDay(time) * 12) % 28; // Find the year in the range 2008..2037 that is equivalent mod 28. // Add 3*28 to give a positive argument to the modulus operator. @@ -129,23 +129,82 @@ function EquivalentTime(t) { // (measured in whole seconds based on the 1970 epoch). // We solve this by mapping the time to a year with same leap-year-ness // and same starting day for the year. The ECMAscript specification says - // we must do this, but for compatability with other browsers, we use + // we must do this, but for compatibility with other browsers, we use // the actual year if it is in the range 1970..2037 if (t >= 0 && t <= 2.1e12) return t; var day = MakeDay(EquivalentYear(YearFromTime(t)), MonthFromTime(t), DateFromTime(t)); return TimeClip(MakeDate(day, TimeWithinDay(t))); } -var daylight_cache_time = $NaN; -var daylight_cache_offset; + +// Because computing the DST offset is a pretty expensive operation +// we keep a cache of last computed offset along with a time interval +// where we know the cache is valid. +var DST_offset_cache = { + // Cached DST offset. + offset: 0, + // Time interval where the cached offset is valid. + start: 0, end: -1, + // Size of next interval expansion. + increment: 0 +}; + function DaylightSavingsOffset(t) { - if (t == daylight_cache_time) { - return daylight_cache_offset; + // Load the cache object from the builtins object. + var cache = DST_offset_cache; + + // Cache the start and the end in local variables for fast access. + var start = cache.start; + var end = cache.end; + + if (start <= t) { + // If the time fits in the cached interval, return the cached offset. + if (t <= end) return cache.offset; + + // Compute a possible new interval end. + var new_end = end + cache.increment; + + if (t <= new_end) { + var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end)); + if (cache.offset == end_offset) { + // If the offset at the end of the new interval still matches + // the offset in the cache, we grow the cached time interval + // and return the offset. + cache.end = new_end; + cache.increment = msPerMonth; + return end_offset; + } else { + var offset = %DateDaylightSavingsOffset(EquivalentTime(t)); + if (offset == end_offset) { + // The offset at the given time is equal to the offset at the + // new end of the interval, so that means that we've just skipped + // the point in time where the DST offset change occurred. Updated + // the interval to reflect this and reset the increment. + cache.start = t; + cache.end = new_end; + cache.increment = msPerMonth; + } else { + // The interval contains a DST offset change and the given time is + // before it. Adjust the increment to avoid a linear search for + // the offset change point and change the end of the interval. + cache.increment /= 3; + cache.end = t; + } + // Update the offset in the cache and return it. + cache.offset = offset; + return offset; + } + } } + + // Compute the DST offset for the time and shrink the cache interval + // to only contain the time. This allows fast repeated DST offset + // computations for the same time. var offset = %DateDaylightSavingsOffset(EquivalentTime(t)); - daylight_cache_time = t; - daylight_cache_offset = offset; + cache.offset = offset; + cache.start = cache.end = t; + cache.increment = msPerMonth; return offset; } @@ -154,7 +213,7 @@ var timezone_cache_time = $NaN; var timezone_cache_timezone; function LocalTimezone(t) { - if(t == timezone_cache_time) { + if (t == timezone_cache_time) { return timezone_cache_timezone; } var timezone = %DateLocalTimezone(EquivalentTime(t)); diff --git a/deps/v8/src/debug-delay.js b/deps/v8/src/debug-delay.js index 0b0501fde6..21cd68a2cb 100644 --- a/deps/v8/src/debug-delay.js +++ b/deps/v8/src/debug-delay.js @@ -1208,6 +1208,10 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) this.backtraceRequest_(request, response); } else if (request.command == 'frame') { this.frameRequest_(request, response); + } else if (request.command == 'scopes') { + this.scopesRequest_(request, response); + } else if (request.command == 'scope') { + this.scopeRequest_(request, response); } else if (request.command == 'evaluate') { this.evaluateRequest_(request, response); } else if (request.command == 'lookup') { @@ -1540,7 +1544,7 @@ DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { // With no arguments just keep the selected frame. if (request.arguments) { - index = request.arguments.number; + var index = request.arguments.number; if (index < 0 || this.exec_state_.frameCount() <= index) { return response.failed('Invalid frame number'); } @@ -1551,6 +1555,67 @@ DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { }; +DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) { + // Get the frame for which the scope or scopes are requested. With no frameNumber + // argument use the currently selected frame. + if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) { + frame_index = request.arguments.frameNumber; + if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) { + return response.failed('Invalid frame number'); + } + return this.exec_state_.frame(frame_index); + } else { + return this.exec_state_.frame(); + } +} + + +DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) { + // No frames no scopes. + if (this.exec_state_.frameCount() == 0) { + return response.failed('No scopes'); + } + + // Get the frame for which the scopes are requested. + var frame = this.frameForScopeRequest_(request); + + // Fill all scopes for this frame. + var total_scopes = frame.scopeCount(); + var scopes = []; + for (var i = 0; i < total_scopes; i++) { + scopes.push(frame.scope(i)); + } + response.body = { + fromScope: 0, + toScope: total_scopes, + totalScopes: total_scopes, + scopes: scopes + } +}; + + +DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) { + // No frames no scopes. + if (this.exec_state_.frameCount() == 0) { + return response.failed('No scopes'); + } + + // Get the frame for which the scope is requested. + var frame = this.frameForScopeRequest_(request); + + // With no scope argument just return top scope. + var scope_index = 0; + if (request.arguments && !IS_UNDEFINED(request.arguments.number)) { + scope_index = %ToNumber(request.arguments.number); + if (scope_index < 0 || frame.scopeCount() <= scope_index) { + return response.failed('Invalid scope number'); + } + } + + response.body = frame.scope(scope_index); +}; + + DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { if (!request.arguments) { return response.failed('Missing arguments'); diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc index 0daf5642de..e37bfb77ca 100644 --- a/deps/v8/src/debug.cc +++ b/deps/v8/src/debug.cc @@ -382,6 +382,7 @@ void BreakLocationIterator::SetDebugBreakAtIC() { // the code copy and will therefore have no effect on the running code // keeping it from using the inlined code. if (code->is_keyed_load_stub()) KeyedLoadIC::ClearInlinedVersion(pc()); + if (code->is_keyed_store_stub()) KeyedStoreIC::ClearInlinedVersion(pc()); } } @@ -389,6 +390,19 @@ void BreakLocationIterator::SetDebugBreakAtIC() { void BreakLocationIterator::ClearDebugBreakAtIC() { // Patch the code to the original invoke. rinfo()->set_target_address(original_rinfo()->target_address()); + + RelocInfo::Mode mode = rmode(); + if (RelocInfo::IsCodeTarget(mode)) { + Address target = original_rinfo()->target_address(); + Handle code(Code::GetCodeFromTargetAddress(target)); + + // Restore the inlined version of keyed stores to get back to the + // fast case. We need to patch back the keyed store because no + // patching happens when running normally. For keyed loads, the + // map check will get patched back when running normally after ICs + // have been cleared at GC. + if (code->is_keyed_store_stub()) KeyedStoreIC::RestoreInlinedVersion(pc()); + } } diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 13e41e34fe..8110e12848 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -332,6 +332,8 @@ DEFINE_bool(log_gc, false, DEFINE_bool(log_handles, false, "Log global handle events.") DEFINE_bool(log_state_changes, false, "Log state changes.") DEFINE_bool(log_suspect, false, "Log suspect operations.") +DEFINE_bool(compress_log, false, + "Compress log to save space (makes log less human-readable).") DEFINE_bool(prof, false, "Log statistical profiling information (implies --log-code).") DEFINE_bool(prof_auto, true, diff --git a/deps/v8/src/frames-inl.h b/deps/v8/src/frames-inl.h index 28be430661..0e2adb9b19 100644 --- a/deps/v8/src/frames-inl.h +++ b/deps/v8/src/frames-inl.h @@ -43,13 +43,7 @@ namespace internal { inline Address StackHandler::address() const { - // NOTE: There's an obvious problem with the address of the NULL - // stack handler. Right now, it benefits us that the subtraction - // leads to a very high address (above everything else on the - // stack), but maybe we should stop relying on it? - const int displacement = StackHandlerConstants::kAddressDisplacement; - Address address = reinterpret_cast
(const_cast(this)); - return address + displacement; + return reinterpret_cast
(const_cast(this)); } @@ -68,13 +62,7 @@ inline bool StackHandler::includes(Address address) const { inline void StackHandler::Iterate(ObjectVisitor* v) const { // Stack handlers do not contain any pointers that need to be - // traversed. The only field that have to worry about is the code - // field which is unused and should always be uninitialized. -#ifdef DEBUG - const int offset = StackHandlerConstants::kCodeOffset; - Object* code = Memory::Object_at(address() + offset); - ASSERT(Smi::cast(code)->value() == StackHandler::kCodeNotPresent); -#endif + // traversed. } @@ -122,11 +110,6 @@ inline Object* StandardFrame::context() const { } -inline Address StandardFrame::caller_sp() const { - return pp(); -} - - inline Address StandardFrame::caller_fp() const { return Memory::Address_at(fp() + StandardFrameConstants::kCallerFPOffset); } @@ -157,13 +140,13 @@ inline bool StandardFrame::IsConstructFrame(Address fp) { inline Object* JavaScriptFrame::receiver() const { const int offset = JavaScriptFrameConstants::kReceiverOffset; - return Memory::Object_at(pp() + offset); + return Memory::Object_at(caller_sp() + offset); } inline void JavaScriptFrame::set_receiver(Object* value) { const int offset = JavaScriptFrameConstants::kReceiverOffset; - Memory::Object_at(pp() + offset) = value; + Memory::Object_at(caller_sp() + offset) = value; } diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc index dd0ea00c0e..5cd83324c6 100644 --- a/deps/v8/src/frames.cc +++ b/deps/v8/src/frames.cc @@ -49,7 +49,9 @@ class StackHandlerIterator BASE_EMBEDDED { StackHandler* handler() const { return handler_; } - bool done() { return handler_->address() > limit_; } + bool done() { + return handler_ == NULL || handler_->address() > limit_; + } void Advance() { ASSERT(!done()); handler_ = handler_->next(); @@ -398,7 +400,7 @@ Code* ExitFrame::code() const { void ExitFrame::ComputeCallerState(State* state) const { // Setup the caller state. - state->sp = pp(); + state->sp = caller_sp(); state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset); state->pc_address = reinterpret_cast(fp() + ExitFrameConstants::kCallerPCOffset); @@ -406,7 +408,7 @@ void ExitFrame::ComputeCallerState(State* state) const { Address ExitFrame::GetCallerStackPointer() const { - return fp() + ExitFrameConstants::kPPDisplacement; + return fp() + ExitFrameConstants::kCallerSPDisplacement; } @@ -451,12 +453,12 @@ bool StandardFrame::IsExpressionInsideHandler(int n) const { Object* JavaScriptFrame::GetParameter(int index) const { ASSERT(index >= 0 && index < ComputeParametersCount()); const int offset = JavaScriptFrameConstants::kParam0Offset; - return Memory::Object_at(pp() + offset - (index * kPointerSize)); + return Memory::Object_at(caller_sp() + offset - (index * kPointerSize)); } int JavaScriptFrame::ComputeParametersCount() const { - Address base = pp() + JavaScriptFrameConstants::kReceiverOffset; + Address base = caller_sp() + JavaScriptFrameConstants::kReceiverOffset; Address limit = fp() + JavaScriptFrameConstants::kSavedRegistersOffset; return (base - limit) / kPointerSize; } @@ -681,7 +683,7 @@ void JavaScriptFrame::Iterate(ObjectVisitor* v) const { const int kBaseOffset = JavaScriptFrameConstants::kSavedRegistersOffset; const int kLimitOffset = JavaScriptFrameConstants::kReceiverOffset; Object** base = &Memory::Object_at(fp() + kBaseOffset); - Object** limit = &Memory::Object_at(pp() + kLimitOffset) + 1; + Object** limit = &Memory::Object_at(caller_sp() + kLimitOffset) + 1; v->VisitPointers(base, limit); } diff --git a/deps/v8/src/frames.h b/deps/v8/src/frames.h index e250609fd9..f002e12161 100644 --- a/deps/v8/src/frames.h +++ b/deps/v8/src/frames.h @@ -78,9 +78,6 @@ class StackHandler BASE_EMBEDDED { void Cook(Code* code); void Uncook(Code* code); - // TODO(1233780): Get rid of the code slot in stack handlers. - static const int kCodeNotPresent = 0; - private: // Accessors. inline State state() const; @@ -132,7 +129,7 @@ class StackFrame BASE_EMBEDDED { // Accessors. Address sp() const { return state_.sp; } Address fp() const { return state_.fp; } - Address pp() const { return GetCallerStackPointer(); } + Address caller_sp() const { return GetCallerStackPointer(); } Address pc() const { return *pc_address(); } void set_pc(Address pc) { *pc_address() = pc; } @@ -140,7 +137,7 @@ class StackFrame BASE_EMBEDDED { Address* pc_address() const { return state_.pc_address; } // Get the id of this stack frame. - Id id() const { return static_cast(OffsetFrom(pp())); } + Id id() const { return static_cast(OffsetFrom(caller_sp())); } // Checks if this frame includes any stack handlers. bool HasHandler() const; @@ -337,7 +334,6 @@ class StandardFrame: public StackFrame { virtual void ComputeCallerState(State* state) const; // Accessors. - inline Address caller_sp() const; inline Address caller_fp() const; inline Address caller_pc() const; diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 772cf329c7..eb70f21a80 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -79,9 +79,15 @@ int Heap::amount_of_external_allocated_memory_at_last_global_gc_ = 0; // semispace_size_ should be a power of 2 and old_generation_size_ should be // a multiple of Page::kPageSize. -int Heap::semispace_size_ = 2*MB; +#if V8_HOST_ARCH_ARM +int Heap::semispace_size_ = 512*KB; +int Heap::old_generation_size_ = 128*MB; +int Heap::initial_semispace_size_ = 128*KB; +#else +int Heap::semispace_size_ = 8*MB; int Heap::old_generation_size_ = 512*MB; -int Heap::initial_semispace_size_ = 256*KB; +int Heap::initial_semispace_size_ = 512*KB; +#endif GCCallback Heap::global_gc_prologue_callback_ = NULL; GCCallback Heap::global_gc_epilogue_callback_ = NULL; @@ -90,9 +96,8 @@ GCCallback Heap::global_gc_epilogue_callback_ = NULL; // ConfigureHeap. int Heap::young_generation_size_ = 0; // Will be 2 * semispace_size_. -// Double the new space after this many scavenge collections. -int Heap::new_space_growth_limit_ = 8; -int Heap::scavenge_count_ = 0; +int Heap::survived_since_last_expansion_ = 0; + Heap::HeapState Heap::gc_state_ = NOT_IN_GC; int Heap::mc_count_ = 0; @@ -421,7 +426,7 @@ void Heap::PerformGarbageCollection(AllocationSpace space, 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 / 3); + old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2); old_gen_exhausted_ = false; // If we have used the mark-compact collector to collect the new @@ -624,16 +629,17 @@ void Heap::Scavenge() { // Implements Cheney's copying algorithm LOG(ResourceEvent("scavenge", "begin")); - scavenge_count_++; + // Used for updating survived_since_last_expansion_ at function end. + int survived_watermark = PromotedSpaceSize(); + if (new_space_.Capacity() < new_space_.MaximumCapacity() && - scavenge_count_ > new_space_growth_limit_) { - // Double the size of the new space, and double the limit. The next - // doubling attempt will occur after the current new_space_growth_limit_ - // more collections. + survived_since_last_expansion_ > new_space_.Capacity()) { + // Double the size of new space if there is room to grow and enough + // data has survived scavenge since the last expansion. // TODO(1240712): NewSpace::Double has a return value which is // ignored here. new_space_.Double(); - new_space_growth_limit_ *= 2; + survived_since_last_expansion_ = 0; } // Flip the semispaces. After flipping, to space is empty, from space has @@ -737,6 +743,10 @@ void Heap::Scavenge() { // Set age mark. new_space_.set_age_mark(new_space_.top()); + // Update how much has survived scavenge. + survived_since_last_expansion_ += + (PromotedSpaceSize() - survived_watermark) + new_space_.Size(); + LOG(ResourceEvent("scavenge", "end")); gc_state_ = NOT_IN_GC; @@ -1766,7 +1776,6 @@ Object* Heap::CreateCode(const CodeDesc& desc, // through the self_reference parameter. code->CopyFrom(desc); if (sinfo != NULL) sinfo->Serialize(code); // write scope info - LOG(CodeAllocateEvent(code, desc.origin)); #ifdef DEBUG code->Verify(); diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index d8080b6a8a..08b2a99350 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -827,8 +827,9 @@ class Heap : public AllStatic { static int young_generation_size_; static int old_generation_size_; - static int new_space_growth_limit_; - static int scavenge_count_; + // For keeping track of how much data has survived + // scavenge since last new space expansion. + static int survived_since_last_expansion_; static int always_allocate_scope_depth_; static bool context_disposed_pending_; diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 434bf070fd..b5efe9e4c7 100644 --- a/deps/v8/src/ia32/assembler-ia32.cc +++ b/deps/v8/src/ia32/assembler-ia32.cc @@ -117,7 +117,8 @@ void CpuFeatures::Probe() { Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL); if (!code->IsCode()) return; - LOG(CodeCreateEvent("Builtin", Code::cast(code), "CpuFeatures::Probe")); + LOG(CodeCreateEvent(Logger::BUILTIN_TAG, + Code::cast(code), "CpuFeatures::Probe")); typedef uint64_t (*F0)(); F0 probe = FUNCTION_CAST(Code::cast(code)->entry()); supported_ = probe(); @@ -1655,6 +1656,22 @@ void Assembler::fchs() { } +void Assembler::fcos() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0xD9); + EMIT(0xFF); +} + + +void Assembler::fsin() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0xD9); + EMIT(0xFE); +} + + void Assembler::fadd(int i) { EnsureSpace ensure_space(this); last_pc_ = pc_; diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index 79f239d7ca..ae16e700f1 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -658,6 +658,8 @@ class Assembler : public Malloced { void fabs(); void fchs(); + void fcos(); + void fsin(); void fadd(int i); void fsub(int i); diff --git a/deps/v8/src/ia32/codegen-ia32-inl.h b/deps/v8/src/ia32/codegen-ia32-inl.h index 49c706d13b..44e937af16 100644 --- a/deps/v8/src/ia32/codegen-ia32-inl.h +++ b/deps/v8/src/ia32/codegen-ia32-inl.h @@ -39,6 +39,16 @@ namespace internal { void DeferredCode::Jump() { __ jmp(&entry_label_); } void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); } +void CodeGenerator::GenerateMathSin(ZoneList* args) { + GenerateFastMathOp(SIN, args); +} + + +void CodeGenerator::GenerateMathCos(ZoneList* args) { + GenerateFastMathOp(COS, args); +} + + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index e9e40619d4..3357f57e45 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -317,9 +317,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { if (function_return_.is_bound()) { function_return_.Jump(&undefined); } else { - // Though this is a (possibly) backward block, the frames - // can only differ on their top element. - function_return_.Bind(&undefined, 1); + function_return_.Bind(&undefined); GenerateReturnSequence(&undefined); } } else if (function_return_.is_linked()) { @@ -329,9 +327,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { // compile an artificial return statement just above, and (b) there // are return statements in the body but (c) they are all shadowed. Result return_value; - // Though this is a (possibly) backward block, the frames can - // only differ on their top element. - function_return_.Bind(&return_value, 1); + function_return_.Bind(&return_value); GenerateReturnSequence(&return_value); } } @@ -718,6 +714,11 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) { class FloatingPointHelper : public AllStatic { public: + // Code pattern for loading a floating point value. Input value must + // be either a smi or a heap number object (fp value). Requirements: + // operand on TOS+1. Returns operand as floating point number on FPU + // stack. + static void LoadFloatOperand(MacroAssembler* masm, Register scratch); // Code pattern for loading floating point values. Input values must // be either smi or heap number objects (fp values). Requirements: // operand_1 on TOS+1 , operand_2 on TOS+2; Returns operands as @@ -734,7 +735,8 @@ class FloatingPointHelper : public AllStatic { static void AllocateHeapNumber(MacroAssembler* masm, Label* need_gc, Register scratch1, - Register scratch2); + Register scratch2, + Register result); }; @@ -1691,6 +1693,8 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, int shift_value = int_value & 0x1f; operand->ToRegister(); if (shift_value == 0) { + // Spill operand so it can be overwritten in the slow case. + frame_->Spill(operand->reg()); DeferredInlineSmiOperation* deferred = new DeferredInlineSmiOperation(op, operand->reg(), @@ -1873,13 +1877,19 @@ void CodeGenerator::Comparison(Condition cc, // Implement comparison against a constant Smi, inlining the case // where both sides are Smis. left_side.ToRegister(); - ASSERT(left_side.is_valid()); + + // Here we split control flow to the stub call and inlined cases + // before finally splitting it to the control destination. We use + // a jump target and branching to duplicate the virtual frame at + // the first split. We manually handle the off-frame references + // by reconstituting them on the non-fall-through path. JumpTarget is_smi; + Register left_reg = left_side.reg(); + Handle right_val = right_side.handle(); __ test(left_side.reg(), Immediate(kSmiTagMask)); - is_smi.Branch(zero, &left_side, &right_side, taken); + is_smi.Branch(zero, taken); - // Setup and call the compare stub, which expects its arguments - // in registers. + // Setup and call the compare stub. CompareStub stub(cc, strict); Result result = frame_->CallStub(&stub, &left_side, &right_side); result.ToRegister(); @@ -1888,12 +1898,12 @@ void CodeGenerator::Comparison(Condition cc, dest->true_target()->Branch(cc); dest->false_target()->Jump(); - is_smi.Bind(&left_side, &right_side); - left_side.ToRegister(); + is_smi.Bind(); + left_side = Result(left_reg); + right_side = Result(right_val); // Test smi equality and comparison by signed int comparison. if (IsUnsafeSmi(right_side.handle())) { right_side.ToRegister(); - ASSERT(right_side.is_valid()); __ cmp(left_side.reg(), Operand(right_side.reg())); } else { __ cmp(Operand(left_side.reg()), Immediate(right_side.handle())); @@ -1945,35 +1955,50 @@ void CodeGenerator::Comparison(Condition cc, (right_side.is_constant() && !right_side.handle()->IsSmi()); left_side.ToRegister(); right_side.ToRegister(); - JumpTarget is_smi; - if (!known_non_smi) { - // Check for the smi case. + + if (known_non_smi) { + // When non-smi, call out to the compare stub. + CompareStub stub(cc, strict); + Result answer = frame_->CallStub(&stub, &left_side, &right_side); + if (cc == equal) { + __ test(answer.reg(), Operand(answer.reg())); + } else { + __ cmp(answer.reg(), 0); + } + answer.Unuse(); + dest->Split(cc); + } else { + // Here we split control flow to the stub call and inlined cases + // before finally splitting it to the control destination. We use + // a jump target and branching to duplicate the virtual frame at + // the first split. We manually handle the off-frame references + // by reconstituting them on the non-fall-through path. + JumpTarget is_smi; + Register left_reg = left_side.reg(); + Register right_reg = right_side.reg(); + Result temp = allocator_->Allocate(); ASSERT(temp.is_valid()); __ mov(temp.reg(), left_side.reg()); __ or_(temp.reg(), Operand(right_side.reg())); __ test(temp.reg(), Immediate(kSmiTagMask)); temp.Unuse(); - is_smi.Branch(zero, &left_side, &right_side, taken); - } - // When non-smi, call out to the compare stub, which expects its - // arguments in registers. - CompareStub stub(cc, strict); - Result answer = frame_->CallStub(&stub, &left_side, &right_side); - if (cc == equal) { - __ test(answer.reg(), Operand(answer.reg())); - } else { - __ cmp(answer.reg(), 0); - } - answer.Unuse(); - if (known_non_smi) { - dest->Split(cc); - } else { + is_smi.Branch(zero, taken); + // When non-smi, call out to the compare stub. + CompareStub stub(cc, strict); + Result answer = frame_->CallStub(&stub, &left_side, &right_side); + if (cc == equal) { + __ test(answer.reg(), Operand(answer.reg())); + } else { + __ cmp(answer.reg(), 0); + } + answer.Unuse(); dest->true_target()->Branch(cc); dest->false_target()->Jump(); - is_smi.Bind(&left_side, &right_side); - left_side.ToRegister(); - right_side.ToRegister(); + + is_smi.Bind(); + left_side = Result(left_reg); + right_side = Result(right_reg); __ cmp(left_side.reg(), Operand(right_side.reg())); right_side.Unuse(); left_side.Unuse(); @@ -2326,9 +2351,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) { // code by jumping to the return site. function_return_.Jump(&return_value); } else { - // Though this is a (possibly) backward block, the frames can - // only differ on their top element. - function_return_.Bind(&return_value, 1); + function_return_.Bind(&return_value); GenerateReturnSequence(&return_value); } } @@ -3253,7 +3276,6 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { // handler structure. if (FLAG_debug_code) { __ mov(eax, Operand::StaticVariable(handler_address)); - __ lea(eax, Operand(eax, StackHandlerConstants::kAddressDisplacement)); __ cmp(esp, Operand(eax)); __ Assert(equal, "stack pointer should point to top handler"); } @@ -3263,6 +3285,7 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { // The next handler address is on top of the frame. Unlink from // the handler list and drop the rest of this handler from the // frame. + ASSERT(StackHandlerConstants::kNextOffset == 0); frame_->EmitPop(Operand::StaticVariable(handler_address)); frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); if (has_unlinks) { @@ -3290,15 +3313,12 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { // Reload sp from the top handler, because some statements that we // break from (eg, for...in) may have left stuff on the stack. - __ mov(edx, Operand::StaticVariable(handler_address)); - const int kNextOffset = StackHandlerConstants::kNextOffset + - StackHandlerConstants::kAddressDisplacement; - __ lea(esp, Operand(edx, kNextOffset)); + __ mov(esp, Operand::StaticVariable(handler_address)); frame_->Forget(frame_->height() - handler_height); + ASSERT(StackHandlerConstants::kNextOffset == 0); frame_->EmitPop(Operand::StaticVariable(handler_address)); frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); - // next_sp popped. if (i == kReturnShadowIndex) { if (!function_return_is_shadowed_) frame_->PrepareForReturn(); @@ -3383,8 +3403,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { if (has_valid_frame()) { // The next handler address is on top of the frame. ASSERT(StackHandlerConstants::kNextOffset == 0); - frame_->EmitPop(eax); - __ mov(Operand::StaticVariable(handler_address), eax); + frame_->EmitPop(Operand::StaticVariable(handler_address)); frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); // Fake a top of stack value (unneeded when FALLING) and set the @@ -3418,13 +3437,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { // Reload sp from the top handler, because some statements that // we break from (eg, for...in) may have left stuff on the // stack. - __ mov(edx, Operand::StaticVariable(handler_address)); - const int kNextOffset = StackHandlerConstants::kNextOffset + - StackHandlerConstants::kAddressDisplacement; - __ lea(esp, Operand(edx, kNextOffset)); + __ mov(esp, Operand::StaticVariable(handler_address)); frame_->Forget(frame_->height() - handler_height); // Unlink this handler and drop it from the frame. + ASSERT(StackHandlerConstants::kNextOffset == 0); frame_->EmitPop(Operand::StaticVariable(handler_address)); frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); @@ -4625,48 +4642,82 @@ void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList* args) { // cons. The slow case will flatten the string, which will ensure that // the answer is in the left hand side the next time around. void CodeGenerator::GenerateFastCharCodeAt(ZoneList* args) { + Comment(masm_, "[ GenerateFastCharCodeAt"); ASSERT(args->length() == 2); - JumpTarget slow_case; - JumpTarget end; - JumpTarget not_a_flat_string; - JumpTarget a_cons_string; - JumpTarget try_again_with_new_string(JumpTarget::BIDIRECTIONAL); - JumpTarget ascii_string; - JumpTarget got_char_code; + Label slow_case; + Label end; + Label not_a_flat_string; + Label a_cons_string; + Label try_again_with_new_string; + Label ascii_string; + Label got_char_code; Load(args->at(0)); Load(args->at(1)); - // Reserve register ecx, to use as shift amount later - Result shift_amount = allocator()->Allocate(ecx); - ASSERT(shift_amount.is_valid()); Result index = frame_->Pop(); - index.ToRegister(); Result object = frame_->Pop(); + + // Get register ecx to use as shift amount later. + Result shift_amount; + if (object.is_register() && object.reg().is(ecx)) { + Result fresh = allocator_->Allocate(); + shift_amount = object; + object = fresh; + __ mov(object.reg(), ecx); + } + if (index.is_register() && index.reg().is(ecx)) { + Result fresh = allocator_->Allocate(); + shift_amount = index; + index = fresh; + __ mov(index.reg(), ecx); + } + // There could be references to ecx in the frame. Allocating will + // spill them, otherwise spill explicitly. + if (shift_amount.is_valid()) { + frame_->Spill(ecx); + } else { + shift_amount = allocator()->Allocate(ecx); + } + ASSERT(shift_amount.is_register()); + ASSERT(shift_amount.reg().is(ecx)); + ASSERT(allocator_->count(ecx) == 1); + + // We will mutate the index register and possibly the object register. + // The case where they are somehow the same register is handled + // because we only mutate them in the case where the receiver is a + // heap object and the index is not. object.ToRegister(); - // If the receiver is a smi return undefined. + index.ToRegister(); + frame_->Spill(object.reg()); + frame_->Spill(index.reg()); + + // We need a single extra temporary register. + Result temp = allocator()->Allocate(); + ASSERT(temp.is_valid()); + + // There is no virtual frame effect from here up to the final result + // push. + + // If the receiver is a smi trigger the slow case. ASSERT(kSmiTag == 0); __ test(object.reg(), Immediate(kSmiTagMask)); - slow_case.Branch(zero, not_taken); + __ j(zero, &slow_case); - // Check for negative or non-smi index. + // If the index is negative or non-smi trigger the slow case. ASSERT(kSmiTag == 0); __ test(index.reg(), Immediate(kSmiTagMask | 0x80000000)); - slow_case.Branch(not_zero, not_taken); - // Get rid of the smi tag on the index. - frame_->Spill(index.reg()); + __ j(not_zero, &slow_case); + // Untag the index. __ sar(index.reg(), kSmiTagSize); - try_again_with_new_string.Bind(&object, &index, &shift_amount); - // Get the type of the heap object. - Result object_type = allocator()->Allocate(); - ASSERT(object_type.is_valid()); - __ mov(object_type.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset)); - __ movzx_b(object_type.reg(), - FieldOperand(object_type.reg(), Map::kInstanceTypeOffset)); - // We don't handle non-strings. - __ test(object_type.reg(), Immediate(kIsNotStringMask)); - slow_case.Branch(not_zero, not_taken); + __ bind(&try_again_with_new_string); + // Fetch the instance type of the receiver into ecx. + __ mov(ecx, FieldOperand(object.reg(), HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + // If the receiver is not a string trigger the slow case. + __ test(ecx, Immediate(kIsNotStringMask)); + __ j(not_zero, &slow_case); // Here we make assumptions about the tag values and the shifts needed. // See the comment in objects.h. @@ -4675,86 +4726,75 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList* args) { String::kMediumLengthShift); ASSERT(kShortStringTag + String::kLongLengthShift == String::kShortLengthShift); - __ mov(shift_amount.reg(), Operand(object_type.reg())); - __ and_(shift_amount.reg(), kStringSizeMask); - __ add(Operand(shift_amount.reg()), Immediate(String::kLongLengthShift)); - // Get the length field. Temporary register now used for length. - Result length = object_type; - __ mov(length.reg(), FieldOperand(object.reg(), String::kLengthOffset)); - __ shr(length.reg()); // shift_amount, in ecx, is implicit operand. + __ and_(ecx, kStringSizeMask); + __ add(Operand(ecx), Immediate(String::kLongLengthShift)); + // Fetch the length field into the temporary register. + __ mov(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset)); + __ shr(temp.reg()); // The shift amount in ecx is implicit operand. // Check for index out of range. - __ cmp(index.reg(), Operand(length.reg())); - slow_case.Branch(greater_equal, not_taken); - length.Unuse(); - // Load the object type into object_type again. - // These two instructions are duplicated from above, to save a register. - __ mov(object_type.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset)); - __ movzx_b(object_type.reg(), - FieldOperand(object_type.reg(), Map::kInstanceTypeOffset)); + __ cmp(index.reg(), Operand(temp.reg())); + __ j(greater_equal, &slow_case); + // Reload the instance type (into the temp register this time).. + __ mov(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset)); + __ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset)); // We need special handling for non-flat strings. ASSERT(kSeqStringTag == 0); - __ test(object_type.reg(), Immediate(kStringRepresentationMask)); - not_a_flat_string.Branch(not_zero, &object, &index, &object_type, - &shift_amount, not_taken); - shift_amount.Unuse(); + __ test(temp.reg(), Immediate(kStringRepresentationMask)); + __ j(not_zero, ¬_a_flat_string); // Check for 1-byte or 2-byte string. - __ test(object_type.reg(), Immediate(kStringEncodingMask)); - ascii_string.Branch(not_zero, &object, &index, &object_type, taken); + __ test(temp.reg(), Immediate(kStringEncodingMask)); + __ j(not_zero, &ascii_string); // 2-byte string. - // Load the 2-byte character code. - __ movzx_w(object_type.reg(), FieldOperand(object.reg(), - index.reg(), - times_2, - SeqTwoByteString::kHeaderSize)); - object.Unuse(); - index.Unuse(); - got_char_code.Jump(&object_type); + // Load the 2-byte character code into the temp register. + __ movzx_w(temp.reg(), FieldOperand(object.reg(), + index.reg(), + times_2, + SeqTwoByteString::kHeaderSize)); + __ jmp(&got_char_code); // ASCII string. - ascii_string.Bind(&object, &index, &object_type); - // Load the byte. - __ movzx_b(object_type.reg(), FieldOperand(object.reg(), - index.reg(), - times_1, - SeqAsciiString::kHeaderSize)); - object.Unuse(); - index.Unuse(); - got_char_code.Bind(&object_type); + __ bind(&ascii_string); + // Load the byte into the temp register. + __ movzx_b(temp.reg(), FieldOperand(object.reg(), + index.reg(), + times_1, + SeqAsciiString::kHeaderSize)); + __ bind(&got_char_code); ASSERT(kSmiTag == 0); - __ shl(object_type.reg(), kSmiTagSize); - frame_->Push(&object_type); - end.Jump(); + __ shl(temp.reg(), kSmiTagSize); + __ jmp(&end); // Handle non-flat strings. - not_a_flat_string.Bind(&object, &index, &object_type, &shift_amount); - __ and_(object_type.reg(), kStringRepresentationMask); - __ cmp(object_type.reg(), kConsStringTag); - a_cons_string.Branch(equal, &object, &index, &shift_amount, taken); - __ cmp(object_type.reg(), kSlicedStringTag); - slow_case.Branch(not_equal, not_taken); - object_type.Unuse(); + __ bind(¬_a_flat_string); + __ and_(temp.reg(), kStringRepresentationMask); + __ cmp(temp.reg(), kConsStringTag); + __ j(equal, &a_cons_string); + __ cmp(temp.reg(), kSlicedStringTag); + __ j(not_equal, &slow_case); // SlicedString. - // Add the offset to the index. + // Add the offset to the index and trigger the slow case on overflow. __ add(index.reg(), FieldOperand(object.reg(), SlicedString::kStartOffset)); - slow_case.Branch(overflow); + __ j(overflow, &slow_case); // Getting the underlying string is done by running the cons string code. // ConsString. - a_cons_string.Bind(&object, &index, &shift_amount); - // Get the first of the two strings. - frame_->Spill(object.reg()); - // Both sliced and cons strings store their source string at the same place. + __ bind(&a_cons_string); + // Get the first of the two strings. Both sliced and cons strings + // store their source string at the same offset. ASSERT(SlicedString::kBufferOffset == ConsString::kFirstOffset); __ mov(object.reg(), FieldOperand(object.reg(), ConsString::kFirstOffset)); - try_again_with_new_string.Jump(&object, &index, &shift_amount); + __ jmp(&try_again_with_new_string); - // No results live at this point. - slow_case.Bind(); - frame_->Push(Factory::undefined_value()); - end.Bind(); + __ bind(&slow_case); + // Move the undefined value into the result register, which will + // trigger the slow case. + __ Set(temp.reg(), Immediate(Factory::undefined_value())); + + __ bind(&end); + frame_->Push(&temp); } @@ -4900,6 +4940,98 @@ void CodeGenerator::GenerateGetFramePointer(ZoneList* args) { } +void CodeGenerator::GenerateRandomPositiveSmi(ZoneList* args) { + ASSERT(args->length() == 0); + frame_->SpillAll(); + + // Make sure the frame is aligned like the OS expects. + static const int kFrameAlignment = OS::ActivationFrameAlignment(); + if (kFrameAlignment > 0) { + ASSERT(IsPowerOf2(kFrameAlignment)); + __ mov(edi, Operand(esp)); // Save in callee-saved register. + __ and_(esp, -kFrameAlignment); + } + + // Call V8::RandomPositiveSmi(). + __ call(FUNCTION_ADDR(V8::RandomPositiveSmi), RelocInfo::RUNTIME_ENTRY); + + // Restore stack pointer from callee-saved register edi. + if (kFrameAlignment > 0) { + __ mov(esp, Operand(edi)); + } + + Result result = allocator_->Allocate(eax); + frame_->Push(&result); +} + + +void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* args) { + JumpTarget done; + JumpTarget call_runtime; + ASSERT(args->length() == 1); + + // Load number and duplicate it. + Load(args->at(0)); + frame_->Dup(); + + // Get the number into an unaliased register and load it onto the + // floating point stack still leaving one copy on the frame. + Result number = frame_->Pop(); + number.ToRegister(); + frame_->Spill(number.reg()); + FloatingPointHelper::LoadFloatOperand(masm_, number.reg()); + number.Unuse(); + + // Perform the operation on the number. + switch (op) { + case SIN: + __ fsin(); + break; + case COS: + __ fcos(); + break; + } + + // Go slow case if argument to operation is out of range. + __ fnstsw_ax(); + __ sahf(); + call_runtime.Branch(parity_even, not_taken); + + // Allocate heap number for result if possible. + Result scratch1 = allocator()->Allocate(); + Result scratch2 = allocator()->Allocate(); + Result heap_number = allocator()->Allocate(); + FloatingPointHelper::AllocateHeapNumber(masm_, + call_runtime.entry_label(), + scratch1.reg(), + scratch2.reg(), + heap_number.reg()); + scratch1.Unuse(); + scratch2.Unuse(); + + // Store the result in the allocated heap number. + __ fstp_d(FieldOperand(heap_number.reg(), HeapNumber::kValueOffset)); + // Replace the extra copy of the argument with the result. + frame_->SetElementAt(0, &heap_number); + done.Jump(); + + call_runtime.Bind(); + // Free ST(0) which was not popped before calling into the runtime. + __ ffree(0); + Result answer; + switch (op) { + case SIN: + answer = frame_->CallRuntime(Runtime::kMath_sin, 1); + break; + case COS: + answer = frame_->CallRuntime(Runtime::kMath_cos, 1); + break; + } + frame_->Push(&answer); + done.Bind(); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (CheckForInlineRuntimeCall(node)) { return; @@ -5040,7 +5172,10 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { break; case Token::SUB: { - UnarySubStub stub; + bool overwrite = + (node->AsBinaryOperation() != NULL && + node->AsBinaryOperation()->ResultOverwriteAllowed()); + UnarySubStub stub(overwrite); // TODO(1222589): remove dependency of TOS being cached inside stub Result operand = frame_->Pop(); Result answer = frame_->CallStub(&stub, &operand); @@ -5446,18 +5581,6 @@ void CodeGenerator::VisitThisFunction(ThisFunction* node) { } -class InstanceofStub: public CodeStub { - public: - InstanceofStub() { } - - void Generate(MacroAssembler* masm); - - private: - Major MajorKey() { return Instanceof; } - int MinorKey() { return 0; } -}; - - void CodeGenerator::VisitCompareOperation(CompareOperation* node) { Comment cmnt(masm_, "[ CompareOperation"); @@ -5728,9 +5851,58 @@ void DeferredReferenceGetKeyedValue::Generate() { } +class DeferredReferenceSetKeyedValue: public DeferredCode { + public: + DeferredReferenceSetKeyedValue(Register value, + Register key, + Register receiver) + : value_(value), key_(key), receiver_(receiver) { + set_comment("[ DeferredReferenceSetKeyedValue"); + } + + virtual void Generate(); + + Label* patch_site() { return &patch_site_; } + + private: + Register value_; + Register key_; + Register receiver_; + Label patch_site_; +}; + + +void DeferredReferenceSetKeyedValue::Generate() { + __ IncrementCounter(&Counters::keyed_store_inline_miss, 1); + // Push receiver and key arguments on the stack. + __ push(receiver_); + __ push(key_); + // Move value argument to eax as expected by the IC stub. + if (!value_.is(eax)) __ mov(eax, value_); + // Call the IC stub. + Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + // The delta from the start of the map-compare instruction to the + // test instruction. We use masm_-> directly here instead of the + // __ macro because the macro sometimes uses macro expansion to turn + // into something that can't return a value. This is encountered + // when doing generated code coverage tests. + int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site()); + // Here we use masm_-> instead of the __ macro because this is the + // instruction that gets patched and coverage code gets in the way. + masm_->test(eax, Immediate(-delta_to_patch_site)); + // Restore value (returned from store IC), key and receiver + // registers. + if (!value_.is(eax)) __ mov(value_, eax); + __ pop(key_); + __ pop(receiver_); +} + + #undef __ #define __ ACCESS_MASM(masm) + Handle Reference::GetName() { ASSERT(type_ == NAMED); Property* property = expression_->AsProperty(); @@ -5849,7 +6021,7 @@ void Reference::GetValue(TypeofState typeof_state) { // a check against an invalid map. In the inline cache code, we // patch the map check if appropriate. if (cgen_->loop_nesting() > 0) { - Comment cmnt(masm, "[ Inlined array index load"); + Comment cmnt(masm, "[ Inlined load from keyed Property"); Result key = cgen_->frame()->Pop(); Result receiver = cgen_->frame()->Pop(); @@ -5990,9 +6162,10 @@ void Reference::TakeValue(TypeofState typeof_state) { void Reference::SetValue(InitState init_state) { ASSERT(cgen_->HasValidEntryRegisters()); ASSERT(!is_illegal()); + MacroAssembler* masm = cgen_->masm(); switch (type_) { case SLOT: { - Comment cmnt(cgen_->masm(), "[ Store to Slot"); + Comment cmnt(masm, "[ Store to Slot"); Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); ASSERT(slot != NULL); cgen_->StoreToSlot(slot, init_state); @@ -6000,7 +6173,7 @@ void Reference::SetValue(InitState init_state) { } case NAMED: { - Comment cmnt(cgen_->masm(), "[ Store to named Property"); + Comment cmnt(masm, "[ Store to named Property"); cgen_->frame()->Push(GetName()); Result answer = cgen_->frame()->CallStoreIC(); cgen_->frame()->Push(&answer); @@ -6008,9 +6181,104 @@ void Reference::SetValue(InitState init_state) { } case KEYED: { - Comment cmnt(cgen_->masm(), "[ Store to keyed Property"); - Result answer = cgen_->frame()->CallKeyedStoreIC(); - cgen_->frame()->Push(&answer); + Comment cmnt(masm, "[ Store to keyed Property"); + + // Generate inlined version of the keyed store if the code is in + // a loop and the key is likely to be a smi. + Property* property = expression()->AsProperty(); + ASSERT(property != NULL); + SmiAnalysis* key_smi_analysis = property->key()->type(); + + if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) { + Comment cmnt(masm, "[ Inlined store to keyed Property"); + + // Get the receiver, key and value into registers. + Result value = cgen_->frame()->Pop(); + Result key = cgen_->frame()->Pop(); + Result receiver = cgen_->frame()->Pop(); + + Result tmp = cgen_->allocator_->Allocate(); + ASSERT(tmp.is_valid()); + + // Determine whether the value is a constant before putting it + // in a register. + bool value_is_constant = value.is_constant(); + + // Make sure that value, key and receiver are in registers. + value.ToRegister(); + key.ToRegister(); + receiver.ToRegister(); + + DeferredReferenceSetKeyedValue* deferred = + new DeferredReferenceSetKeyedValue(value.reg(), + key.reg(), + receiver.reg()); + + // Check that the value is a smi if it is not a constant. We + // can skip the write barrier for smis and constants. + if (!value_is_constant) { + __ test(value.reg(), Immediate(kSmiTagMask)); + deferred->Branch(not_zero); + } + + // Check that the key is a non-negative smi. + __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000)); + deferred->Branch(not_zero); + + // Check that the receiver is not a smi. + __ test(receiver.reg(), Immediate(kSmiTagMask)); + deferred->Branch(zero); + + // Check that the receiver is a JSArray. + __ mov(tmp.reg(), + FieldOperand(receiver.reg(), HeapObject::kMapOffset)); + __ movzx_b(tmp.reg(), + FieldOperand(tmp.reg(), Map::kInstanceTypeOffset)); + __ cmp(tmp.reg(), JS_ARRAY_TYPE); + deferred->Branch(not_equal); + + // Check that the key is within bounds. Both the key and the + // length of the JSArray are smis. + __ cmp(key.reg(), + FieldOperand(receiver.reg(), JSArray::kLengthOffset)); + deferred->Branch(greater_equal); + + // Get the elements array from the receiver and check that it + // is not a dictionary. + __ mov(tmp.reg(), + FieldOperand(receiver.reg(), JSObject::kElementsOffset)); + // Bind the deferred code patch site to be able to locate the + // fixed array map comparison. When debugging, we patch this + // comparison to always fail so that we will hit the IC call + // in the deferred code which will allow the debugger to + // break for fast case stores. + __ bind(deferred->patch_site()); + __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset), + Immediate(Factory::fixed_array_map())); + deferred->Branch(not_equal); + + // Store the value. + __ mov(Operand(tmp.reg(), + key.reg(), + times_2, + Array::kHeaderSize - kHeapObjectTag), + value.reg()); + __ IncrementCounter(&Counters::keyed_store_inline, 1); + + deferred->BindExit(); + + cgen_->frame()->Push(&receiver); + cgen_->frame()->Push(&key); + cgen_->frame()->Push(&value); + } else { + Result answer = cgen_->frame()->CallKeyedStoreIC(); + // Make sure that we do not have a test instruction after the + // call. A test instruction after the call is used to + // indicate that we have generated an inline version of the + // keyed store. + __ nop(); + cgen_->frame()->Push(&answer); + } break; } @@ -6267,7 +6535,8 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime, ecx, - edx); + edx, + eax); __ bind(&skip_allocation); break; default: UNREACHABLE(); @@ -6375,7 +6644,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { // Fall through! case NO_OVERWRITE: FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime, - ecx, edx); + ecx, edx, eax); __ bind(&skip_allocation); break; default: UNREACHABLE(); @@ -6460,22 +6729,42 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm, Label* need_gc, Register scratch1, - Register scratch2) { + Register scratch2, + Register result) { ExternalReference allocation_top = ExternalReference::new_space_allocation_top_address(); ExternalReference allocation_limit = ExternalReference::new_space_allocation_limit_address(); __ mov(Operand(scratch1), Immediate(allocation_top)); - __ mov(eax, Operand(scratch1, 0)); - __ lea(scratch2, Operand(eax, HeapNumber::kSize)); // scratch2: new top + __ mov(result, Operand(scratch1, 0)); + __ lea(scratch2, Operand(result, HeapNumber::kSize)); // scratch2: new top __ cmp(scratch2, Operand::StaticVariable(allocation_limit)); __ j(above, need_gc, not_taken); __ mov(Operand(scratch1, 0), scratch2); // store new top - __ mov(Operand(eax, HeapObject::kMapOffset), + __ mov(Operand(result, HeapObject::kMapOffset), Immediate(Factory::heap_number_map())); // Tag old top and use as result. - __ add(Operand(eax), Immediate(kHeapObjectTag)); + __ add(Operand(result), Immediate(kHeapObjectTag)); +} + + +void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, + Register scratch) { + Label load_smi, done; + + __ test(scratch, Immediate(kSmiTagMask)); + __ j(zero, &load_smi, not_taken); + __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset)); + __ jmp(&done); + + __ bind(&load_smi); + __ sar(scratch, kSmiTagSize); + __ push(scratch); + __ fild_s(Operand(esp, 0)); + __ pop(scratch); + + __ bind(&done); } @@ -6577,13 +6866,21 @@ void UnarySubStub::Generate(MacroAssembler* masm) { __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset)); __ cmp(edx, Factory::heap_number_map()); __ j(not_equal, &slow); - __ mov(edx, Operand(eax)); - // edx: operand - FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx); - // eax: allocated 'empty' number - __ fld_d(FieldOperand(edx, HeapNumber::kValueOffset)); - __ fchs(); - __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); + if (overwrite_) { + __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset)); + __ xor_(edx, HeapNumber::kSignMask); // Flip sign. + __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), edx); + } else { + __ mov(edx, Operand(eax)); + // edx: operand + FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx, eax); + // eax: allocated 'empty' number + __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset)); + __ xor_(ecx, HeapNumber::kSignMask); // Flip sign. + __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ecx); + __ mov(ecx, FieldOperand(edx, HeapNumber::kMantissaOffset)); + __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx); + } __ bind(&done); @@ -6727,7 +7024,7 @@ void CompareStub::Generate(MacroAssembler* masm) { // The representation of NaN values has all exponent bits (52..62) set, // and not all mantissa bits (0..51) clear. // Read top bits of double representation (second word of value). - __ mov(eax, FieldOperand(edx, HeapNumber::kValueOffset + kPointerSize)); + __ mov(eax, FieldOperand(edx, HeapNumber::kExponentOffset)); // Test that exponent bits are all set. __ not_(eax); __ test(eax, Immediate(0x7ff00000)); @@ -6737,7 +7034,7 @@ void CompareStub::Generate(MacroAssembler* masm) { // Shift out flag and all exponent bits, retaining only mantissa. __ shl(eax, 12); // Or with all low-bits of mantissa. - __ or_(eax, FieldOperand(edx, HeapNumber::kValueOffset)); + __ or_(eax, FieldOperand(edx, HeapNumber::kMantissaOffset)); // Return zero equal if all bits in mantissa is zero (it's an Infinity) // and non-zero if not (it's a NaN). __ ret(0); @@ -6935,26 +7232,33 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { - ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code + // eax holds the exception. + + // Adjust this code if not the case. + ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // Drop the sp to the top of the handler. ExternalReference handler_address(Top::k_handler_address); - __ mov(edx, Operand::StaticVariable(handler_address)); - __ mov(ecx, Operand(edx, -1 * kPointerSize)); // get next in chain - __ mov(Operand::StaticVariable(handler_address), ecx); - __ mov(esp, Operand(edx)); - __ pop(edi); + __ mov(esp, Operand::StaticVariable(handler_address)); + + // Restore next handler and frame pointer, discard handler state. + ASSERT(StackHandlerConstants::kNextOffset == 0); + __ pop(Operand::StaticVariable(handler_address)); + ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); __ pop(ebp); - __ pop(edx); // remove code pointer - __ pop(edx); // remove state + __ pop(edx); // Remove state. - // Before returning we restore the context from the frame pointer if not NULL. - // The frame pointer is NULL in the exception handler of a JS entry frame. - __ xor_(esi, Operand(esi)); // tentatively set context pointer to NULL + // Before returning we restore the context from the frame pointer if + // not NULL. The frame pointer is NULL in the exception handler of + // a JS entry frame. + __ xor_(esi, Operand(esi)); // Tentatively set context pointer to NULL. Label skip; __ cmp(ebp, 0); __ j(equal, &skip, not_taken); __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ bind(&skip); + ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); __ ret(0); } @@ -7040,51 +7344,49 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { - // Fetch top stack handler. + // Adjust this code if not the case. + ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // Drop sp to the top stack handler. ExternalReference handler_address(Top::k_handler_address); - __ mov(edx, Operand::StaticVariable(handler_address)); + __ mov(esp, Operand::StaticVariable(handler_address)); // Unwind the handlers until the ENTRY handler is found. Label loop, done; __ bind(&loop); // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kAddressDisplacement + - StackHandlerConstants::kStateOffset; - __ cmp(Operand(edx, kStateOffset), Immediate(StackHandler::ENTRY)); + const int kStateOffset = StackHandlerConstants::kStateOffset; + __ cmp(Operand(esp, kStateOffset), Immediate(StackHandler::ENTRY)); __ j(equal, &done); // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kAddressDisplacement + - StackHandlerConstants::kNextOffset; - __ mov(edx, Operand(edx, kNextOffset)); + const int kNextOffset = StackHandlerConstants::kNextOffset; + __ mov(esp, Operand(esp, kNextOffset)); __ jmp(&loop); __ bind(&done); // Set the top handler address to next handler past the current ENTRY handler. - __ mov(eax, Operand(edx, kNextOffset)); - __ mov(Operand::StaticVariable(handler_address), eax); + ASSERT(StackHandlerConstants::kNextOffset == 0); + __ pop(Operand::StaticVariable(handler_address)); // Set external caught exception to false. - __ mov(eax, false); ExternalReference external_caught(Top::k_external_caught_exception_address); + __ mov(eax, false); __ mov(Operand::StaticVariable(external_caught), eax); // Set pending exception and eax to out of memory exception. - __ mov(eax, reinterpret_cast(Failure::OutOfMemoryException())); ExternalReference pending_exception(Top::k_pending_exception_address); + __ mov(eax, reinterpret_cast(Failure::OutOfMemoryException())); __ mov(Operand::StaticVariable(pending_exception), eax); - // Restore the stack to the address of the ENTRY handler - __ mov(esp, Operand(edx)); - // Clear the context pointer; __ xor_(esi, Operand(esi)); - // Restore registers from handler. - __ pop(edi); // PP - __ pop(ebp); // FP - __ pop(edx); // Code - __ pop(edx); // State + // Restore fp from handler and discard handler state. + ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); + __ pop(ebp); + __ pop(edx); // State. + ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); __ ret(0); } @@ -7095,12 +7397,11 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // ebp: frame pointer (restored after C call) // esp: stack pointer (restored after C call) // esi: current context (C callee-saved) - // edi: caller's parameter pointer pp (C callee-saved) + // edi: JS function of the caller (C callee-saved) - // NOTE: Invocations of builtins may return failure objects - // instead of a proper result. The builtin entry handles - // this by performing a garbage collection and retrying the - // builtin once. + // NOTE: Invocations of builtins may return failure objects instead + // of a proper result. The builtin entry handles this by performing + // a garbage collection and retrying the builtin (twice). StackFrame::Type frame_type = is_debug_break ? StackFrame::EXIT_DEBUG : @@ -7203,7 +7504,6 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Invoke: Link this frame into the handler chain. __ bind(&invoke); __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); - __ push(eax); // flush TOS // Clear any pending exceptions. __ mov(edx, diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index 9b609a1562..e409513488 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -518,6 +518,15 @@ class CodeGenerator: public AstVisitor { void GenerateGetFramePointer(ZoneList* args); + // Fast support for Math.random(). + void GenerateRandomPositiveSmi(ZoneList* args); + + // Fast support for Math.sin and Math.cos. + enum MathOp { SIN, COS }; + void GenerateFastMathOp(MathOp op, ZoneList* args); + inline void GenerateMathSin(ZoneList* args); + inline void GenerateMathCos(ZoneList* args); + // Methods and constants for fast case switch statement support. // // Only allow fast-case switch if the range of labels is at most diff --git a/deps/v8/src/ia32/frames-ia32.h b/deps/v8/src/ia32/frames-ia32.h index aec1f48359..3a7c86bf73 100644 --- a/deps/v8/src/ia32/frames-ia32.h +++ b/deps/v8/src/ia32/frames-ia32.h @@ -55,16 +55,10 @@ typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; class StackHandlerConstants : public AllStatic { public: static const int kNextOffset = 0 * kPointerSize; - static const int kPPOffset = 1 * kPointerSize; - static const int kFPOffset = 2 * kPointerSize; + static const int kFPOffset = 1 * kPointerSize; + static const int kStateOffset = 2 * kPointerSize; + static const int kPCOffset = 3 * kPointerSize; - // TODO(1233780): Get rid of the code slot in stack handlers. - static const int kCodeOffset = 3 * kPointerSize; - - static const int kStateOffset = 4 * kPointerSize; - static const int kPCOffset = 5 * kPointerSize; - - static const int kAddressDisplacement = -1 * kPointerSize; static const int kSize = kPCOffset + kPointerSize; }; @@ -85,12 +79,12 @@ class ExitFrameConstants : public AllStatic { static const int kDebugMarkOffset = -2 * kPointerSize; static const int kSPOffset = -1 * kPointerSize; - // Let the parameters pointer for exit frames point just below the - // frame structure on the stack (frame pointer and return address). - static const int kPPDisplacement = +2 * kPointerSize; - static const int kCallerFPOffset = 0 * kPointerSize; static const int kCallerPCOffset = +1 * kPointerSize; + + // FP-relative displacement of the caller's SP. It points just + // below the saved PC. + static const int kCallerSPDisplacement = +2 * kPointerSize; }; @@ -112,7 +106,7 @@ class JavaScriptFrameConstants : public AllStatic { static const int kSavedRegistersOffset = +2 * kPointerSize; static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset; - // CallerSP-relative (aka PP-relative) + // Caller SP-relative. static const int kParam0Offset = -2 * kPointerSize; static const int kReceiverOffset = -1 * kPointerSize; }; @@ -136,157 +130,6 @@ inline Object* JavaScriptFrame::function_slot_object() const { } -// ---------------------------------------------------- - - - - - // C Entry frames: - - // lower | Stack | - // addresses | ^ | - // | | | - // | | - // +-------------+ - // | entry_pc | - // +-------------+ <--+ entry_sp - // . | - // . | - // . | - // +-------------+ | - // -3 | entry_sp --+----+ - // e +-------------+ - // n -2 | C function | - // t +-------------+ - // r -1 | caller_pp | - // y +-------------+ <--- fp (frame pointer, ebp) - // 0 | caller_fp | - // f +-------------+ - // r 1 | caller_pc | - // a +-------------+ <--- caller_sp (stack pointer, esp) - // m 2 | | - // e | arguments | - // | | - // +- - - - - - -+ - // | argument0 | - // +=============+ - // | | - // | caller | - // higher | expressions | - // addresses | | - - - // Proper JS frames: - - // lower | Stack | - // addresses | ^ | - // | | | - // | | - // ----------- +=============+ <--- sp (stack pointer, esp) - // | function | - // +-------------+ - // | | - // | expressions | - // | | - // +-------------+ - // a | | - // c | locals | - // t | | - // i +- - - - - - -+ <--- - // v -4 | local0 | ^ - // a +-------------+ | - // t -3 | code | | - // i +-------------+ | - // o -2 | context | | kLocal0Offset - // n +-------------+ | - // -1 | caller_pp | v - // f +-------------+ <--- fp (frame pointer, ebp) - // r 0 | caller_fp | - // a +-------------+ - // m 1 | caller_pc | - // e +-------------+ <--- caller_sp (incl. parameters) - // 2 | | - // | parameters | - // | | - // +- - - - - - -+ <--- - // -2 | parameter0 | ^ - // +-------------+ | kParam0Offset - // -1 | receiver | v - // ----------- +=============+ <--- pp (parameter pointer, edi) - // 0 | function | - // +-------------+ - // | | - // | caller | - // higher | expressions | - // addresses | | - - - // JS entry frames: When calling from C to JS, we construct two extra - // frames: An entry frame (C) and a trampoline frame (JS). The - // following pictures shows the two frames: - - // lower | Stack | - // addresses | ^ | - // | | | - // | | - // ----------- +=============+ <--- sp (stack pointer, esp) - // | | - // | parameters | - // t | | - // r +- - - - - - -+ - // a | parameter0 | - // m +-------------+ - // p | receiver | - // o +-------------+ <--- - // l | function | ^ - // i +-------------+ | - // n -3 | code | | kLocal0Offset - // e +-------------+ - // -2 | NULL | context is always NULL - // +-------------+ - // f -1 | NULL | caller pp is always NULL for entry frames - // r +-------------+ <--- fp (frame pointer, ebp) - // a 0 | caller fp | - // m +-------------+ - // e 1 | caller pc | - // +-------------+ <--- caller_sp (incl. parameters) - // | 0 | - // ----------- +=============+ <--- pp (parameter pointer, edi) - // | 0 | - // +-------------+ <--- - // . ^ - // . | try-handler (HandlerOffsets::kSize) - // . v - // +-------------+ <--- - // -5 | next top pp | - // +-------------+ - // e -4 | next top fp | - // n +-------------+ <--- - // t -3 | ebx | ^ - // r +-------------+ | - // y -2 | esi | | callee-saved registers - // +-------------+ | - // -1 | edi | v - // f +-------------+ <--- fp - // r 0 | caller fp | - // a +-------------+ pp == NULL (parameter pointer) - // m 1 | caller pc | - // e +-------------+ <--- caller sp - // 2 | code entry | ^ - // +-------------+ | - // 3 | function | | - // +-------------+ | arguments passed from C code - // 4 | receiver | | - // +-------------+ | - // 5 | argc | | - // +-------------+ | - // 6 | argv | v - // +-------------+ <--- - // | | - // higher | | - // addresses | | - - } } // namespace v8::internal #endif // V8_IA32_FRAMES_IA32_H_ diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index d7f264d495..5da9b2f929 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -747,6 +747,21 @@ void KeyedLoadIC::ClearInlinedVersion(Address address) { } +void KeyedStoreIC::ClearInlinedVersion(Address address) { + // Insert null as the elements map to check for. This will make + // sure that the elements fast-case map check fails so that control + // flows to the IC instead of the inlined version. + PatchInlinedStore(address, Heap::null_value()); +} + + +void KeyedStoreIC::RestoreInlinedVersion(Address address) { + // Restore the fast-case elements map check so that the inlined + // version can be used again. + PatchInlinedStore(address, Heap::fixed_array_map()); +} + + bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { // The address of the instruction following the call. Address test_instruction_address = address + 4; @@ -774,7 +789,7 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { } -bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { +static bool PatchInlinedMapCheck(Address address, Object* map) { Address test_instruction_address = address + 4; // 4 = stub address // The keyed load has a fast inlined case if the IC call instruction // is immediately followed by a test instruction. @@ -795,6 +810,16 @@ bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { } +bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { + return PatchInlinedMapCheck(address, map); +} + + +bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) { + return PatchInlinedMapCheck(address, map); +} + + // Defined in ic.cc. Object* KeyedLoadIC_Miss(Arguments args); diff --git a/deps/v8/src/ia32/jump-target-ia32.cc b/deps/v8/src/ia32/jump-target-ia32.cc index 9644a16aab..587fb2de4e 100644 --- a/deps/v8/src/ia32/jump-target-ia32.cc +++ b/deps/v8/src/ia32/jump-target-ia32.cc @@ -164,7 +164,7 @@ void JumpTarget::Call() { } -void JumpTarget::DoBind(int mergable_elements) { +void JumpTarget::DoBind() { ASSERT(cgen() != NULL); ASSERT(!is_bound()); @@ -210,7 +210,7 @@ void JumpTarget::DoBind(int mergable_elements) { // Fast case: no forward jumps, possible backward ones. Remove // constants and copies above the watermark on the fall-through // frame and use it as the entry frame. - cgen()->frame()->MakeMergable(mergable_elements); + cgen()->frame()->MakeMergable(); entry_frame_ = new VirtualFrame(cgen()->frame()); } __ bind(&entry_label_); @@ -252,7 +252,7 @@ void JumpTarget::DoBind(int mergable_elements) { } // Compute the frame to use for entry to the block. - ComputeEntryFrame(mergable_elements); + ComputeEntryFrame(); // Some moves required to merge to an expected frame require purely // frame state changes, and do not require any code generation. diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index 7636c4ed80..479b8ca014 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -358,7 +358,7 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG); // Setup the frame structure on the stack. - ASSERT(ExitFrameConstants::kPPDisplacement == +2 * kPointerSize); + ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize); ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize); ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize); push(ebp); @@ -448,7 +448,8 @@ void MacroAssembler::LeaveExitFrame(StackFrame::Type type) { void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { - ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code + // Adjust this code if not the case. + ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); // The pc (return address) is already on TOS. if (try_location == IN_JAVASCRIPT) { if (type == TRY_CATCH_HANDLER) { @@ -456,23 +457,18 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, } else { push(Immediate(StackHandler::TRY_FINALLY)); } - push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent))); push(ebp); - push(edi); } else { ASSERT(try_location == IN_JS_ENTRY); - // The parameter pointer is meaningless here and ebp does not - // point to a JS frame. So we save NULL for both pp and ebp. We - // expect the code throwing an exception to check ebp before - // dereferencing it to restore the context. + // The frame pointer does not point to a JS frame so we save NULL + // for ebp. We expect the code throwing an exception to check ebp + // before dereferencing it to restore the context. push(Immediate(StackHandler::ENTRY)); - push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent))); - push(Immediate(0)); // NULL frame pointer - push(Immediate(0)); // NULL parameter pointer + push(Immediate(0)); // NULL frame pointer. } - // Cached TOS. - mov(eax, Operand::StaticVariable(ExternalReference(Top::k_handler_address))); - // Link this handler. + // Save the current handler as the next handler. + push(Operand::StaticVariable(ExternalReference(Top::k_handler_address))); + // Link this handler as the new current one. mov(Operand::StaticVariable(ExternalReference(Top::k_handler_address)), esp); } diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 940a8b4627..42620dd8c5 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -154,9 +154,8 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // Exception handling - // Push a new try handler and link into try handler chain. - // The return address must be pushed before calling this helper. - // On exit, eax contains TOS (next_sp). + // Push a new try handler and link into try handler chain. The return + // address must be pushed before calling this helper. void PushTryHandler(CodeLocation try_location, HandlerType type); @@ -286,7 +285,7 @@ class MacroAssembler: public Assembler { List unresolved_; bool generating_stub_; bool allow_stub_calls_; - Handle code_object_; // This handle will be patched with the code + Handle code_object_; // This handle will be patched with the // code object on installation. // Helper functions for generating invokes. diff --git a/deps/v8/src/ia32/virtual-frame-ia32.cc b/deps/v8/src/ia32/virtual-frame-ia32.cc index 5f85de70db..3d97a66fa0 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.cc +++ b/deps/v8/src/ia32/virtual-frame-ia32.cc @@ -174,14 +174,8 @@ void VirtualFrame::SyncRange(int begin, int end) { } -void VirtualFrame::MakeMergable(int mergable_elements) { - if (mergable_elements == JumpTarget::kAllElements) { - mergable_elements = element_count(); - } - ASSERT(mergable_elements <= element_count()); - - int start_index = element_count() - mergable_elements; - for (int i = start_index; i < element_count(); i++) { +void VirtualFrame::MakeMergable() { + for (int i = 0; i < element_count(); i++) { FrameElement element = elements_[i]; if (element.is_constant() || element.is_copy()) { @@ -775,14 +769,10 @@ void VirtualFrame::StoreToFrameSlotAt(int index) { void VirtualFrame::PushTryHandler(HandlerType type) { ASSERT(cgen()->HasValidEntryRegisters()); - // Grow the expression stack by handler size less two (the return address - // is already pushed by a call instruction, and PushTryHandler from the - // macro assembler will leave the top of stack in the eax register to be - // pushed separately). - Adjust(kHandlerSize - 2); + // Grow the expression stack by handler size less one (the return + // address is already pushed by a call instruction). + Adjust(kHandlerSize - 1); __ PushTryHandler(IN_JAVASCRIPT, type); - // TODO(1222589): remove the reliance of PushTryHandler on a cached TOS - EmitPush(eax); } diff --git a/deps/v8/src/ia32/virtual-frame-ia32.h b/deps/v8/src/ia32/virtual-frame-ia32.h index 6e6ebd50a9..b69b800b04 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.h +++ b/deps/v8/src/ia32/virtual-frame-ia32.h @@ -153,11 +153,8 @@ class VirtualFrame : public ZoneObject { void SyncRange(int begin, int end); // Make this frame so that an arbitrary frame of the same height can - // be merged to it. Copies and constants are removed from the - // topmost mergable_elements elements of the frame. A - // mergable_elements of JumpTarget::kAllElements indicates constants - // and copies are should be removed from the entire frame. - void MakeMergable(int mergable_elements); + // be merged to it. Copies and constants are removed from the frame. + void MakeMergable(); // Prepare this virtual frame for merging to an expected frame by // performing some state changes that do not require generating diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc index 657614a39b..16235db210 100644 --- a/deps/v8/src/ic.cc +++ b/deps/v8/src/ic.cc @@ -849,6 +849,20 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, } +static bool StoreICableLookup(LookupResult* lookup) { + // Bail out if we didn't find a result. + if (!lookup->IsValid() || !lookup->IsCacheable()) return false; + + // If the property is read-only, we leave the IC in its current + // state. + if (lookup->IsReadOnly()) return false; + + if (!lookup->IsLoaded()) return false; + + return true; +} + + Object* StoreIC::Store(State state, Handle object, Handle name, @@ -873,12 +887,12 @@ Object* StoreIC::Store(State state, } // Lookup the property locally in the receiver. - LookupResult lookup; - receiver->LocalLookup(*name, &lookup); - - // Update inline cache and stub cache. - if (FLAG_use_ic && lookup.IsLoaded()) { - UpdateCaches(&lookup, state, receiver, name, value); + if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) { + LookupResult lookup; + receiver->LocalLookup(*name, &lookup); + if (StoreICableLookup(&lookup)) { + UpdateCaches(&lookup, state, receiver, name, value); + } } // Set the property. @@ -893,14 +907,9 @@ void StoreIC::UpdateCaches(LookupResult* lookup, Handle value) { ASSERT(lookup->IsLoaded()); // Skip JSGlobalProxy. - if (receiver->IsJSGlobalProxy()) return; + ASSERT(!receiver->IsJSGlobalProxy()); - // Bail out if we didn't find a result. - if (!lookup->IsValid() || !lookup->IsCacheable()) return; - - // If the property is read-only, we leave the IC in its current - // state. - if (lookup->IsReadOnly()) return; + ASSERT(StoreICableLookup(lookup)); // If the property has a non-field type allowing map transitions // where there is extra room in the object, we leave the IC in its diff --git a/deps/v8/src/ic.h b/deps/v8/src/ic.h index bd94fd89e5..9c96ba2fef 100644 --- a/deps/v8/src/ic.h +++ b/deps/v8/src/ic.h @@ -356,6 +356,12 @@ class KeyedStoreIC: public IC { static void GenerateGeneric(MacroAssembler* masm); static void GenerateExtendStorage(MacroAssembler* masm); + // Clear the inlined version so the IC is always hit. + static void ClearInlinedVersion(Address address); + + // Restore the inlined version so the fast case can get hit. + static void RestoreInlinedVersion(Address address); + private: static void Generate(MacroAssembler* masm, const ExternalReference& f); @@ -378,6 +384,11 @@ class KeyedStoreIC: public IC { } static void Clear(Address address, Code* target); + + // Support for patching the map that is checked in an inlined + // version of keyed store. + static bool PatchInlinedStore(Address address, Object* map); + friend class IC; }; diff --git a/deps/v8/src/jump-target.cc b/deps/v8/src/jump-target.cc index a8eda6bd98..a9d777073a 100644 --- a/deps/v8/src/jump-target.cc +++ b/deps/v8/src/jump-target.cc @@ -48,7 +48,7 @@ void JumpTarget::Unuse() { } -void JumpTarget::ComputeEntryFrame(int mergable_elements) { +void JumpTarget::ComputeEntryFrame() { // Given: a collection of frames reaching by forward CFG edges and // the directionality of the block. Compute: an entry frame for the // block. @@ -77,14 +77,6 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) { int length = initial_frame->element_count(); ZoneList elements(length); - // Convert the number of mergable elements (counted from the top - // down) to a frame high-water mark (counted from the bottom up). - // Elements strictly above the high-water index will be mergable in - // entry frames for bidirectional jump targets. - int high_water_mark = (mergable_elements == kAllElements) - ? VirtualFrame::kIllegalIndex // All frame indices are above this. - : length - mergable_elements - 1; // Top index if m_e == 0. - // Initially populate the list of elements based on the initial // frame. for (int i = 0; i < length; i++) { @@ -92,7 +84,7 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) { // We do not allow copies or constants in bidirectional frames. All // elements above the water mark on bidirectional frames have // unknown static types. - if (direction_ == BIDIRECTIONAL && i > high_water_mark) { + if (direction_ == BIDIRECTIONAL) { if (element.is_constant() || element.is_copy()) { elements.Add(NULL); continue; @@ -158,7 +150,7 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) { int best_reg_num = RegisterAllocator::kInvalidRegister; StaticType type; // Initially invalid. - if (direction_ != BIDIRECTIONAL || i < high_water_mark) { + if (direction_ != BIDIRECTIONAL) { type = reaching_frames_[0]->elements_[i].static_type(); } @@ -241,25 +233,6 @@ void JumpTarget::Jump(Result* arg) { } -void JumpTarget::Jump(Result* arg0, Result* arg1) { - ASSERT(cgen()->has_valid_frame()); - - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - DoJump(); -} - - -void JumpTarget::Jump(Result* arg0, Result* arg1, Result* arg2) { - ASSERT(cgen()->has_valid_frame()); - - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - cgen()->frame()->Push(arg2); - DoJump(); -} - - void JumpTarget::Branch(Condition cc, Hint hint) { DoBranch(cc, hint); } @@ -295,84 +268,6 @@ void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) { } -void JumpTarget::Branch(Condition cc, Result* arg0, Result* arg1, Hint hint) { - ASSERT(cgen()->frame() != NULL); - - // We want to check that non-frame registers at the call site stay in - // the same registers on the fall-through branch. - DECLARE_ARGCHECK_VARS(arg0); - DECLARE_ARGCHECK_VARS(arg1); - - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - DoBranch(cc, hint); - *arg1 = cgen()->frame()->Pop(); - *arg0 = cgen()->frame()->Pop(); - - ASSERT_ARGCHECK(arg0); - ASSERT_ARGCHECK(arg1); -} - - -void JumpTarget::Branch(Condition cc, - Result* arg0, - Result* arg1, - Result* arg2, - Hint hint) { - ASSERT(cgen()->frame() != NULL); - - // We want to check that non-frame registers at the call site stay in - // the same registers on the fall-through branch. - DECLARE_ARGCHECK_VARS(arg0); - DECLARE_ARGCHECK_VARS(arg1); - DECLARE_ARGCHECK_VARS(arg2); - - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - cgen()->frame()->Push(arg2); - DoBranch(cc, hint); - *arg2 = cgen()->frame()->Pop(); - *arg1 = cgen()->frame()->Pop(); - *arg0 = cgen()->frame()->Pop(); - - ASSERT_ARGCHECK(arg0); - ASSERT_ARGCHECK(arg1); - ASSERT_ARGCHECK(arg2); -} - - -void JumpTarget::Branch(Condition cc, - Result* arg0, - Result* arg1, - Result* arg2, - Result* arg3, - Hint hint) { - ASSERT(cgen()->frame() != NULL); - - // We want to check that non-frame registers at the call site stay in - // the same registers on the fall-through branch. - DECLARE_ARGCHECK_VARS(arg0); - DECLARE_ARGCHECK_VARS(arg1); - DECLARE_ARGCHECK_VARS(arg2); - DECLARE_ARGCHECK_VARS(arg3); - - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - cgen()->frame()->Push(arg2); - cgen()->frame()->Push(arg3); - DoBranch(cc, hint); - *arg3 = cgen()->frame()->Pop(); - *arg2 = cgen()->frame()->Pop(); - *arg1 = cgen()->frame()->Pop(); - *arg0 = cgen()->frame()->Pop(); - - ASSERT_ARGCHECK(arg0); - ASSERT_ARGCHECK(arg1); - ASSERT_ARGCHECK(arg2); - ASSERT_ARGCHECK(arg3); -} - - void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) { ASSERT(cgen()->has_valid_frame()); @@ -400,66 +295,20 @@ void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) { #undef ASSERT_ARGCHECK -void JumpTarget::Bind(int mergable_elements) { - DoBind(mergable_elements); +void JumpTarget::Bind() { + DoBind(); } -void JumpTarget::Bind(Result* arg, int mergable_elements) { +void JumpTarget::Bind(Result* arg) { if (cgen()->has_valid_frame()) { cgen()->frame()->Push(arg); } - DoBind(mergable_elements); + DoBind(); *arg = cgen()->frame()->Pop(); } -void JumpTarget::Bind(Result* arg0, Result* arg1, int mergable_elements) { - if (cgen()->has_valid_frame()) { - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - } - DoBind(mergable_elements); - *arg1 = cgen()->frame()->Pop(); - *arg0 = cgen()->frame()->Pop(); -} - - -void JumpTarget::Bind(Result* arg0, - Result* arg1, - Result* arg2, - int mergable_elements) { - if (cgen()->has_valid_frame()) { - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - cgen()->frame()->Push(arg2); - } - DoBind(mergable_elements); - *arg2 = cgen()->frame()->Pop(); - *arg1 = cgen()->frame()->Pop(); - *arg0 = cgen()->frame()->Pop(); -} - - -void JumpTarget::Bind(Result* arg0, - Result* arg1, - Result* arg2, - Result* arg3, - int mergable_elements) { - if (cgen()->has_valid_frame()) { - cgen()->frame()->Push(arg0); - cgen()->frame()->Push(arg1); - cgen()->frame()->Push(arg2); - cgen()->frame()->Push(arg3); - } - DoBind(mergable_elements); - *arg3 = cgen()->frame()->Pop(); - *arg2 = cgen()->frame()->Pop(); - *arg1 = cgen()->frame()->Pop(); - *arg0 = cgen()->frame()->Pop(); -} - - void JumpTarget::AddReachingFrame(VirtualFrame* frame) { ASSERT(reaching_frames_.length() == merge_labels_.length()); ASSERT(entry_frame_ == NULL); @@ -531,7 +380,7 @@ void BreakTarget::Branch(Condition cc, Hint hint) { } -void BreakTarget::Bind(int mergable_elements) { +void BreakTarget::Bind() { #ifdef DEBUG // All the forward-reaching frames should have been adjusted at the // jumps to this target. @@ -547,11 +396,11 @@ void BreakTarget::Bind(int mergable_elements) { int count = cgen()->frame()->height() - expected_height_; cgen()->frame()->ForgetElements(count); } - DoBind(mergable_elements); + DoBind(); } -void BreakTarget::Bind(Result* arg, int mergable_elements) { +void BreakTarget::Bind(Result* arg) { #ifdef DEBUG // All the forward-reaching frames should have been adjusted at the // jumps to this target. @@ -568,7 +417,7 @@ void BreakTarget::Bind(Result* arg, int mergable_elements) { cgen()->frame()->ForgetElements(count); cgen()->frame()->Push(arg); } - DoBind(mergable_elements); + DoBind(); *arg = cgen()->frame()->Pop(); } diff --git a/deps/v8/src/jump-target.h b/deps/v8/src/jump-target.h index 7585fafbfc..0c42f1b71d 100644 --- a/deps/v8/src/jump-target.h +++ b/deps/v8/src/jump-target.h @@ -107,52 +107,18 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. // jump and there will be no current frame after the jump. virtual void Jump(); virtual void Jump(Result* arg); - void Jump(Result* arg0, Result* arg1); - void Jump(Result* arg0, Result* arg1, Result* arg2); // Emit a conditional branch to the target. There must be a current // frame at the branch. The current frame will fall through to the // code after the branch. virtual void Branch(Condition cc, Hint hint = no_hint); virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint); - void Branch(Condition cc, Result* arg0, Result* arg1, Hint hint = no_hint); - void Branch(Condition cc, - Result* arg0, - Result* arg1, - Result* arg2, - Hint hint = no_hint); - void Branch(Condition cc, - Result* arg0, - Result* arg1, - Result* arg2, - Result* arg3, - Hint hint = no_hint); // Bind a jump target. If there is no current frame at the binding // site, there must be at least one frame reaching via a forward // jump. - // - // The number of mergable elements is a number of frame elements - // counting from the top down which must be "mergable" (not - // constants or copies) in the entry frame at the jump target. - // Backward jumps to the target must contain the same constants and - // sharing as the entry frame, except for the mergable elements. - // - // A mergable elements argument of kAllElements indicates that all - // frame elements must be mergable. Mergable elements are ignored - // completely for forward-only jump targets. - virtual void Bind(int mergable_elements = kAllElements); - virtual void Bind(Result* arg, int mergable_elements = kAllElements); - void Bind(Result* arg0, Result* arg1, int mergable_elements = kAllElements); - void Bind(Result* arg0, - Result* arg1, - Result* arg2, - int mergable_elements = kAllElements); - void Bind(Result* arg0, - Result* arg1, - Result* arg2, - Result* arg3, - int mergable_elements = kAllElements); + virtual void Bind(); + virtual void Bind(Result* arg); // Emit a call to a jump target. There must be a current frame at // the call. The frame at the target is the same as the current @@ -160,8 +126,6 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. // after the call is the same as the frame before the call. void Call(); - static const int kAllElements = -1; // Not a valid number of elements. - static void set_compiling_deferred_code(bool flag) { compiling_deferred_code_ = flag; } @@ -188,7 +152,7 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. // return values using the virtual frame. void DoJump(); void DoBranch(Condition cc, Hint hint); - void DoBind(int mergable_elements); + void DoBind(); private: static bool compiling_deferred_code_; @@ -202,9 +166,8 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. // target. inline void InitializeEntryElement(int index, FrameElement* target); - // Compute a frame to use for entry to this block. Mergable - // elements is as described for the Bind function. - void ComputeEntryFrame(int mergable_elements); + // Compute a frame to use for entry to this block. + void ComputeEntryFrame(); DISALLOW_COPY_AND_ASSIGN(JumpTarget); }; @@ -251,8 +214,8 @@ class BreakTarget : public JumpTarget { // Bind a break target. If there is no current frame at the binding // site, there must be at least one frame reaching via a forward // jump. - virtual void Bind(int mergable_elements = kAllElements); - virtual void Bind(Result* arg, int mergable_elements = kAllElements); + virtual void Bind(); + virtual void Bind(Result* arg); // Setter for expected height. void set_expected_height(int expected) { expected_height_ = expected; } diff --git a/deps/v8/src/log-utils.cc b/deps/v8/src/log-utils.cc index 43610497ef..028eb3a015 100644 --- a/deps/v8/src/log-utils.cc +++ b/deps/v8/src/log-utils.cc @@ -123,7 +123,7 @@ bool Log::is_stopped_ = false; Log::WritePtr Log::Write = NULL; FILE* Log::output_handle_ = NULL; LogDynamicBuffer* Log::output_buffer_ = NULL; -// Must be the same message as in Logger::PauseProfiler +// Must be the same message as in Logger::PauseProfiler. const char* Log::kDynamicBufferSeal = "profiler,\"pause\"\n"; Mutex* Log::mutex_ = NULL; char* Log::message_buffer_ = NULL; @@ -173,6 +173,9 @@ void Log::Close() { } Write = NULL; + DeleteArray(message_buffer_); + message_buffer_ = NULL; + delete mutex_; mutex_ = NULL; @@ -212,13 +215,13 @@ void LogMessageBuilder::Append(const char* format, ...) { Log::kMessageBufferSize - pos_); va_list args; va_start(args, format); - Append(format, args); + AppendVA(format, args); va_end(args); ASSERT(pos_ <= Log::kMessageBufferSize); } -void LogMessageBuilder::Append(const char* format, va_list args) { +void LogMessageBuilder::AppendVA(const char* format, va_list args) { Vector buf(Log::message_buffer_ + pos_, Log::kMessageBufferSize - pos_); int result = v8::internal::OS::VSNPrintF(buf, format, args); @@ -250,6 +253,27 @@ void LogMessageBuilder::Append(String* str) { } +void LogMessageBuilder::AppendAddress(Address addr) { + static Address last_address_ = NULL; + AppendAddress(addr, last_address_); + last_address_ = addr; +} + + +void LogMessageBuilder::AppendAddress(Address addr, Address bias) { + if (!FLAG_compress_log || bias == NULL) { + Append("0x%" V8PRIxPTR, addr); + } else { + intptr_t delta = addr - bias; + // To avoid printing negative offsets in an unsigned form, + // we are printing an absolute value with a sign. + const char sign = delta >= 0 ? '+' : '-'; + if (sign == '-') { delta = -delta; } + Append("%c%" V8PRIxPTR, sign, delta); + } +} + + void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) { AssertNoAllocation no_heap_allocation; // Ensure string stay valid. int len = str->length(); @@ -280,6 +304,24 @@ void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) { } +bool LogMessageBuilder::StoreInCompressor(LogRecordCompressor* compressor) { + return compressor->Store(Vector(Log::message_buffer_, pos_)); +} + + +bool LogMessageBuilder::RetrieveCompressedPrevious( + LogRecordCompressor* compressor, const char* prefix) { + pos_ = 0; + if (prefix[0] != '\0') Append(prefix); + Vector prev_record(Log::message_buffer_ + pos_, + Log::kMessageBufferSize - pos_); + const bool has_prev = compressor->RetrievePreviousCompressed(&prev_record); + if (!has_prev) return false; + pos_ += prev_record.length(); + return true; +} + + void LogMessageBuilder::WriteToLogFile() { ASSERT(pos_ <= Log::kMessageBufferSize); const int written = Log::Write(Log::message_buffer_, pos_); @@ -297,6 +339,145 @@ void LogMessageBuilder::WriteCStringToLogFile(const char* str) { } } + +// Formatting string for back references to the whole line. E.g. "#2" means +// "the second line above". +const char* LogRecordCompressor::kLineBackwardReferenceFormat = "#%d"; + +// Formatting string for back references. E.g. "#2:10" means +// "the second line above, start from char 10 (0-based)". +const char* LogRecordCompressor::kBackwardReferenceFormat = "#%d:%d"; + + +LogRecordCompressor::~LogRecordCompressor() { + for (int i = 0; i < buffer_.length(); ++i) { + buffer_[i].Dispose(); + } +} + + +static int GetNumberLength(int number) { + ASSERT(number >= 0); + ASSERT(number < 10000); + if (number < 10) return 1; + if (number < 100) return 2; + if (number < 1000) return 3; + return 4; +} + + +int LogRecordCompressor::GetBackwardReferenceSize(int distance, int pos) { + // See kLineBackwardReferenceFormat and kBackwardReferenceFormat. + return pos == 0 ? GetNumberLength(distance) + 1 + : GetNumberLength(distance) + GetNumberLength(pos) + 2; +} + + +void LogRecordCompressor::PrintBackwardReference(Vector dest, + int distance, + int pos) { + if (pos == 0) { + OS::SNPrintF(dest, kLineBackwardReferenceFormat, distance); + } else { + OS::SNPrintF(dest, kBackwardReferenceFormat, distance, pos); + } +} + + +bool LogRecordCompressor::Store(const Vector& record) { + // Check if the record is the same as the last stored one. + if (curr_ != -1) { + Vector& curr = buffer_[curr_]; + if (record.length() == curr.length() + && strncmp(record.start(), curr.start(), record.length()) == 0) { + return false; + } + } + // buffer_ is circular. + prev_ = curr_++; + curr_ %= buffer_.length(); + Vector record_copy = Vector::New(record.length()); + memcpy(record_copy.start(), record.start(), record.length()); + buffer_[curr_].Dispose(); + buffer_[curr_] = + Vector(record_copy.start(), record_copy.length()); + return true; +} + + +bool LogRecordCompressor::RetrievePreviousCompressed( + Vector* prev_record) { + if (prev_ == -1) return false; + + int index = prev_; + // Distance from prev_. + int distance = 0; + // Best compression result among records in the buffer. + struct { + intptr_t truncated_len; + int distance; + int copy_from_pos; + int backref_size; + } best = {-1, 0, 0, 0}; + Vector& prev = buffer_[prev_]; + const char* const prev_start = prev.start(); + const char* const prev_end = prev.start() + prev.length(); + do { + // We're moving backwards until we reach the current record. + // Remember that buffer_ is circular. + if (--index == -1) index = buffer_.length() - 1; + ++distance; + if (index == curr_) break; + + Vector& data = buffer_[index]; + if (data.start() == NULL) break; + const char* const data_end = data.start() + data.length(); + const char* prev_ptr = prev_end; + const char* data_ptr = data_end; + // Compare strings backwards, stop on the last matching character. + while (prev_ptr != prev_start && data_ptr != data.start() + && *(prev_ptr - 1) == *(data_ptr - 1)) { + --prev_ptr; + --data_ptr; + } + const intptr_t truncated_len = prev_end - prev_ptr; + const int copy_from_pos = data_ptr - data.start(); + // Check if the length of compressed tail is enough. + if (truncated_len <= kMaxBackwardReferenceSize + && truncated_len <= GetBackwardReferenceSize(distance, copy_from_pos)) { + continue; + } + + // Record compression results. + if (truncated_len > best.truncated_len) { + best.truncated_len = truncated_len; + best.distance = distance; + best.copy_from_pos = copy_from_pos; + best.backref_size = GetBackwardReferenceSize(distance, copy_from_pos); + } + } while (true); + + if (best.distance == 0) { + // Can't compress the previous record. Return as is. + ASSERT(prev_record->length() >= prev.length()); + memcpy(prev_record->start(), prev.start(), prev.length()); + prev_record->Truncate(prev.length()); + } else { + // Copy the uncompressible part unchanged. + const intptr_t unchanged_len = prev.length() - best.truncated_len; + // + 1 for '\0'. + ASSERT(prev_record->length() >= unchanged_len + best.backref_size + 1); + memcpy(prev_record->start(), prev.start(), unchanged_len); + // Append the backward reference. + Vector backref( + prev_record->start() + unchanged_len, best.backref_size + 1); + PrintBackwardReference(backref, best.distance, best.copy_from_pos); + ASSERT(strlen(backref.start()) - best.backref_size == 0); + prev_record->Truncate(unchanged_len + best.backref_size); + } + return true; +} + #endif // ENABLE_LOGGING_AND_PROFILING } } // namespace v8::internal diff --git a/deps/v8/src/log-utils.h b/deps/v8/src/log-utils.h index 2e8b3a3647..ad669d53d7 100644 --- a/deps/v8/src/log-utils.h +++ b/deps/v8/src/log-utils.h @@ -170,6 +170,50 @@ class Log : public AllStatic { static char* message_buffer_; friend class LogMessageBuilder; + friend class LogRecordCompressor; +}; + + +// An utility class for performing backward reference compression +// of string ends. It operates using a window of previous strings. +class LogRecordCompressor { + public: + // 'window_size' is the size of backward lookup window. + explicit LogRecordCompressor(int window_size) + : buffer_(window_size + kNoCompressionWindowSize), + kMaxBackwardReferenceSize( + GetBackwardReferenceSize(window_size, Log::kMessageBufferSize)), + curr_(-1), prev_(-1) { + } + + ~LogRecordCompressor(); + + // Fills vector with a compressed version of the previous record. + // Returns false if there is no previous record. + bool RetrievePreviousCompressed(Vector* prev_record); + + // Stores a record if it differs from a previous one (or there's no previous). + // Returns true, if the record has been stored. + bool Store(const Vector& record); + + private: + // The minimum size of a buffer: a place needed for the current and + // the previous record. Since there is no place for precedessors of a previous + // record, it can't be compressed at all. + static const int kNoCompressionWindowSize = 2; + + // Formatting strings for back references. + static const char* kLineBackwardReferenceFormat; + static const char* kBackwardReferenceFormat; + + static int GetBackwardReferenceSize(int distance, int pos); + + static void PrintBackwardReference(Vector dest, int distance, int pos); + + ScopedVector< Vector > buffer_; + const int kMaxBackwardReferenceSize; + int curr_; + int prev_; }; @@ -186,7 +230,7 @@ class LogMessageBuilder BASE_EMBEDDED { void Append(const char* format, ...); // Append string data to the log message. - void Append(const char* format, va_list args); + void AppendVA(const char* format, va_list args); // Append a character to the log message. void Append(const char c); @@ -194,8 +238,29 @@ class LogMessageBuilder BASE_EMBEDDED { // Append a heap string. void Append(String* str); + // Appends an address, compressing it if needed by offsetting + // from Logger::last_address_. + void AppendAddress(Address addr); + + // Appends an address, compressing it if needed. + void AppendAddress(Address addr, Address bias); + void AppendDetailed(String* str, bool show_impl_info); + // Stores log message into compressor, returns true if the message + // was stored (i.e. doesn't repeat the previous one). + bool StoreInCompressor(LogRecordCompressor* compressor); + + // Sets log message to a previous version of compressed message. + // Returns false, if there is no previous message. + bool RetrieveCompressedPrevious(LogRecordCompressor* compressor) { + return RetrieveCompressedPrevious(compressor, ""); + } + + // Does the same at the version without arguments, and sets a prefix. + bool RetrieveCompressedPrevious(LogRecordCompressor* compressor, + const char* prefix); + // Write the log message to the log file currently opened. void WriteToLogFile(); diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index c1edf4d185..af49128eaa 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -262,6 +262,7 @@ void Profiler::Engage() { Logger::ticker_->SetProfiler(this); Logger::ProfilerBeginEvent(); + Logger::LogAliases(); } @@ -301,6 +302,20 @@ Profiler* Logger::profiler_ = NULL; VMState* Logger::current_state_ = NULL; VMState Logger::bottom_state_(EXTERNAL); SlidingStateWindow* Logger::sliding_state_window_ = NULL; +const char** Logger::log_events_ = NULL; +CompressionHelper* Logger::compression_helper_ = NULL; + +#define DECLARE_LONG_EVENT(ignore1, long_name, ignore2) long_name, +const char* kLongLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = { + LOG_EVENTS_AND_TAGS_LIST(DECLARE_LONG_EVENT) +}; +#undef DECLARE_LONG_EVENT + +#define DECLARE_SHORT_EVENT(ignore1, ignore2, short_name) short_name, +const char* kCompressedLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = { + LOG_EVENTS_AND_TAGS_LIST(DECLARE_SHORT_EVENT) +}; +#undef DECLARE_SHORT_EVENT bool Logger::IsEnabled() { @@ -312,6 +327,20 @@ void Logger::ProfilerBeginEvent() { if (!Log::IsEnabled()) return; LogMessageBuilder msg; msg.Append("profiler,\"begin\",%d\n", kSamplingIntervalMs); + if (FLAG_compress_log) { + msg.Append("profiler,\"compression\",%d\n", kCompressionWindowSize); + } + msg.WriteToLogFile(); +} + + +void Logger::LogAliases() { + if (!Log::IsEnabled() || !FLAG_compress_log) return; + LogMessageBuilder msg; + for (int i = 0; i < NUMBER_OF_LOG_EVENTS; ++i) { + msg.Append("alias,%s,%s\n", + kCompressedLogEventsNames[i], kLongLogEventsNames[i]); + } msg.WriteToLogFile(); } @@ -373,7 +402,7 @@ void Logger::ApiEvent(const char* format, ...) { LogMessageBuilder msg; va_list ap; va_start(ap, format); - msg.Append(format, ap); + msg.AppendVA(format, ap); va_end(ap); msg.WriteToLogFile(); } @@ -594,12 +623,15 @@ void Logger::DeleteEvent(const char* name, void* object) { } -void Logger::CodeCreateEvent(const char* tag, Code* code, const char* comment) { +void Logger::CodeCreateEvent(LogEventsAndTags tag, + Code* code, + const char* comment) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"", tag, code->address(), - code->ExecutableSize()); + msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.AppendAddress(code->address()); + msg.Append(",%d,\"", code->ExecutableSize()); for (const char* p = comment; *p != '\0'; p++) { if (*p == '"') { msg.Append('\\'); @@ -613,20 +645,22 @@ void Logger::CodeCreateEvent(const char* tag, Code* code, const char* comment) { } -void Logger::CodeCreateEvent(const char* tag, Code* code, String* name) { +void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; SmartPointer str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"%s\"\n", - tag, code->address(), code->ExecutableSize(), *str); + msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.AppendAddress(code->address()); + msg.Append(",%d,\"%s\"\n", code->ExecutableSize(), *str); msg.WriteToLogFile(); #endif } -void Logger::CodeCreateEvent(const char* tag, Code* code, String* name, +void Logger::CodeCreateEvent(LogEventsAndTags tag, + Code* code, String* name, String* source, int line) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; @@ -635,23 +669,22 @@ void Logger::CodeCreateEvent(const char* tag, Code* code, String* name, name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); SmartPointer sourcestr = source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"%s %s:%d\"\n", - tag, code->address(), - code->ExecutableSize(), - *str, *sourcestr, line); + msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.AppendAddress(code->address()); + msg.Append(",%d,\"%s %s:%d\"\n", + code->ExecutableSize(), *str, *sourcestr, line); msg.WriteToLogFile(); #endif } -void Logger::CodeCreateEvent(const char* tag, Code* code, int args_count) { +void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"args_count: %d\"\n", tag, - code->address(), - code->ExecutableSize(), - args_count); + msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]); + msg.AppendAddress(code->address()); + msg.Append(",%d,\"args_count: %d\"\n", code->ExecutableSize(), args_count); msg.WriteToLogFile(); #endif } @@ -661,9 +694,10 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"", "RegExp", - code->address(), - code->ExecutableSize()); + msg.Append("%s,%s,", + log_events_[CODE_CREATION_EVENT], log_events_[REG_EXP_TAG]); + msg.AppendAddress(code->address()); + msg.Append(",%d,\"", code->ExecutableSize()); msg.AppendDetailed(source, false); msg.Append("\"\n"); msg.WriteToLogFile(); @@ -671,23 +705,57 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) { } -void Logger::CodeAllocateEvent(Code* code, Assembler* assem) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::IsEnabled() || !FLAG_log_code) return; - LogMessageBuilder msg; - msg.Append("code-allocate,0x%" V8PRIxPTR ",0x%" V8PRIxPTR "\n", - code->address(), - assem); - msg.WriteToLogFile(); -#endif -} + +// A class that contains all common code dealing with record compression. +class CompressionHelper { + public: + explicit CompressionHelper(int window_size) + : compressor_(window_size), repeat_count_(0) { } + + // Handles storing message in compressor, retrieving the previous one and + // prefixing it with repeat count, if needed. + // Returns true if message needs to be written to log. + bool HandleMessage(LogMessageBuilder* msg) { + if (!msg->StoreInCompressor(&compressor_)) { + // Current message repeats the previous one, don't write it. + ++repeat_count_; + return false; + } + if (repeat_count_ == 0) { + return msg->RetrieveCompressedPrevious(&compressor_); + } + OS::SNPrintF(prefix_, "%s,%d,", + Logger::log_events_[Logger::REPEAT_META_EVENT], + repeat_count_ + 1); + repeat_count_ = 0; + return msg->RetrieveCompressedPrevious(&compressor_, prefix_.start()); + } + + private: + LogRecordCompressor compressor_; + int repeat_count_; + EmbeddedVector prefix_; +}; + +#endif // ENABLE_LOGGING_AND_PROFILING void Logger::CodeMoveEvent(Address from, Address to) { #ifdef ENABLE_LOGGING_AND_PROFILING + static Address prev_to_ = NULL; if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-move,0x%" V8PRIxPTR ",0x%" V8PRIxPTR "\n", from, to); + msg.Append("%s,", log_events_[CODE_MOVE_EVENT]); + msg.AppendAddress(from); + msg.Append(','); + msg.AppendAddress(to, prev_to_); + prev_to_ = to; + if (FLAG_compress_log) { + ASSERT(compression_helper_ != NULL); + if (!compression_helper_->HandleMessage(&msg)) return; + } + msg.Append('\n'); msg.WriteToLogFile(); #endif } @@ -697,7 +765,13 @@ void Logger::CodeDeleteEvent(Address from) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-delete,0x%" V8PRIxPTR "\n", from); + msg.Append("%s,", log_events_[CODE_DELETE_EVENT]); + msg.AppendAddress(from); + if (FLAG_compress_log) { + ASSERT(compression_helper_ != NULL); + if (!compression_helper_->HandleMessage(&msg)) return; + } + msg.Append('\n'); msg.WriteToLogFile(); #endif } @@ -802,14 +876,26 @@ void Logger::DebugEvent(const char* event_type, Vector parameter) { #ifdef ENABLE_LOGGING_AND_PROFILING void Logger::TickEvent(TickSample* sample, bool overflow) { if (!Log::IsEnabled() || !FLAG_prof) return; + static Address prev_sp = NULL; LogMessageBuilder msg; - msg.Append("tick,0x%" V8PRIxPTR ",0x%" V8PRIxPTR ",%d", - sample->pc, sample->sp, static_cast(sample->state)); + msg.Append("%s,", log_events_[TICK_EVENT]); + Address prev_addr = reinterpret_cast
(sample->pc); + msg.AppendAddress(prev_addr); + msg.Append(','); + msg.AppendAddress(reinterpret_cast
(sample->sp), prev_sp); + prev_sp = reinterpret_cast
(sample->sp); + msg.Append(",%d", static_cast(sample->state)); if (overflow) { msg.Append(",overflow"); } for (int i = 0; i < sample->frames_count; ++i) { - msg.Append(",0x%" V8PRIxPTR, sample->stack[i]); + msg.Append(','); + msg.AppendAddress(sample->stack[i], prev_addr); + prev_addr = sample->stack[i]; + } + if (FLAG_compress_log) { + ASSERT(compression_helper_ != NULL); + if (!compression_helper_->HandleMessage(&msg)) return; } msg.Append('\n'); msg.WriteToLogFile(); @@ -913,17 +999,19 @@ void Logger::LogCompiledFunctions() { int line_num = GetScriptLineNumber(script, shared->start_position()); if (line_num > 0) { line_num += script->line_offset()->value() + 1; - LOG(CodeCreateEvent("LazyCompile", shared->code(), *func_name, + LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, + shared->code(), *func_name, *script_name, line_num)); } else { // Can't distinguish enum and script here, so always use Script. - LOG(CodeCreateEvent("Script", shared->code(), *script_name)); + LOG(CodeCreateEvent(Logger::SCRIPT_TAG, + shared->code(), *script_name)); } continue; } } // If no script or script has no name. - LOG(CodeCreateEvent("LazyCompile", shared->code(), *func_name)); + LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, shared->code(), *func_name)); } DeleteArray(sfis); @@ -1013,6 +1101,12 @@ bool Logger::Setup() { sliding_state_window_ = new SlidingStateWindow(); } + log_events_ = FLAG_compress_log ? + kCompressedLogEventsNames : kLongLogEventsNames; + if (FLAG_compress_log) { + compression_helper_ = new CompressionHelper(kCompressionWindowSize); + } + if (FLAG_prof) { profiler_ = new Profiler(); if (!FLAG_prof_auto) @@ -1041,6 +1135,9 @@ void Logger::TearDown() { profiler_ = NULL; } + delete compression_helper_; + compression_helper_ = NULL; + delete sliding_state_window_; sliding_state_window_ = NULL; diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h index 2f8f81c95b..08e957a12e 100644 --- a/deps/v8/src/log.h +++ b/deps/v8/src/log.h @@ -71,6 +71,7 @@ class Profiler; class Semaphore; class SlidingStateWindow; class LogMessageBuilder; +class CompressionHelper; #undef LOG #ifdef ENABLE_LOGGING_AND_PROFILING @@ -102,8 +103,41 @@ class VMState BASE_EMBEDDED { }; +#define LOG_EVENTS_AND_TAGS_LIST(V) \ + V(CODE_CREATION_EVENT, "code-creation", "cc") \ + V(CODE_MOVE_EVENT, "code-move", "cm") \ + V(CODE_DELETE_EVENT, "code-delete", "cd") \ + V(TICK_EVENT, "tick", "t") \ + V(REPEAT_META_EVENT, "repeat", "r") \ + V(BUILTIN_TAG, "Builtin", "bi") \ + V(CALL_DEBUG_BREAK_TAG, "CallDebugBreak", "cdb") \ + V(CALL_DEBUG_PREPARE_STEP_IN_TAG, "CallDebugPrepareStepIn", "cdbsi") \ + V(CALL_IC_TAG, "CallIC", "cic") \ + V(CALL_INITIALIZE_TAG, "CallInitialize", "ci") \ + V(CALL_MEGAMORPHIC_TAG, "CallMegamorphic", "cmm") \ + V(CALL_MISS_TAG, "CallMiss", "cm") \ + V(CALL_NORMAL_TAG, "CallNormal", "cn") \ + V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic", "cpm") \ + V(EVAL_TAG, "Eval", "e") \ + V(FUNCTION_TAG, "Function", "f") \ + V(KEYED_LOAD_IC_TAG, "KeyedLoadIC", "klic") \ + V(KEYED_STORE_IC_TAG, "KeyedStoreIC", "ksic") \ + V(LAZY_COMPILE_TAG, "LazyCompile", "lc") \ + V(LOAD_IC_TAG, "LoadIC", "lic") \ + V(REG_EXP_TAG, "RegExp", "re") \ + V(SCRIPT_TAG, "Script", "sc") \ + V(STORE_IC_TAG, "StoreIC", "sic") \ + V(STUB_TAG, "Stub", "s") + class Logger { public: +#define DECLARE_ENUM(enum_item, ignore1, ignore2) enum_item, + enum LogEventsAndTags { + LOG_EVENTS_AND_TAGS_LIST(DECLARE_ENUM) + NUMBER_OF_LOG_EVENTS + }; +#undef DECLARE_ENUM + // Acquires resources for logging if the right flags are set. static bool Setup(); @@ -163,14 +197,14 @@ class Logger { // ==== Events logged by --log-code. ==== // Emits a code create event. - static void CodeCreateEvent(const char* tag, Code* code, const char* source); - static void CodeCreateEvent(const char* tag, Code* code, String* name); - static void CodeCreateEvent(const char* tag, Code* code, String* name, + static void CodeCreateEvent(LogEventsAndTags tag, + Code* code, const char* source); + static void CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name); + static void CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name, String* source, int line); - static void CodeCreateEvent(const char* tag, Code* code, int args_count); + static void CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count); // Emits a code create event for a RegExp. static void RegExpCodeCreateEvent(Code* code, String* source); - static void CodeAllocateEvent(Code* code, Assembler* assem); // Emits a code move event. static void CodeMoveEvent(Address from, Address to); // Emits a code delete event. @@ -223,9 +257,15 @@ class Logger { // Profiler's sampling interval (in milliseconds). static const int kSamplingIntervalMs = 1; + // Size of window used for log records compression. + static const int kCompressionWindowSize = 4; + // Emits the profiler's first message. static void ProfilerBeginEvent(); + // Emits aliases for compressed messages. + static void LogAliases(); + // Emits the source code of a regexp. Used by regexp events. static void LogRegExpSource(Handle regexp); @@ -261,8 +301,15 @@ class Logger { // recent VM states. static SlidingStateWindow* sliding_state_window_; + // An array of log events names. + static const char** log_events_; + + // An instance of helper created if log compression is enabled. + static CompressionHelper* compression_helper_; + // Internal implementation classes with access to // private members. + friend class CompressionHelper; friend class EventLog; friend class TimeLog; friend class Profiler; diff --git a/deps/v8/src/macros.py b/deps/v8/src/macros.py index ebfd816a01..fdbdb58b79 100644 --- a/deps/v8/src/macros.py +++ b/deps/v8/src/macros.py @@ -60,6 +60,7 @@ const msPerSecond = 1000; const msPerMinute = 60000; const msPerHour = 3600000; const msPerDay = 86400000; +const msPerMonth = 2592000000; # For apinatives.js const kUninitialized = -1; diff --git a/deps/v8/src/math.js b/deps/v8/src/math.js index 86d6dd101d..1f5ce87af9 100644 --- a/deps/v8/src/math.js +++ b/deps/v8/src/math.js @@ -44,39 +44,73 @@ $Math.__proto__ = global.Object.prototype; // ECMA 262 - 15.8.2.1 function MathAbs(x) { - if (%_IsSmi(x)) { - return x >= 0 ? x : -x; - } else { - return %Math_abs(ToNumber(x)); - } + if (%_IsSmi(x)) return x >= 0 ? x : -x; + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_abs(x); } // ECMA 262 - 15.8.2.2 -function MathAcos(x) { return %Math_acos(ToNumber(x)); } +function MathAcos(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_acos(x); +} // ECMA 262 - 15.8.2.3 -function MathAsin(x) { return %Math_asin(ToNumber(x)); } +function MathAsin(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_asin(x); +} // ECMA 262 - 15.8.2.4 -function MathAtan(x) { return %Math_atan(ToNumber(x)); } +function MathAtan(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_atan(x); +} // ECMA 262 - 15.8.2.5 -function MathAtan2(x, y) { return %Math_atan2(ToNumber(x), ToNumber(y)); } +function MathAtan2(x, y) { + if (!IS_NUMBER(x)) x = ToNumber(x); + if (!IS_NUMBER(y)) y = ToNumber(y); + return %Math_atan2(x, y); +} // ECMA 262 - 15.8.2.6 -function MathCeil(x) { return %Math_ceil(ToNumber(x)); } +function MathCeil(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_ceil(x); +} // ECMA 262 - 15.8.2.7 -function MathCos(x) { return %Math_cos(ToNumber(x)); } +function MathCos(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %_Math_cos(x); +} // ECMA 262 - 15.8.2.8 -function MathExp(x) { return %Math_exp(ToNumber(x)); } +function MathExp(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_exp(x); +} // ECMA 262 - 15.8.2.9 -function MathFloor(x) { return %Math_floor(ToNumber(x)); } +function MathFloor(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + if (0 < x && x <= 0x7FFFFFFF) { + // Numbers in the range [0, 2^31) can be floored by converting + // them to an unsigned 32-bit value using the shift operator. + // We avoid doing so for -0, because the result of Math.floor(-0) + // has to be -0, which wouldn't be the case with the shift. + return x << 0; + } else { + return %Math_floor(x); + } +} // ECMA 262 - 15.8.2.10 -function MathLog(x) { return %Math_log(ToNumber(x)); } +function MathLog(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_log(x); +} // ECMA 262 - 15.8.2.11 function MathMax(arg1, arg2) { // length == 2 @@ -103,22 +137,40 @@ function MathMin(arg1, arg2) { // length == 2 } // ECMA 262 - 15.8.2.13 -function MathPow(x, y) { return %Math_pow(ToNumber(x), ToNumber(y)); } +function MathPow(x, y) { + if (!IS_NUMBER(x)) x = ToNumber(x); + if (!IS_NUMBER(y)) y = ToNumber(y); + return %Math_pow(x, y); +} // ECMA 262 - 15.8.2.14 -function MathRandom() { return %Math_random(); } +function MathRandom() { + return %_RandomPositiveSmi() / 0x40000000; +} // ECMA 262 - 15.8.2.15 -function MathRound(x) { return %Math_round(ToNumber(x)); } +function MathRound(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_round(x); +} // ECMA 262 - 15.8.2.16 -function MathSin(x) { return %Math_sin(ToNumber(x)); } +function MathSin(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %_Math_sin(x); +} // ECMA 262 - 15.8.2.17 -function MathSqrt(x) { return %Math_sqrt(ToNumber(x)); } +function MathSqrt(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_sqrt(x); +} // ECMA 262 - 15.8.2.18 -function MathTan(x) { return %Math_tan(ToNumber(x)); } +function MathTan(x) { + if (!IS_NUMBER(x)) x = ToNumber(x); + return %Math_tan(x); +} // ------------------------------------------------------------------- diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js index df8a2d1d7e..7805d47578 100644 --- a/deps/v8/src/messages.js +++ b/deps/v8/src/messages.js @@ -230,6 +230,40 @@ function MakeError(type, args) { return MakeGenericError($Error, type, args); } +/** + * Find a line number given a specific source position. + * @param {number} position The source position. + * @return {number} 0 if input too small, -1 if input too large, + else the line number. + */ +Script.prototype.lineFromPosition = function(position) { + var lower = 0; + var upper = this.lineCount() - 1; + + // We'll never find invalid positions so bail right away. + if (position > this.line_ends[upper]) { + return -1; + } + + // This means we don't have to safe-guard indexing line_ends[i - 1]. + if (position <= this.line_ends[0]) { + return 0; + } + + // Binary search to find line # from position range. + while (upper >= 1) { + var i = (lower + upper) >> 1; + + if (position > this.line_ends[i]) { + lower = i + 1; + } else if (position <= this.line_ends[i - 1]) { + upper = i - 1; + } else { + return i; + } + } + return -1; +} /** * Get information on a specific source position. @@ -241,19 +275,7 @@ function MakeError(type, args) { */ Script.prototype.locationFromPosition = function (position, include_resource_offset) { - var lineCount = this.lineCount(); - var line = -1; - if (position <= this.line_ends[0]) { - line = 0; - } else { - for (var i = 1; i < lineCount; i++) { - if (this.line_ends[i - 1] < position && position <= this.line_ends[i]) { - line = i; - break; - } - } - } - + var line = this.lineFromPosition(position); if (line == -1) return null; // Determine start, end and column. @@ -308,16 +330,13 @@ Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_p if (line == 0) { return this.locationFromPosition(offset_position + column, false); } else { - // Find the line where the offset position is located - var lineCount = this.lineCount(); - var offset_line; - for (var i = 0; i < lineCount; i++) { - if (offset_position <= this.line_ends[i]) { - offset_line = i; - break; - } + // Find the line where the offset position is located. + var offset_line = this.lineFromPosition(offset_position); + + if (offset_line == -1 || offset_line + line >= this.lineCount()) { + return null; } - if (offset_line + line >= lineCount) return null; + return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here. } } diff --git a/deps/v8/src/mirror-delay.js b/deps/v8/src/mirror-delay.js index f5a12c793b..060586dc3b 100644 --- a/deps/v8/src/mirror-delay.js +++ b/deps/v8/src/mirror-delay.js @@ -34,9 +34,14 @@ RegExp; Date; +// Handle id counters. var next_handle_ = 0; +var next_transient_handle_ = -1; + +// Mirror cache. var mirror_cache_ = []; + /** * Clear the mirror handle cache. */ @@ -50,19 +55,25 @@ function ClearMirrorCache() { * Returns the mirror for a specified value or object. * * @param {value or Object} value the value or object to retreive the mirror for + * @param {boolean} transient indicate whether this object is transient and + * should not be added to the mirror cache. The default is not transient. * @returns {Mirror} the mirror reflects the passed value or object */ -function MakeMirror(value) { +function MakeMirror(value, opt_transient) { var mirror; - for (id in mirror_cache_) { - mirror = mirror_cache_[id]; - if (mirror.value() === value) { - return mirror; - } - // Special check for NaN as NaN == NaN is false. - if (mirror.isNumber() && isNaN(mirror.value()) && - typeof value == 'number' && isNaN(value)) { - return mirror; + + // Look for non transient mirrors in the mirror cache. + if (!opt_transient) { + for (id in mirror_cache_) { + mirror = mirror_cache_[id]; + if (mirror.value() === value) { + return mirror; + } + // Special check for NaN as NaN == NaN is false. + if (mirror.isNumber() && isNaN(mirror.value()) && + typeof value == 'number' && isNaN(value)) { + return mirror; + } } } @@ -89,7 +100,7 @@ function MakeMirror(value) { } else if (IS_SCRIPT(value)) { mirror = new ScriptMirror(value); } else { - mirror = new ObjectMirror(value); + mirror = new ObjectMirror(value, OBJECT_TYPE, opt_transient); } mirror_cache_[mirror.handle()] = mirror; @@ -155,6 +166,7 @@ const PROPERTY_TYPE = 'property'; const FRAME_TYPE = 'frame'; const SCRIPT_TYPE = 'script'; const CONTEXT_TYPE = 'context'; +const SCOPE_TYPE = 'scope'; // Maximum length when sending strings through the JSON protocol. const kMaxProtocolStringLength = 80; @@ -185,6 +197,13 @@ PropertyAttribute.DontEnum = DONT_ENUM; PropertyAttribute.DontDelete = DONT_DELETE; +// A copy of the scope types from runtime.cc. +ScopeType = { Global: 0, + Local: 1, + With: 2, + Closure: 3 }; + + // Mirror hierarchy: // - Mirror // - ValueMirror @@ -372,6 +391,15 @@ Mirror.prototype.isContext = function() { } +/** + * Check whether the mirror reflects a scope. + * @returns {boolean} True if the mirror reflects a scope + */ +Mirror.prototype.isScope = function() { + return this instanceof ScopeMirror; +} + + /** * Allocate a handle id for this object. */ @@ -380,6 +408,15 @@ Mirror.prototype.allocateHandle_ = function() { } +/** + * Allocate a transient handle id for this object. Transient handles are + * negative. + */ +Mirror.prototype.allocateTransientHandle_ = function() { + this.handle_ = next_transient_handle_--; +} + + Mirror.prototype.toText = function() { // Simpel to text which is used when on specialization in subclass. return "#<" + builtins.GetInstanceName(this.constructor.name) + ">"; @@ -390,13 +427,19 @@ Mirror.prototype.toText = function() { * Base class for all value mirror objects. * @param {string} type The type of the mirror * @param {value} value The value reflected by this mirror + * @param {boolean} transient indicate whether this object is transient with a + * transient handle * @constructor * @extends Mirror */ -function ValueMirror(type, value) { +function ValueMirror(type, value, transient) { Mirror.call(this, type); this.value_ = value; - this.allocateHandle_(); + if (!transient) { + this.allocateHandle_(); + } else { + this.allocateTransientHandle_(); + } } inherits(ValueMirror, Mirror); @@ -525,11 +568,13 @@ StringMirror.prototype.toText = function() { /** * Mirror object for objects. * @param {object} value The object reflected by this mirror + * @param {boolean} transient indicate whether this object is transient with a + * transient handle * @constructor * @extends ValueMirror */ -function ObjectMirror(value, type) { - ValueMirror.call(this, type || OBJECT_TYPE, value); +function ObjectMirror(value, type, transient) { + ValueMirror.call(this, type || OBJECT_TYPE, value, transient); } inherits(ObjectMirror, ValueMirror); @@ -1080,7 +1125,7 @@ PropertyMirror.prototype.isIndexed = function() { PropertyMirror.prototype.value = function() { - return MakeMirror(this.value_); + return MakeMirror(this.value_, false); } @@ -1135,7 +1180,7 @@ PropertyMirror.prototype.getter = function() { if (this.hasGetter()) { return MakeMirror(this.getter_); } else { - return new UndefinedMirror(); + return GetUndefinedMirror(); } } @@ -1149,7 +1194,7 @@ PropertyMirror.prototype.setter = function() { if (this.hasSetter()) { return MakeMirror(this.setter_); } else { - return new UndefinedMirror(); + return GetUndefinedMirror(); } } @@ -1294,6 +1339,11 @@ FrameDetails.prototype.localValue = function(index) { } +FrameDetails.prototype.scopeCount = function() { + return %GetScopeCount(this.break_id_, this.frameId()); +} + + /** * Mirror object for stack frames. * @param {number} break_id The break id in the VM for which this frame is @@ -1419,6 +1469,16 @@ FrameMirror.prototype.sourceLineText = function() { }; +FrameMirror.prototype.scopeCount = function() { + return this.details_.scopeCount(); +}; + + +FrameMirror.prototype.scope = function(index) { + return new ScopeMirror(this, index); +}; + + FrameMirror.prototype.evaluate = function(source, disable_break) { var result = %DebugEvaluate(this.break_id_, this.details_.frameId(), source, Boolean(disable_break)); @@ -1562,6 +1622,70 @@ FrameMirror.prototype.toText = function(opt_locals) { } +const kScopeDetailsTypeIndex = 0; +const kScopeDetailsObjectIndex = 1; + +function ScopeDetails(frame, index) { + this.break_id_ = frame.break_id_; + this.details_ = %GetScopeDetails(frame.break_id_, + frame.details_.frameId(), + index); +} + + +ScopeDetails.prototype.type = function() { + %CheckExecutionState(this.break_id_); + return this.details_[kScopeDetailsTypeIndex]; +} + + +ScopeDetails.prototype.object = function() { + %CheckExecutionState(this.break_id_); + return this.details_[kScopeDetailsObjectIndex]; +} + + +/** + * Mirror object for scope. + * @param {FrameMirror} frame The frame this scope is a part of + * @param {number} index The scope index in the frame + * @constructor + * @extends Mirror + */ +function ScopeMirror(frame, index) { + Mirror.call(this, SCOPE_TYPE); + this.frame_index_ = frame.index_; + this.scope_index_ = index; + this.details_ = new ScopeDetails(frame, index); +} +inherits(ScopeMirror, Mirror); + + +ScopeMirror.prototype.frameIndex = function() { + return this.frame_index_; +}; + + +ScopeMirror.prototype.scopeIndex = function() { + return this.scope_index_; +}; + + +ScopeMirror.prototype.scopeType = function() { + return this.details_.type(); +}; + + +ScopeMirror.prototype.scopeObject = function() { + // For local and closure scopes create a transient mirror as these objects are + // created on the fly materializing the local or closure scopes and + // therefore will not preserve identity. + var transient = this.scopeType() == ScopeType.Local || + this.scopeType() == ScopeType.Closure; + return MakeMirror(this.details_.object(), transient); +}; + + /** * Mirror object for script source. * @param {Script} script The script object @@ -1829,6 +1953,7 @@ JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ = return o; }; + JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, details) { // If serializing a reference to a mirror just return the reference and add @@ -1900,6 +2025,11 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, this.serializeFrame_(mirror, content); break; + case SCOPE_TYPE: + // Add object representation. + this.serializeScope_(mirror, content); + break; + case SCRIPT_TYPE: // Script is represented by id, name and source attributes. if (mirror.name()) { @@ -2102,6 +2232,14 @@ JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { } +JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) { + content.index = mirror.scopeIndex(); + content.frameIndex = mirror.frameIndex(); + content.type = mirror.scopeType(); + content.object = this.serializeReference(mirror.scopeObject()); +} + + /** * Convert a number to a protocol value. For all finite numbers the number * itself is returned. For non finite numbers NaN, Infinite and diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 0546578ab1..cbd36e0a37 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1695,7 +1695,7 @@ Object* JSObject::SetProperty(LookupResult* result, // Check access rights if needed. if (IsAccessCheckNeeded() - && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { + && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { return SetPropertyWithFailedAccessCheck(result, name, value); } @@ -5203,27 +5203,6 @@ bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) { } -Object* JSObject::SetElementPostInterceptor(uint32_t index, Object* value) { - if (HasFastElements()) return SetFastElement(index, value); - - // Dictionary case. - ASSERT(!HasFastElements()); - - FixedArray* elms = FixedArray::cast(elements()); - Object* result = Dictionary::cast(elms)->AtNumberPut(index, value); - if (result->IsFailure()) return result; - if (elms != FixedArray::cast(result)) { - set_elements(FixedArray::cast(result)); - } - - if (IsJSArray()) { - return JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value); - } - - return value; -} - - Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) { // Make sure that the top context does not change when doing // callbacks or interceptor calls. @@ -5250,7 +5229,7 @@ Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) { if (!result.IsEmpty()) return *value_handle; } Object* raw_result = - this_handle->SetElementPostInterceptor(index, *value_handle); + this_handle->SetElementWithoutInterceptor(index, *value_handle); RETURN_IF_SCHEDULED_EXCEPTION(); return raw_result; } @@ -5332,6 +5311,11 @@ Object* JSObject::SetElement(uint32_t index, Object* value) { return SetElementWithInterceptor(index, value); } + return SetElementWithoutInterceptor(index, value); +} + + +Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) { // Fast case. if (HasFastElements()) return SetFastElement(index, value); @@ -5438,7 +5422,21 @@ Object* JSObject::GetElementPostInterceptor(JSObject* receiver, Dictionary* dictionary = element_dictionary(); int entry = dictionary->FindNumberEntry(index); if (entry != -1) { - return dictionary->ValueAt(entry); + Object* element = dictionary->ValueAt(entry); + PropertyDetails details = dictionary->DetailsAt(entry); + if (details.type() == CALLBACKS) { + // Only accessors allowed as elements. + FixedArray* structure = FixedArray::cast(element); + Object* getter = structure->get(kGetterIndex); + if (getter->IsJSFunction()) { + return GetPropertyWithDefinedGetter(receiver, + JSFunction::cast(getter)); + } else { + // Getter is not a function. + return Heap::undefined_value(); + } + } + return element; } } @@ -6436,10 +6434,6 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { AssertNoAllocation no_alloc; - // Loose all details on properties when moving them around. - // Elements do not have special details like properties. - PropertyDetails no_details = PropertyDetails(NONE, NORMAL); - uint32_t pos = 0; uint32_t undefs = 0; for (int i = 0; i < capacity; i++) { @@ -6450,21 +6444,27 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0); ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32); Object* value = dict->ValueAt(i); + PropertyDetails details = dict->DetailsAt(i); + if (details.type() == CALLBACKS) { + // Bail out and do the sorting of undefineds and array holes in JS. + return Smi::FromInt(-1); + } uint32_t key = NumberToUint32(k); if (key < limit) { if (value->IsUndefined()) { undefs++; } else { - new_dict->AddNumberEntry(pos, value, no_details); + new_dict->AddNumberEntry(pos, value, details); pos++; } } else { - new_dict->AddNumberEntry(key, value, no_details); + new_dict->AddNumberEntry(key, value, details); } } } uint32_t result = pos; + PropertyDetails no_details = PropertyDetails(NONE, NORMAL); while (undefs > 0) { new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details); pos++; diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 493d22b416..21907f8f3a 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1162,8 +1162,28 @@ class HeapNumber: public HeapObject { // Layout description. static const int kValueOffset = HeapObject::kHeaderSize; + // IEEE doubles are two 32 bit words. The first is just mantissa, the second + // is a mixture of sign, exponent and mantissa. Our current platforms are all + // little endian apart from non-EABI arm which is little endian with big + // endian floating point word ordering! +#if !defined(V8_HOST_ARCH_ARM) || __ARM_EABI__ + static const int kMantissaOffset = kValueOffset; + static const int kExponentOffset = kValueOffset + 4; +#else + static const int kMantissaOffset = kValueOffset + 4; + static const int kExponentOffset = kValueOffset; +# define BIG_ENDIAN_FLOATING_POINT 1 +#endif static const int kSize = kValueOffset + kDoubleSize; + static const uint32_t kSignMask = 0x80000000u; + static const uint32_t kExponentMask = 0x7ff00000u; + static const uint32_t kMantissaMask = 0xfffffu; + static const int kExponentBias = 1023; + static const int kExponentShift = 20; + static const int kMantissaBitsInTopWord = 20; + static const int kNonMantissaBitsInTopWord = 12; + private: DISALLOW_IMPLICIT_CONSTRUCTORS(HeapNumber); }; @@ -1518,7 +1538,7 @@ class JSObject: public HeapObject { private: Object* SetElementWithInterceptor(uint32_t index, Object* value); - Object* SetElementPostInterceptor(uint32_t index, Object* value); + Object* SetElementWithoutInterceptor(uint32_t index, Object* value); Object* GetElementPostInterceptor(JSObject* receiver, uint32_t index); @@ -2470,7 +2490,7 @@ class Map: public HeapObject { return ((1 << kIsHiddenPrototype) & bit_field()) != 0; } - // Tells whether the instance has a named interceptor. + // Records and queries whether the instance has a named interceptor. inline void set_has_named_interceptor() { set_bit_field(bit_field() | (1 << kHasNamedInterceptor)); } @@ -2479,7 +2499,7 @@ class Map: public HeapObject { return ((1 << kHasNamedInterceptor) & bit_field()) != 0; } - // Tells whether the instance has a named interceptor. + // Records and queries whether the instance has an indexed interceptor. inline void set_has_indexed_interceptor() { set_bit_field(bit_field() | (1 << kHasIndexedInterceptor)); } @@ -4008,10 +4028,9 @@ class JSArray: public JSObject { // If an accessor was found and it does not have a setter, // the request is ignored. // -// To allow shadow an accessor property, the accessor can -// have READ_ONLY property attribute so that a new value -// is added to the local object to shadow the accessor -// in prototypes. +// If the accessor in the prototype has the READ_ONLY property attribute, then +// a new value is added to the local object when the property is set. +// This shadows the accessor in the prototype. class AccessorInfo: public Struct { public: DECL_ACCESSORS(getter, Object) diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 271c3fd167..a9a5e32e23 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -2647,6 +2647,21 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) { } } + // Convert constant divisions to multiplications for speed. + if (op == Token::DIV && + y && y->AsLiteral() && y->AsLiteral()->handle()->IsNumber()) { + double y_val = y->AsLiteral()->handle()->Number(); + int64_t y_int = static_cast(y_val); + // There are rounding issues with this optimization, but they don't + // apply if the number to be divided with has a reciprocal that can + // be precisely represented as a floating point number. This is + // the case if the number is an integer power of 2. + if (static_cast(y_int) == y_val && IsPowerOf2(y_int)) { + y = NewNumberLiteral(1 / y_val); + op = Token::MUL; + } + } + // For now we distinguish between comparisons and other binary // operations. (We could combine the two and get rid of this // code an AST node eventually.) diff --git a/deps/v8/src/regexp-macro-assembler-irregexp.cc b/deps/v8/src/regexp-macro-assembler-irregexp.cc index b87c51f900..eea3c23eae 100644 --- a/deps/v8/src/regexp-macro-assembler-irregexp.cc +++ b/deps/v8/src/regexp-macro-assembler-irregexp.cc @@ -47,6 +47,7 @@ RegExpMacroAssemblerIrregexp::RegExpMacroAssemblerIrregexp(Vector buffer) RegExpMacroAssemblerIrregexp::~RegExpMacroAssemblerIrregexp() { if (backtrack_.is_linked()) backtrack_.Unuse(); + if (own_buffer_) buffer_.Dispose(); } diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 78be512925..d1c9162d1e 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -2416,6 +2416,19 @@ static Object* Runtime_NumberToRadixString(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 2); + // Fast case where the result is a one character string. + if (args[0]->IsSmi() && args[1]->IsSmi()) { + int value = Smi::cast(args[0])->value(); + int radix = Smi::cast(args[1])->value(); + if (value >= 0 && value < radix) { + RUNTIME_ASSERT(radix <= 36); + // Character array used for conversion. + static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]); + } + } + + // Slow case. CONVERT_DOUBLE_CHECKED(value, args[0]); if (isnan(value)) { return Heap::AllocateStringFromAscii(CStrVector("NaN")); @@ -4168,24 +4181,6 @@ static Object* Runtime_Math_pow(Arguments args) { } } -// Returns a number value with positive sign, greater than or equal to -// 0 but less than 1, chosen randomly. -static Object* Runtime_Math_random(Arguments args) { - NoHandleAllocation ha; - ASSERT(args.length() == 0); - - // To get much better precision, we combine the results of two - // invocations of random(). The result is computed by normalizing a - // double in the range [0, RAND_MAX + 1) obtained by adding the - // high-order bits in the range [0, RAND_MAX] with the low-order - // bits in the range [0, 1). - double lo = static_cast(random()) * (1.0 / (RAND_MAX + 1.0)); - double hi = static_cast(random()); - double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0)); - ASSERT(result >= 0 && result < 1); - return Heap::AllocateHeapNumber(result); -} - static Object* Runtime_Math_round(Arguments args) { NoHandleAllocation ha; @@ -4821,8 +4816,8 @@ static Object* Runtime_DebugPrint(Arguments args) { // and print some interesting cpu debugging info. JavaScriptFrameIterator it; JavaScriptFrame* frame = it.frame(); - PrintF("fp = %p, sp = %p, pp = %p: ", - frame->fp(), frame->sp(), frame->pp()); + PrintF("fp = %p, sp = %p, caller_sp = %p: ", + frame->fp(), frame->sp(), frame->caller_sp()); } else { PrintF("DebugPrint: "); } @@ -6106,6 +6101,405 @@ static Object* Runtime_GetFrameDetails(Arguments args) { } +// Copy all the context locals into an object used to materialize a scope. +static void CopyContextLocalsToScopeObject(Handle code, + ScopeInfo<>& scope_info, + Handle context, + Handle scope_object) { + // Fill all context locals to the context extension. + for (int i = Context::MIN_CONTEXT_SLOTS; + i < scope_info.number_of_context_slots(); + i++) { + int context_index = + ScopeInfo<>::ContextSlotIndex(*code, + *scope_info.context_slot_name(i), + NULL); + + // Don't include the arguments shadow (.arguments) context variable. + if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) { + SetProperty(scope_object, + scope_info.context_slot_name(i), + Handle(context->get(context_index)), NONE); + } + } +} + + +// Create a plain JSObject which materializes the local scope for the specified +// frame. +static Handle MaterializeLocalScope(JavaScriptFrame* frame) { + Handle function(JSFunction::cast(frame->function())); + Handle code(function->code()); + ScopeInfo<> scope_info(*code); + + // Allocate and initialize a JSObject with all the arguments, stack locals + // heap locals and extension properties of the debugged function. + Handle local_scope = Factory::NewJSObject(Top::object_function()); + + // First fill all parameters. + for (int i = 0; i < scope_info.number_of_parameters(); ++i) { + SetProperty(local_scope, + scope_info.parameter_name(i), + Handle(frame->GetParameter(i)), NONE); + } + + // Second fill all stack locals. + for (int i = 0; i < scope_info.number_of_stack_slots(); i++) { + SetProperty(local_scope, + scope_info.stack_slot_name(i), + Handle(frame->GetExpression(i)), NONE); + } + + // Third fill all context locals. + Handle frame_context(Context::cast(frame->context())); + Handle function_context(frame_context->fcontext()); + CopyContextLocalsToScopeObject(code, scope_info, + function_context, local_scope); + + // Finally copy any properties from the function context extension. This will + // be variables introduced by eval. + if (function_context->closure() == *function) { + if (function_context->has_extension() && + !function_context->IsGlobalContext()) { + Handle ext(JSObject::cast(function_context->extension())); + Handle keys = GetKeysInFixedArrayFor(ext); + for (int i = 0; i < keys->length(); i++) { + // Names of variables introduced by eval are strings. + ASSERT(keys->get(i)->IsString()); + Handle key(String::cast(keys->get(i))); + SetProperty(local_scope, key, GetProperty(ext, key), NONE); + } + } + } + return local_scope; +} + + +// Create a plain JSObject which materializes the closure content for the +// context. +static Handle MaterializeClosure(Handle context) { + ASSERT(context->is_function_context()); + + Handle code(context->closure()->code()); + ScopeInfo<> scope_info(*code); + + // Allocate and initialize a JSObject with all the content of theis function + // closure. + Handle closure_scope = Factory::NewJSObject(Top::object_function()); + + // Check whether the arguments shadow object exists. + int arguments_shadow_index = + ScopeInfo<>::ContextSlotIndex(*code, + Heap::arguments_shadow_symbol(), + NULL); + if (arguments_shadow_index >= 0) { + // In this case all the arguments are available in the arguments shadow + // object. + Handle arguments_shadow( + JSObject::cast(context->get(arguments_shadow_index))); + for (int i = 0; i < scope_info.number_of_parameters(); ++i) { + SetProperty(closure_scope, + scope_info.parameter_name(i), + Handle(arguments_shadow->GetElement(i)), NONE); + } + } + + // Fill all context locals to the context extension. + CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope); + + // Finally copy any properties from the function context extension. This will + // be variables introduced by eval. + if (context->has_extension()) { + Handle ext(JSObject::cast(context->extension())); + Handle keys = GetKeysInFixedArrayFor(ext); + for (int i = 0; i < keys->length(); i++) { + // Names of variables introduced by eval are strings. + ASSERT(keys->get(i)->IsString()); + Handle key(String::cast(keys->get(i))); + SetProperty(closure_scope, key, GetProperty(ext, key), NONE); + } + } + + return closure_scope; +} + + +// Iterate over the actual scopes visible from a stack frame. All scopes are +// backed by an actual context except the local scope, which is inserted +// "artifically" in the context chain. +class ScopeIterator { + public: + enum ScopeType { + ScopeTypeGlobal = 0, + ScopeTypeLocal, + ScopeTypeWith, + ScopeTypeClosure + }; + + explicit ScopeIterator(JavaScriptFrame* frame) + : frame_(frame), + function_(JSFunction::cast(frame->function())), + context_(Context::cast(frame->context())), + local_done_(false), + at_local_(false) { + + // Check whether the first scope is actually a local scope. + if (context_->IsGlobalContext()) { + // If there is a stack slot for .result then this local scope has been + // created for evaluating top level code and it is not a real local scope. + // Checking for the existence of .result seems fragile, but the scope info + // saved with the code object does not otherwise have that information. + Handle code(function_->code()); + int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol()); + at_local_ = index < 0; + } else if (context_->is_function_context()) { + at_local_ = true; + } + } + + // More scopes? + bool Done() { return context_.is_null(); } + + // Move to the next scope. + void Next() { + // If at a local scope mark the local scope as passed. + if (at_local_) { + at_local_ = false; + local_done_ = true; + + // If the current context is not associated with the local scope the + // current context is the next real scope, so don't move to the next + // context in this case. + if (context_->closure() != *function_) { + return; + } + } + + // The global scope is always the last in the chain. + if (context_->IsGlobalContext()) { + context_ = Handle(); + return; + } + + // Move to the next context. + if (context_->is_function_context()) { + context_ = Handle(Context::cast(context_->closure()->context())); + } else { + context_ = Handle(context_->previous()); + } + + // If passing the local scope indicate that the current scope is now the + // local scope. + if (!local_done_ && + (context_->IsGlobalContext() || (context_->is_function_context()))) { + at_local_ = true; + } + } + + // Return the type of the current scope. + int Type() { + if (at_local_) { + return ScopeTypeLocal; + } + if (context_->IsGlobalContext()) { + ASSERT(context_->global()->IsGlobalObject()); + return ScopeTypeGlobal; + } + if (context_->is_function_context()) { + return ScopeTypeClosure; + } + ASSERT(context_->has_extension()); + ASSERT(!context_->extension()->IsJSContextExtensionObject()); + return ScopeTypeWith; + } + + // Return the JavaScript object with the content of the current scope. + Handle ScopeObject() { + switch (Type()) { + case ScopeIterator::ScopeTypeGlobal: + return Handle(CurrentContext()->global()); + break; + case ScopeIterator::ScopeTypeLocal: + // Materialize the content of the local scope into a JSObject. + return MaterializeLocalScope(frame_); + break; + case ScopeIterator::ScopeTypeWith: + // Return the with object. + return Handle(CurrentContext()->extension()); + break; + case ScopeIterator::ScopeTypeClosure: + // Materialize the content of the closure scope into a JSObject. + return MaterializeClosure(CurrentContext()); + break; + } + UNREACHABLE(); + return Handle(); + } + + // Return the context for this scope. For the local context there might not + // be an actual context. + Handle CurrentContext() { + if (at_local_ && context_->closure() != *function_) { + return Handle(); + } + return context_; + } + +#ifdef DEBUG + // Debug print of the content of the current scope. + void DebugPrint() { + switch (Type()) { + case ScopeIterator::ScopeTypeGlobal: + PrintF("Global:\n"); + CurrentContext()->Print(); + break; + + case ScopeIterator::ScopeTypeLocal: { + PrintF("Local:\n"); + Handle code(function_->code()); + ScopeInfo<> scope_info(*code); + scope_info.Print(); + if (!CurrentContext().is_null()) { + CurrentContext()->Print(); + if (CurrentContext()->has_extension()) { + Handle extension = + Handle(CurrentContext()->extension()); + if (extension->IsJSContextExtensionObject()) { + extension->Print(); + } + } + } + break; + } + + case ScopeIterator::ScopeTypeWith: { + PrintF("With:\n"); + Handle extension = + Handle(CurrentContext()->extension()); + extension->Print(); + break; + } + + case ScopeIterator::ScopeTypeClosure: { + PrintF("Closure:\n"); + CurrentContext()->Print(); + if (CurrentContext()->has_extension()) { + Handle extension = + Handle(CurrentContext()->extension()); + if (extension->IsJSContextExtensionObject()) { + extension->Print(); + } + } + break; + } + + default: + UNREACHABLE(); + } + PrintF("\n"); + } +#endif + + private: + JavaScriptFrame* frame_; + Handle function_; + Handle context_; + bool local_done_; + bool at_local_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); +}; + + +static Object* Runtime_GetScopeCount(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 2); + + // Check arguments. + Object* check = Runtime_CheckExecutionState(args); + if (check->IsFailure()) return check; + CONVERT_CHECKED(Smi, wrapped_id, args[1]); + + // Get the frame where the debugging is performed. + StackFrame::Id id = UnwrapFrameId(wrapped_id); + JavaScriptFrameIterator it(id); + JavaScriptFrame* frame = it.frame(); + + // Count the visible scopes. + int n = 0; + for (ScopeIterator it(frame); !it.Done(); it.Next()) { + n++; + } + + return Smi::FromInt(n); +} + + +static const int kScopeDetailsTypeIndex = 0; +static const int kScopeDetailsObjectIndex = 1; +static const int kScopeDetailsSize = 2; + +// Return an array with scope details +// args[0]: number: break id +// args[1]: number: frame index +// args[2]: number: scope index +// +// The array returned contains the following information: +// 0: Scope type +// 1: Scope object +static Object* Runtime_GetScopeDetails(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 3); + + // Check arguments. + Object* check = Runtime_CheckExecutionState(args); + if (check->IsFailure()) return check; + CONVERT_CHECKED(Smi, wrapped_id, args[1]); + CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]); + + // Get the frame where the debugging is performed. + StackFrame::Id id = UnwrapFrameId(wrapped_id); + JavaScriptFrameIterator frame_it(id); + JavaScriptFrame* frame = frame_it.frame(); + + // Find the requested scope. + int n = 0; + ScopeIterator it(frame); + for (; !it.Done() && n < index; it.Next()) { + n++; + } + if (it.Done()) { + return Heap::undefined_value(); + } + + // Calculate the size of the result. + int details_size = kScopeDetailsSize; + Handle details = Factory::NewFixedArray(details_size); + + // Fill in scope details. + details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type())); + details->set(kScopeDetailsObjectIndex, *it.ScopeObject()); + + return *Factory::NewJSArrayWithElements(details); +} + + +static Object* Runtime_DebugPrintScopes(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 0); + +#ifdef DEBUG + // Print the scopes for the top frame. + StackFrameLocator locator; + JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); + for (ScopeIterator it(frame); !it.Done(); it.Next()) { + it.DebugPrint(); + } +#endif + return Heap::undefined_value(); +} + + static Object* Runtime_GetCFrames(Arguments args) { HandleScope scope; ASSERT(args.length() == 1); @@ -6568,54 +6962,17 @@ static Object* Runtime_DebugEvaluate(Arguments args) { ASSERT(go_between_sinfo.number_of_context_slots() == 0); #endif - // Allocate and initialize a context extension object with all the - // arguments, stack locals heap locals and extension properties of the - // debugged function. - Handle context_ext = Factory::NewJSObject(Top::object_function()); - // First fill all parameters to the context extension. - for (int i = 0; i < sinfo.number_of_parameters(); ++i) { - SetProperty(context_ext, - sinfo.parameter_name(i), - Handle(frame->GetParameter(i)), NONE); - } - // Second fill all stack locals to the context extension. - for (int i = 0; i < sinfo.number_of_stack_slots(); i++) { - SetProperty(context_ext, - sinfo.stack_slot_name(i), - Handle(frame->GetExpression(i)), NONE); - } - // Third fill all context locals to the context extension. - Handle frame_context(Context::cast(frame->context())); - Handle function_context(frame_context->fcontext()); - for (int i = Context::MIN_CONTEXT_SLOTS; - i < sinfo.number_of_context_slots(); - ++i) { - int context_index = - ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL); - SetProperty(context_ext, - sinfo.context_slot_name(i), - Handle(function_context->get(context_index)), NONE); - } - // Finally copy any properties from the function context extension. This will - // be variables introduced by eval. - if (function_context->has_extension() && - !function_context->IsGlobalContext()) { - Handle ext(JSObject::cast(function_context->extension())); - Handle keys = GetKeysInFixedArrayFor(ext); - for (int i = 0; i < keys->length(); i++) { - // Names of variables introduced by eval are strings. - ASSERT(keys->get(i)->IsString()); - Handle key(String::cast(keys->get(i))); - SetProperty(context_ext, key, GetProperty(ext, key), NONE); - } - } + // Materialize the content of the local scope into a JSObject. + Handle local_scope = MaterializeLocalScope(frame); // Allocate a new context for the debug evaluation and set the extension // object build. Handle context = Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between); - context->set_extension(*context_ext); + context->set_extension(*local_scope); // Copy any with contexts present and chain them in front of this context. + Handle frame_context(Context::cast(frame->context())); + Handle function_context(frame_context->fcontext()); context = CopyWithContextChain(frame_context, context); // Wrap the evaluation statement in a new function compiled in the newly @@ -6657,6 +7014,13 @@ static Object* Runtime_DebugEvaluate(Arguments args) { Execution::Call(Handle::cast(evaluation_function), receiver, argc, argv, &has_pending_exception); if (has_pending_exception) return Failure::Exception(); + + // Skip the global proxy as it has no properties and always delegates to the + // real global object. + if (result->IsJSGlobalProxy()) { + result = Handle(JSObject::cast(result->GetPrototype())); + } + return *result; } diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index 30bb7c5aec..15dd9b4917 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -135,7 +135,6 @@ namespace internal { F(Math_floor, 1) \ F(Math_log, 1) \ F(Math_pow, 2) \ - F(Math_random, 0) \ F(Math_round, 1) \ F(Math_sin, 1) \ F(Math_sqrt, 1) \ @@ -288,6 +287,9 @@ namespace internal { F(CheckExecutionState, 1) \ F(GetFrameCount, 1) \ F(GetFrameDetails, 2) \ + F(GetScopeCount, 2) \ + F(GetScopeDetails, 3) \ + F(DebugPrintScopes, 0) \ F(GetCFrames, 1) \ F(GetThreadCount, 1) \ F(GetThreadDetails, 2) \ diff --git a/deps/v8/src/runtime.js b/deps/v8/src/runtime.js index c8ccf9f846..d4b49704ae 100644 --- a/deps/v8/src/runtime.js +++ b/deps/v8/src/runtime.js @@ -97,12 +97,12 @@ function STRICT_EQUALS(x) { if (IS_STRING(this)) { if (!IS_STRING(x)) return 1; // not equal return %StringEquals(this, x); - } + } if (IS_NUMBER(this)) { if (!IS_NUMBER(x)) return 1; // not equal return %NumberEquals(this, x); - } + } // If anything else gets here, we just do simple identity check. // Objects (including functions), null, undefined and booleans were @@ -148,7 +148,7 @@ function ADD(x) { // Default implementation. var a = %ToPrimitive(this, NO_HINT); var b = %ToPrimitive(x, NO_HINT); - + if (IS_STRING(a)) { return %StringAdd(a, %ToString(b)); } else if (IS_STRING(b)) { @@ -160,40 +160,48 @@ function ADD(x) { // Left operand (this) is already a string. -function STRING_ADD_LEFT(x) { - x = %ToString(%ToPrimitive(x, NO_HINT)); - return %StringAdd(this, x); +function STRING_ADD_LEFT(y) { + if (!IS_STRING(y)) y = %ToString(%ToPrimitive(y, NO_HINT)); + return %StringAdd(this, y); } -// Right operand (x) is already a string. -function STRING_ADD_RIGHT(x) { - var a = %ToString(%ToPrimitive(this, NO_HINT)); - return %StringAdd(a, x); +// Right operand (y) is already a string. +function STRING_ADD_RIGHT(y) { + var x = IS_STRING(this) ? this : %ToString(%ToPrimitive(this, NO_HINT)); + return %StringAdd(x, y); } // ECMA-262, section 11.6.2, page 50. -function SUB(x) { - return %NumberSub(%ToNumber(this), %ToNumber(x)); +function SUB(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberSub(x, y); } // ECMA-262, section 11.5.1, page 48. -function MUL(x) { - return %NumberMul(%ToNumber(this), %ToNumber(x)); +function MUL(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberMul(x, y); } // ECMA-262, section 11.5.2, page 49. -function DIV(x) { - return %NumberDiv(%ToNumber(this), %ToNumber(x)); +function DIV(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberDiv(x, y); } // ECMA-262, section 11.5.3, page 49. -function MOD(x) { - return %NumberMod(%ToNumber(this), %ToNumber(x)); +function MOD(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberMod(x, y); } @@ -204,50 +212,92 @@ function MOD(x) { */ // ECMA-262, section 11.10, page 57. -function BIT_OR(x) { - return %NumberOr(%ToNumber(this), %ToNumber(x)); +function BIT_OR(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberOr(x, y); } // ECMA-262, section 11.10, page 57. -function BIT_AND(x) { - return %NumberAnd(%ToNumber(this), %ToNumber(x)); +function BIT_AND(y) { + var x; + if (IS_NUMBER(this)) { + x = this; + if (!IS_NUMBER(y)) y = %ToNumber(y); + } else { + x = %ToNumber(this); + // Make sure to convert the right operand to a number before + // bailing out in the fast case, but after converting the + // left operand. This ensures that valueOf methods on the right + // operand are always executed. + if (!IS_NUMBER(y)) y = %ToNumber(y); + // Optimize for the case where we end up AND'ing a value + // that doesn't convert to a number. This is common in + // certain benchmarks. + if (NUMBER_IS_NAN(x)) return 0; + } + return %NumberAnd(x, y); } // ECMA-262, section 11.10, page 57. -function BIT_XOR(x) { - return %NumberXor(%ToNumber(this), %ToNumber(x)); +function BIT_XOR(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberXor(x, y); } // ECMA-262, section 11.4.7, page 47. function UNARY_MINUS() { - return %NumberUnaryMinus(%ToNumber(this)); + var x = IS_NUMBER(this) ? this : %ToNumber(this); + return %NumberUnaryMinus(x); } // ECMA-262, section 11.4.8, page 48. function BIT_NOT() { - return %NumberNot(%ToNumber(this)); + var x = IS_NUMBER(this) ? this : %ToNumber(this); + return %NumberNot(x); } // ECMA-262, section 11.7.1, page 51. -function SHL(x) { - return %NumberShl(%ToNumber(this), %ToNumber(x)); +function SHL(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberShl(x, y); } // ECMA-262, section 11.7.2, page 51. -function SAR(x) { - return %NumberSar(%ToNumber(this), %ToNumber(x)); +function SAR(y) { + var x; + if (IS_NUMBER(this)) { + x = this; + if (!IS_NUMBER(y)) y = %ToNumber(y); + } else { + x = %ToNumber(this); + // Make sure to convert the right operand to a number before + // bailing out in the fast case, but after converting the + // left operand. This ensures that valueOf methods on the right + // operand are always executed. + if (!IS_NUMBER(y)) y = %ToNumber(y); + // Optimize for the case where we end up shifting a value + // that doesn't convert to a number. This is common in + // certain benchmarks. + if (NUMBER_IS_NAN(x)) return 0; + } + return %NumberSar(x, y); } // ECMA-262, section 11.7.3, page 52. -function SHR(x) { - return %NumberShr(%ToNumber(this), %ToNumber(x)); +function SHR(y) { + var x = IS_NUMBER(this) ? this : %ToNumber(this); + if (!IS_NUMBER(y)) y = %ToNumber(y); + return %NumberShr(x, y); } diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc index fb66d2785a..eb497fb2e8 100644 --- a/deps/v8/src/serialize.cc +++ b/deps/v8/src/serialize.cc @@ -450,20 +450,26 @@ void ExternalReferenceTable::AddFromId(TypeCode type, const char* name) { Address address; switch (type) { - case C_BUILTIN: - address = Builtins::c_function_address( - static_cast(id)); + case C_BUILTIN: { + ExternalReference ref(static_cast(id)); + address = ref.address(); break; - case BUILTIN: - address = Builtins::builtin_address(static_cast(id)); + } + case BUILTIN: { + ExternalReference ref(static_cast(id)); + address = ref.address(); break; - case RUNTIME_FUNCTION: - address = Runtime::FunctionForId( - static_cast(id))->entry; + } + case RUNTIME_FUNCTION: { + ExternalReference ref(static_cast(id)); + address = ref.address(); break; - case IC_UTILITY: - address = IC::AddressFromUtilityId(static_cast(id)); + } + case IC_UTILITY: { + ExternalReference ref(IC_Utility(static_cast(id))); + address = ref.address(); break; + } default: UNREACHABLE(); return; @@ -642,10 +648,14 @@ void ExternalReferenceTable::PopulateTable() { "StubCache::secondary_->value"); // Runtime entries - Add(FUNCTION_ADDR(Runtime::PerformGC), + Add(ExternalReference::perform_gc_function().address(), RUNTIME_ENTRY, 1, "Runtime::PerformGC"); + Add(ExternalReference::random_positive_smi_function().address(), + RUNTIME_ENTRY, + 2, + "V8::RandomPositiveSmi"); // Miscellaneous Add(ExternalReference::builtin_passed_function().address(), diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index f7e5456ef8..0c80378c92 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -103,7 +103,7 @@ Object* StubCache::ComputeLoadField(String* name, LoadStubCompiler compiler; code = compiler.CompileLoadField(receiver, holder, field_index, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("LoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return code; } @@ -122,7 +122,7 @@ Object* StubCache::ComputeLoadCallback(String* name, LoadStubCompiler compiler; code = compiler.CompileLoadCallback(receiver, holder, callback, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("LoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return code; } @@ -141,7 +141,7 @@ Object* StubCache::ComputeLoadConstant(String* name, LoadStubCompiler compiler; code = compiler.CompileLoadConstant(receiver, holder, value, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("LoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return code; } @@ -158,7 +158,7 @@ Object* StubCache::ComputeLoadInterceptor(String* name, LoadStubCompiler compiler; code = compiler.CompileLoadInterceptor(receiver, holder, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("LoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return code; } @@ -182,7 +182,7 @@ Object* StubCache::ComputeKeyedLoadField(String* name, KeyedLoadStubCompiler compiler; code = compiler.CompileLoadField(name, receiver, holder, field_index); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedLoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -201,7 +201,7 @@ Object* StubCache::ComputeKeyedLoadConstant(String* name, KeyedLoadStubCompiler compiler; code = compiler.CompileLoadConstant(name, receiver, holder, value); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedLoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -219,7 +219,7 @@ Object* StubCache::ComputeKeyedLoadInterceptor(String* name, KeyedLoadStubCompiler compiler; code = compiler.CompileLoadInterceptor(receiver, holder, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedLoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -238,7 +238,7 @@ Object* StubCache::ComputeKeyedLoadCallback(String* name, KeyedLoadStubCompiler compiler; code = compiler.CompileLoadCallback(name, receiver, holder, callback); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedLoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -256,7 +256,7 @@ Object* StubCache::ComputeKeyedLoadArrayLength(String* name, KeyedLoadStubCompiler compiler; code = compiler.CompileLoadArrayLength(name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedLoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -273,7 +273,7 @@ Object* StubCache::ComputeKeyedLoadStringLength(String* name, KeyedLoadStubCompiler compiler; code = compiler.CompileLoadStringLength(name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedLoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -290,7 +290,7 @@ Object* StubCache::ComputeKeyedLoadFunctionPrototype(String* name, KeyedLoadStubCompiler compiler; code = compiler.CompileLoadFunctionPrototype(name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedLoadIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -309,7 +309,7 @@ Object* StubCache::ComputeStoreField(String* name, StoreStubCompiler compiler; code = compiler.CompileStoreField(receiver, field_index, transition, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("StoreIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -327,7 +327,7 @@ Object* StubCache::ComputeStoreCallback(String* name, StoreStubCompiler compiler; code = compiler.CompileStoreCallback(receiver, callback, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("StoreIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -344,7 +344,7 @@ Object* StubCache::ComputeStoreInterceptor(String* name, StoreStubCompiler compiler; code = compiler.CompileStoreInterceptor(receiver, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("StoreIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -361,7 +361,7 @@ Object* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver, KeyedStoreStubCompiler compiler; code = compiler.CompileStoreField(receiver, field_index, transition, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("KeyedStoreIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -412,7 +412,7 @@ Object* StubCache::ComputeCallConstant(int argc, CallStubCompiler compiler(argc); code = compiler.CompileCallConstant(object, holder, function, check, flags); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("CallIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); Object* result = map->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -445,7 +445,7 @@ Object* StubCache::ComputeCallField(int argc, CallStubCompiler compiler(argc); code = compiler.CompileCallField(object, holder, index, name, flags); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("CallIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); Object* result = map->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -478,7 +478,7 @@ Object* StubCache::ComputeCallInterceptor(int argc, CallStubCompiler compiler(argc); code = compiler.CompileCallInterceptor(object, holder, name); if (code->IsFailure()) return code; - LOG(CodeCreateEvent("CallIC", Code::cast(code), name)); + LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); Object* result = map->UpdateCodeCache(name, Code::cast(code)); if (result->IsFailure()) return result; } @@ -632,7 +632,8 @@ Object* StubCache::ComputeLazyCompile(int argc) { if (result->IsCode()) { Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("LazyCompile", code, code->arguments_count())); + LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, + code, code->arguments_count())); } return result; } @@ -780,7 +781,8 @@ Object* StubCompiler::CompileCallInitialize(Code::Flags flags) { Counters::call_initialize_stubs.Increment(); Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("CallInitialize", code, code->arguments_count())); + LOG(CodeCreateEvent(Logger::CALL_INITIALIZE_TAG, + code, code->arguments_count())); } return result; } @@ -795,7 +797,8 @@ Object* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { Counters::call_premonomorphic_stubs.Increment(); Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("CallPreMonomorphic", code, code->arguments_count())); + LOG(CodeCreateEvent(Logger::CALL_PRE_MONOMORPHIC_TAG, + code, code->arguments_count())); } return result; } @@ -810,7 +813,8 @@ Object* StubCompiler::CompileCallNormal(Code::Flags flags) { Counters::call_normal_stubs.Increment(); Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("CallNormal", code, code->arguments_count())); + LOG(CodeCreateEvent(Logger::CALL_NORMAL_TAG, + code, code->arguments_count())); } return result; } @@ -825,7 +829,8 @@ Object* StubCompiler::CompileCallMegamorphic(Code::Flags flags) { Counters::call_megamorphic_stubs.Increment(); Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("CallMegamorphic", code, code->arguments_count())); + LOG(CodeCreateEvent(Logger::CALL_MEGAMORPHIC_TAG, + code, code->arguments_count())); } return result; } @@ -840,7 +845,7 @@ Object* StubCompiler::CompileCallMiss(Code::Flags flags) { Counters::call_megamorphic_stubs.Increment(); Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("CallMiss", code, code->arguments_count())); + LOG(CodeCreateEvent(Logger::CALL_MISS_TAG, code, code->arguments_count())); } return result; } @@ -854,7 +859,8 @@ Object* StubCompiler::CompileCallDebugBreak(Code::Flags flags) { if (!result->IsFailure()) { Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("CallDebugBreak", code, code->arguments_count())); + LOG(CodeCreateEvent(Logger::CALL_DEBUG_BREAK_TAG, + code, code->arguments_count())); } return result; } @@ -870,8 +876,8 @@ Object* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { if (!result->IsFailure()) { Code* code = Code::cast(result); USE(code); - LOG(CodeCreateEvent("CallDebugPrepareStepIn", code, - code->arguments_count())); + LOG(CodeCreateEvent(Logger::CALL_DEBUG_PREPARE_STEP_IN_TAG, + code, code->arguments_count())); } return result; } diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index 137e2c4f0c..91662eea6c 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -362,6 +362,11 @@ class Vector { Sort(PointerValueCompare); } + void Truncate(int length) { + ASSERT(length <= length_); + length_ = length; + } + // Releases the array underlying this vector. Once disposed the // vector is empty. void Dispose() { diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h index 4111312ea0..06f116efea 100644 --- a/deps/v8/src/v8-counters.h +++ b/deps/v8/src/v8-counters.h @@ -131,6 +131,8 @@ namespace internal { SC(named_load_inline, V8.NamedLoadInline) \ SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \ SC(keyed_store_field, V8.KeyedStoreField) \ + SC(keyed_store_inline, V8.KeyedStoreInline) \ + SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \ SC(for_in, V8.ForIn) \ SC(enum_cache_hits, V8.EnumCacheHits) \ SC(enum_cache_misses, V8.EnumCacheMisses) \ diff --git a/deps/v8/src/v8.cc b/deps/v8/src/v8.cc index 17cb2dfe78..72f74aa1d5 100644 --- a/deps/v8/src/v8.cc +++ b/deps/v8/src/v8.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-2009 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: @@ -33,6 +33,10 @@ #include "stub-cache.h" #include "oprofile-agent.h" +#if V8_TARGET_ARCH_ARM +#include "arm/simulator-arm.h" +#endif + namespace v8 { namespace internal { @@ -62,6 +66,11 @@ bool V8::Initialize(Deserializer *des) { // Setup the platform OS support. OS::Setup(); + // Initialize other runtime facilities +#if !V8_HOST_ARCH_ARM && V8_TARGET_ARCH_ARM + ::assembler::arm::Simulator::Initialize(); +#endif + // Setup the object heap ASSERT(!Heap::HasBeenSetup()); if (!Heap::Setup(create_heap_objects)) { @@ -69,7 +78,6 @@ bool V8::Initialize(Deserializer *des) { return false; } - // Initialize other runtime facilities Bootstrapper::Initialize(create_heap_objects); Builtins::Setup(create_heap_objects); Top::Initialize(); @@ -130,4 +138,29 @@ void V8::TearDown() { } +uint32_t V8::Random() { + // Random number generator using George Marsaglia's MWC algorithm. + static uint32_t hi = 0; + static uint32_t lo = 0; + + // Initialize seed using the system random(). If one of the seeds + // should ever become zero again, or if random() returns zero, we + // avoid getting stuck with zero bits in hi or lo by re-initializing + // them on demand. + if (hi == 0) hi = random(); + if (lo == 0) lo = random(); + + // Mix the bits. + hi = 36969 * (hi & 0xFFFF) + (hi >> 16); + lo = 18273 * (lo & 0xFFFF) + (lo >> 16); + return (hi << 16) + (lo & 0xFFFF); +} + + +Smi* V8::RandomPositiveSmi() { + uint32_t random = Random(); + ASSERT(IsPowerOf2(Smi::kMaxValue + 1)); + return Smi::FromInt(random & Smi::kMaxValue); +} + } } // namespace v8::internal diff --git a/deps/v8/src/v8.h b/deps/v8/src/v8.h index 8cb3c7da16..4e906df929 100644 --- a/deps/v8/src/v8.h +++ b/deps/v8/src/v8.h @@ -80,10 +80,10 @@ class V8 : public AllStatic { public: // Global actions. - // If Initialize is called with des == NULL, the - // initial state is created from scratch. If a non-null Deserializer - // is given, the initial state is created by reading the - // deserialized data into an empty heap. + // If Initialize is called with des == NULL, the initial state is + // created from scratch. If a non-null Deserializer is given, the + // initial state is created by reading the deserialized data into an + // empty heap. static bool Initialize(Deserializer* des); static void TearDown(); static bool IsRunning() { return is_running_; } @@ -93,6 +93,11 @@ class V8 : public AllStatic { // Report process out of memory. Implementation found in api.cc. static void FatalProcessOutOfMemory(const char* location); + + // Random number generation support. Not cryptographically safe. + static uint32_t Random(); + static Smi* RandomPositiveSmi(); + private: // True if engine is currently running static bool is_running_; diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index d613e940d3..c10906022a 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,8 +34,8 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 1 #define MINOR_VERSION 2 -#define BUILD_NUMBER 7 -#define PATCH_LEVEL 0 +#define BUILD_NUMBER 8 +#define PATCH_LEVEL 1 #define CANDIDATE_VERSION false // Define SONAME to have the SCons build the put a specific SONAME into the diff --git a/deps/v8/src/x64/assembler-x64-inl.h b/deps/v8/src/x64/assembler-x64-inl.h index 18225681ed..ec27983cc1 100644 --- a/deps/v8/src/x64/assembler-x64-inl.h +++ b/deps/v8/src/x64/assembler-x64-inl.h @@ -151,11 +151,6 @@ void RelocInfo::apply(int delta) { if (rmode_ == RUNTIME_ENTRY || IsCodeTarget(rmode_)) { intptr_t* p = reinterpret_cast(pc_); *p -= delta; // relocate entry - } else if (rmode_ == JS_RETURN && IsCallInstruction()) { - // Special handling of js_return when a break point is set (call - // instruction has been inserted). - intptr_t* p = reinterpret_cast(pc_ + 1); - *p -= delta; // relocate entry } else if (IsInternalReference(rmode_)) { // absolute code pointer inside code object moves with the code object. intptr_t* p = reinterpret_cast(pc_); @@ -249,27 +244,9 @@ Object** RelocInfo::call_object_address() { // ----------------------------------------------------------------------------- // Implementation of Operand -Operand::Operand(Register base, int32_t disp) { - len_ = 1; - if (base.is(rsp) || base.is(r12)) { - // SIB byte is needed to encode (rsp + offset) or (r12 + offset). - set_sib(kTimes1, rsp, base); - } - - if (disp == 0 && !base.is(rbp) && !base.is(r13)) { - set_modrm(0, rsp); - } else if (is_int8(disp)) { - set_modrm(1, base); - set_disp8(disp); - } else { - set_modrm(2, base); - set_disp32(disp); - } -} - void Operand::set_modrm(int mod, Register rm) { ASSERT((mod & -4) == 0); - buf_[0] = mod << 6 | (rm.code() & 0x7); + buf_[0] = (mod << 6) | (rm.code() & 0x7); // Set REX.B to the high bit of rm.code(). rex_ |= (rm.code() >> 3); } @@ -278,7 +255,8 @@ void Operand::set_modrm(int mod, Register rm) { void Operand::set_sib(ScaleFactor scale, Register index, Register base) { ASSERT(len_ == 1); ASSERT(is_uint2(scale)); - // Use SIB with no index register only for base rsp or r12. + // Use SIB with no index register only for base rsp or r12. Otherwise we + // would skip the SIB byte entirely. ASSERT(!index.is(rsp) || base.is(rsp) || base.is(r12)); buf_[1] = scale << 6 | (index.code() & 0x7) << 3 | (base.code() & 0x7); rex_ |= (index.code() >> 3) << 1 | base.code() >> 3; diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc index 77bbf52405..cc64471356 100644 --- a/deps/v8/src/x64/assembler-x64.cc +++ b/deps/v8/src/x64/assembler-x64.cc @@ -72,7 +72,49 @@ XMMRegister xmm13 = { 13 }; XMMRegister xmm14 = { 14 }; XMMRegister xmm15 = { 15 }; + +Operand::Operand(Register base, int32_t disp): rex_(0) { + len_ = 1; + if (base.is(rsp) || base.is(r12)) { + // SIB byte is needed to encode (rsp + offset) or (r12 + offset). + set_sib(kTimes1, rsp, base); + } + + if (disp == 0 && !base.is(rbp) && !base.is(r13)) { + set_modrm(0, base); + } else if (is_int8(disp)) { + set_modrm(1, base); + set_disp8(disp); + } else { + set_modrm(2, base); + set_disp32(disp); + } +} + + +Operand::Operand(Register base, + Register index, + ScaleFactor scale, + int32_t disp): rex_(0) { + ASSERT(!index.is(rsp)); + len_ = 1; + set_sib(scale, index, base); + if (disp == 0 && !base.is(rbp) && !base.is(r13)) { + // This call to set_modrm doesn't overwrite the REX.B (or REX.X) bits + // possibly set by set_sib. + set_modrm(0, rsp); + } else if (is_int8(disp)) { + set_modrm(1, rsp); + set_disp8(disp); + } else { + set_modrm(2, rsp); + set_disp32(disp); + } +} + + // Safe default is no features. +// TODO(X64): Safe defaults include SSE2 for X64. uint64_t CpuFeatures::supported_ = 0; uint64_t CpuFeatures::enabled_ = 0; @@ -140,7 +182,8 @@ void CpuFeatures::Probe() { Object* code = Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL); if (!code->IsCode()) return; - LOG(CodeCreateEvent("Builtin", Code::cast(code), "CpuFeatures::Probe")); + LOG(CodeCreateEvent(Logger::BUILTIN_TAG, + Code::cast(code), "CpuFeatures::Probe")); typedef uint64_t (*F0)(); F0 probe = FUNCTION_CAST(Code::cast(code)->entry()); supported_ = probe(); @@ -398,16 +441,47 @@ void Assembler::immediate_arithmetic_op(byte subcode, emit_rex_64(dst); if (is_int8(src.value_)) { emit(0x83); - emit_operand(Register::toRegister(subcode), dst); + emit_operand(subcode, dst); emit(src.value_); } else { emit(0x81); - emit_operand(Register::toRegister(subcode), dst); + emit_operand(subcode, dst); emitl(src.value_); } } +void Assembler::immediate_arithmetic_op_32(byte subcode, + const Operand& dst, + Immediate src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst); + if (is_int8(src.value_)) { + emit(0x83); + emit_operand(subcode, dst); + emit(src.value_); + } else { + emit(0x81); + emit_operand(subcode, dst); + emitl(src.value_); + } +} + + +void Assembler::immediate_arithmetic_op_8(byte subcode, + const Operand& dst, + Immediate src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst); + ASSERT(is_int8(src.value_)); + emit(0x80); + emit_operand(subcode, dst); + emit(src.value_); +} + + void Assembler::shift(Register dst, Immediate shift_amount, int subcode) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -486,14 +560,6 @@ void Assembler::call(Register adr) { emit_modrm(0x2, adr); } -void Assembler::cpuid() { - ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID)); - EnsureSpace ensure_space(this); - last_pc_ = pc_; - emit(0x0F); - emit(0xA2); -} - void Assembler::call(const Operand& op) { EnsureSpace ensure_space(this); @@ -505,6 +571,15 @@ void Assembler::call(const Operand& op) { } +void Assembler::cpuid() { + ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID)); + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x0F); + emit(0xA2); +} + + void Assembler::cqo() { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -513,7 +588,7 @@ void Assembler::cqo() { } -void Assembler::dec(Register dst) { +void Assembler::decq(Register dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; emit_rex_64(dst); @@ -522,7 +597,7 @@ void Assembler::dec(Register dst) { } -void Assembler::dec(const Operand& dst) { +void Assembler::decq(const Operand& dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; emit_rex_64(dst); @@ -531,6 +606,15 @@ void Assembler::dec(const Operand& dst) { } +void Assembler::decl(const Operand& dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst); + emit(0xFF); + emit_operand(1, dst); +} + + void Assembler::enter(Immediate size) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -582,7 +666,7 @@ void Assembler::imul(Register dst, Register src, Immediate imm) { } -void Assembler::inc(Register dst) { +void Assembler::incq(Register dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; emit_rex_64(dst); @@ -591,7 +675,7 @@ void Assembler::inc(Register dst) { } -void Assembler::inc(const Operand& dst) { +void Assembler::incq(const Operand& dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; emit_rex_64(dst); @@ -600,6 +684,15 @@ void Assembler::inc(const Operand& dst) { } +void Assembler::incl(const Operand& dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst); + emit(0xFF); + emit_operand(0, dst); +} + + void Assembler::int3() { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -768,6 +861,16 @@ void Assembler::movl(const Operand& dst, Register src) { } +void Assembler::movl(const Operand& dst, Immediate value) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst); + emit(0xC7); + emit_operand(0x0, dst); + emit(value); // Only 32-bit immediates are possible, not 8-bit immediates. +} + + void Assembler::movl(Register dst, Immediate value) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -843,6 +946,31 @@ void Assembler::movq(Register dst, ExternalReference ref) { } +void Assembler::movq(const Operand& dst, Immediate value) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xC7); + emit_operand(0, dst); + emit(value); +} + + +void Assembler::movq(Register dst, Handle value, RelocInfo::Mode mode) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + ASSERT(!Heap::InNewSpace(*value)); + emit_rex_64(dst); + emit(0xB8 | dst.code() & 0x7); + if (value->IsHeapObject()) { + emitq(reinterpret_cast(value.location()), mode); + } else { + ASSERT_EQ(RelocInfo::NONE, mode); + emitq(reinterpret_cast(*value), RelocInfo::NONE); + } +} + + void Assembler::mul(Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -1063,6 +1191,13 @@ void Assembler::rcl(Register dst, uint8_t imm8) { } } +void Assembler::rdtsc() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x0F); + emit(0x31); +} + void Assembler::ret(int imm16) { EnsureSpace ensure_space(this); @@ -1078,6 +1213,19 @@ void Assembler::ret(int imm16) { } +void Assembler::setcc(Condition cc, Register reg) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + ASSERT(0 <= cc && cc < 16); + if (reg.code() > 3) { // Use x64 byte registers, where different. + emit_rex_32(reg); + } + emit(0x0F); + emit(0x90 | cc); + emit_modrm(0x0, reg); +} + + void Assembler::shld(Register dst, Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -1128,6 +1276,7 @@ void Assembler::store_rax(ExternalReference ref) { void Assembler::testb(Register reg, Immediate mask) { + ASSERT(is_int8(mask.value_)); EnsureSpace ensure_space(this); last_pc_ = pc_; if (reg.is(rax)) { @@ -1146,6 +1295,7 @@ void Assembler::testb(Register reg, Immediate mask) { void Assembler::testb(const Operand& op, Immediate mask) { + ASSERT(is_int8(mask.value_)); EnsureSpace ensure_space(this); last_pc_ = pc_; emit_optional_rex_32(rax, op); @@ -1198,6 +1348,22 @@ void Assembler::testq(Register dst, Register src) { } +void Assembler::testq(Register dst, Immediate mask) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (dst.is(rax)) { + emit_rex_64(); + emit(0xA9); + emit(mask); + } else { + emit_rex_64(dst); + emit(0xF7); + emit_modrm(0, dst); + emit(mask); + } +} + + // Relocation information implementations void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { @@ -1360,19 +1526,7 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* a, return NULL; } - -StackFrame::Type ExitFrame::GetStateForFramePointer(unsigned char* a, - StackFrame::State* b) { - // TODO(X64): UNIMPLEMENTED - return NONE; -} - -int JavaScriptFrame::GetProvidedParametersCount() const { - UNIMPLEMENTED(); - return 0; -} - -void JumpTarget::DoBind(int a) { +void JumpTarget::DoBind() { UNIMPLEMENTED(); } @@ -1384,7 +1538,6 @@ void JumpTarget::DoJump() { UNIMPLEMENTED(); } - Object* LoadStubCompiler::CompileLoadCallback(JSObject* a, JSObject* b, AccessorInfo* c, @@ -1416,11 +1569,6 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* a, return NULL; } -StackFrame::Type StackFrame::ComputeType(StackFrame::State* a) { - UNIMPLEMENTED(); - return NONE; -} - Object* StoreStubCompiler::CompileStoreCallback(JSObject* a, AccessorInfo* b, String* c) { @@ -1446,102 +1594,4 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags a) { return NULL; } -void VirtualFrame::Drop(int a) { - UNIMPLEMENTED(); -} - -int VirtualFrame::InvalidateFrameSlotAt(int a) { - UNIMPLEMENTED(); - return -1; -} - -void VirtualFrame::MergeTo(VirtualFrame* a) { - UNIMPLEMENTED(); -} - -Result VirtualFrame::Pop() { - UNIMPLEMENTED(); - return Result(NULL); -} - -Result VirtualFrame::RawCallStub(CodeStub* a) { - UNIMPLEMENTED(); - return Result(NULL); -} - -void VirtualFrame::SyncElementBelowStackPointer(int a) { - UNIMPLEMENTED(); -} - -void VirtualFrame::SyncElementByPushing(int a) { - UNIMPLEMENTED(); -} - -void VirtualFrame::SyncRange(int a, int b) { - UNIMPLEMENTED(); -} - -VirtualFrame::VirtualFrame() : elements_(0) { - UNIMPLEMENTED(); -} - -byte* ArgumentsAdaptorFrame::GetCallerStackPointer() const { - UNIMPLEMENTED(); - return NULL; -} - -void CodeGenerator::GenerateArgumentsAccess(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateArgumentsLength(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateFastCharCodeAt(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateIsArray(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateIsSmi(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateLog(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateObjectEquals(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateSetValueOf(ZoneList* a) { - UNIMPLEMENTED(); -} - -void CodeGenerator::GenerateValueOf(ZoneList* a) { - UNIMPLEMENTED(); -} - -void ExitFrame::Iterate(ObjectVisitor* a) const { - UNIMPLEMENTED(); -} - -byte* InternalFrame::GetCallerStackPointer() const { - UNIMPLEMENTED(); - return NULL; -} - -byte* JavaScriptFrame::GetCallerStackPointer() const { - UNIMPLEMENTED(); - return NULL; -} - } } // namespace v8::internal diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h index b4882571e2..650c218f11 100644 --- a/deps/v8/src/x64/assembler-x64.h +++ b/deps/v8/src/x64/assembler-x64.h @@ -77,7 +77,7 @@ static inline bool is_int32(int64_t x) { struct Register { static Register toRegister(int code) { - Register r = {code}; + Register r = { code }; return r; } bool is_valid() const { return 0 <= code_ && code_ < 16; } @@ -89,11 +89,11 @@ struct Register { return code_; } int bit() const { - UNIMPLEMENTED(); - return 0; + return 1 << code_; } - // (unfortunately we can't make this private in a struct) + // (unfortunately we can't make this private in a struct when initializing + // by assignment.) int code_; }; @@ -250,7 +250,7 @@ enum ScaleFactor { class Operand BASE_EMBEDDED { public: // [base + disp/r] - INLINE(Operand(Register base, int32_t disp)); + Operand(Register base, int32_t disp); // [base + index*scale + disp/r] Operand(Register base, @@ -385,7 +385,8 @@ class Assembler : public Malloced { // // If we need versions of an assembly instruction that operate on different // width arguments, we add a single-letter suffix specifying the width. - // This is done for the following instructions: mov, cmp. + // This is done for the following instructions: mov, cmp, inc, dec, + // add, sub, and test. // There are no versions of these instructions without the suffix. // - Instructions on 8-bit (byte) operands/registers have a trailing 'b'. // - Instructions on 16-bit (word) operands/registers have a trailing 'w'. @@ -423,10 +424,10 @@ class Assembler : public Malloced { void movl(Register dst, Register src); void movl(Register dst, const Operand& src); void movl(const Operand& dst, Register src); + void movl(const Operand& dst, Immediate imm); // Load a 32-bit immediate value, zero-extended to 64 bits. void movl(Register dst, Immediate imm32); - void movq(Register dst, int32_t imm32); void movq(Register dst, const Operand& src); // Sign extends immediate 32-bit value to 64 bits. void movq(Register dst, Immediate x); @@ -434,7 +435,8 @@ class Assembler : public Malloced { // Move 64 bit register value to 64-bit memory location. void movq(const Operand& dst, Register src); - + // Move sign extended immediate to memory location. + void movq(const Operand& dst, Immediate value); // New x64 instructions to load a 64-bit immediate into a register. // All 64-bit immediates must have a relocation mode. void movq(Register dst, void* ptr, RelocInfo::Mode rmode); @@ -444,66 +446,63 @@ class Assembler : public Malloced { void movq(Register dst, ExternalReference ext); void movq(Register dst, Handle handle, RelocInfo::Mode rmode); - // New x64 instruction to load from an immediate 64-bit pointer into RAX. void load_rax(void* ptr, RelocInfo::Mode rmode); void load_rax(ExternalReference ext); - void movsx_b(Register dst, const Operand& src); - - void movsx_w(Register dst, const Operand& src); - - void movzx_b(Register dst, const Operand& src); - - void movzx_w(Register dst, const Operand& src); - // Conditional moves - void cmov(Condition cc, Register dst, int32_t imm32); - void cmov(Condition cc, Register dst, Handle handle); - void cmov(Condition cc, Register dst, const Operand& src); + // Implement conditional moves here. // Exchange two registers void xchg(Register dst, Register src); // Arithmetics - void add(Register dst, Register src) { + void addq(Register dst, Register src) { arithmetic_op(0x03, dst, src); } - void add(Register dst, const Operand& src) { + void addq(Register dst, const Operand& src) { arithmetic_op(0x03, dst, src); } - void add(const Operand& dst, Register src) { + void addq(const Operand& dst, Register src) { arithmetic_op(0x01, src, dst); } - void add(Register dst, Immediate src) { + void addq(Register dst, Immediate src) { immediate_arithmetic_op(0x0, dst, src); } - void add(const Operand& dst, Immediate src) { + void addq(const Operand& dst, Immediate src) { immediate_arithmetic_op(0x0, dst, src); } - void cmp(Register dst, Register src) { + void addl(const Operand& dst, Immediate src) { + immediate_arithmetic_op_32(0x0, dst, src); + } + + void cmpb(const Operand& dst, Immediate src) { + immediate_arithmetic_op_8(0x7, dst, src); + } + + void cmpq(Register dst, Register src) { arithmetic_op(0x3B, dst, src); } - void cmp(Register dst, const Operand& src) { + void cmpq(Register dst, const Operand& src) { arithmetic_op(0x3B, dst, src); } - void cmp(const Operand& dst, Register src) { + void cmpq(const Operand& dst, Register src) { arithmetic_op(0x39, src, dst); } - void cmp(Register dst, Immediate src) { + void cmpq(Register dst, Immediate src) { immediate_arithmetic_op(0x7, dst, src); } - void cmp(const Operand& dst, Immediate src) { + void cmpq(const Operand& dst, Immediate src) { immediate_arithmetic_op(0x7, dst, src); } @@ -527,15 +526,9 @@ class Assembler : public Malloced { immediate_arithmetic_op(0x4, dst, src); } - void cmpb(const Operand& op, int8_t imm8); - void cmpb_al(const Operand& op); - void cmpw_ax(const Operand& op); - void cmpw(const Operand& op, Immediate imm16); - - void dec_b(Register dst); - - void dec(Register dst); - void dec(const Operand& dst); + void decq(Register dst); + void decq(const Operand& dst); + void decl(const Operand& dst); // Sign-extends rax into rdx:rax. void cqo(); @@ -548,8 +541,9 @@ class Assembler : public Malloced { // Performs the operation dst = src * imm. void imul(Register dst, Register src, Immediate imm); - void inc(Register dst); - void inc(const Operand& dst); + void incq(Register dst); + void incq(const Operand& dst); + void incl(const Operand& dst); void lea(Register dst, const Operand& src); @@ -621,32 +615,37 @@ class Assembler : public Malloced { void store_rax(void* dst, RelocInfo::Mode mode); void store_rax(ExternalReference ref); - void sub(Register dst, Register src) { + void subq(Register dst, Register src) { arithmetic_op(0x2B, dst, src); } - void sub(Register dst, const Operand& src) { + void subq(Register dst, const Operand& src) { arithmetic_op(0x2B, dst, src); } - void sub(const Operand& dst, Register src) { + void subq(const Operand& dst, Register src) { arithmetic_op(0x29, src, dst); } - void sub(Register dst, Immediate src) { + void subq(Register dst, Immediate src) { immediate_arithmetic_op(0x5, dst, src); } - void sub(const Operand& dst, Immediate src) { + void subq(const Operand& dst, Immediate src) { immediate_arithmetic_op(0x5, dst, src); } + void subl(const Operand& dst, Immediate src) { + immediate_arithmetic_op_32(0x5, dst, src); + } + void testb(Register reg, Immediate mask); void testb(const Operand& op, Immediate mask); void testl(Register reg, Immediate mask); void testl(const Operand& op, Immediate mask); void testq(const Operand& op, Register reg); void testq(Register dst, Register src); + void testq(Register dst, Immediate mask); void xor_(Register dst, Register src) { arithmetic_op(0x33, dst, src); @@ -668,18 +667,19 @@ class Assembler : public Malloced { immediate_arithmetic_op(0x6, dst, src); } - // Bit operations. void bt(const Operand& dst, Register src); void bts(const Operand& dst, Register src); // Miscellaneous + void cpuid(); void hlt(); void int3(); void nop(); void nop(int n); void rdtsc(); void ret(int imm16); + void setcc(Condition cc, Register reg); // Label operations & relative jumps (PPUM Appendix D) // @@ -717,8 +717,6 @@ class Assembler : public Malloced { // Conditional jumps void j(Condition cc, Label* L); - void j(Condition cc, byte* entry, RelocInfo::Mode rmode); - void j(Condition cc, Handle code); // Floating-point operations void fld(int i); @@ -774,11 +772,6 @@ class Assembler : public Malloced { void frndint(); - void sahf(); - void setcc(Condition cc, Register reg); - - void cpuid(); - // SSE2 instructions void cvttss2si(Register dst, const Operand& src); void cvttsd2si(Register dst, const Operand& src); @@ -791,8 +784,8 @@ class Assembler : public Malloced { void divsd(XMMRegister dst, XMMRegister src); // Use either movsd or movlpd. - void movdbl(XMMRegister dst, const Operand& src); - void movdbl(const Operand& dst, XMMRegister src); + // void movdbl(XMMRegister dst, const Operand& src); + // void movdbl(const Operand& dst, XMMRegister src); // Debugging void Print(); @@ -813,11 +806,11 @@ class Assembler : public Malloced { // Writes a doubleword of data in the code stream. // Used for inline tables, e.g., jump-tables. - void dd(uint32_t data); + // void dd(uint32_t data); // Writes a quadword of data in the code stream. // Used for inline tables, e.g., jump-tables. - void dd(uint64_t data, RelocInfo::Mode reloc_info); + // void dd(uint64_t data, RelocInfo::Mode reloc_info); // Writes the absolute address of a bound label at the given position in // the generated code. That positions should have the relocation mode @@ -841,11 +834,11 @@ class Assembler : public Malloced { static const int kMinimalBufferSize = 4*KB; protected: - void movsd(XMMRegister dst, const Operand& src); - void movsd(const Operand& dst, XMMRegister src); + // void movsd(XMMRegister dst, const Operand& src); + // void movsd(const Operand& dst, XMMRegister src); - void emit_sse_operand(XMMRegister reg, const Operand& adr); - void emit_sse_operand(XMMRegister dst, XMMRegister src); + // void emit_sse_operand(XMMRegister reg, const Operand& adr); + // void emit_sse_operand(XMMRegister dst, XMMRegister src); private: @@ -969,15 +962,23 @@ class Assembler : public Malloced { void arithmetic_op(byte opcode, Register reg, const Operand& op); void immediate_arithmetic_op(byte subcode, Register dst, Immediate src); void immediate_arithmetic_op(byte subcode, const Operand& dst, Immediate src); + // Operate on a 32-bit word in memory. + void immediate_arithmetic_op_32(byte subcode, + const Operand& dst, + Immediate src); + // Operate on a byte in memory. + void immediate_arithmetic_op_8(byte subcode, + const Operand& dst, + Immediate src); // Emit machine code for a shift operation. void shift(Register dst, Immediate shift_amount, int subcode); // Shift dst by cl % 64 bits. void shift(Register dst, int subcode); - void emit_farith(int b1, int b2, int i); + // void emit_farith(int b1, int b2, int i); // labels - void print(Label* L); + // void print(Label* L); void bind_to(Label* L, int pos); void link_to(Label* L, Label* appendix); diff --git a/deps/v8/src/x64/builtins-x64.cc b/deps/v8/src/x64/builtins-x64.cc index 3f1cd9faba..eb9c43f788 100644 --- a/deps/v8/src/x64/builtins-x64.cc +++ b/deps/v8/src/x64/builtins-x64.cc @@ -27,19 +27,138 @@ #include "v8.h" #include "codegen-inl.h" +#include "macro-assembler.h" namespace v8 { namespace internal { +#define __ ACCESS_MASM(masm) + void Builtins::Generate_Adaptor(MacroAssembler* masm, Builtins::CFunctionId id) { masm->int3(); // UNIMPLEMENTED. } +static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { + __ push(rbp); + __ movq(rbp, rsp); + + // Store the arguments adaptor context sentinel. + __ push(Immediate(ArgumentsAdaptorFrame::SENTINEL)); + + // Push the function on the stack. + __ push(rdi); + + // Preserve the number of arguments on the stack. Must preserve both + // eax and ebx because these registers are used when copying the + // arguments and the receiver. + ASSERT(kSmiTagSize == 1); + __ lea(rcx, Operand(rax, rax, kTimes1, kSmiTag)); + __ push(rcx); +} + + +static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { + // Retrieve the number of arguments from the stack. Number is a Smi. + __ movq(rbx, Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset)); + + // Leave the frame. + __ movq(rsp, rbp); + __ pop(rbp); + + // Remove caller arguments from the stack. + // rbx holds a Smi, so we convery to dword offset by multiplying by 4. + ASSERT_EQ(kSmiTagSize, 1 && kSmiTag == 0); + ASSERT_EQ(kPointerSize, (1 << kSmiTagSize) * 4); + __ pop(rcx); + __ lea(rsp, Operand(rsp, rbx, kTimes4, 1 * kPointerSize)); // 1 ~ receiver + __ push(rcx); +} + + void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { - masm->int3(); // UNIMPLEMENTED. + // ----------- S t a t e ------------- + // -- rax : actual number of arguments + // -- rbx : expected number of arguments + // -- rdx : code entry to call + // ----------------------------------- + + Label invoke, dont_adapt_arguments; + __ IncrementCounter(&Counters::arguments_adaptors, 1); + + Label enough, too_few; + __ cmpq(rax, rbx); + __ j(less, &too_few); + __ cmpq(rbx, Immediate(SharedFunctionInfo::kDontAdaptArgumentsSentinel)); + __ j(equal, &dont_adapt_arguments); + + { // Enough parameters: Actual >= expected. + __ bind(&enough); + EnterArgumentsAdaptorFrame(masm); + + // Copy receiver and all expected arguments. + const int offset = StandardFrameConstants::kCallerSPOffset; + __ lea(rax, Operand(rbp, rax, kTimesPointerSize, offset)); + __ movq(rcx, Immediate(-1)); // account for receiver + + Label copy; + __ bind(©); + __ incq(rcx); + __ push(Operand(rax, 0)); + __ subq(rax, Immediate(kPointerSize)); + __ cmpq(rcx, rbx); + __ j(less, ©); + __ jmp(&invoke); + } + + { // Too few parameters: Actual < expected. + __ bind(&too_few); + EnterArgumentsAdaptorFrame(masm); + + // Copy receiver and all actual arguments. + const int offset = StandardFrameConstants::kCallerSPOffset; + __ lea(rdi, Operand(rbp, rax, kTimesPointerSize, offset)); + __ movq(rcx, Immediate(-1)); // account for receiver + + Label copy; + __ bind(©); + __ incq(rcx); + __ push(Operand(rdi, 0)); + __ subq(rdi, Immediate(kPointerSize)); + __ cmpq(rcx, rax); + __ j(less, ©); + + // Fill remaining expected arguments with undefined values. + Label fill; + __ movq(kScratchRegister, + Factory::undefined_value(), + RelocInfo::EMBEDDED_OBJECT); + __ bind(&fill); + __ incq(rcx); + __ push(kScratchRegister); + __ cmpq(rcx, rbx); + __ j(less, &fill); + + // Restore function pointer. + __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + } + + // Call the entry point. + __ bind(&invoke); + __ call(rdx); + + // Leave frame and return. + LeaveArgumentsAdaptorFrame(masm); + __ ret(0); + + // ------------------------------------------- + // Dont adapt arguments. + // ------------------------------------------- + __ bind(&dont_adapt_arguments); + __ jmp(rdx); } + void Builtins::Generate_FunctionApply(MacroAssembler* masm) { masm->int3(); // UNIMPLEMENTED. } @@ -52,14 +171,125 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { masm->int3(); // UNIMPLEMENTED. } -void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { - masm->int3(); // UNIMPLEMENTED. +static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, + bool is_construct) { + // Expects five C++ function parameters. + // - Address entry (ignored) + // - JSFunction* function ( + // - Object* receiver + // - int argc + // - Object*** argv + // (see Handle::Invoke in execution.cc). + + // Platform specific argument handling. After this, the stack contains + // an internal frame and the pushed function and receiver, and + // register rax and rbx holds the argument count and argument array, + // while rdi holds the function pointer and rsi the context. +#ifdef __MSVC__ + // MSVC parameters in: + // rcx : entry (ignored) + // rdx : function + // r8 : receiver + // r9 : argc + // [rsp+0x20] : argv + + // Clear the context before we push it when entering the JS frame. + __ xor_(rsi, rsi); + // Enter an internal frame. + __ EnterInternalFrame(); + + // Load the function context into rsi. + __ movq(rsi, FieldOperand(rdx, JSFunction::kContextOffset)); + + // Push the function and the receiver onto the stack. + __ push(rdx); + __ push(r8); + + // Load the number of arguments and setup pointer to the arguments. + __ movq(rax, r9); + // Load the previous frame pointer to access C argument on stack + __ movq(kScratchRegister, Operand(rbp, 0)); + __ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset)); + // Load the function pointer into rdi. + __ movq(rdi, rdx); +#else // !defined(__MSVC__) + // GCC parameters in: + // rdi : entry (ignored) + // rsi : function + // rdx : receiver + // rcx : argc + // r8 : argv + + __ movq(rdi, rsi); + // rdi : function + + // Clear the context before we push it when entering the JS frame. + __ xor_(rsi, rsi); + // Enter an internal frame. + __ EnterInternalFrame(); + + // Push the function and receiver and setup the context. + __ push(rdi); + __ push(rdx); + __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + + // Load the number of arguments and setup pointer to the arguments. + __ movq(rax, rcx); + __ movq(rbx, r8); +#endif // __MSVC__ + // Current stack contents: + // [rsp + 2 * kPointerSize ... ]: Internal frame + // [rsp + kPointerSize] : function + // [rsp] : receiver + // Current register contents: + // rax : argc + // rbx : argv + // rsi : context + // rdi : function + + // Copy arguments to the stack in a loop. + // Register rbx points to array of pointers to handle locations. + // Push the values of these handles. + Label loop, entry; + __ xor_(rcx, rcx); // Set loop variable to 0. + __ jmp(&entry); + __ bind(&loop); + __ movq(kScratchRegister, Operand(rbx, rcx, kTimesPointerSize, 0)); + __ push(Operand(kScratchRegister, 0)); // dereference handle + __ addq(rcx, Immediate(1)); + __ bind(&entry); + __ cmpq(rcx, rax); + __ j(not_equal, &loop); + + // Invoke the code. + if (is_construct) { + // Expects rdi to hold function pointer. + __ movq(kScratchRegister, + Handle(Builtins::builtin(Builtins::JSConstructCall)), + RelocInfo::CODE_TARGET); + __ call(kScratchRegister); + } else { + ParameterCount actual(rax); + // Function must be in rdi. + __ InvokeFunction(rdi, actual, CALL_FUNCTION); + } + + // Exit the JS frame. Notice that this also removes the empty + // context and the function left on the stack by the code + // invocation. + __ LeaveInternalFrame(); + // TODO(X64): Is argument correct? Is there a receiver to remove? + __ ret(1 * kPointerSize); // remove receiver } + void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { - masm->int3(); // UNIMPLEMENTED. + Generate_JSEntryTrampolineHelper(masm, false); } -} } // namespace v8::internal +void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { + Generate_JSEntryTrampolineHelper(masm, true); +} +} } // namespace v8::internal diff --git a/deps/v8/src/x64/codegen-x64-inl.h b/deps/v8/src/x64/codegen-x64-inl.h index 0d5b0e21dc..733378de2e 100644 --- a/deps/v8/src/x64/codegen-x64-inl.h +++ b/deps/v8/src/x64/codegen-x64-inl.h @@ -37,6 +37,17 @@ namespace internal { void DeferredCode::Jump() { UNIMPLEMENTED(); } void DeferredCode::Branch(Condition cc) { UNIMPLEMENTED(); } + +void CodeGenerator::GenerateMathSin(ZoneList* args) { + GenerateFastMathOp(SIN, args); +} + + +void CodeGenerator::GenerateMathCos(ZoneList* args) { + GenerateFastMathOp(COS, args); +} + + } } // namespace v8::internal #endif // V8_X64_CODEGEN_X64_INL_H_ diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index ca58e09a75..dc32227b44 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -30,6 +30,8 @@ #include "macro-assembler.h" #include "register-allocator-inl.h" #include "codegen.h" +// TEST +#include "compiler.h" namespace v8 { namespace internal { @@ -41,6 +43,37 @@ void DeferredCode::SaveRegisters() { UNIMPLEMENTED(); } void DeferredCode::RestoreRegisters() { UNIMPLEMENTED(); } +// ------------------------------------------------------------------------- +// CodeGenState implementation. + +CodeGenState::CodeGenState(CodeGenerator* owner) + : owner_(owner), + typeof_state_(NOT_INSIDE_TYPEOF), + destination_(NULL), + previous_(NULL) { + owner_->set_state(this); +} + + +CodeGenState::CodeGenState(CodeGenerator* owner, + TypeofState typeof_state, + ControlDestination* destination) + : owner_(owner), + typeof_state_(typeof_state), + destination_(destination), + previous_(owner->state()) { + owner_->set_state(this); +} + + +CodeGenState::~CodeGenState() { + ASSERT(owner_->state() == this); + owner_->set_state(previous_); +} + + +// ----------------------------------------------------------------------------- +// CodeGenerator implementation. CodeGenerator::CodeGenerator(int buffer_size, Handle