diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 7e8c0deb59..0786ed9296 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,9 +1,23 @@ -2010-03-17: Version 2.1.5 +2010-03-22: Version 2.1.7 + + Fixed issue 650. + + Fixed a bug where __proto__ was sometimes enumerated (issue 646). + + Performance improvements for arithmetic operations. + + Performance improvements for string operations. + + Print script name and line number information in stack trace. + + +2010-03-17: Version 2.1.6 Performance improvements for arithmetic operations. Performance improvements for string operations. + 2010-03-10: Version 2.1.4 Fixed code cache lookup for keyed IC's (issue http://crbug.com/37853). diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index f4bba6beb1..bf42fd4e61 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -43,6 +43,7 @@ SOURCES = { bootstrapper.cc builtins.cc checks.cc + circular-queue.cc code-stubs.cc codegen.cc compilation-cache.cc @@ -50,11 +51,13 @@ SOURCES = { contexts.cc conversions.cc counters.cc + cpu-profiler.cc data-flow.cc dateparser.cc debug-agent.cc debug.cc disassembler.cc + diy-fp.cc execution.cc factory.cc flags.cc @@ -63,6 +66,7 @@ SOURCES = { full-codegen.cc func-name-inferrer.cc global-handles.cc + fast-dtoa.cc handles.cc hashmap.cc heap-profiler.cc diff --git a/deps/v8/src/arm/assembler-arm-inl.h b/deps/v8/src/arm/assembler-arm-inl.h index 354436cb14..3f0854e333 100644 --- a/deps/v8/src/arm/assembler-arm-inl.h +++ b/deps/v8/src/arm/assembler-arm-inl.h @@ -144,12 +144,21 @@ void RelocInfo::set_call_object(Object* target) { bool RelocInfo::IsPatchedReturnSequence() { - // On ARM a "call instruction" is actually two instructions. - // mov lr, pc - // ldr pc, [pc, #XXX] - return (Assembler::instr_at(pc_) == kMovLrPc) - && ((Assembler::instr_at(pc_ + Assembler::kInstrSize) & kLdrPCPattern) - == kLdrPCPattern); + Instr current_instr = Assembler::instr_at(pc_); + Instr next_instr = Assembler::instr_at(pc_ + Assembler::kInstrSize); +#ifdef USE_BLX + // A patched return sequence is: + // ldr ip, [pc, #0] + // blx ip + return ((current_instr & kLdrPCMask) == kLdrPCPattern) + && ((next_instr & kBlxRegMask) == kBlxRegPattern); +#else + // A patched return sequence is: + // mov lr, pc + // ldr pc, [pc, #-4] + return (current_instr == kMovLrPc) + && ((next_instr & kLdrPCMask) == kLdrPCPattern); +#endif } @@ -225,6 +234,16 @@ Address Assembler::target_address_address_at(Address pc) { target_pc -= kInstrSize; instr = Memory::int32_at(target_pc); } + +#ifdef USE_BLX + // If we have a blx instruction, the instruction before it is + // what needs to be patched. + if ((instr & kBlxRegMask) == kBlxRegPattern) { + target_pc -= kInstrSize; + instr = Memory::int32_at(target_pc); + } +#endif + // Verify that the instruction to patch is a // ldr , [pc +/- offset_12]. ASSERT((instr & 0x0f7f0000) == 0x051f0000); diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc index 6b226fd3df..d4cd818c17 100644 --- a/deps/v8/src/arm/assembler-arm.cc +++ b/deps/v8/src/arm/assembler-arm.cc @@ -240,8 +240,14 @@ static const Instr kPopRegPattern = al | B26 | L | 4 | PostIndex | sp.code() * B16; // mov lr, pc const Instr kMovLrPc = al | 13*B21 | pc.code() | lr.code() * B12; -// ldr pc, [pc, #XXX] -const Instr kLdrPCPattern = al | B26 | L | pc.code() * B16; +// ldr rd, [pc, #offset] +const Instr kLdrPCMask = CondMask | 15 * B24 | 7 * B20 | 15 * B16; +const Instr kLdrPCPattern = al | 5 * B24 | L | pc.code() * B16; +// blxcc rm +const Instr kBlxRegMask = + 15 * B24 | 15 * B20 | 15 * B16 | 15 * B12 | 15 * B8 | 15 * B4; +const Instr kBlxRegPattern = + B24 | B21 | 15 * B16 | 15 * B12 | 15 * B8 | 3 * B4; // Spare buffer. static const int kMinimalBufferSize = 4*KB; diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index c972c57b5d..539a6b8990 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -509,7 +509,10 @@ typedef int32_t Instr; extern const Instr kMovLrPc; +extern const Instr kLdrPCMask; extern const Instr kLdrPCPattern; +extern const Instr kBlxRegMask; +extern const Instr kBlxRegPattern; class Assembler : public Malloced { @@ -590,12 +593,34 @@ class Assembler : public Malloced { static const int kInstrSize = sizeof(Instr); // Distance between the instruction referring to the address of the call - // target (ldr pc, [target addr in const pool]) and the return address + // target and the return address. +#ifdef USE_BLX + // Call sequence is: + // ldr ip, [pc, #...] @ call address + // blx ip + // @ return address + static const int kCallTargetAddressOffset = 2 * kInstrSize; +#else + // Call sequence is: + // mov lr, pc + // ldr pc, [pc, #...] @ call address + // @ return address static const int kCallTargetAddressOffset = kInstrSize; +#endif // Distance between start of patched return sequence and the emitted address // to jump to. - static const int kPatchReturnSequenceAddressOffset = kInstrSize; +#ifdef USE_BLX + // Return sequence is: + // ldr ip, [pc, #0] @ emited address and start + // blx ip + static const int kPatchReturnSequenceAddressOffset = 0 * kInstrSize; +#else + // Return sequence is: + // mov lr, pc @ start of sequence + // ldr pc, [pc, #-4] @ emited address + static const int kPatchReturnSequenceAddressOffset = kInstrSize; +#endif // Difference between address of current opcode and value read from pc // register. diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h index 8a32c95b68..2b883f3b34 100644 --- a/deps/v8/src/arm/constants-arm.h +++ b/deps/v8/src/arm/constants-arm.h @@ -72,6 +72,11 @@ # define CAN_USE_THUMB_INSTRUCTIONS 1 #endif +// Using blx may yield better code, so use it when required or when available +#if defined(USE_THUMB_INTERWORK) || defined(CAN_USE_ARMV5_INSTRUCTIONS) +#define USE_BLX 1 +#endif + namespace assembler { namespace arm { diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc index e6b61b4d2d..bc81b19d21 100644 --- a/deps/v8/src/arm/debug-arm.cc +++ b/deps/v8/src/arm/debug-arm.cc @@ -46,13 +46,23 @@ void BreakLocationIterator::SetDebugBreakAtReturn() { // add sp, sp, #4 // bx lr // to a call to the debug break return code. + // #if USE_BLX + // ldr ip, [pc, #0] + // blx ip + // #else // mov lr, pc // ldr pc, [pc, #-4] + // #endif // // bktp 0 CodePatcher patcher(rinfo()->pc(), 4); +#ifdef USE_BLX + patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0)); + patcher.masm()->blx(v8::internal::ip); +#else patcher.masm()->mov(v8::internal::lr, v8::internal::pc); patcher.masm()->ldr(v8::internal::pc, MemOperand(v8::internal::pc, -4)); +#endif patcher.Emit(Debug::debug_break_return()->entry()); patcher.masm()->bkpt(0); } diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index bc779eb8de..691c08c4b6 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -58,11 +58,6 @@ MacroAssembler::MacroAssembler(void* buffer, int size) #endif -// Using blx may yield better code, so use it when required or when available -#if defined(USE_THUMB_INTERWORK) || defined(CAN_USE_ARMV5_INSTRUCTIONS) -#define USE_BLX 1 -#endif - // Using bx does not yield better code, so use it only when required #if defined(USE_THUMB_INTERWORK) #define USE_BX 1 @@ -117,16 +112,33 @@ void MacroAssembler::Call(Register target, Condition cond) { void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode, Condition cond) { +#if USE_BLX + // On ARMv5 and after the recommended call sequence is: + // ldr ip, [pc, #...] + // blx ip + + // The two instructions (ldr and blx) could be separated by a literal + // pool and the code would still work. The issue comes from the + // patching code which expect the ldr to be just above the blx. + BlockConstPoolFor(2); + // Statement positions are expected to be recorded when the target + // address is loaded. The mov method will automatically record + // positions when pc is the target, since this is not the case here + // we have to do it explicitly. + WriteRecordedPositions(); + + mov(ip, Operand(target, rmode), LeaveCC, cond); + blx(ip, cond); + + ASSERT(kCallTargetAddressOffset == 2 * kInstrSize); +#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); - // 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 - // instruction rather than the second one, which would make it harder to patch - // (two instructions before the return address, instead of one). + ASSERT(kCallTargetAddressOffset == kInstrSize); +#endif } diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 5d9e51304a..8c70d95873 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -415,7 +415,7 @@ class MacroAssembler: public Assembler { Register object2, Register scratch1, Register scratch2, - Label *failure); + Label* failure); // Checks if both objects are sequential ASCII strings and jumps to label // if either is not. diff --git a/deps/v8/src/arm/regexp-macro-assembler-arm.cc b/deps/v8/src/arm/regexp-macro-assembler-arm.cc index f621be47be..13d464d176 100644 --- a/deps/v8/src/arm/regexp-macro-assembler-arm.cc +++ b/deps/v8/src/arm/regexp-macro-assembler-arm.cc @@ -648,16 +648,17 @@ Handle RegExpMacroAssemblerARM::GetCode(Handle source) { __ ldr(r0, MemOperand(frame_pointer(), kInputStart)); // Find negative length (offset of start relative to end). __ sub(current_input_offset(), r0, end_of_input_address()); - // Set r0 to address of char before start of input + // Set r0 to address of char before start of the input string // (effectively string position -1). + __ ldr(r1, MemOperand(frame_pointer(), kStartIndex)); __ sub(r0, current_input_offset(), Operand(char_size())); + __ sub(r0, r0, Operand(r1, LSL, (mode_ == UC16) ? 1 : 0)); // Store this value in a local variable, for use when clearing // position registers. __ str(r0, MemOperand(frame_pointer(), kInputStartMinusOne)); // Determine whether the start index is zero, that is at the start of the // string, and store that value in a local variable. - __ ldr(r1, MemOperand(frame_pointer(), kStartIndex)); __ tst(r1, Operand(r1)); __ mov(r1, Operand(1), LeaveCC, eq); __ mov(r1, Operand(0), LeaveCC, ne); @@ -700,12 +701,15 @@ Handle RegExpMacroAssemblerARM::GetCode(Handle source) { // copy captures to output __ ldr(r1, MemOperand(frame_pointer(), kInputStart)); __ ldr(r0, MemOperand(frame_pointer(), kRegisterOutput)); + __ ldr(r2, MemOperand(frame_pointer(), kStartIndex)); __ sub(r1, end_of_input_address(), r1); // r1 is length of input in bytes. if (mode_ == UC16) { __ mov(r1, Operand(r1, LSR, 1)); } // r1 is length of input in characters. + __ add(r1, r1, Operand(r2)); + // r1 is length of string in characters. ASSERT_EQ(0, num_saved_registers_ % 2); // Always an even number of capture registers. This allows us to diff --git a/deps/v8/src/arm/virtual-frame-arm.h b/deps/v8/src/arm/virtual-frame-arm.h index 7375b31833..9ac7a05480 100644 --- a/deps/v8/src/arm/virtual-frame-arm.h +++ b/deps/v8/src/arm/virtual-frame-arm.h @@ -365,6 +365,7 @@ class VirtualFrame : public ZoneObject { inline void Nip(int num_dropped); inline void SetTypeForLocalAt(int index, NumberInfo info); + inline void SetTypeForParamAt(int index, NumberInfo info); private: static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset; diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js index 914b82a80a..a29015a5ef 100644 --- a/deps/v8/src/array.js +++ b/deps/v8/src/array.js @@ -994,11 +994,16 @@ function ArrayIndexOf(element, index) { // If index is still negative, search the entire array. if (index < 0) index = 0; } + if (!IS_UNDEFINED(element)) { + for (var i = index; i < length; i++) { + if (this[i] === element) return i; + } + return -1; + } // Lookup through the array. for (var i = index; i < length; i++) { - var current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { - if (current === element) return i; + if (IS_UNDEFINED(this[i]) && i in this) { + return i; } } return -1; @@ -1018,10 +1023,15 @@ function ArrayLastIndexOf(element, index) { else if (index >= length) index = length - 1; } // Lookup through the array. + if (!IS_UNDEFINED(element)) { + for (var i = index; i >= 0; i--) { + if (this[i] === element) return i; + } + return -1; + } for (var i = index; i >= 0; i--) { - var current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { - if (current === element) return i; + if (IS_UNDEFINED(this[i]) && i in this) { + return i; } } return -1; diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index 262bf17c73..8248f62a8f 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -196,10 +196,7 @@ class Expression: public AstNode { kTestValue }; - Expression() - : bitfields_(0), - def_(NULL), - defined_vars_(NULL) {} + Expression() : bitfields_(0) {} virtual Expression* AsExpression() { return this; } @@ -233,15 +230,6 @@ class Expression: public AstNode { // Static type information for this expression. StaticType* type() { return &type_; } - // Data flow information. - DefinitionInfo* var_def() { return def_; } - void set_var_def(DefinitionInfo* def) { def_ = def; } - - ZoneList* defined_vars() { return defined_vars_; } - void set_defined_vars(ZoneList* defined_vars) { - defined_vars_ = defined_vars; - } - // AST analysis results // True if the expression rooted at this node can be compiled by the @@ -284,9 +272,6 @@ class Expression: public AstNode { uint32_t bitfields_; StaticType type_; - DefinitionInfo* def_; - ZoneList* defined_vars_; - // Using template BitField. class SideEffectFreeField : public BitField {}; class NoNegativeZeroField : public BitField {}; diff --git a/deps/v8/src/cached-powers.h b/deps/v8/src/cached-powers.h new file mode 100644 index 0000000000..314ccca3c4 --- /dev/null +++ b/deps/v8/src/cached-powers.h @@ -0,0 +1,119 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_CACHED_POWERS_H_ +#define V8_CACHED_POWERS_H_ + +#include "diy-fp.h" + +namespace v8 { +namespace internal { + +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; + +// The following defines implement the interface between this file and the +// generated 'powers_ten.h'. +// GRISU_CACHE_NAME(1) contains all possible cached powers. +// GRISU_CACHE_NAME(i) contains GRISU_CACHE_NAME(1) where only every 'i'th +// element is kept. More formally GRISU_CACHE_NAME(i) contains the elements j*i +// with 0 <= j < k with k such that j*k < the size of GRISU_CACHE_NAME(1). +// The higher 'i' is the fewer elements we use. +// Given that there are less elements, the exponent-distance between two +// elements in the cache grows. The variable GRISU_CACHE_MAX_DISTANCE(i) stores +// the maximum distance between two elements. +#define GRISU_CACHE_STRUCT CachedPower +#define GRISU_CACHE_NAME(i) kCachedPowers##i +#define GRISU_CACHE_MAX_DISTANCE(i) kCachedPowersMaxDistance##i +#define GRISU_CACHE_OFFSET kCachedPowerOffset +#define GRISU_UINT64_C V8_2PART_UINT64_C +// The following include imports the precompiled cached powers. +#include "powers-ten.h" // NOLINT + +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) + +// We can't use a function since we reference variables depending on the 'i'. +// This way the compiler is able to see at compile time that only one +// cache-array variable is used and thus can remove all the others. +#define COMPUTE_FOR_CACHE(i) \ + if (!found && (gamma - alpha + 1 >= GRISU_CACHE_MAX_DISTANCE(i))) { \ + int kQ = DiyFp::kSignificandSize; \ + double k = ceiling((alpha - e + kQ - 1) * kD_1_LOG2_10); \ + int index = (GRISU_CACHE_OFFSET + static_cast(k) - 1) / i + 1; \ + cached_power = GRISU_CACHE_NAME(i)[index]; \ + found = true; \ + } \ + +static void GetCachedPower(int e, int alpha, int gamma, int* mk, DiyFp* c_mk) { + // The following if statement should be optimized by the compiler so that only + // one array is referenced and the others are not included in the object file. + bool found = false; + CachedPower cached_power; + COMPUTE_FOR_CACHE(20); + COMPUTE_FOR_CACHE(19); + COMPUTE_FOR_CACHE(18); + COMPUTE_FOR_CACHE(17); + COMPUTE_FOR_CACHE(16); + COMPUTE_FOR_CACHE(15); + COMPUTE_FOR_CACHE(14); + COMPUTE_FOR_CACHE(13); + COMPUTE_FOR_CACHE(12); + COMPUTE_FOR_CACHE(11); + COMPUTE_FOR_CACHE(10); + COMPUTE_FOR_CACHE(9); + COMPUTE_FOR_CACHE(8); + COMPUTE_FOR_CACHE(7); + COMPUTE_FOR_CACHE(6); + COMPUTE_FOR_CACHE(5); + COMPUTE_FOR_CACHE(4); + COMPUTE_FOR_CACHE(3); + COMPUTE_FOR_CACHE(2); + COMPUTE_FOR_CACHE(1); + if (!found) { + UNIMPLEMENTED(); + // Silence compiler warnings. + cached_power.significand = 0; + cached_power.binary_exponent = 0; + cached_power.decimal_exponent = 0; + } + *c_mk = DiyFp(cached_power.significand, cached_power.binary_exponent); + *mk = cached_power.decimal_exponent; + ASSERT((alpha <= c_mk->e() + e) && (c_mk->e() + e <= gamma)); +} +#undef GRISU_REDUCTION +#undef GRISU_CACHE_STRUCT +#undef GRISU_CACHE_NAME +#undef GRISU_CACHE_MAX_DISTANCE +#undef GRISU_CACHE_OFFSET +#undef GRISU_UINT64_C + +} } // namespace v8::internal + +#endif // V8_CACHED_POWERS_H_ diff --git a/deps/v8/src/checks.h b/deps/v8/src/checks.h index eeb748b4a8..cdcd18ad22 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -80,6 +80,7 @@ static inline void CheckEqualsHelper(const char* file, int line, } } + // Helper function used by the CHECK_EQ function when given int64_t // arguments. Should not be called directly. static inline void CheckEqualsHelper(const char* file, int line, @@ -202,6 +203,27 @@ static inline void CheckEqualsHelper(const char* file, } +static inline void CheckNonEqualsHelper(const char* file, + int line, + const char* expected_source, + double expected, + const char* value_source, + double value) { + // Force values to 64 bit memory to truncate 80 bit precision on IA32. + volatile double* exp = new double[1]; + *exp = expected; + volatile double* val = new double[1]; + *val = value; + if (*exp == *val) { + V8_Fatal(file, line, + "CHECK_NE(%s, %s) failed\n# Value: %f", + expected_source, value_source, *val); + } + delete[] exp; + delete[] val; +} + + namespace v8 { class Value; template class Handle; diff --git a/deps/v8/src/circular-queue-inl.h b/deps/v8/src/circular-queue-inl.h new file mode 100644 index 0000000000..ffe8fb003e --- /dev/null +++ b/deps/v8/src/circular-queue-inl.h @@ -0,0 +1,101 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_CIRCULAR_BUFFER_INL_H_ +#define V8_CIRCULAR_BUFFER_INL_H_ + +#include "circular-queue.h" + +namespace v8 { +namespace internal { + + +template +CircularQueue::CircularQueue(int desired_buffer_size_in_bytes) + : buffer_(NewArray(desired_buffer_size_in_bytes / sizeof(Record))), + buffer_end_(buffer_ + desired_buffer_size_in_bytes / sizeof(Record)), + enqueue_semaphore_(OS::CreateSemaphore((buffer_end_ - buffer_) - 1)), + enqueue_pos_(buffer_), + dequeue_pos_(buffer_) { + // To be able to distinguish between a full and an empty queue + // state, the queue must be capable of containing at least 2 + // records. + ASSERT((buffer_end_ - buffer_) >= 2); +} + + +template +CircularQueue::~CircularQueue() { + DeleteArray(buffer_); + delete enqueue_semaphore_; +} + + +template +void CircularQueue::Dequeue(Record* rec) { + ASSERT(!IsEmpty()); + *rec = *dequeue_pos_; + dequeue_pos_ = Next(dequeue_pos_); + // Tell we have a spare record. + enqueue_semaphore_->Signal(); +} + + +template +void CircularQueue::Enqueue(const Record& rec) { + // Wait until we have at least one spare record. + enqueue_semaphore_->Wait(); + ASSERT(Next(enqueue_pos_) != dequeue_pos_); + *enqueue_pos_ = rec; + enqueue_pos_ = Next(enqueue_pos_); +} + + +template +Record* CircularQueue::Next(Record* curr) { + return ++curr != buffer_end_ ? curr : buffer_; +} + + +void* SamplingCircularQueue::Enqueue() { + Cell* enqueue_pos = reinterpret_cast( + Thread::GetThreadLocal(producer_key_)); + WrapPositionIfNeeded(&enqueue_pos); + Thread::SetThreadLocal(producer_key_, enqueue_pos + record_size_); + return enqueue_pos; +} + + +void SamplingCircularQueue::WrapPositionIfNeeded( + SamplingCircularQueue::Cell** pos) { + if (**pos == kEnd) *pos = buffer_; +} + + +} } // namespace v8::internal + +#endif // V8_CIRCULAR_BUFFER_INL_H_ diff --git a/deps/v8/src/circular-queue.cc b/deps/v8/src/circular-queue.cc new file mode 100644 index 0000000000..5f7a33eb3a --- /dev/null +++ b/deps/v8/src/circular-queue.cc @@ -0,0 +1,131 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "circular-queue-inl.h" + +namespace v8 { +namespace internal { + + +SamplingCircularQueue::SamplingCircularQueue(int record_size_in_bytes, + int desired_chunk_size_in_bytes, + int buffer_size_in_chunks) + : record_size_(record_size_in_bytes / sizeof(Cell)), + chunk_size_in_bytes_(desired_chunk_size_in_bytes / record_size_in_bytes * + record_size_in_bytes), + chunk_size_(chunk_size_in_bytes_ / sizeof(Cell)), + buffer_size_(chunk_size_ * buffer_size_in_chunks), + // The distance ensures that producer and consumer never step on + // each other's chunks and helps eviction of produced data from + // the CPU cache (having that chunk size is bigger than the cache.) + producer_consumer_distance_(2 * chunk_size_), + buffer_(NewArray(buffer_size_ + 1)) { + ASSERT(buffer_size_in_chunks > 2); + // Only need to keep the first cell of a chunk clean. + for (int i = 0; i < buffer_size_; i += chunk_size_) { + buffer_[i] = kClear; + } + buffer_[buffer_size_] = kEnd; +} + + +SamplingCircularQueue::~SamplingCircularQueue() { + DeleteArray(buffer_); +} + + +void SamplingCircularQueue::SetUpProducer() { + producer_key_ = Thread::CreateThreadLocalKey(); + Thread::SetThreadLocal(producer_key_, buffer_); +} + + +void SamplingCircularQueue::TearDownProducer() { + Thread::DeleteThreadLocalKey(producer_key_); +} + + +void SamplingCircularQueue::SetUpConsumer() { + consumer_key_ = Thread::CreateThreadLocalKey(); + ConsumerPosition* cp = new ConsumerPosition; + cp->dequeue_chunk_pos = buffer_; + cp->dequeue_chunk_poll_pos = buffer_ + producer_consumer_distance_; + cp->dequeue_pos = NULL; + Thread::SetThreadLocal(consumer_key_, cp); +} + + +void SamplingCircularQueue::TearDownConsumer() { + delete reinterpret_cast( + Thread::GetThreadLocal(consumer_key_)); + Thread::DeleteThreadLocalKey(consumer_key_); +} + + +void* SamplingCircularQueue::StartDequeue() { + ConsumerPosition* cp = reinterpret_cast( + Thread::GetThreadLocal(consumer_key_)); + if (cp->dequeue_pos != NULL) { + return cp->dequeue_pos; + } else { + if (*cp->dequeue_chunk_poll_pos != kClear) { + cp->dequeue_pos = cp->dequeue_chunk_pos; + cp->dequeue_end_pos = cp->dequeue_pos + chunk_size_; + return cp->dequeue_pos; + } else { + return NULL; + } + } +} + + +void SamplingCircularQueue::FinishDequeue() { + ConsumerPosition* cp = reinterpret_cast( + Thread::GetThreadLocal(consumer_key_)); + cp->dequeue_pos += record_size_; + if (cp->dequeue_pos < cp->dequeue_end_pos) return; + // Move to next chunk. + cp->dequeue_pos = NULL; + *cp->dequeue_chunk_pos = kClear; + cp->dequeue_chunk_pos += chunk_size_; + WrapPositionIfNeeded(&cp->dequeue_chunk_pos); + cp->dequeue_chunk_poll_pos += chunk_size_; + WrapPositionIfNeeded(&cp->dequeue_chunk_poll_pos); +} + + +void SamplingCircularQueue::FlushResidualRecords() { + ConsumerPosition* cp = reinterpret_cast( + Thread::GetThreadLocal(consumer_key_)); + // Eliminate producer / consumer distance. + cp->dequeue_chunk_poll_pos = cp->dequeue_chunk_pos; +} + + +} } // namespace v8::internal diff --git a/deps/v8/src/circular-queue.h b/deps/v8/src/circular-queue.h new file mode 100644 index 0000000000..11159e0388 --- /dev/null +++ b/deps/v8/src/circular-queue.h @@ -0,0 +1,130 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_CIRCULAR_QUEUE_H_ +#define V8_CIRCULAR_QUEUE_H_ + +namespace v8 { +namespace internal { + + +// Lock-based blocking circular queue for small records. Intended for +// transfer of small records between a single producer and a single +// consumer. Blocks on enqueue operation if the queue is full. +template +class CircularQueue { + public: + inline explicit CircularQueue(int desired_buffer_size_in_bytes); + inline ~CircularQueue(); + + INLINE(void Dequeue(Record* rec)); + INLINE(void Enqueue(const Record& rec)); + INLINE(bool IsEmpty()) { return enqueue_pos_ == dequeue_pos_; } + + private: + INLINE(Record* Next(Record* curr)); + + Record* buffer_; + Record* const buffer_end_; + Semaphore* enqueue_semaphore_; + Record* enqueue_pos_; + Record* dequeue_pos_; + + DISALLOW_COPY_AND_ASSIGN(CircularQueue); +}; + + +// Lock-free cache-friendly sampling circular queue for large +// records. Intended for fast transfer of large records between a +// single producer and a single consumer. If the queue is full, +// previous unread records are overwritten. The queue is designed with +// a goal in mind to evade cache lines thrashing by preventing +// simultaneous reads and writes to adjanced memory locations. +// +// IMPORTANT: as a producer never checks for chunks cleanness, it is +// possible that it can catch up and overwrite a chunk that a consumer +// is currently reading, resulting in a corrupt record being read. +class SamplingCircularQueue { + public: + // Executed on the application thread. + SamplingCircularQueue(int record_size_in_bytes, + int desired_chunk_size_in_bytes, + int buffer_size_in_chunks); + ~SamplingCircularQueue(); + + // Executed on the producer (sampler) or application thread. + void SetUpProducer(); + // Enqueue returns a pointer to a memory location for storing the next + // record. + INLINE(void* Enqueue()); + void TearDownProducer(); + + // Executed on the consumer (analyzer) thread. + void SetUpConsumer(); + // StartDequeue returns a pointer to a memory location for retrieving + // the next record. After the record had been read by a consumer, + // FinishDequeue must be called. Until that moment, subsequent calls + // to StartDequeue will return the same pointer. + void* StartDequeue(); + void FinishDequeue(); + // Due to a presence of slipping between the producer and the consumer, + // the queue must be notified whether producing has been finished in order + // to process remaining records from the buffer. + void FlushResidualRecords(); + void TearDownConsumer(); + + typedef AtomicWord Cell; + // Reserved values for the first cell of a record. + static const Cell kClear = 0; // Marks clean (processed) chunks. + static const Cell kEnd = -1; // Marks the end of the buffer. + + private: + struct ConsumerPosition { + Cell* dequeue_chunk_pos; + Cell* dequeue_chunk_poll_pos; + Cell* dequeue_pos; + Cell* dequeue_end_pos; + }; + + INLINE(void WrapPositionIfNeeded(Cell** pos)); + + const int record_size_; + const int chunk_size_in_bytes_; + const int chunk_size_; + const int buffer_size_; + const int producer_consumer_distance_; + Cell* buffer_; + // Store producer and consumer data in TLS to avoid modifying the + // same CPU cache line from two threads simultaneously. + Thread::LocalStorageKey consumer_key_; + Thread::LocalStorageKey producer_key_; +}; + + +} } // namespace v8::internal + +#endif // V8_CIRCULAR_QUEUE_H_ diff --git a/deps/v8/src/conversions.cc b/deps/v8/src/conversions.cc index fd6d38d84d..d0abc2b312 100644 --- a/deps/v8/src/conversions.cc +++ b/deps/v8/src/conversions.cc @@ -31,6 +31,7 @@ #include "conversions-inl.h" #include "factory.h" +#include "fast-dtoa.h" #include "scanner.h" namespace v8 { @@ -382,8 +383,17 @@ const char* DoubleToCString(double v, Vector buffer) { int decimal_point; int sign; - char* decimal_rep = dtoa(v, 0, 0, &decimal_point, &sign, NULL); - int length = StrLength(decimal_rep); + char* decimal_rep; + bool used_gay_dtoa = false; + char fast_dtoa_buffer[kFastDtoaMaximalLength + 1]; + int length; + if (FastDtoa(v, fast_dtoa_buffer, &sign, &length, &decimal_point)) { + decimal_rep = fast_dtoa_buffer; + } else { + decimal_rep = dtoa(v, 0, 0, &decimal_point, &sign, NULL); + used_gay_dtoa = true; + length = StrLength(decimal_rep); + } if (sign) builder.AddCharacter('-'); @@ -418,7 +428,7 @@ const char* DoubleToCString(double v, Vector buffer) { builder.AddFormatted("%d", exponent); } - freedtoa(decimal_rep); + if (used_gay_dtoa) freedtoa(decimal_rep); } } return builder.Finalize(); diff --git a/deps/v8/src/counters.h b/deps/v8/src/counters.h index 5f4dca927e..aed46cfb36 100644 --- a/deps/v8/src/counters.h +++ b/deps/v8/src/counters.h @@ -65,7 +65,7 @@ class StatsTable : public AllStatic { // may receive a different location to store it's counter. // The return value must not be cached and re-used across // threads, although a single thread is free to cache it. - static int *FindLocation(const char* name) { + static int* FindLocation(const char* name) { if (!lookup_function_) return NULL; return lookup_function_(name); } diff --git a/deps/v8/src/cpu-profiler-inl.h b/deps/v8/src/cpu-profiler-inl.h new file mode 100644 index 0000000000..26ab643c3a --- /dev/null +++ b/deps/v8/src/cpu-profiler-inl.h @@ -0,0 +1,50 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_CPU_PROFILER_INL_H_ +#define V8_CPU_PROFILER_INL_H_ + +#include "circular-queue-inl.h" +#include "profile-generator-inl.h" + +#include "cpu-profiler.h" + +namespace v8 { +namespace internal { + + +TickSample* ProfilerEventsProcessor::TickSampleEvent() { + TickSampleEventRecord* evt = + reinterpret_cast(ticks_buffer_.Enqueue()); + evt->order = enqueue_order_; // No increment! + return &evt->sample; +} + + +} } // namespace v8::internal + +#endif // V8_CPU_PROFILER_INL_H_ diff --git a/deps/v8/src/cpu-profiler.cc b/deps/v8/src/cpu-profiler.cc new file mode 100644 index 0000000000..d36f511209 --- /dev/null +++ b/deps/v8/src/cpu-profiler.cc @@ -0,0 +1,201 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "cpu-profiler-inl.h" + +namespace v8 { +namespace internal { + + +static const int kEventsBufferSize = 256*KB; +static const int kTickSamplesBufferChunkSize = 64*KB; +static const int kTickSamplesBufferChunksCount = 16; + + +ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator) + : generator_(generator), + running_(false), + events_buffer_(kEventsBufferSize), + ticks_buffer_(sizeof(TickSampleEventRecord), + kTickSamplesBufferChunkSize, + kTickSamplesBufferChunksCount), + enqueue_order_(0) { } + + +void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag, + String* name, + String* resource_name, + int line_number, + Address start, + unsigned size) { + CodeEventsContainer evt_rec; + CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; + rec->type = CodeEventRecord::CODE_CREATION; + rec->order = ++enqueue_order_; + rec->start = start; + rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number); + rec->size = size; + events_buffer_.Enqueue(evt_rec); +} + + +void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag, + const char* name, + Address start, + unsigned size) { + CodeEventsContainer evt_rec; + CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; + rec->type = CodeEventRecord::CODE_CREATION; + rec->order = ++enqueue_order_; + rec->start = start; + rec->entry = generator_->NewCodeEntry(tag, name); + rec->size = size; + events_buffer_.Enqueue(evt_rec); +} + + +void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag, + int args_count, + Address start, + unsigned size) { + CodeEventsContainer evt_rec; + CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; + rec->type = CodeEventRecord::CODE_CREATION; + rec->order = ++enqueue_order_; + rec->start = start; + rec->entry = generator_->NewCodeEntry(tag, args_count); + rec->size = size; + events_buffer_.Enqueue(evt_rec); +} + + +void ProfilerEventsProcessor::CodeMoveEvent(Address from, Address to) { + CodeEventsContainer evt_rec; + CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_; + rec->type = CodeEventRecord::CODE_MOVE; + rec->order = ++enqueue_order_; + rec->from = from; + rec->to = to; + events_buffer_.Enqueue(evt_rec); +} + + +void ProfilerEventsProcessor::CodeDeleteEvent(Address from) { + CodeEventsContainer evt_rec; + CodeDeleteEventRecord* rec = &evt_rec.CodeDeleteEventRecord_; + rec->type = CodeEventRecord::CODE_DELETE; + rec->order = ++enqueue_order_; + rec->start = from; + events_buffer_.Enqueue(evt_rec); +} + + +void ProfilerEventsProcessor::FunctionCreateEvent(Address alias, + Address start) { + CodeEventsContainer evt_rec; + CodeAliasEventRecord* rec = &evt_rec.CodeAliasEventRecord_; + rec->type = CodeEventRecord::CODE_ALIAS; + rec->order = ++enqueue_order_; + rec->alias = alias; + rec->start = start; + events_buffer_.Enqueue(evt_rec); +} + + +void ProfilerEventsProcessor::FunctionMoveEvent(Address from, Address to) { + CodeMoveEvent(from, to); +} + + +void ProfilerEventsProcessor::FunctionDeleteEvent(Address from) { + CodeDeleteEvent(from); +} + + +bool ProfilerEventsProcessor::ProcessCodeEvent(unsigned* dequeue_order) { + if (!events_buffer_.IsEmpty()) { + CodeEventsContainer record; + events_buffer_.Dequeue(&record); + switch (record.generic.type) { +#define PROFILER_TYPE_CASE(type, clss) \ + case CodeEventRecord::type: \ + record.clss##_.UpdateCodeMap(generator_->code_map()); \ + break; + + CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE) + +#undef PROFILER_TYPE_CASE + default: return true; // Skip record. + } + *dequeue_order = record.generic.order; + return true; + } + return false; +} + + +bool ProfilerEventsProcessor::ProcessTicks(unsigned dequeue_order) { + while (true) { + const TickSampleEventRecord* rec = + reinterpret_cast(ticks_buffer_.StartDequeue()); + if (rec == NULL) return false; + if (rec->order == dequeue_order) { + generator_->RecordTickSample(rec->sample); + ticks_buffer_.FinishDequeue(); + } else { + return true; + } + } +} + + +void ProfilerEventsProcessor::Run() { + ticks_buffer_.SetUpConsumer(); + unsigned dequeue_order = 0; + running_ = true; + + while (running_) { + // Process ticks until we have any. + if (ProcessTicks(dequeue_order)) { + // All ticks of the current dequeue_order are processed, + // proceed to the next code event. + ProcessCodeEvent(&dequeue_order); + } + YieldCPU(); + } + + // Process remaining tick events. + ticks_buffer_.FlushResidualRecords(); + // Perform processing until we have tick events, skip remaining code events. + while (ProcessTicks(dequeue_order) && ProcessCodeEvent(&dequeue_order)) { } + ticks_buffer_.TearDownConsumer(); +} + + +} } // namespace v8::internal diff --git a/deps/v8/src/cpu-profiler.h b/deps/v8/src/cpu-profiler.h new file mode 100644 index 0000000000..ccfac5c5c7 --- /dev/null +++ b/deps/v8/src/cpu-profiler.h @@ -0,0 +1,188 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_CPU_PROFILER_H_ +#define V8_CPU_PROFILER_H_ + +#include "circular-queue.h" +#include "profile-generator.h" + +namespace v8 { +namespace internal { + + +#define CODE_EVENTS_TYPE_LIST(V) \ + V(CODE_CREATION, CodeCreateEventRecord) \ + V(CODE_MOVE, CodeMoveEventRecord) \ + V(CODE_DELETE, CodeDeleteEventRecord) \ + V(CODE_ALIAS, CodeAliasEventRecord) + + +class CodeEventRecord { + public: +#define DECLARE_TYPE(type, ignore) type, + enum Type { + NONE = 0, + CODE_EVENTS_TYPE_LIST(DECLARE_TYPE) + NUMBER_OF_TYPES + }; +#undef DECLARE_TYPE + + Type type; + unsigned order; +}; + + +class CodeCreateEventRecord : public CodeEventRecord { + public: + Address start; + CodeEntry* entry; + unsigned size; + + INLINE(void UpdateCodeMap(CodeMap* code_map)) { + code_map->AddCode(start, entry, size); + } +}; + + +class CodeMoveEventRecord : public CodeEventRecord { + public: + Address from; + Address to; + + INLINE(void UpdateCodeMap(CodeMap* code_map)) { + code_map->MoveCode(from, to); + } +}; + + +class CodeDeleteEventRecord : public CodeEventRecord { + public: + Address start; + + INLINE(void UpdateCodeMap(CodeMap* code_map)) { + code_map->DeleteCode(start); + } +}; + + +class CodeAliasEventRecord : public CodeEventRecord { + public: + Address alias; + Address start; + + INLINE(void UpdateCodeMap(CodeMap* code_map)) { + code_map->AddAlias(alias, start); + } +}; + + +class TickSampleEventRecord { + public: + // In memory, the first machine word of a TickSampleEventRecord will be the + // first entry of TickSample, that is -- a program counter field. + // TickSample is put first, because 'order' can become equal to + // SamplingCircularQueue::kClear, while program counter can't. + TickSample sample; + unsigned order; + +#if defined(__GNUC__) && (__GNUC__ < 4) + // Added to avoid 'all member functions in class are private' warning. + INLINE(unsigned get_order() const) { return order; } + // Added to avoid 'class only defines private constructors and + // has no friends' warning. + friend class TickSampleEventRecordFriend; +#endif + private: + // Disable instantiation. + TickSampleEventRecord(); + + DISALLOW_COPY_AND_ASSIGN(TickSampleEventRecord); +}; + + +// This class implements both the profile events processor thread and +// methods called by event producers: VM and stack sampler threads. +class ProfilerEventsProcessor : public Thread { + public: + explicit ProfilerEventsProcessor(ProfileGenerator* generator); + virtual ~ProfilerEventsProcessor() { } + + // Thread control. + virtual void Run(); + inline void Stop() { running_ = false; } + INLINE(bool running()) { return running_; } + + // Events adding methods. Called by VM threads. + void CodeCreateEvent(Logger::LogEventsAndTags tag, + String* name, + String* resource_name, int line_number, + Address start, unsigned size); + void CodeCreateEvent(Logger::LogEventsAndTags tag, + const char* name, + Address start, unsigned size); + void CodeCreateEvent(Logger::LogEventsAndTags tag, + int args_count, + Address start, unsigned size); + void CodeMoveEvent(Address from, Address to); + void CodeDeleteEvent(Address from); + void FunctionCreateEvent(Address alias, Address start); + void FunctionMoveEvent(Address from, Address to); + void FunctionDeleteEvent(Address from); + + // Tick sampler registration. Called by sampler thread or signal handler. + inline void SetUpSamplesProducer() { ticks_buffer_.SetUpProducer(); } + // Tick sample events are filled directly in the buffer of the circular + // queue (because the structure is of fixed width, but usually not all + // stack frame entries are filled.) This method returns a pointer to the + // next record of the buffer. + INLINE(TickSample* TickSampleEvent()); + inline void TearDownSamplesProducer() { ticks_buffer_.TearDownProducer(); } + + private: + union CodeEventsContainer { + CodeEventRecord generic; +#define DECLARE_CLASS(ignore, type) type type##_; + CODE_EVENTS_TYPE_LIST(DECLARE_CLASS) +#undef DECLARE_TYPE + }; + + // Called from events processing thread (Run() method.) + bool ProcessCodeEvent(unsigned* dequeue_order); + bool ProcessTicks(unsigned dequeue_order); + + ProfileGenerator* generator_; + bool running_; + CircularQueue events_buffer_; + SamplingCircularQueue ticks_buffer_; + unsigned enqueue_order_; +}; + + +} } // namespace v8::internal + +#endif // V8_CPU_PROFILER_H_ diff --git a/deps/v8/src/data-flow.cc b/deps/v8/src/data-flow.cc index 278b82bc63..141718dc86 100644 --- a/deps/v8/src/data-flow.cc +++ b/deps/v8/src/data-flow.cc @@ -770,293 +770,6 @@ void AstLabeler::VisitDeclaration(Declaration* decl) { } -ZoneList* VarUseMap::Lookup(Variable* var) { - HashMap::Entry* entry = HashMap::Lookup(var, var->name()->Hash(), true); - if (entry->value == NULL) { - entry->value = new ZoneList(1); - } - return reinterpret_cast*>(entry->value); -} - - -void LivenessAnalyzer::Analyze(FunctionLiteral* fun) { - // Process the function body. - VisitStatements(fun->body()); - - // All variables are implicitly defined at the function start. - // Record a definition of all variables live at function entry. - for (HashMap::Entry* p = live_vars_.Start(); - p != NULL; - p = live_vars_.Next(p)) { - Variable* var = reinterpret_cast(p->key); - RecordDef(var, fun); - } -} - - -void LivenessAnalyzer::VisitStatements(ZoneList* stmts) { - // Visit statements right-to-left. - for (int i = stmts->length() - 1; i >= 0; i--) { - Visit(stmts->at(i)); - } -} - - -void LivenessAnalyzer::RecordUse(Variable* var, Expression* expr) { - ASSERT(var->is_global() || var->is_this()); - ZoneList* uses = live_vars_.Lookup(var); - uses->Add(expr); -} - - -void LivenessAnalyzer::RecordDef(Variable* var, Expression* expr) { - ASSERT(var->is_global() || var->is_this()); - - // We do not support other expressions that can define variables. - ASSERT(expr->AsFunctionLiteral() != NULL); - - // Add the variable to the list of defined variables. - if (expr->defined_vars() == NULL) { - expr->set_defined_vars(new ZoneList(1)); - } - DefinitionInfo* def = new DefinitionInfo(); - expr->AsFunctionLiteral()->defined_vars()->Add(def); - - // Compute the last use of the definition. The variable uses are - // inserted in reversed evaluation order. The first element - // in the list of live uses is the last use. - ZoneList* uses = live_vars_.Lookup(var); - while (uses->length() > 0) { - Expression* use_site = uses->RemoveLast(); - use_site->set_var_def(def); - if (uses->length() == 0) { - def->set_last_use(use_site); - } - } -} - - -// Visitor functions for live variable analysis. -void LivenessAnalyzer::VisitDeclaration(Declaration* decl) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitBlock(Block* stmt) { - VisitStatements(stmt->statements()); -} - - -void LivenessAnalyzer::VisitExpressionStatement( - ExpressionStatement* stmt) { - Visit(stmt->expression()); -} - - -void LivenessAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) { - // Do nothing. -} - - -void LivenessAnalyzer::VisitIfStatement(IfStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitContinueStatement(ContinueStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitBreakStatement(BreakStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitReturnStatement(ReturnStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitWithEnterStatement( - WithEnterStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitWithExitStatement(WithExitStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitWhileStatement(WhileStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitForStatement(ForStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitForInStatement(ForInStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitTryCatchStatement(TryCatchStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitTryFinallyStatement( - TryFinallyStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitDebuggerStatement( - DebuggerStatement* stmt) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitFunctionBoilerplateLiteral( - FunctionBoilerplateLiteral* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitConditional(Conditional* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitSlot(Slot* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitVariableProxy(VariableProxy* expr) { - Variable* var = expr->var(); - ASSERT(var->is_global()); - ASSERT(!var->is_this()); - RecordUse(var, expr); -} - - -void LivenessAnalyzer::VisitLiteral(Literal* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitCatchExtensionObject( - CatchExtensionObject* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitAssignment(Assignment* expr) { - Property* prop = expr->target()->AsProperty(); - ASSERT(prop != NULL); - ASSERT(prop->key()->IsPropertyName()); - VariableProxy* proxy = prop->obj()->AsVariableProxy(); - ASSERT(proxy != NULL && proxy->var()->is_this()); - - // Record use of this at the assignment node. Assignments to - // this-properties are treated like unary operations. - RecordUse(proxy->var(), expr); - - // Visit right-hand side. - Visit(expr->value()); -} - - -void LivenessAnalyzer::VisitThrow(Throw* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitProperty(Property* expr) { - ASSERT(expr->key()->IsPropertyName()); - VariableProxy* proxy = expr->obj()->AsVariableProxy(); - ASSERT(proxy != NULL && proxy->var()->is_this()); - RecordUse(proxy->var(), expr); -} - - -void LivenessAnalyzer::VisitCall(Call* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitCallNew(CallNew* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitCallRuntime(CallRuntime* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitUnaryOperation(UnaryOperation* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitCountOperation(CountOperation* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitBinaryOperation(BinaryOperation* expr) { - // Visit child nodes in reverse evaluation order. - Visit(expr->right()); - Visit(expr->left()); -} - - -void LivenessAnalyzer::VisitCompareOperation(CompareOperation* expr) { - UNREACHABLE(); -} - - -void LivenessAnalyzer::VisitThisFunction(ThisFunction* expr) { - UNREACHABLE(); -} - - AssignedVariablesAnalyzer::AssignedVariablesAnalyzer(FunctionLiteral* fun) : fun_(fun), av_(fun->scope()->num_parameters() + fun->scope()->num_stack_slots()) {} diff --git a/deps/v8/src/data-flow.h b/deps/v8/src/data-flow.h index 97020137a7..74a370c0da 100644 --- a/deps/v8/src/data-flow.h +++ b/deps/v8/src/data-flow.h @@ -550,55 +550,6 @@ class AstLabeler: public AstVisitor { }; -class VarUseMap : public HashMap { - public: - VarUseMap() : HashMap(VarMatch) {} - - ZoneList* Lookup(Variable* var); - - private: - static bool VarMatch(void* key1, void* key2) { return key1 == key2; } -}; - - -class DefinitionInfo : public ZoneObject { - public: - explicit DefinitionInfo() : last_use_(NULL) {} - - Expression* last_use() { return last_use_; } - void set_last_use(Expression* expr) { last_use_ = expr; } - - private: - Expression* last_use_; - Register location_; -}; - - -class LivenessAnalyzer : public AstVisitor { - public: - LivenessAnalyzer() {} - - void Analyze(FunctionLiteral* fun); - - private: - void VisitStatements(ZoneList* stmts); - - void RecordUse(Variable* var, Expression* expr); - void RecordDef(Variable* var, Expression* expr); - - - // AST node visit functions. -#define DECLARE_VISIT(type) virtual void Visit##type(type* node); - AST_NODE_LIST(DECLARE_VISIT) -#undef DECLARE_VISIT - - // Map for tracking the live variables. - VarUseMap live_vars_; - - DISALLOW_COPY_AND_ASSIGN(LivenessAnalyzer); -}; - - // Computes the set of assigned variables and annotates variables proxies // that are trivial sub-expressions and for-loops where the loop variable // is guaranteed to be a smi. diff --git a/deps/v8/src/diy-fp.cc b/deps/v8/src/diy-fp.cc new file mode 100644 index 0000000000..c54bd1d326 --- /dev/null +++ b/deps/v8/src/diy-fp.cc @@ -0,0 +1,58 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "diy-fp.h" + +namespace v8 { +namespace internal { + +void DiyFp::Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFu; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; +} + +} } // namespace v8::internal diff --git a/deps/v8/src/diy-fp.h b/deps/v8/src/diy-fp.h new file mode 100644 index 0000000000..cfe05ef742 --- /dev/null +++ b/deps/v8/src/diy-fp.h @@ -0,0 +1,117 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_DIY_FP_H_ +#define V8_DIY_FP_H_ + +namespace v8 { +namespace internal { + +// This "Do It Yourself Floating Point" class implements a floating-point number +// with a uint64 significand and an int exponent. Normalized DiyFp numbers will +// have the most significant bit of the significand set. +// Multiplication and Subtraction do not normalize their results. +// DiyFp are not designed to contain special doubles (NaN and Infinity). +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + // this = this - other. + // The exponents of both numbers must be the same and the significand of this + // must be bigger than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and this must be bigger + // than other. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + + // this = this * other. + void Multiply(const DiyFp& other); + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + // This method is mainly called for normalizing boundaries. In general + // boundaries need to be shifted by 10 bits. We thus optimize for this case. + const uint64_t k10MSBits = V8_2PART_UINT64_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = V8_2PART_UINT64_C(0x80000000, 00000000); + + uint64_t f_; + int e_; +}; + +} } // namespace v8::internal + +#endif // V8_DIY_FP_H_ diff --git a/deps/v8/src/double.h b/deps/v8/src/double.h new file mode 100644 index 0000000000..65f8c9444b --- /dev/null +++ b/deps/v8/src/double.h @@ -0,0 +1,169 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_DOUBLE_H_ +#define V8_DOUBLE_H_ + +#include "diy-fp.h" + +namespace v8 { +namespace internal { + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return BitCast(d); } +static double uint64_to_double(uint64_t d64) { return BitCast(d64); } + +// Helper functions for doubles. +class Double { + public: + static const uint64_t kSignMask = V8_2PART_UINT64_C(0x80000000, 00000000); + static const uint64_t kExponentMask = V8_2PART_UINT64_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = + V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = V8_2PART_UINT64_C(0x00100000, 00000000); + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + + DiyFp AsDiyFp() const { + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // this->Significand() must not be 0. + DiyFp AsNormalizedDiyFp() const { + uint64_t f = Significand(); + int e = Exponent(); + + ASSERT(f != 0); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. Don't forget the hidden bit (the '-1'). + f <<= DiyFp::kSignificandSize - kSignificandSize - 1; + e -= DiyFp::kSignificandSize - kSignificandSize - 1; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = static_cast((d64 & kExponentMask) >> kSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + + // Returns the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + DiyFp v = this->AsDiyFp(); + bool significand_is_zero = (v.f() == kHiddenBit); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (significand_is_zero && v.e() != kDenormalExponent) { + // The boundary is closer. Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + double value() const { return uint64_to_double(d64_); } + + private: + static const int kSignificandSize = 52; // Excludes the hidden bit. + static const int kExponentBias = 0x3FF + kSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + + uint64_t d64_; +}; + +} } // namespace v8::internal + +#endif // V8_DOUBLE_H_ diff --git a/deps/v8/src/fast-codegen.cc b/deps/v8/src/fast-codegen.cc index 602d6b88ce..5d0b9c1dbb 100644 --- a/deps/v8/src/fast-codegen.cc +++ b/deps/v8/src/fast-codegen.cc @@ -436,9 +436,6 @@ Handle FastCodeGenerator::MakeCode(CompilationInfo* info) { AstLabeler labeler; labeler.Label(info); - LivenessAnalyzer analyzer; - analyzer.Analyze(info->function()); - CodeGenerator::MakeCodePrologue(info); const int kInitialBufferSize = 4 * KB; @@ -598,8 +595,8 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { Comment cmnt(masm(), ";; Global"); if (FLAG_print_ir) { SmartPointer name = expr->name()->ToCString(); - PrintF("%d: t%d = Global(%s) // last_use = %d\n", expr->num(), - expr->num(), *name, expr->var_def()->last_use()->num()); + PrintF("%d: t%d = Global(%s)\n", expr->num(), + expr->num(), *name); } EmitGlobalVariableLoad(cell); } @@ -653,9 +650,8 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { SmartPointer name_string = name->ToCString(); PrintF("%d: ", expr->num()); if (!destination().is(no_reg)) PrintF("t%d = ", expr->num()); - PrintF("Store(this, \"%s\", t%d) // last_use(this) = %d\n", *name_string, - expr->value()->num(), - expr->var_def()->last_use()->num()); + PrintF("Store(this, \"%s\", t%d)\n", *name_string, + expr->value()->num()); } EmitThisPropertyStore(name); @@ -678,9 +674,8 @@ void FastCodeGenerator::VisitProperty(Property* expr) { Comment cmnt(masm(), ";; Load from this"); if (FLAG_print_ir) { SmartPointer name_string = name->ToCString(); - PrintF("%d: t%d = Load(this, \"%s\") // last_use(this) = %d\n", - expr->num(), expr->num(), *name_string, - expr->var_def()->last_use()->num()); + PrintF("%d: t%d = Load(this, \"%s\")\n", + expr->num(), expr->num(), *name_string); } EmitThisPropertyLoad(name); } diff --git a/deps/v8/src/fast-dtoa.cc b/deps/v8/src/fast-dtoa.cc new file mode 100644 index 0000000000..029954416a --- /dev/null +++ b/deps/v8/src/fast-dtoa.cc @@ -0,0 +1,508 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "fast-dtoa.h" + +#include "cached-powers.h" +#include "diy-fp.h" +#include "double.h" + +namespace v8 { +namespace internal { + +// The minimal and maximal target exponent define the range of w's binary +// exponent, where 'w' is the result of multiplying the input by a cached power +// of ten. +// +// A different range might be chosen on a different platform, to optimize digit +// generation, but a smaller range requires more powers of ten to be cached. +static const int minimal_target_exponent = -60; +static const int maximal_target_exponent = -32; + + +// Adjusts the last digit of the generated number, and screens out generated +// solutions that may be inaccurate. A solution may be inaccurate if it is +// outside the safe interval, or if we ctannot prove that it is closer to the +// input than a neighboring representation of the same length. +// +// Input: * buffer containing the digits of too_high / 10^kappa +// * the buffer's length +// * distance_too_high_w == (too_high - w).f() * unit +// * unsafe_interval == (too_high - too_low).f() * unit +// * rest = (too_high - buffer * 10^kappa).f() * unit +// * ten_kappa = 10^kappa * unit +// * unit = the common multiplier +// Output: returns true if the buffer is guaranteed to contain the closest +// representable number to the input. +// Modifies the generated digits in the buffer to approach (round towards) w. +bool RoundWeed(char* buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_low[ (often written as "(w_low; w_low)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when rounding + // the buffer. If we have two potential representations we need to make sure + // that the chosen one is closer to w_low and w_high since v can be anywhere + // between them. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + + + +static const uint32_t kTen4 = 10000; +static const uint32_t kTen5 = 100000; +static const uint32_t kTen6 = 1000000; +static const uint32_t kTen7 = 10000000; +static const uint32_t kTen8 = 100000000; +static const uint32_t kTen9 = 1000000000; + +// Returns the biggest power of ten that is less than or equal than the given +// number. We furthermore receive the maximum number of bits 'number' has. +// If number_bits == 0 then 0^-1 is returned +// The number of bits must be <= 32. +// Precondition: (1 << number_bits) <= number < (1 << (number_bits + 1)). +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent) { + switch (number_bits) { + case 32: + case 31: + case 30: + if (kTen9 <= number) { + *power = kTen9; + *exponent = 9; + break; + } // else fallthrough + case 29: + case 28: + case 27: + if (kTen8 <= number) { + *power = kTen8; + *exponent = 8; + break; + } // else fallthrough + case 26: + case 25: + case 24: + if (kTen7 <= number) { + *power = kTen7; + *exponent = 7; + break; + } // else fallthrough + case 23: + case 22: + case 21: + case 20: + if (kTen6 <= number) { + *power = kTen6; + *exponent = 6; + break; + } // else fallthrough + case 19: + case 18: + case 17: + if (kTen5 <= number) { + *power = kTen5; + *exponent = 5; + break; + } // else fallthrough + case 16: + case 15: + case 14: + if (kTen4 <= number) { + *power = kTen4; + *exponent = 4; + break; + } // else fallthrough + case 13: + case 12: + case 11: + case 10: + if (1000 <= number) { + *power = 1000; + *exponent = 3; + break; + } // else fallthrough + case 9: + case 8: + case 7: + if (100 <= number) { + *power = 100; + *exponent = 2; + break; + } // else fallthrough + case 6: + case 5: + case 4: + if (10 <= number) { + *power = 10; + *exponent = 1; + break; + } // else fallthrough + case 3: + case 2: + case 1: + if (1 <= number) { + *power = 1; + *exponent = 0; + break; + } // else fallthrough + case 0: + *power = 0; + *exponent = -1; + break; + default: + // Following assignments are here to silence compiler warnings. + *power = 0; + *exponent = 0; + UNREACHABLE(); + } +} + + +// Generates the digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by minimal_target_exponent and +// maximal_target_exponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * low, w and high are correct up to 1 ulp (unit in the last place). That +// is, their error must be less that a unit of their last digits. +// * low.e() == w.e() == high.e() +// * low < w < high, and taking into account their error: low~ <= high~ +// * minimal_target_exponent <= w.e() <= maximal_target_exponent +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but len contains the number of digits. +// * buffer contains the shortest possible decimal digit-sequence +// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the +// correct values of low and high (without their error). +// * if more than one decimal representation gives the minimal number of +// decimal digits then the one closest to W (where W is the correct value +// of w) is chosen. +// Remark: this procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely (~0.5%). +// +// Say, for the sake of example, that +// w.e() == -48, and w.f() == 0x1234567890abcdef +// w's value can be computed by w.f() * 2^w.e() +// We can obtain w's integral digits by simply shifting w.f() by -w.e(). +// -> w's integral part is 0x1234 +// w's fractional part is therefore 0x567890abcdef. +// Printing w's integral part is easy (simply print 0x1234 in decimal). +// In order to print its fraction we repeatedly multiply the fraction by 10 and +// get each digit. Example the first digit after the comma would be computed by +// (0x567890abcdef * 10) >> 48. -> 3 +// The whole thing becomes slightly more complicated because we want to stop +// once we have enough digits. That is, once the digits inside the buffer +// represent 'w' we can stop. Everything inside the interval low - high +// represents w. However we have to pay attention to low, high and w's +// imprecision. +bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + char* buffer, + int* length, + int* kappa) { + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(minimal_target_exponent <= w.e() && w.e() <= maximal_target_exponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divider; + int divider_exponent; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + ÷r, ÷r_exponent); + *kappa = divider_exponent + 1; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divider exponent + 1. And the divider is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divider; + buffer[*length] = '0' + digit; + (*length)++; + integrals %= divider; + (*kappa)--; + // Note that kappa now equals the exponent of the divider and that the + // invariant thus holds again. + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast(divider) << -one.e(), unit); + } + divider /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Instead of multiplying by 10 we multiply by 5 (cheaper operation) and + // increase its (imaginary) exponent. At the same time we decrease the + // divider's (one's) exponent and shift its significand. + // Basically, if fractionals was a DiyFp (with fractionals.e == one.e): + // fractionals.f *= 10; + // fractionals.f >>= 1; fractionals.e++; // value remains unchanged. + // one.f >>= 1; one.e++; // value remains unchanged. + // and we have again fractionals.e == one.e which allows us to divide + // fractionals.f() by one.f() + // We simply combine the *= 10 and the >>= 1. + while (true) { + fractionals *= 5; + unit *= 5; + unsafe_interval.set_f(unsafe_interval.f() * 5); + unsafe_interval.set_e(unsafe_interval.e() + 1); // Will be optimized out. + one.set_f(one.f() >> 1); + one.set_e(one.e() + 1); + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } +} + + +// Provides a decimal representation of v. +// Returns true if it succeeds, otherwise the result cannot be trusted. +// There will be *length digits inside the buffer (not null-terminated). +// If the function returns true then +// v == (double) (buffer * 10^decimal_exponent). +// The digits in the buffer are the shortest representation possible: no +// 0.09999999999999999 instead of 0.1. The shorter representation will even be +// chosen even if the longer one would be closer to v. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the closest will be +// computed. +bool grisu3(double v, char* buffer, int* length, int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + GetCachedPower(w.e() + DiyFp::kSignificandSize, minimal_target_exponent, + maximal_target_exponent, &mk, &ten_mk); + ASSERT(minimal_target_exponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize && + maximal_target_exponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terriffic. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" und the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +bool FastDtoa(double v, char* buffer, int* sign, int* length, int* point) { + ASSERT(v != 0); + ASSERT(!Double(v).IsSpecial()); + + if (v < 0) { + v = -v; + *sign = 1; + } else { + *sign = 0; + } + int decimal_exponent; + bool result = grisu3(v, buffer, length, &decimal_exponent); + *point = *length + decimal_exponent; + buffer[*length] = '\0'; + return result; +} + +} } // namespace v8::internal diff --git a/deps/v8/src/fast-dtoa.h b/deps/v8/src/fast-dtoa.h new file mode 100644 index 0000000000..91f6ce76b3 --- /dev/null +++ b/deps/v8/src/fast-dtoa.h @@ -0,0 +1,55 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_FAST_DTOA_H_ +#define V8_FAST_DTOA_H_ + +namespace v8 { +namespace internal { + +// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not +// include the terminating '\0' character. +static const int kFastDtoaMaximalLength = 17; + +// Provides a decimal representation of v. +// v must not be (positive or negative) zero and it must not be Infinity or NaN. +// Returns true if it succeeds, otherwise the result can not be trusted. +// There will be *length digits inside the buffer followed by a null terminator. +// If the function returns true then +// v == (double) (buffer * 10^(point - length)). +// The digits in the buffer are the shortest representation possible: no +// 0.099999999999 instead of 0.1. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the buffer will contain the +// one closest to v. +// The variable 'sign' will be '0' if the given number is positive, and '1' +// otherwise. +bool FastDtoa(double d, char* buffer, int* sign, int* length, int* point); + +} } // namespace v8::internal + +#endif // V8_FAST_DTOA_H_ diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc index 3bf4c93e39..24550a2ed0 100644 --- a/deps/v8/src/frames.cc +++ b/deps/v8/src/frames.cc @@ -306,14 +306,12 @@ void SafeStackTraceFrameIterator::Advance() { void StackHandler::Cook(Code* code) { - ASSERT(MarkCompactCollector::IsCompacting()); ASSERT(code->contains(pc())); set_pc(AddressFrom
(pc() - code->instruction_start())); } void StackHandler::Uncook(Code* code) { - ASSERT(MarkCompactCollector::HasCompacted()); set_pc(code->instruction_start() + OffsetFrom(pc())); ASSERT(code->contains(pc())); } @@ -329,9 +327,6 @@ bool StackFrame::HasHandler() const { void StackFrame::CookFramesForThread(ThreadLocalTop* thread) { - // Only cooking frames when the collector is compacting and thus moving code - // around. - ASSERT(MarkCompactCollector::IsCompacting()); ASSERT(!thread->stack_is_cooked()); for (StackFrameIterator it(thread); !it.done(); it.Advance()) { it.frame()->Cook(); @@ -341,9 +336,6 @@ void StackFrame::CookFramesForThread(ThreadLocalTop* thread) { void StackFrame::UncookFramesForThread(ThreadLocalTop* thread) { - // Only uncooking frames when the collector is compacting and thus moving code - // around. - ASSERT(MarkCompactCollector::HasCompacted()); ASSERT(thread->stack_is_cooked()); for (StackFrameIterator it(thread); !it.done(); it.Advance()) { it.frame()->Uncook(); @@ -520,6 +512,31 @@ void JavaScriptFrame::Print(StringStream* accumulator, Code* code = NULL; if (IsConstructor()) accumulator->Add("new "); accumulator->PrintFunction(function, receiver, &code); + + if (function->IsJSFunction()) { + Handle shared(JSFunction::cast(function)->shared()); + Object* script_obj = shared->script(); + if (script_obj->IsScript()) { + Handle