mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
96 changed files with 107107 additions and 894 deletions
@ -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<int>(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_
|
@ -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<typename Record> |
|||
CircularQueue<Record>::CircularQueue(int desired_buffer_size_in_bytes) |
|||
: buffer_(NewArray<Record>(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<typename Record> |
|||
CircularQueue<Record>::~CircularQueue() { |
|||
DeleteArray(buffer_); |
|||
delete enqueue_semaphore_; |
|||
} |
|||
|
|||
|
|||
template<typename Record> |
|||
void CircularQueue<Record>::Dequeue(Record* rec) { |
|||
ASSERT(!IsEmpty()); |
|||
*rec = *dequeue_pos_; |
|||
dequeue_pos_ = Next(dequeue_pos_); |
|||
// Tell we have a spare record.
|
|||
enqueue_semaphore_->Signal(); |
|||
} |
|||
|
|||
|
|||
template<typename Record> |
|||
void CircularQueue<Record>::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<typename Record> |
|||
Record* CircularQueue<Record>::Next(Record* curr) { |
|||
return ++curr != buffer_end_ ? curr : buffer_; |
|||
} |
|||
|
|||
|
|||
void* SamplingCircularQueue::Enqueue() { |
|||
Cell* enqueue_pos = reinterpret_cast<Cell*>( |
|||
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_
|
@ -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<Cell>(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<ConsumerPosition*>( |
|||
Thread::GetThreadLocal(consumer_key_)); |
|||
Thread::DeleteThreadLocalKey(consumer_key_); |
|||
} |
|||
|
|||
|
|||
void* SamplingCircularQueue::StartDequeue() { |
|||
ConsumerPosition* cp = reinterpret_cast<ConsumerPosition*>( |
|||
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<ConsumerPosition*>( |
|||
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<ConsumerPosition*>( |
|||
Thread::GetThreadLocal(consumer_key_)); |
|||
// Eliminate producer / consumer distance.
|
|||
cp->dequeue_chunk_poll_pos = cp->dequeue_chunk_pos; |
|||
} |
|||
|
|||
|
|||
} } // namespace v8::internal
|
@ -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<typename Record> |
|||
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_
|
@ -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<TickSampleEventRecord*>(ticks_buffer_.Enqueue()); |
|||
evt->order = enqueue_order_; // No increment!
|
|||
return &evt->sample; |
|||
} |
|||
|
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // V8_CPU_PROFILER_INL_H_
|
@ -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<TickSampleEventRecord*>(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
|
@ -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<CodeEventsContainer> events_buffer_; |
|||
SamplingCircularQueue ticks_buffer_; |
|||
unsigned enqueue_order_; |
|||
}; |
|||
|
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // V8_CPU_PROFILER_H_
|
@ -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
|
@ -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_
|
@ -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<uint64_t>(d); } |
|||
static double uint64_to_double(uint64_t d64) { return BitCast<double>(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<int>((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_
|
@ -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<uint64_t>(1) << -w.e(), w.e()); |
|||
// Division by one is a shift.
|
|||
uint32_t integrals = static_cast<uint32_t>(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<uint64_t>(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<uint64_t>(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<int>(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
|
@ -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_
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,44 @@ |
|||
// Copyright 2006-2008 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 GAY_SHORTEST_H_ |
|||
#define GAY_SHORTEST_H_ |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
struct GayShortest { |
|||
double v; |
|||
const char* representation; |
|||
int decimal_point; |
|||
}; |
|||
|
|||
Vector<const GayShortest> PrecomputedShortestRepresentations(); |
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // GAY_SHORTEST_H_
|
@ -0,0 +1,127 @@ |
|||
// Copyright 2010 the V8 project authors. All rights reserved.
|
|||
//
|
|||
// Tests of circular queues.
|
|||
|
|||
#include "v8.h" |
|||
#include "circular-queue-inl.h" |
|||
#include "cctest.h" |
|||
|
|||
namespace i = v8::internal; |
|||
|
|||
using i::CircularQueue; |
|||
using i::SamplingCircularQueue; |
|||
|
|||
|
|||
TEST(SingleRecordCircularQueue) { |
|||
typedef int Record; |
|||
CircularQueue<Record> cq(sizeof(Record) * 2); |
|||
CHECK(cq.IsEmpty()); |
|||
cq.Enqueue(1); |
|||
CHECK(!cq.IsEmpty()); |
|||
Record rec = 0; |
|||
cq.Dequeue(&rec); |
|||
CHECK_EQ(1, rec); |
|||
CHECK(cq.IsEmpty()); |
|||
} |
|||
|
|||
|
|||
TEST(MultipleRecordsCircularQueue) { |
|||
typedef int Record; |
|||
const int kQueueSize = 10; |
|||
CircularQueue<Record> cq(sizeof(Record) * (kQueueSize + 1)); |
|||
CHECK(cq.IsEmpty()); |
|||
cq.Enqueue(1); |
|||
CHECK(!cq.IsEmpty()); |
|||
for (int i = 2; i <= 5; ++i) { |
|||
cq.Enqueue(i); |
|||
CHECK(!cq.IsEmpty()); |
|||
} |
|||
Record rec = 0; |
|||
for (int i = 1; i <= 4; ++i) { |
|||
CHECK(!cq.IsEmpty()); |
|||
cq.Dequeue(&rec); |
|||
CHECK_EQ(i, rec); |
|||
} |
|||
for (int i = 6; i <= 12; ++i) { |
|||
cq.Enqueue(i); |
|||
CHECK(!cq.IsEmpty()); |
|||
} |
|||
for (int i = 5; i <= 12; ++i) { |
|||
CHECK(!cq.IsEmpty()); |
|||
cq.Dequeue(&rec); |
|||
CHECK_EQ(i, rec); |
|||
} |
|||
CHECK(cq.IsEmpty()); |
|||
} |
|||
|
|||
|
|||
TEST(SamplingCircularQueue) { |
|||
typedef SamplingCircularQueue::Cell Record; |
|||
const int kRecordsPerChunk = 4; |
|||
SamplingCircularQueue scq(sizeof(Record), |
|||
kRecordsPerChunk * sizeof(Record), |
|||
3); |
|||
scq.SetUpProducer(); |
|||
scq.SetUpConsumer(); |
|||
|
|||
// Check that we are using non-reserved values.
|
|||
CHECK_NE(SamplingCircularQueue::kClear, 1); |
|||
CHECK_NE(SamplingCircularQueue::kEnd, 1); |
|||
// Fill up the first chunk.
|
|||
CHECK_EQ(NULL, scq.StartDequeue()); |
|||
for (Record i = 1; i < 1 + kRecordsPerChunk; ++i) { |
|||
Record* rec = reinterpret_cast<Record*>(scq.Enqueue()); |
|||
CHECK_NE(NULL, rec); |
|||
*rec = i; |
|||
CHECK_EQ(NULL, scq.StartDequeue()); |
|||
} |
|||
|
|||
// Fill up the second chunk. Consumption must still be unavailable.
|
|||
CHECK_EQ(NULL, scq.StartDequeue()); |
|||
for (Record i = 10; i < 10 + kRecordsPerChunk; ++i) { |
|||
Record* rec = reinterpret_cast<Record*>(scq.Enqueue()); |
|||
CHECK_NE(NULL, rec); |
|||
*rec = i; |
|||
CHECK_EQ(NULL, scq.StartDequeue()); |
|||
} |
|||
|
|||
Record* rec = reinterpret_cast<Record*>(scq.Enqueue()); |
|||
CHECK_NE(NULL, rec); |
|||
*rec = 20; |
|||
// Now as we started filling up the third chunk, consumption
|
|||
// must become possible.
|
|||
CHECK_NE(NULL, scq.StartDequeue()); |
|||
|
|||
// Consume the first chunk.
|
|||
for (Record i = 1; i < 1 + kRecordsPerChunk; ++i) { |
|||
Record* rec = reinterpret_cast<Record*>(scq.StartDequeue()); |
|||
CHECK_NE(NULL, rec); |
|||
CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(*rec)); |
|||
CHECK_EQ(rec, reinterpret_cast<Record*>(scq.StartDequeue())); |
|||
scq.FinishDequeue(); |
|||
CHECK_NE(rec, reinterpret_cast<Record*>(scq.StartDequeue())); |
|||
} |
|||
// Now consumption must not be possible, as consumer now polls
|
|||
// the first chunk for emptinness.
|
|||
CHECK_EQ(NULL, scq.StartDequeue()); |
|||
|
|||
scq.FlushResidualRecords(); |
|||
// From now, consumer no more polls ahead of the current chunk,
|
|||
// so it's possible to consume the second chunk.
|
|||
CHECK_NE(NULL, scq.StartDequeue()); |
|||
// Consume the second chunk
|
|||
for (Record i = 10; i < 10 + kRecordsPerChunk; ++i) { |
|||
Record* rec = reinterpret_cast<Record*>(scq.StartDequeue()); |
|||
CHECK_NE(NULL, rec); |
|||
CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(*rec)); |
|||
CHECK_EQ(rec, reinterpret_cast<Record*>(scq.StartDequeue())); |
|||
scq.FinishDequeue(); |
|||
CHECK_NE(rec, reinterpret_cast<Record*>(scq.StartDequeue())); |
|||
} |
|||
// Consumption must still be possible as the first cell of the
|
|||
// last chunk is not clean.
|
|||
CHECK_NE(NULL, scq.StartDequeue()); |
|||
|
|||
scq.TearDownConsumer(); |
|||
scq.TearDownProducer(); |
|||
} |
@ -0,0 +1,202 @@ |
|||
// Copyright 2010 the V8 project authors. All rights reserved.
|
|||
//
|
|||
// Tests of profiles generator and utilities.
|
|||
|
|||
#include "v8.h" |
|||
#include "cpu-profiler-inl.h" |
|||
#include "cctest.h" |
|||
|
|||
namespace i = v8::internal; |
|||
|
|||
using i::CodeEntry; |
|||
using i::CpuProfilesCollection; |
|||
using i::ProfileGenerator; |
|||
using i::ProfileNode; |
|||
using i::ProfilerEventsProcessor; |
|||
|
|||
|
|||
TEST(StartStop) { |
|||
CpuProfilesCollection profiles; |
|||
ProfileGenerator generator(&profiles); |
|||
ProfilerEventsProcessor processor(&generator); |
|||
processor.Start(); |
|||
while (!processor.running()) { |
|||
i::Thread::YieldCPU(); |
|||
} |
|||
processor.Stop(); |
|||
processor.Join(); |
|||
} |
|||
|
|||
static v8::Persistent<v8::Context> env; |
|||
|
|||
static void InitializeVM() { |
|||
if (env.IsEmpty()) env = v8::Context::New(); |
|||
v8::HandleScope scope; |
|||
env->Enter(); |
|||
} |
|||
|
|||
static inline i::Address ToAddress(int n) { |
|||
return reinterpret_cast<i::Address>(n); |
|||
} |
|||
|
|||
static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc, |
|||
i::Address frame1, |
|||
i::Address frame2 = NULL, |
|||
i::Address frame3 = NULL) { |
|||
i::TickSample* sample = proc->TickSampleEvent(); |
|||
sample->pc = frame1; |
|||
sample->function = frame1; |
|||
sample->frames_count = 0; |
|||
if (frame2 != NULL) { |
|||
sample->stack[0] = frame2; |
|||
sample->frames_count = 1; |
|||
} |
|||
if (frame3 != NULL) { |
|||
sample->stack[1] = frame3; |
|||
sample->frames_count = 2; |
|||
} |
|||
} |
|||
|
|||
TEST(CodeEvents) { |
|||
InitializeVM(); |
|||
CpuProfilesCollection profiles; |
|||
profiles.AddProfile(0); |
|||
ProfileGenerator generator(&profiles); |
|||
ProfilerEventsProcessor processor(&generator); |
|||
processor.Start(); |
|||
processor.SetUpSamplesProducer(); |
|||
while (!processor.running()) { |
|||
i::Thread::YieldCPU(); |
|||
} |
|||
|
|||
// Enqueue code creation events.
|
|||
i::HandleScope scope; |
|||
const char* aaa_str = "aaa"; |
|||
i::Handle<i::String> aaa_name = i::Factory::NewStringFromAscii( |
|||
i::Vector<const char>(aaa_str, strlen(aaa_str))); |
|||
processor.CodeCreateEvent(i::Logger::FUNCTION_TAG, |
|||
*aaa_name, |
|||
i::Heap::empty_string(), |
|||
0, |
|||
ToAddress(0x1000), |
|||
0x100); |
|||
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG, |
|||
"bbb", |
|||
ToAddress(0x1200), |
|||
0x80); |
|||
processor.CodeCreateEvent(i::Logger::STUB_TAG, 5, ToAddress(0x1300), 0x10); |
|||
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG, |
|||
"ddd", |
|||
ToAddress(0x1400), |
|||
0x80); |
|||
processor.CodeMoveEvent(ToAddress(0x1400), ToAddress(0x1500)); |
|||
processor.CodeCreateEvent(i::Logger::STUB_TAG, 3, ToAddress(0x1600), 0x10); |
|||
processor.CodeDeleteEvent(ToAddress(0x1600)); |
|||
processor.FunctionCreateEvent(ToAddress(0x1700), ToAddress(0x1000)); |
|||
// Enqueue a tick event to enable code events processing.
|
|||
EnqueueTickSampleEvent(&processor, ToAddress(0x1000)); |
|||
|
|||
processor.Stop(); |
|||
processor.Join(); |
|||
|
|||
// Check the state of profile generator.
|
|||
CodeEntry* entry1 = generator.code_map()->FindEntry(ToAddress(0x1000)); |
|||
CHECK_NE(NULL, entry1); |
|||
CHECK_EQ(aaa_str, entry1->name()); |
|||
CodeEntry* entry2 = generator.code_map()->FindEntry(ToAddress(0x1200)); |
|||
CHECK_NE(NULL, entry2); |
|||
CHECK_EQ("bbb", entry2->name()); |
|||
CodeEntry* entry3 = generator.code_map()->FindEntry(ToAddress(0x1300)); |
|||
CHECK_NE(NULL, entry3); |
|||
CHECK_EQ("args_count: 5", entry3->name()); |
|||
CHECK_EQ(NULL, generator.code_map()->FindEntry(ToAddress(0x1400))); |
|||
CodeEntry* entry4 = generator.code_map()->FindEntry(ToAddress(0x1500)); |
|||
CHECK_NE(NULL, entry4); |
|||
CHECK_EQ("ddd", entry4->name()); |
|||
CHECK_EQ(NULL, generator.code_map()->FindEntry(ToAddress(0x1600))); |
|||
CodeEntry* entry5 = generator.code_map()->FindEntry(ToAddress(0x1700)); |
|||
CHECK_NE(NULL, entry5); |
|||
CHECK_EQ(aaa_str, entry5->name()); |
|||
|
|||
processor.TearDownSamplesProducer(); |
|||
} |
|||
|
|||
|
|||
template<typename T> |
|||
static int CompareProfileNodes(const T* p1, const T* p2) { |
|||
return strcmp((*p1)->entry()->name(), (*p2)->entry()->name()); |
|||
} |
|||
|
|||
TEST(TickEvents) { |
|||
CpuProfilesCollection profiles; |
|||
profiles.AddProfile(0); |
|||
ProfileGenerator generator(&profiles); |
|||
ProfilerEventsProcessor processor(&generator); |
|||
processor.Start(); |
|||
processor.SetUpSamplesProducer(); |
|||
while (!processor.running()) { |
|||
i::Thread::YieldCPU(); |
|||
} |
|||
|
|||
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG, |
|||
"bbb", |
|||
ToAddress(0x1200), |
|||
0x80); |
|||
processor.CodeCreateEvent(i::Logger::STUB_TAG, 5, ToAddress(0x1300), 0x10); |
|||
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG, |
|||
"ddd", |
|||
ToAddress(0x1400), |
|||
0x80); |
|||
EnqueueTickSampleEvent(&processor, ToAddress(0x1210)); |
|||
EnqueueTickSampleEvent(&processor, ToAddress(0x1305), ToAddress(0x1220)); |
|||
EnqueueTickSampleEvent(&processor, |
|||
ToAddress(0x1404), |
|||
ToAddress(0x1305), |
|||
ToAddress(0x1230)); |
|||
|
|||
processor.Stop(); |
|||
processor.Join(); |
|||
|
|||
// Check call trees.
|
|||
i::List<ProfileNode*> top_down_root_children; |
|||
profiles.profile()->top_down()->root()->GetChildren(&top_down_root_children); |
|||
CHECK_EQ(1, top_down_root_children.length()); |
|||
CHECK_EQ("bbb", top_down_root_children.last()->entry()->name()); |
|||
i::List<ProfileNode*> top_down_bbb_children; |
|||
top_down_root_children.last()->GetChildren(&top_down_bbb_children); |
|||
CHECK_EQ(1, top_down_bbb_children.length()); |
|||
CHECK_EQ("args_count: 5", top_down_bbb_children.last()->entry()->name()); |
|||
i::List<ProfileNode*> top_down_stub_children; |
|||
top_down_bbb_children.last()->GetChildren(&top_down_stub_children); |
|||
CHECK_EQ(1, top_down_stub_children.length()); |
|||
CHECK_EQ("ddd", top_down_stub_children.last()->entry()->name()); |
|||
i::List<ProfileNode*> top_down_ddd_children; |
|||
top_down_stub_children.last()->GetChildren(&top_down_ddd_children); |
|||
CHECK_EQ(0, top_down_ddd_children.length()); |
|||
|
|||
i::List<ProfileNode*> bottom_up_root_children; |
|||
profiles.profile()->bottom_up()->root()->GetChildren( |
|||
&bottom_up_root_children); |
|||
CHECK_EQ(3, bottom_up_root_children.length()); |
|||
bottom_up_root_children.Sort(&CompareProfileNodes); |
|||
CHECK_EQ("args_count: 5", bottom_up_root_children[0]->entry()->name()); |
|||
CHECK_EQ("bbb", bottom_up_root_children[1]->entry()->name()); |
|||
CHECK_EQ("ddd", bottom_up_root_children[2]->entry()->name()); |
|||
i::List<ProfileNode*> bottom_up_stub_children; |
|||
bottom_up_root_children[0]->GetChildren(&bottom_up_stub_children); |
|||
CHECK_EQ(1, bottom_up_stub_children.length()); |
|||
CHECK_EQ("bbb", bottom_up_stub_children.last()->entry()->name()); |
|||
i::List<ProfileNode*> bottom_up_bbb_children; |
|||
bottom_up_root_children[1]->GetChildren(&bottom_up_bbb_children); |
|||
CHECK_EQ(0, bottom_up_bbb_children.length()); |
|||
i::List<ProfileNode*> bottom_up_ddd_children; |
|||
bottom_up_root_children[2]->GetChildren(&bottom_up_ddd_children); |
|||
CHECK_EQ(1, bottom_up_ddd_children.length()); |
|||
CHECK_EQ("args_count: 5", bottom_up_ddd_children.last()->entry()->name()); |
|||
i::List<ProfileNode*> bottom_up_ddd_stub_children; |
|||
bottom_up_ddd_children.last()->GetChildren(&bottom_up_ddd_stub_children); |
|||
CHECK_EQ(1, bottom_up_ddd_stub_children.length()); |
|||
CHECK_EQ("bbb", bottom_up_ddd_stub_children.last()->entry()->name()); |
|||
|
|||
processor.TearDownSamplesProducer(); |
|||
} |
@ -0,0 +1,67 @@ |
|||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
|||
|
|||
#include <stdlib.h> |
|||
|
|||
#include "v8.h" |
|||
|
|||
#include "platform.h" |
|||
#include "cctest.h" |
|||
#include "diy-fp.h" |
|||
|
|||
|
|||
using namespace v8::internal; |
|||
|
|||
|
|||
TEST(Subtract) { |
|||
DiyFp diy_fp1 = DiyFp(3, 0); |
|||
DiyFp diy_fp2 = DiyFp(1, 0); |
|||
DiyFp diff = DiyFp::Minus(diy_fp1, diy_fp2); |
|||
|
|||
CHECK(2 == diff.f()); // NOLINT
|
|||
CHECK_EQ(0, diff.e()); |
|||
diy_fp1.Subtract(diy_fp2); |
|||
CHECK(2 == diy_fp1.f()); // NOLINT
|
|||
CHECK_EQ(0, diy_fp1.e()); |
|||
} |
|||
|
|||
|
|||
TEST(Multiply) { |
|||
DiyFp diy_fp1 = DiyFp(3, 0); |
|||
DiyFp diy_fp2 = DiyFp(2, 0); |
|||
DiyFp product = DiyFp::Times(diy_fp1, diy_fp2); |
|||
|
|||
CHECK(0 == product.f()); // NOLINT
|
|||
CHECK_EQ(64, product.e()); |
|||
diy_fp1.Multiply(diy_fp2); |
|||
CHECK(0 == diy_fp1.f()); // NOLINT
|
|||
CHECK_EQ(64, diy_fp1.e()); |
|||
|
|||
diy_fp1 = DiyFp(V8_2PART_UINT64_C(0x80000000, 00000000), 11); |
|||
diy_fp2 = DiyFp(2, 13); |
|||
product = DiyFp::Times(diy_fp1, diy_fp2); |
|||
CHECK(1 == product.f()); // NOLINT
|
|||
CHECK_EQ(11 + 13 + 64, product.e()); |
|||
|
|||
// Test rounding.
|
|||
diy_fp1 = DiyFp(V8_2PART_UINT64_C(0x80000000, 00000001), 11); |
|||
diy_fp2 = DiyFp(1, 13); |
|||
product = DiyFp::Times(diy_fp1, diy_fp2); |
|||
CHECK(1 == product.f()); // NOLINT
|
|||
CHECK_EQ(11 + 13 + 64, product.e()); |
|||
|
|||
diy_fp1 = DiyFp(V8_2PART_UINT64_C(0x7fffffff, ffffffff), 11); |
|||
diy_fp2 = DiyFp(1, 13); |
|||
product = DiyFp::Times(diy_fp1, diy_fp2); |
|||
CHECK(0 == product.f()); // NOLINT
|
|||
CHECK_EQ(11 + 13 + 64, product.e()); |
|||
|
|||
// Halfway cases are allowed to round either way. So don't check for it.
|
|||
|
|||
// Big numbers.
|
|||
diy_fp1 = DiyFp(V8_2PART_UINT64_C(0xFFFFFFFF, FFFFFFFF), 11); |
|||
diy_fp2 = DiyFp(V8_2PART_UINT64_C(0xFFFFFFFF, FFFFFFFF), 13); |
|||
// 128bit result: 0xfffffffffffffffe0000000000000001
|
|||
product = DiyFp::Times(diy_fp1, diy_fp2); |
|||
CHECK(V8_2PART_UINT64_C(0xFFFFFFFF, FFFFFFFe) == product.f()); |
|||
CHECK_EQ(11 + 13 + 64, product.e()); |
|||
} |
@ -0,0 +1,204 @@ |
|||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
|||
|
|||
#include <stdlib.h> |
|||
|
|||
#include "v8.h" |
|||
|
|||
#include "platform.h" |
|||
#include "cctest.h" |
|||
#include "diy-fp.h" |
|||
#include "double.h" |
|||
|
|||
|
|||
using namespace v8::internal; |
|||
|
|||
|
|||
TEST(Uint64Conversions) { |
|||
// Start by checking the byte-order.
|
|||
uint64_t ordered = V8_2PART_UINT64_C(0x01234567, 89ABCDEF); |
|||
CHECK_EQ(3512700564088504e-318, Double(ordered).value()); |
|||
|
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
CHECK_EQ(5e-324, Double(min_double64).value()); |
|||
|
|||
uint64_t max_double64 = V8_2PART_UINT64_C(0x7fefffff, ffffffff); |
|||
CHECK_EQ(1.7976931348623157e308, Double(max_double64).value()); |
|||
} |
|||
|
|||
TEST(AsDiyFp) { |
|||
uint64_t ordered = V8_2PART_UINT64_C(0x01234567, 89ABCDEF); |
|||
DiyFp diy_fp = Double(ordered).AsDiyFp(); |
|||
CHECK_EQ(0x12 - 0x3FF - 52, diy_fp.e()); |
|||
// The 52 mantissa bits, plus the implicit 1 in bit 52 as a UINT64.
|
|||
CHECK(V8_2PART_UINT64_C(0x00134567, 89ABCDEF) == diy_fp.f()); // NOLINT
|
|||
|
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
diy_fp = Double(min_double64).AsDiyFp(); |
|||
CHECK_EQ(-0x3FF - 52 + 1, diy_fp.e()); |
|||
// This is a denormal; so no hidden bit.
|
|||
CHECK(1 == diy_fp.f()); // NOLINT
|
|||
|
|||
uint64_t max_double64 = V8_2PART_UINT64_C(0x7fefffff, ffffffff); |
|||
diy_fp = Double(max_double64).AsDiyFp(); |
|||
CHECK_EQ(0x7FE - 0x3FF - 52, diy_fp.e()); |
|||
CHECK(V8_2PART_UINT64_C(0x001fffff, ffffffff) == diy_fp.f()); // NOLINT
|
|||
} |
|||
|
|||
|
|||
TEST(AsNormalizedDiyFp) { |
|||
uint64_t ordered = V8_2PART_UINT64_C(0x01234567, 89ABCDEF); |
|||
DiyFp diy_fp = Double(ordered).AsNormalizedDiyFp(); |
|||
CHECK_EQ(0x12 - 0x3FF - 52 - 11, diy_fp.e()); |
|||
CHECK((V8_2PART_UINT64_C(0x00134567, 89ABCDEF) << 11) == |
|||
diy_fp.f()); // NOLINT
|
|||
|
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
diy_fp = Double(min_double64).AsNormalizedDiyFp(); |
|||
CHECK_EQ(-0x3FF - 52 + 1 - 63, diy_fp.e()); |
|||
// This is a denormal; so no hidden bit.
|
|||
CHECK(V8_2PART_UINT64_C(0x80000000, 00000000) == diy_fp.f()); // NOLINT
|
|||
|
|||
uint64_t max_double64 = V8_2PART_UINT64_C(0x7fefffff, ffffffff); |
|||
diy_fp = Double(max_double64).AsNormalizedDiyFp(); |
|||
CHECK_EQ(0x7FE - 0x3FF - 52 - 11, diy_fp.e()); |
|||
CHECK((V8_2PART_UINT64_C(0x001fffff, ffffffff) << 11) == |
|||
diy_fp.f()); // NOLINT
|
|||
} |
|||
|
|||
|
|||
TEST(IsDenormal) { |
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
CHECK(Double(min_double64).IsDenormal()); |
|||
uint64_t bits = V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); |
|||
CHECK(Double(bits).IsDenormal()); |
|||
bits = V8_2PART_UINT64_C(0x00100000, 00000000); |
|||
CHECK(!Double(bits).IsDenormal()); |
|||
} |
|||
|
|||
|
|||
TEST(IsSpecial) { |
|||
CHECK(Double(V8_INFINITY).IsSpecial()); |
|||
CHECK(Double(-V8_INFINITY).IsSpecial()); |
|||
CHECK(Double(OS::nan_value()).IsSpecial()); |
|||
uint64_t bits = V8_2PART_UINT64_C(0xFFF12345, 00000000); |
|||
CHECK(Double(bits).IsSpecial()); |
|||
// Denormals are not special:
|
|||
CHECK(!Double(5e-324).IsSpecial()); |
|||
CHECK(!Double(-5e-324).IsSpecial()); |
|||
// And some random numbers:
|
|||
CHECK(!Double(0.0).IsSpecial()); |
|||
CHECK(!Double(-0.0).IsSpecial()); |
|||
CHECK(!Double(1.0).IsSpecial()); |
|||
CHECK(!Double(-1.0).IsSpecial()); |
|||
CHECK(!Double(1000000.0).IsSpecial()); |
|||
CHECK(!Double(-1000000.0).IsSpecial()); |
|||
CHECK(!Double(1e23).IsSpecial()); |
|||
CHECK(!Double(-1e23).IsSpecial()); |
|||
CHECK(!Double(1.7976931348623157e308).IsSpecial()); |
|||
CHECK(!Double(-1.7976931348623157e308).IsSpecial()); |
|||
} |
|||
|
|||
|
|||
TEST(IsInfinite) { |
|||
CHECK(Double(V8_INFINITY).IsInfinite()); |
|||
CHECK(Double(-V8_INFINITY).IsInfinite()); |
|||
CHECK(!Double(OS::nan_value()).IsInfinite()); |
|||
CHECK(!Double(0.0).IsInfinite()); |
|||
CHECK(!Double(-0.0).IsInfinite()); |
|||
CHECK(!Double(1.0).IsInfinite()); |
|||
CHECK(!Double(-1.0).IsInfinite()); |
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
CHECK(!Double(min_double64).IsInfinite()); |
|||
} |
|||
|
|||
|
|||
TEST(IsNan) { |
|||
CHECK(Double(OS::nan_value()).IsNan()); |
|||
uint64_t other_nan = V8_2PART_UINT64_C(0xFFFFFFFF, 00000001); |
|||
CHECK(Double(other_nan).IsNan()); |
|||
CHECK(!Double(V8_INFINITY).IsNan()); |
|||
CHECK(!Double(-V8_INFINITY).IsNan()); |
|||
CHECK(!Double(0.0).IsNan()); |
|||
CHECK(!Double(-0.0).IsNan()); |
|||
CHECK(!Double(1.0).IsNan()); |
|||
CHECK(!Double(-1.0).IsNan()); |
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
CHECK(!Double(min_double64).IsNan()); |
|||
} |
|||
|
|||
|
|||
TEST(Sign) { |
|||
CHECK_EQ(1, Double(1.0).Sign()); |
|||
CHECK_EQ(1, Double(V8_INFINITY).Sign()); |
|||
CHECK_EQ(-1, Double(-V8_INFINITY).Sign()); |
|||
CHECK_EQ(1, Double(0.0).Sign()); |
|||
CHECK_EQ(-1, Double(-0.0).Sign()); |
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
CHECK_EQ(1, Double(min_double64).Sign()); |
|||
} |
|||
|
|||
|
|||
TEST(NormalizedBoundaries) { |
|||
DiyFp boundary_plus; |
|||
DiyFp boundary_minus; |
|||
DiyFp diy_fp = Double(1.5).AsNormalizedDiyFp(); |
|||
Double(1.5).NormalizedBoundaries(&boundary_minus, &boundary_plus); |
|||
CHECK_EQ(diy_fp.e(), boundary_minus.e()); |
|||
CHECK_EQ(diy_fp.e(), boundary_plus.e()); |
|||
// 1.5 does not have a significand of the form 2^p (for some p).
|
|||
// Therefore its boundaries are at the same distance.
|
|||
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f()); |
|||
CHECK((1 << 10) == diy_fp.f() - boundary_minus.f()); // NOLINT
|
|||
|
|||
diy_fp = Double(1.0).AsNormalizedDiyFp(); |
|||
Double(1.0).NormalizedBoundaries(&boundary_minus, &boundary_plus); |
|||
CHECK_EQ(diy_fp.e(), boundary_minus.e()); |
|||
CHECK_EQ(diy_fp.e(), boundary_plus.e()); |
|||
// 1.0 does have a significand of the form 2^p (for some p).
|
|||
// Therefore its lower boundary is twice as close as the upper boundary.
|
|||
CHECK_GT(boundary_plus.f() - diy_fp.f(), diy_fp.f() - boundary_minus.f()); |
|||
CHECK((1 << 9) == diy_fp.f() - boundary_minus.f()); // NOLINT
|
|||
CHECK((1 << 10) == boundary_plus.f() - diy_fp.f()); // NOLINT
|
|||
|
|||
uint64_t min_double64 = V8_2PART_UINT64_C(0x00000000, 00000001); |
|||
diy_fp = Double(min_double64).AsNormalizedDiyFp(); |
|||
Double(min_double64).NormalizedBoundaries(&boundary_minus, &boundary_plus); |
|||
CHECK_EQ(diy_fp.e(), boundary_minus.e()); |
|||
CHECK_EQ(diy_fp.e(), boundary_plus.e()); |
|||
// min-value does not have a significand of the form 2^p (for some p).
|
|||
// Therefore its boundaries are at the same distance.
|
|||
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f()); |
|||
// Denormals have their boundaries much closer.
|
|||
CHECK((static_cast<uint64_t>(1) << 62) == |
|||
diy_fp.f() - boundary_minus.f()); // NOLINT
|
|||
|
|||
uint64_t smallest_normal64 = V8_2PART_UINT64_C(0x00100000, 00000000); |
|||
diy_fp = Double(smallest_normal64).AsNormalizedDiyFp(); |
|||
Double(smallest_normal64).NormalizedBoundaries(&boundary_minus, |
|||
&boundary_plus); |
|||
CHECK_EQ(diy_fp.e(), boundary_minus.e()); |
|||
CHECK_EQ(diy_fp.e(), boundary_plus.e()); |
|||
// Even though the significand is of the form 2^p (for some p), its boundaries
|
|||
// are at the same distance. (This is the only exception).
|
|||
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f()); |
|||
CHECK((1 << 10) == diy_fp.f() - boundary_minus.f()); // NOLINT
|
|||
|
|||
uint64_t largest_denormal64 = V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); |
|||
diy_fp = Double(largest_denormal64).AsNormalizedDiyFp(); |
|||
Double(largest_denormal64).NormalizedBoundaries(&boundary_minus, |
|||
&boundary_plus); |
|||
CHECK_EQ(diy_fp.e(), boundary_minus.e()); |
|||
CHECK_EQ(diy_fp.e(), boundary_plus.e()); |
|||
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f()); |
|||
CHECK((1 << 11) == diy_fp.f() - boundary_minus.f()); // NOLINT
|
|||
|
|||
uint64_t max_double64 = V8_2PART_UINT64_C(0x7fefffff, ffffffff); |
|||
diy_fp = Double(max_double64).AsNormalizedDiyFp(); |
|||
Double(max_double64).NormalizedBoundaries(&boundary_minus, &boundary_plus); |
|||
CHECK_EQ(diy_fp.e(), boundary_minus.e()); |
|||
CHECK_EQ(diy_fp.e(), boundary_plus.e()); |
|||
// max-value does not have a significand of the form 2^p (for some p).
|
|||
// Therefore its boundaries are at the same distance.
|
|||
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f()); |
|||
CHECK((1 << 10) == diy_fp.f() - boundary_minus.f()); // NOLINT
|
|||
} |
@ -0,0 +1,116 @@ |
|||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
|||
|
|||
#include <stdlib.h> |
|||
|
|||
#include "v8.h" |
|||
|
|||
#include "platform.h" |
|||
#include "cctest.h" |
|||
#include "diy-fp.h" |
|||
#include "double.h" |
|||
#include "fast-dtoa.h" |
|||
#include "gay-shortest.h" |
|||
|
|||
using namespace v8::internal; |
|||
|
|||
static const int kBufferSize = 100; |
|||
|
|||
TEST(FastDtoaVariousDoubles) { |
|||
char buffer[kBufferSize]; |
|||
int sign; |
|||
int length; |
|||
int point; |
|||
int status; |
|||
|
|||
double min_double = 5e-324; |
|||
status = FastDtoa(min_double, buffer, &sign, &length, &point); |
|||
CHECK(status); |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("5", buffer); |
|||
CHECK_EQ(-323, point); |
|||
|
|||
double max_double = 1.7976931348623157e308; |
|||
status = FastDtoa(max_double, buffer, &sign, &length, &point); |
|||
CHECK(status); |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("17976931348623157", buffer); |
|||
CHECK_EQ(309, point); |
|||
|
|||
status = FastDtoa(4294967272.0, buffer, &sign, &length, &point); |
|||
CHECK(status); |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("4294967272", buffer); |
|||
CHECK_EQ(10, point); |
|||
|
|||
status = FastDtoa(4.1855804968213567e298, buffer, &sign, &length, &point); |
|||
CHECK(status); |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("4185580496821357", buffer); |
|||
CHECK_EQ(299, point); |
|||
|
|||
status = FastDtoa(5.5626846462680035e-309, buffer, &sign, &length, &point); |
|||
CHECK(status); |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("5562684646268003", buffer); |
|||
CHECK_EQ(-308, point); |
|||
|
|||
status = FastDtoa(2147483648.0, buffer, &sign, &length, &point); |
|||
CHECK(status); |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("2147483648", buffer); |
|||
CHECK_EQ(10, point); |
|||
|
|||
status = FastDtoa(3.5844466002796428e+298, buffer, &sign, &length, &point); |
|||
if (status) { // Not all FastDtoa variants manage to compute this number.
|
|||
CHECK_EQ("35844466002796428", buffer); |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ(299, point); |
|||
} |
|||
|
|||
uint64_t smallest_normal64 = V8_2PART_UINT64_C(0x00100000, 00000000); |
|||
double v = Double(smallest_normal64).value(); |
|||
status = FastDtoa(v, buffer, &sign, &length, &point); |
|||
if (status) { |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("22250738585072014", buffer); |
|||
CHECK_EQ(-307, point); |
|||
} |
|||
|
|||
uint64_t largest_denormal64 = V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); |
|||
v = Double(largest_denormal64).value(); |
|||
status = FastDtoa(v, buffer, &sign, &length, &point); |
|||
if (status) { |
|||
CHECK_EQ(0, sign); |
|||
CHECK_EQ("2225073858507201", buffer); |
|||
CHECK_EQ(-307, point); |
|||
} |
|||
} |
|||
|
|||
|
|||
TEST(FastDtoaGayShortest) { |
|||
char buffer[kBufferSize]; |
|||
bool status; |
|||
int sign; |
|||
int length; |
|||
int point; |
|||
int succeeded = 0; |
|||
int total = 0; |
|||
bool needed_max_length = false; |
|||
|
|||
Vector<const GayShortest> precomputed = PrecomputedShortestRepresentations(); |
|||
for (int i = 0; i < precomputed.length(); ++i) { |
|||
const GayShortest current_test = precomputed[i]; |
|||
total++; |
|||
double v = current_test.v; |
|||
status = FastDtoa(v, buffer, &sign, &length, &point); |
|||
CHECK_GE(kFastDtoaMaximalLength, length); |
|||
if (!status) continue; |
|||
if (length == kFastDtoaMaximalLength) needed_max_length = true; |
|||
succeeded++; |
|||
CHECK_EQ(0, sign); // All precomputed numbers are positive.
|
|||
CHECK_EQ(current_test.decimal_point, point); |
|||
CHECK_EQ(current_test.representation, buffer); |
|||
} |
|||
CHECK_GT(succeeded*1.0/total, 0.99); |
|||
CHECK(needed_max_length); |
|||
} |
@ -0,0 +1,33 @@ |
|||
// 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.
|
|||
|
|||
// Regression test for http://code.google.com/p/v8/issues/detail?id=646.
|
|||
|
|||
function f() { this.__proto__ = 42 } |
|||
var count = 0; |
|||
for (var x in new f()) count++; |
|||
assertEquals(0, count); |
@ -0,0 +1,286 @@ |
|||
;; 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. |
|||
|
|||
;; This is a Scheme script for the Bigloo compiler. Bigloo must be compiled with |
|||
;; support for bignums. The compilation of the script can be done as follows: |
|||
;; bigloo -static-bigloo -o generate-ten-powers generate-ten-powers.scm |
|||
;; |
|||
;; Generate approximations of 10^k. |
|||
|
|||
(module gen-ten-powers |
|||
(static (class Cached-Fast |
|||
v::bignum |
|||
e::bint |
|||
exact?::bool)) |
|||
(main my-main)) |
|||
|
|||
|
|||
;;----------------bignum shifts ----------------------------------------------- |
|||
(define (bit-lshbx::bignum x::bignum by::bint) |
|||
(if (<fx by 0) |
|||
#z0 |
|||
(*bx x (exptbx #z2 (fixnum->bignum by))))) |
|||
|
|||
(define (bit-rshbx::bignum x::bignum by::bint) |
|||
(if (<fx by 0) |
|||
#z0 |
|||
(/bx x (exptbx #z2 (fixnum->bignum by))))) |
|||
|
|||
;;----------------the actual power generation ------------------------------- |
|||
|
|||
;; e should be an indication. it might be too small. |
|||
(define (round-n-cut n e nb-bits) |
|||
(define max-container (- (bit-lshbx #z1 nb-bits) 1)) |
|||
(define (round n) |
|||
(case *round* |
|||
((down) n) |
|||
((up) |
|||
(+bx n |
|||
;; with the -1 it will only round up if the cut off part is |
|||
;; non-zero |
|||
(-bx (bit-lshbx #z1 |
|||
(-fx (+fx e nb-bits) 1)) |
|||
#z1))) |
|||
((round) |
|||
(+bx n |
|||
(bit-lshbx #z1 |
|||
(-fx (+fx e nb-bits) 2)))))) |
|||
(let* ((shift (-fx (+fx e nb-bits) 1)) |
|||
(cut (bit-rshbx (round n) shift)) |
|||
(exact? (=bx n (bit-lshbx cut shift)))) |
|||
(if (<=bx cut max-container) |
|||
(values cut e exact?) |
|||
(round-n-cut n (+fx e 1) nb-bits)))) |
|||
|
|||
(define (rounded-/bx x y) |
|||
(case *round* |
|||
((down) (/bx x y)) |
|||
((up) (+bx (/bx x y) #z1)) |
|||
((round) (let ((tmp (/bx (*bx #z2 x) y))) |
|||
(if (zerobx? (remainderbx tmp #z2)) |
|||
(/bx tmp #z2) |
|||
(+bx (/bx tmp #z2) #z1)))))) |
|||
|
|||
(define (generate-powers from to mantissa-size) |
|||
(let* ((nb-bits mantissa-size) |
|||
(offset (- from)) |
|||
(nb-elements (+ (- from) to 1)) |
|||
(vec (make-vector nb-elements)) |
|||
(max-container (- (bit-lshbx #z1 nb-bits) 1))) |
|||
;; the negative ones. 10^-1, 10^-2, etc. |
|||
;; We already know, that we can't be exact, so exact? will always be #f. |
|||
;; Basically we will have a ten^i that we will *10 at each iteration. We |
|||
;; want to create the matissa of 1/ten^i. However the mantissa must be |
|||
;; normalized (start with a 1). -> we have to shift the number. |
|||
;; We shift by multiplying with two^e. -> We encode two^e*(1/ten^i) == |
|||
;; two^e/ten^i. |
|||
(let loop ((i 1) |
|||
(ten^i #z10) |
|||
(two^e #z1) |
|||
(e 0)) |
|||
(unless (< (- i) from) |
|||
(if (>bx (/bx (*bx #z2 two^e) ten^i) max-container) |
|||
;; another shift would make the number too big. We are |
|||
;; hence normalized now. |
|||
(begin |
|||
(vector-set! vec (-fx offset i) |
|||
(instantiate::Cached-Fast |
|||
(v (rounded-/bx two^e ten^i)) |
|||
(e (negfx e)) |
|||
(exact? #f))) |
|||
(loop (+fx i 1) (*bx ten^i #z10) two^e e)) |
|||
(loop i ten^i (bit-lshbx two^e 1) (+fx e 1))))) |
|||
;; the positive ones 10^0, 10^1, etc. |
|||
;; start with 1.0. mantissa: 10...0 (1 followed by nb-bits-1 bits) |
|||
;; -> e = -(nb-bits-1) |
|||
;; exact? is true when the container can still hold the complete 10^i |
|||
(let loop ((i 0) |
|||
(n (bit-lshbx #z1 (-fx nb-bits 1))) |
|||
(e (-fx 1 nb-bits))) |
|||
(when (<= i to) |
|||
(receive (cut e exact?) |
|||
(round-n-cut n e nb-bits) |
|||
(vector-set! vec (+fx i offset) |
|||
(instantiate::Cached-Fast |
|||
(v cut) |
|||
(e e) |
|||
(exact? exact?))) |
|||
(loop (+fx i 1) (*bx n #z10) e)))) |
|||
vec)) |
|||
|
|||
(define (print-c powers from to struct-type |
|||
cache-name max-distance-name offset-name macro64) |
|||
(define (display-power power k) |
|||
(with-access::Cached-Fast power (v e exact?) |
|||
(let ((tmp-p (open-output-string))) |
|||
;; really hackish way of getting the digits |
|||
(display (format "~x" v) tmp-p) |
|||
(let ((str (close-output-port tmp-p))) |
|||
(printf " {~a(0x~a, ~a), ~a, ~a},\n" |
|||
macro64 |
|||
(substring str 0 8) |
|||
(substring str 8 16) |
|||
e |
|||
k))))) |
|||
(define (print-powers-reduced n) |
|||
(print "static const " struct-type " " cache-name |
|||
"(" n ")" |
|||
"[] = {") |
|||
(let loop ((i 0) |
|||
(nb-elements 0) |
|||
(last-e 0) |
|||
(max-distance 0)) |
|||
(cond |
|||
((>= i (vector-length powers)) |
|||
(print " };") |
|||
(print "static const int " max-distance-name "(" n ") = " |
|||
max-distance ";") |
|||
(print "// nb elements (" n "): " nb-elements)) |
|||
(else |
|||
(let* ((power (vector-ref powers i)) |
|||
(e (Cached-Fast-e power))) |
|||
(display-power power (+ i from)) |
|||
(loop (+ i n) |
|||
(+ nb-elements 1) |
|||
e |
|||
(cond |
|||
((=fx i 0) max-distance) |
|||
((> (- e last-e) max-distance) (- e last-e)) |
|||
(else max-distance)))))))) |
|||
(print "// Copyright 2010 the V8 project authors. All rights reserved.") |
|||
(print "// ------------ GENERATED FILE ----------------") |
|||
(print "// command used:") |
|||
(print "// " |
|||
(apply string-append (map (lambda (str) |
|||
(string-append " " str)) |
|||
*main-args*)) |
|||
" // NOLINT") |
|||
(print) |
|||
(print |
|||
"// This file is intended to be included inside another .h or .cc files\n" |
|||
"// with the following defines set:\n" |
|||
"// GRISU_CACHE_STRUCT: should expand to the name of a struct that will\n" |
|||
"// hold the cached powers of ten. Each entry will hold a 64-bit\n" |
|||
"// significand, a 16-bit signed binary exponent, and a 16-bit\n" |
|||
"// signed decimal exponent. Each entry will be constructed as follows:\n" |
|||
"// { significand, binary_exponent, decimal_exponent }.\n" |
|||
"// GRISU_CACHE_NAME(i): generates the name for the different caches.\n" |
|||
"// The parameter i will be a number in the range 1-20. A cache will\n" |
|||
"// hold every i'th element of a full cache. GRISU_CACHE_NAME(1) will\n" |
|||
"// thus hold all elements. The higher i the fewer elements it has.\n" |
|||
"// Ideally the user should only reference one cache and let the\n" |
|||
"// compiler remove the unused ones.\n" |
|||
"// GRISU_CACHE_MAX_DISTANCE(i): generates the name for the maximum\n" |
|||
"// binary exponent distance between all elements of a given cache.\n" |
|||
"// GRISU_CACHE_OFFSET: is used as variable name for the decimal\n" |
|||
"// exponent offset. It is equal to -cache[0].decimal_exponent.\n" |
|||
"// GRISU_UINT64_C: used to construct 64-bit values in a platform\n" |
|||
"// independent way. In order to encode 0x123456789ABCDEF0 the macro\n" |
|||
"// will be invoked as follows: GRISU_UINT64_C(0x12345678,9ABCDEF0).\n") |
|||
(print) |
|||
(print-powers-reduced 1) |
|||
(print-powers-reduced 2) |
|||
(print-powers-reduced 3) |
|||
(print-powers-reduced 4) |
|||
(print-powers-reduced 5) |
|||
(print-powers-reduced 6) |
|||
(print-powers-reduced 7) |
|||
(print-powers-reduced 8) |
|||
(print-powers-reduced 9) |
|||
(print-powers-reduced 10) |
|||
(print-powers-reduced 11) |
|||
(print-powers-reduced 12) |
|||
(print-powers-reduced 13) |
|||
(print-powers-reduced 14) |
|||
(print-powers-reduced 15) |
|||
(print-powers-reduced 16) |
|||
(print-powers-reduced 17) |
|||
(print-powers-reduced 18) |
|||
(print-powers-reduced 19) |
|||
(print-powers-reduced 20) |
|||
(print "static const int GRISU_CACHE_OFFSET = " (- from) ";")) |
|||
|
|||
;;----------------main -------------------------------------------------------- |
|||
(define *main-args* #f) |
|||
(define *mantissa-size* #f) |
|||
(define *dest* #f) |
|||
(define *round* #f) |
|||
(define *from* #f) |
|||
(define *to* #f) |
|||
|
|||
(define (my-main args) |
|||
(set! *main-args* args) |
|||
(args-parse (cdr args) |
|||
(section "Help") |
|||
(("?") (args-parse-usage #f)) |
|||
((("-h" "--help") (help "?, -h, --help" "This help message")) |
|||
(args-parse-usage #f)) |
|||
(section "Misc") |
|||
(("-o" ?file (help "The output file")) |
|||
(set! *dest* file)) |
|||
(("--mantissa-size" ?size (help "Container-size in bits")) |
|||
(set! *mantissa-size* (string->number size))) |
|||
(("--round" ?direction (help "Round bignums (down, round or up)")) |
|||
(set! *round* (string->symbol direction))) |
|||
(("--from" ?from (help "start at 10^from")) |
|||
(set! *from* (string->number from))) |
|||
(("--to" ?to (help "go up to 10^to")) |
|||
(set! *to* (string->number to))) |
|||
(else |
|||
(print "Illegal argument `" else "'. Usage:") |
|||
(args-parse-usage #f))) |
|||
(when (not *from*) |
|||
(error "generate-ten-powers" |
|||
"Missing from" |
|||
#f)) |
|||
(when (not *to*) |
|||
(error "generate-ten-powers" |
|||
"Missing to" |
|||
#f)) |
|||
(when (not *mantissa-size*) |
|||
(error "generate-ten-powers" |
|||
"Missing mantissa size" |
|||
#f)) |
|||
(when (not (memv *round* '(up down round))) |
|||
(error "generate-ten-powers" |
|||
"Missing round-method" |
|||
*round*)) |
|||
|
|||
(let ((dividers (generate-powers *from* *to* *mantissa-size*)) |
|||
(p (if (not *dest*) |
|||
(current-output-port) |
|||
(open-output-file *dest*)))) |
|||
(unwind-protect |
|||
(with-output-to-port p |
|||
(lambda () |
|||
(print-c dividers *from* *to* |
|||
"GRISU_CACHE_STRUCT" "GRISU_CACHE_NAME" |
|||
"GRISU_CACHE_MAX_DISTANCE" "GRISU_CACHE_OFFSET" |
|||
"GRISU_UINT64_C" |
|||
))) |
|||
(if *dest* |
|||
(close-output-port p))))) |
Loading…
Reference in new issue