mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
206 changed files with 24637 additions and 25568 deletions
@ -1,918 +0,0 @@ |
|||
diff --git a/deps/v8/SConstruct b/deps/v8/SConstruct
|
|||
index 7219e9d..b8de1b8 100644
|
|||
--- a/deps/v8/SConstruct
|
|||
+++ b/deps/v8/SConstruct
|
|||
@@ -670,7 +670,7 @@ SIMPLE_OPTIONS = {
|
|||
'help': 'the toolchain to use (' + TOOLCHAIN_GUESS + ')' |
|||
}, |
|||
'os': { |
|||
- 'values': ['freebsd', 'linux', 'macos', 'win32', 'android', 'openbsd', 'solaris'],
|
|||
+ 'values': ['freebsd', 'linux', 'macos', 'win32', 'android', 'openbsd', 'solaris', 'cygwin'],
|
|||
'default': OS_GUESS, |
|||
'help': 'the os to build for (' + OS_GUESS + ')' |
|||
}, |
|||
diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript
|
|||
index 8466a0c..9ff3414 100755
|
|||
--- a/deps/v8/src/SConscript
|
|||
+++ b/deps/v8/src/SConscript
|
|||
@@ -206,6 +206,7 @@ SOURCES = {
|
|||
'os:android': ['platform-linux.cc', 'platform-posix.cc'], |
|||
'os:macos': ['platform-macos.cc', 'platform-posix.cc'], |
|||
'os:solaris': ['platform-solaris.cc', 'platform-posix.cc'], |
|||
+ 'os:cygwin': ['platform-cygwin.cc', 'platform-posix.cc'],
|
|||
'os:nullos': ['platform-nullos.cc'], |
|||
'os:win32': ['platform-win32.cc'], |
|||
'mode:release': [], |
|||
diff --git a/deps/v8/src/platform-cygwin.cc b/deps/v8/src/platform-cygwin.cc
|
|||
new file mode 100644 |
|||
index 0000000..34410e8
|
|||
--- /dev/null
|
|||
+++ b/deps/v8/src/platform-cygwin.cc
|
|||
@@ -0,0 +1,858 @@
|
|||
+// 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.
|
|||
+
|
|||
+// Platform specific code for Cygwin goes here. For the POSIX comaptible parts
|
|||
+// the implementation is in platform-posix.cc.
|
|||
+
|
|||
+#include <pthread.h>
|
|||
+#include <semaphore.h>
|
|||
+#include <signal.h>
|
|||
+#include <sys/time.h>
|
|||
+#include <sys/resource.h>
|
|||
+#include <sys/types.h>
|
|||
+#include <stdlib.h>
|
|||
+
|
|||
+// Ubuntu Dapper requires memory pages to be marked as
|
|||
+// executable. Otherwise, OS raises an exception when executing code
|
|||
+// in that page.
|
|||
+#include <sys/types.h> // mmap & munmap
|
|||
+#include <sys/mman.h> // mmap & munmap
|
|||
+#include <sys/stat.h> // open
|
|||
+#include <fcntl.h> // open
|
|||
+#include <unistd.h> // sysconf
|
|||
+#ifdef __GLIBC__
|
|||
+#include <execinfo.h> // backtrace, backtrace_symbols
|
|||
+#endif // def __GLIBC__
|
|||
+#include <strings.h> // index
|
|||
+#include <errno.h>
|
|||
+#include <stdarg.h>
|
|||
+
|
|||
+#undef MAP_TYPE
|
|||
+
|
|||
+#include "v8.h"
|
|||
+
|
|||
+#include "platform.h"
|
|||
+#include "top.h"
|
|||
+#include "v8threads.h"
|
|||
+
|
|||
+
|
|||
+namespace v8 {
|
|||
+namespace internal {
|
|||
+
|
|||
+// 0 is never a valid thread id on Linux since tids and pids share a
|
|||
+// name space and pid 0 is reserved (see man 2 kill).
|
|||
+static const pthread_t kNoThread = (pthread_t) 0;
|
|||
+
|
|||
+
|
|||
+double ceiling(double x) {
|
|||
+ return ceil(x);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void OS::Setup() {
|
|||
+ // Seed the random number generator.
|
|||
+ // Convert the current time to a 64-bit integer first, before converting it
|
|||
+ // to an unsigned. Going directly can cause an overflow and the seed to be
|
|||
+ // set to all ones. The seed will be identical for different instances that
|
|||
+ // call this setup code within the same millisecond.
|
|||
+ uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
|
|||
+ srandom(static_cast<unsigned int>(seed));
|
|||
+}
|
|||
+
|
|||
+
|
|||
+uint64_t OS::CpuFeaturesImpliedByPlatform() {
|
|||
+#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
|
|||
+ // Here gcc is telling us that we are on an ARM and gcc is assuming that we
|
|||
+ // have VFP3 instructions. If gcc can assume it then so can we.
|
|||
+ return 1u << VFP3;
|
|||
+#elif CAN_USE_ARMV7_INSTRUCTIONS
|
|||
+ return 1u << ARMv7;
|
|||
+#else
|
|||
+ return 0; // Linux runs on anything.
|
|||
+#endif
|
|||
+}
|
|||
+
|
|||
+
|
|||
+#ifdef __arm__
|
|||
+bool OS::ArmCpuHasFeature(CpuFeature feature) {
|
|||
+ const char* search_string = NULL;
|
|||
+ const char* file_name = "/proc/cpuinfo";
|
|||
+ // Simple detection of VFP at runtime for Linux.
|
|||
+ // It is based on /proc/cpuinfo, which reveals hardware configuration
|
|||
+ // to user-space applications. According to ARM (mid 2009), no similar
|
|||
+ // facility is universally available on the ARM architectures,
|
|||
+ // so it's up to individual OSes to provide such.
|
|||
+ //
|
|||
+ // This is written as a straight shot one pass parser
|
|||
+ // and not using STL string and ifstream because,
|
|||
+ // on Linux, it's reading from a (non-mmap-able)
|
|||
+ // character special device.
|
|||
+ switch (feature) {
|
|||
+ case VFP3:
|
|||
+ search_string = "vfp";
|
|||
+ break;
|
|||
+ case ARMv7:
|
|||
+ search_string = "ARMv7";
|
|||
+ break;
|
|||
+ default:
|
|||
+ UNREACHABLE();
|
|||
+ }
|
|||
+
|
|||
+ FILE* f = NULL;
|
|||
+ const char* what = search_string;
|
|||
+
|
|||
+ if (NULL == (f = fopen(file_name, "r")))
|
|||
+ return false;
|
|||
+
|
|||
+ int k;
|
|||
+ while (EOF != (k = fgetc(f))) {
|
|||
+ if (k == *what) {
|
|||
+ ++what;
|
|||
+ while ((*what != '\0') && (*what == fgetc(f))) {
|
|||
+ ++what;
|
|||
+ }
|
|||
+ if (*what == '\0') {
|
|||
+ fclose(f);
|
|||
+ return true;
|
|||
+ } else {
|
|||
+ what = search_string;
|
|||
+ }
|
|||
+ }
|
|||
+ }
|
|||
+ fclose(f);
|
|||
+
|
|||
+ // Did not find string in the proc file.
|
|||
+ return false;
|
|||
+}
|
|||
+#endif // def __arm__
|
|||
+
|
|||
+
|
|||
+int OS::ActivationFrameAlignment() {
|
|||
+#ifdef V8_TARGET_ARCH_ARM
|
|||
+ // On EABI ARM targets this is required for fp correctness in the
|
|||
+ // runtime system.
|
|||
+ return 8;
|
|||
+#elif V8_TARGET_ARCH_MIPS
|
|||
+ return 8;
|
|||
+#endif
|
|||
+ // With gcc 4.4 the tree vectorization optimizer can generate code
|
|||
+ // that requires 16 byte alignment such as movdqa on x86.
|
|||
+ return 16;
|
|||
+}
|
|||
+
|
|||
+void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
|
|||
+ __asm__ __volatile__("" : : : "memory");
|
|||
+ // An x86 store acts as a release barrier.
|
|||
+ *ptr = value;
|
|||
+}
|
|||
+
|
|||
+const char* OS::LocalTimezone(double time) {
|
|||
+ if (isnan(time)) return "";
|
|||
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
|
|||
+ struct tm* t = localtime(&tv);
|
|||
+ if (NULL == t) return "";
|
|||
+ return tzname[0]; // The location of the timezone string on Cywin.
|
|||
+}
|
|||
+
|
|||
+
|
|||
+double OS::LocalTimeOffset() {
|
|||
+ //
|
|||
+ // On Cygwin, struct tm does not contain a tm_gmtoff field.
|
|||
+ time_t utc = time(NULL);
|
|||
+ ASSERT(utc != -1);
|
|||
+ struct tm* loc = localtime(&utc);
|
|||
+ ASSERT(loc != NULL);
|
|||
+ return static_cast<double>((mktime(loc) - utc) * msPerSecond);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+// We keep the lowest and highest addresses mapped as a quick way of
|
|||
+// determining that pointers are outside the heap (used mostly in assertions
|
|||
+// and verification). The estimate is conservative, ie, not all addresses in
|
|||
+// 'allocated' space are actually allocated to our heap. The range is
|
|||
+// [lowest, highest), inclusive on the low and and exclusive on the high end.
|
|||
+static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
|
|||
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
|
|||
+
|
|||
+
|
|||
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
|
|||
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
|
|||
+ highest_ever_allocated =
|
|||
+ Max(highest_ever_allocated,
|
|||
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
|
|||
+}
|
|||
+
|
|||
+
|
|||
+bool OS::IsOutsideAllocatedSpace(void* address) {
|
|||
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+size_t OS::AllocateAlignment() {
|
|||
+ return sysconf(_SC_PAGESIZE);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void* OS::Allocate(const size_t requested,
|
|||
+ size_t* allocated,
|
|||
+ bool is_executable) {
|
|||
+ const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
|
|||
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
|||
+ void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|||
+ if (mbase == MAP_FAILED) {
|
|||
+ LOG(StringEvent("OS::Allocate", "mmap failed"));
|
|||
+ return NULL;
|
|||
+ }
|
|||
+ *allocated = msize;
|
|||
+ UpdateAllocatedSpaceLimits(mbase, msize);
|
|||
+ return mbase;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void OS::Free(void* address, const size_t size) {
|
|||
+ // TODO(1240712): munmap has a return value which is ignored here.
|
|||
+ int result = munmap(address, size);
|
|||
+ USE(result);
|
|||
+ ASSERT(result == 0);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+#ifdef ENABLE_HEAP_PROTECTION
|
|||
+
|
|||
+void OS::Protect(void* address, size_t size) {
|
|||
+ // TODO(1240712): mprotect has a return value which is ignored here.
|
|||
+ mprotect(address, size, PROT_READ);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void OS::Unprotect(void* address, size_t size, bool is_executable) {
|
|||
+ // TODO(1240712): mprotect has a return value which is ignored here.
|
|||
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
|||
+ mprotect(address, size, prot);
|
|||
+}
|
|||
+
|
|||
+#endif
|
|||
+
|
|||
+
|
|||
+void OS::Sleep(int milliseconds) {
|
|||
+ unsigned int ms = static_cast<unsigned int>(milliseconds);
|
|||
+ usleep(1000 * ms);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void OS::Abort() {
|
|||
+ // Redirect to std abort to signal abnormal program termination.
|
|||
+ abort();
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void OS::DebugBreak() {
|
|||
+// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
|
|||
+// which is the architecture of generated code).
|
|||
+#if (defined(__arm__) || defined(__thumb__)) && \
|
|||
+ defined(CAN_USE_ARMV5_INSTRUCTIONS)
|
|||
+ asm("bkpt 0");
|
|||
+#elif defined(__mips__)
|
|||
+ asm("break");
|
|||
+#else
|
|||
+ asm("int $3");
|
|||
+#endif
|
|||
+}
|
|||
+
|
|||
+
|
|||
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
|
|||
+ public:
|
|||
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
|
|||
+ : file_(file), memory_(memory), size_(size) { }
|
|||
+ virtual ~PosixMemoryMappedFile();
|
|||
+ virtual void* memory() { return memory_; }
|
|||
+ private:
|
|||
+ FILE* file_;
|
|||
+ void* memory_;
|
|||
+ int size_;
|
|||
+};
|
|||
+
|
|||
+
|
|||
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
|
|||
+ void* initial) {
|
|||
+ FILE* file = fopen(name, "w+");
|
|||
+ if (file == NULL) return NULL;
|
|||
+ int result = fwrite(initial, size, 1, file);
|
|||
+ if (result < 1) {
|
|||
+ fclose(file);
|
|||
+ return NULL;
|
|||
+ }
|
|||
+ void* memory =
|
|||
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
|
|||
+ return new PosixMemoryMappedFile(file, memory, size);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
|
|||
+ if (memory_) munmap(memory_, size_);
|
|||
+ fclose(file_);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void OS::LogSharedLibraryAddresses() {
|
|||
+#ifdef ENABLE_LOGGING_AND_PROFILING
|
|||
+ // This function assumes that the layout of the file is as follows:
|
|||
+ // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
|
|||
+ // If we encounter an unexpected situation we abort scanning further entries.
|
|||
+ FILE* fp = fopen("/proc/self/maps", "r");
|
|||
+ if (fp == NULL) return;
|
|||
+
|
|||
+ // Allocate enough room to be able to store a full file name.
|
|||
+ const int kLibNameLen = FILENAME_MAX + 1;
|
|||
+ char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
|
|||
+
|
|||
+ // This loop will terminate once the scanning hits an EOF.
|
|||
+ while (true) {
|
|||
+ uintptr_t start, end;
|
|||
+ char attr_r, attr_w, attr_x, attr_p;
|
|||
+ // Parse the addresses and permission bits at the beginning of the line.
|
|||
+ if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
|
|||
+ if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
|
|||
+
|
|||
+ int c;
|
|||
+ if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
|
|||
+ // Found a read-only executable entry. Skip characters until we reach
|
|||
+ // the beginning of the filename or the end of the line.
|
|||
+ do {
|
|||
+ c = getc(fp);
|
|||
+ } while ((c != EOF) && (c != '\n') && (c != '/'));
|
|||
+ if (c == EOF) break; // EOF: Was unexpected, just exit.
|
|||
+
|
|||
+ // Process the filename if found.
|
|||
+ if (c == '/') {
|
|||
+ ungetc(c, fp); // Push the '/' back into the stream to be read below.
|
|||
+
|
|||
+ // Read to the end of the line. Exit if the read fails.
|
|||
+ if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
|
|||
+
|
|||
+ // Drop the newline character read by fgets. We do not need to check
|
|||
+ // for a zero-length string because we know that we at least read the
|
|||
+ // '/' character.
|
|||
+ lib_name[strlen(lib_name) - 1] = '\0';
|
|||
+ } else {
|
|||
+ // No library name found, just record the raw address range.
|
|||
+ snprintf(lib_name, kLibNameLen,
|
|||
+ "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
|
|||
+ }
|
|||
+ LOG(SharedLibraryEvent(lib_name, start, end));
|
|||
+ } else {
|
|||
+ // Entry not describing executable data. Skip to end of line to setup
|
|||
+ // reading the next entry.
|
|||
+ do {
|
|||
+ c = getc(fp);
|
|||
+ } while ((c != EOF) && (c != '\n'));
|
|||
+ if (c == EOF) break;
|
|||
+ }
|
|||
+ }
|
|||
+ free(lib_name);
|
|||
+ fclose(fp);
|
|||
+#endif
|
|||
+}
|
|||
+
|
|||
+
|
|||
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
|
|||
+ // backtrace is a glibc extension.
|
|||
+#ifdef __GLIBC__
|
|||
+ int frames_size = frames.length();
|
|||
+ ScopedVector<void*> addresses(frames_size);
|
|||
+
|
|||
+ int frames_count = backtrace(addresses.start(), frames_size);
|
|||
+
|
|||
+ char** symbols = backtrace_symbols(addresses.start(), frames_count);
|
|||
+ if (symbols == NULL) {
|
|||
+ return kStackWalkError;
|
|||
+ }
|
|||
+
|
|||
+ for (int i = 0; i < frames_count; i++) {
|
|||
+ frames[i].address = addresses[i];
|
|||
+ // Format a text representation of the frame based on the information
|
|||
+ // available.
|
|||
+ SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
|
|||
+ "%s",
|
|||
+ symbols[i]);
|
|||
+ // Make sure line termination is in place.
|
|||
+ frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
|
|||
+ }
|
|||
+
|
|||
+ free(symbols);
|
|||
+
|
|||
+ return frames_count;
|
|||
+#else // ndef __GLIBC__
|
|||
+ return 0;
|
|||
+#endif // ndef __GLIBC__
|
|||
+}
|
|||
+
|
|||
+
|
|||
+// Constants used for mmap.
|
|||
+static const int kMmapFd = -1;
|
|||
+static const int kMmapFdOffset = 0;
|
|||
+
|
|||
+
|
|||
+VirtualMemory::VirtualMemory(size_t size) {
|
|||
+ address_ = mmap(NULL, size, PROT_NONE,
|
|||
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
|
|||
+ kMmapFd, kMmapFdOffset);
|
|||
+ size_ = size;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+VirtualMemory::~VirtualMemory() {
|
|||
+ if (IsReserved()) {
|
|||
+ if (0 == munmap(address(), size())) address_ = MAP_FAILED;
|
|||
+ }
|
|||
+}
|
|||
+
|
|||
+
|
|||
+bool VirtualMemory::IsReserved() {
|
|||
+ return address_ != MAP_FAILED;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
|
|||
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
|||
+
|
|||
+#ifdef HAS_MAP_FIXED
|
|||
+ if (MAP_FAILED == mmap(address, size, prot,
|
|||
+ MAP_PRIVATE | MAP_ANONYMOUS, // | MAP_FIXED, - Cygwin doesn't have MAP_FIXED
|
|||
+ kMmapFd, kMmapFdOffset)) {
|
|||
+ return false;
|
|||
+ }
|
|||
+#else
|
|||
+ if (mprotect(address, size, prot) != 0) {
|
|||
+ return false;
|
|||
+ }
|
|||
+#endif
|
|||
+
|
|||
+ UpdateAllocatedSpaceLimits(address, size);
|
|||
+ return true;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+bool VirtualMemory::Uncommit(void* address, size_t size) {
|
|||
+ return mmap(address, size, PROT_NONE,
|
|||
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, // | MAP_FIXED, - Cygwin doesn't have MAP_FIXED
|
|||
+ kMmapFd, kMmapFdOffset) != MAP_FAILED;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+class ThreadHandle::PlatformData : public Malloced {
|
|||
+ public:
|
|||
+ explicit PlatformData(ThreadHandle::Kind kind) {
|
|||
+ Initialize(kind);
|
|||
+ }
|
|||
+
|
|||
+ void Initialize(ThreadHandle::Kind kind) {
|
|||
+ switch (kind) {
|
|||
+ case ThreadHandle::SELF: thread_ = pthread_self(); break;
|
|||
+ case ThreadHandle::INVALID: thread_ = kNoThread; break;
|
|||
+ }
|
|||
+ }
|
|||
+
|
|||
+ pthread_t thread_; // Thread handle for pthread.
|
|||
+};
|
|||
+
|
|||
+
|
|||
+ThreadHandle::ThreadHandle(Kind kind) {
|
|||
+ data_ = new PlatformData(kind);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
|
|||
+ data_->Initialize(kind);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+ThreadHandle::~ThreadHandle() {
|
|||
+ delete data_;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+bool ThreadHandle::IsSelf() const {
|
|||
+ return pthread_equal(data_->thread_, pthread_self());
|
|||
+}
|
|||
+
|
|||
+
|
|||
+bool ThreadHandle::IsValid() const {
|
|||
+ return data_->thread_ != kNoThread;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
|
|||
+}
|
|||
+
|
|||
+
|
|||
+Thread::~Thread() {
|
|||
+}
|
|||
+
|
|||
+
|
|||
+static void* ThreadEntry(void* arg) {
|
|||
+ Thread* thread = reinterpret_cast<Thread*>(arg);
|
|||
+ // This is also initialized by the first argument to pthread_create() but we
|
|||
+ // don't know which thread will run first (the original thread or the new
|
|||
+ // one) so we initialize it here too.
|
|||
+ thread->thread_handle_data()->thread_ = pthread_self();
|
|||
+ ASSERT(thread->IsValid());
|
|||
+ thread->Run();
|
|||
+ return NULL;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void Thread::Start() {
|
|||
+ pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
|
|||
+ ASSERT(IsValid());
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void Thread::Join() {
|
|||
+ pthread_join(thread_handle_data()->thread_, NULL);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
|
|||
+ pthread_key_t key;
|
|||
+ int result = pthread_key_create(&key, NULL);
|
|||
+ USE(result);
|
|||
+ ASSERT(result == 0);
|
|||
+ return static_cast<LocalStorageKey>(key);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
|
|||
+ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
|
|||
+ int result = pthread_key_delete(pthread_key);
|
|||
+ USE(result);
|
|||
+ ASSERT(result == 0);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void* Thread::GetThreadLocal(LocalStorageKey key) {
|
|||
+ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
|
|||
+ return pthread_getspecific(pthread_key);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
|
|||
+ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
|
|||
+ pthread_setspecific(pthread_key, value);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void Thread::YieldCPU() {
|
|||
+ sched_yield();
|
|||
+}
|
|||
+
|
|||
+
|
|||
+class CygwinMutex : public Mutex {
|
|||
+ public:
|
|||
+
|
|||
+ CygwinMutex() {
|
|||
+ pthread_mutexattr_t attrs;
|
|||
+ memset(&attrs, 0, sizeof(attrs));
|
|||
+
|
|||
+ int result = pthread_mutexattr_init(&attrs);
|
|||
+ ASSERT(result == 0);
|
|||
+ result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
|
|||
+ ASSERT(result == 0);
|
|||
+ result = pthread_mutex_init(&mutex_, &attrs);
|
|||
+ ASSERT(result == 0);
|
|||
+ }
|
|||
+
|
|||
+ virtual ~CygwinMutex() { pthread_mutex_destroy(&mutex_); }
|
|||
+
|
|||
+ virtual int Lock() {
|
|||
+ int result = pthread_mutex_lock(&mutex_);
|
|||
+ return result;
|
|||
+ }
|
|||
+
|
|||
+ virtual int Unlock() {
|
|||
+ int result = pthread_mutex_unlock(&mutex_);
|
|||
+ return result;
|
|||
+ }
|
|||
+
|
|||
+ private:
|
|||
+ pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
|
|||
+};
|
|||
+
|
|||
+
|
|||
+Mutex* OS::CreateMutex() {
|
|||
+ return new CygwinMutex();
|
|||
+}
|
|||
+
|
|||
+
|
|||
+class CygwinSemaphore : public Semaphore {
|
|||
+ public:
|
|||
+ explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); }
|
|||
+ virtual ~CygwinSemaphore() { sem_destroy(&sem_); }
|
|||
+
|
|||
+ virtual void Wait();
|
|||
+ virtual bool Wait(int timeout);
|
|||
+ virtual void Signal() { sem_post(&sem_); }
|
|||
+ private:
|
|||
+ sem_t sem_;
|
|||
+};
|
|||
+
|
|||
+
|
|||
+void CygwinSemaphore::Wait() {
|
|||
+ while (true) {
|
|||
+ int result = sem_wait(&sem_);
|
|||
+ if (result == 0) return; // Successfully got semaphore.
|
|||
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
|
|||
+ }
|
|||
+}
|
|||
+
|
|||
+
|
|||
+#ifndef TIMEVAL_TO_TIMESPEC
|
|||
+#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
|
|||
+ (ts)->tv_sec = (tv)->tv_sec; \
|
|||
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
|
|||
+} while (false)
|
|||
+#endif
|
|||
+
|
|||
+
|
|||
+bool CygwinSemaphore::Wait(int timeout) {
|
|||
+ const long kOneSecondMicros = 1000000; // NOLINT
|
|||
+
|
|||
+ // Split timeout into second and nanosecond parts.
|
|||
+ struct timeval delta;
|
|||
+ delta.tv_usec = timeout % kOneSecondMicros;
|
|||
+ delta.tv_sec = timeout / kOneSecondMicros;
|
|||
+
|
|||
+ struct timeval current_time;
|
|||
+ // Get the current time.
|
|||
+ if (gettimeofday(¤t_time, NULL) == -1) {
|
|||
+ return false;
|
|||
+ }
|
|||
+
|
|||
+ // Calculate time for end of timeout.
|
|||
+ struct timeval end_time;
|
|||
+ timeradd(¤t_time, &delta, &end_time);
|
|||
+
|
|||
+ struct timespec ts;
|
|||
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
|
|||
+ // Wait for semaphore signalled or timeout.
|
|||
+ while (true) {
|
|||
+ int result = sem_timedwait(&sem_, &ts);
|
|||
+ if (result == 0) return true; // Successfully got semaphore.
|
|||
+ if (result > 0) {
|
|||
+ // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
|
|||
+ errno = result;
|
|||
+ result = -1;
|
|||
+ }
|
|||
+ if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
|
|||
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
|
|||
+ }
|
|||
+}
|
|||
+
|
|||
+
|
|||
+Semaphore* OS::CreateSemaphore(int count) {
|
|||
+ return new CygwinSemaphore(count);
|
|||
+}
|
|||
+
|
|||
+
|
|||
+#ifdef ENABLE_LOGGING_AND_PROFILING
|
|||
+
|
|||
+static Sampler* active_sampler_ = NULL;
|
|||
+static pthread_t vm_thread_ = 0;
|
|||
+
|
|||
+
|
|||
+#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
|
|||
+// Android runs a fairly new Linux kernel, so signal info is there,
|
|||
+// but the C library doesn't have the structs defined.
|
|||
+
|
|||
+struct sigcontext {
|
|||
+ uint32_t trap_no;
|
|||
+ uint32_t error_code;
|
|||
+ uint32_t oldmask;
|
|||
+ uint32_t gregs[16];
|
|||
+ uint32_t arm_cpsr;
|
|||
+ uint32_t fault_address;
|
|||
+};
|
|||
+typedef uint32_t __sigset_t;
|
|||
+typedef struct sigcontext mcontext_t;
|
|||
+typedef struct ucontext {
|
|||
+ uint32_t uc_flags;
|
|||
+ struct ucontext* uc_link;
|
|||
+ stack_t uc_stack;
|
|||
+ mcontext_t uc_mcontext;
|
|||
+ __sigset_t uc_sigmask;
|
|||
+} ucontext_t;
|
|||
+enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
|
|||
+
|
|||
+#endif
|
|||
+
|
|||
+
|
|||
+// A function that determines if a signal handler is called in the context
|
|||
+// of a VM thread.
|
|||
+//
|
|||
+// The problem is that SIGPROF signal can be delivered to an arbitrary thread
|
|||
+// (see http://code.google.com/p/google-perftools/issues/detail?id=106#c2)
|
|||
+// So, if the signal is being handled in the context of a non-VM thread,
|
|||
+// it means that the VM thread is running, and trying to sample its stack can
|
|||
+// cause a crash.
|
|||
+static inline bool IsVmThread() {
|
|||
+ // In the case of a single VM thread, this check is enough.
|
|||
+ if (pthread_equal(pthread_self(), vm_thread_)) return true;
|
|||
+ // If there are multiple threads that use VM, they must have a thread id
|
|||
+ // stored in TLS. To verify that the thread is really executing VM,
|
|||
+ // we check Top's data. Having that ThreadManager::RestoreThread first
|
|||
+ // restores ThreadLocalTop from TLS, and only then erases the TLS value,
|
|||
+ // reading Top::thread_id() should not be affected by races.
|
|||
+ if (ThreadManager::HasId() && !ThreadManager::IsArchived() &&
|
|||
+ ThreadManager::CurrentId() == Top::thread_id()) {
|
|||
+ return true;
|
|||
+ }
|
|||
+ return false;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
|
|||
+#ifndef V8_HOST_ARCH_MIPS
|
|||
+ USE(info);
|
|||
+ if (signal != SIGPROF) return;
|
|||
+ if (active_sampler_ == NULL) return;
|
|||
+
|
|||
+ TickSample sample_obj;
|
|||
+ TickSample* sample = CpuProfiler::TickSampleEvent();
|
|||
+ if (sample == NULL) sample = &sample_obj;
|
|||
+
|
|||
+ // We always sample the VM state.
|
|||
+ sample->state = VMState::current_state();
|
|||
+
|
|||
+#if 0
|
|||
+ // If profiling, we extract the current pc and sp.
|
|||
+ if (active_sampler_->IsProfiling()) {
|
|||
+ // Extracting the sample from the context is extremely machine dependent.
|
|||
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
|
|||
+ mcontext_t& mcontext = ucontext->uc_mcontext;
|
|||
+#if V8_HOST_ARCH_IA32
|
|||
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
|
|||
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
|
|||
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
|
|||
+#elif V8_HOST_ARCH_X64
|
|||
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
|
|||
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
|
|||
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
|
|||
+#elif V8_HOST_ARCH_ARM
|
|||
+// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
|
|||
+#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
|
|||
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
|
|||
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
|
|||
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
|
|||
+#else
|
|||
+ sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
|
|||
+ sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
|
|||
+ sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
|
|||
+#endif
|
|||
+#elif V8_HOST_ARCH_MIPS
|
|||
+ // Implement this on MIPS.
|
|||
+ UNIMPLEMENTED();
|
|||
+#endif
|
|||
+ if (IsVmThread()) {
|
|||
+ active_sampler_->SampleStack(sample);
|
|||
+ }
|
|||
+ }
|
|||
+#endif
|
|||
+
|
|||
+ active_sampler_->Tick(sample);
|
|||
+#endif
|
|||
+}
|
|||
+
|
|||
+
|
|||
+class Sampler::PlatformData : public Malloced {
|
|||
+ public:
|
|||
+ PlatformData() {
|
|||
+ signal_handler_installed_ = false;
|
|||
+ }
|
|||
+
|
|||
+ bool signal_handler_installed_;
|
|||
+ struct sigaction old_signal_handler_;
|
|||
+ struct itimerval old_timer_value_;
|
|||
+};
|
|||
+
|
|||
+
|
|||
+Sampler::Sampler(int interval, bool profiling)
|
|||
+ : interval_(interval), profiling_(profiling), active_(false) {
|
|||
+ data_ = new PlatformData();
|
|||
+}
|
|||
+
|
|||
+
|
|||
+Sampler::~Sampler() {
|
|||
+ delete data_;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void Sampler::Start() {
|
|||
+ // There can only be one active sampler at the time on POSIX
|
|||
+ // platforms.
|
|||
+ if (active_sampler_ != NULL) return;
|
|||
+
|
|||
+ vm_thread_ = pthread_self();
|
|||
+
|
|||
+ // Request profiling signals.
|
|||
+ struct sigaction sa;
|
|||
+ sa.sa_sigaction = ProfilerSignalHandler;
|
|||
+ sigemptyset(&sa.sa_mask);
|
|||
+ sa.sa_flags = SA_SIGINFO;
|
|||
+ if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
|
|||
+ data_->signal_handler_installed_ = true;
|
|||
+
|
|||
+ // Set the itimer to generate a tick for each interval.
|
|||
+ itimerval itimer;
|
|||
+ itimer.it_interval.tv_sec = interval_ / 1000;
|
|||
+ itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
|
|||
+ itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
|
|||
+ itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
|
|||
+ setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
|
|||
+
|
|||
+ // Set this sampler as the active sampler.
|
|||
+ active_sampler_ = this;
|
|||
+ active_ = true;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+void Sampler::Stop() {
|
|||
+ // Restore old signal handler
|
|||
+ if (data_->signal_handler_installed_) {
|
|||
+ setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
|
|||
+ sigaction(SIGPROF, &data_->old_signal_handler_, 0);
|
|||
+ data_->signal_handler_installed_ = false;
|
|||
+ }
|
|||
+
|
|||
+ // This sampler is no longer the active sampler.
|
|||
+ active_sampler_ = NULL;
|
|||
+ active_ = false;
|
|||
+}
|
|||
+
|
|||
+
|
|||
+#endif // ENABLE_LOGGING_AND_PROFILING
|
|||
+
|
|||
+} } // namespace v8::internal
|
|||
diff --git a/deps/v8/src/platform.h b/deps/v8/src/platform.h
|
|||
index d63ca5e..1091ba6 100644
|
|||
--- a/deps/v8/src/platform.h
|
|||
+++ b/deps/v8/src/platform.h
|
|||
@@ -360,7 +360,11 @@ class ThreadHandle {
|
|||
class Thread: public ThreadHandle { |
|||
public: |
|||
// Opaque data type for thread-local storage keys. |
|||
+#ifndef __CYGWIN__
|
|||
enum LocalStorageKey {}; |
|||
+#else
|
|||
+ typedef void *LocalStorageKey;
|
|||
+#endif
|
|||
|
|||
// Create new thread. |
|||
Thread(); |
|||
diff --git a/deps/v8/tools/utils.py b/deps/v8/tools/utils.py
|
|||
index 3a55722..505c398 100644
|
|||
--- a/deps/v8/tools/utils.py
|
|||
+++ b/deps/v8/tools/utils.py
|
|||
@@ -59,6 +59,8 @@ def GuessOS():
|
|||
return 'openbsd' |
|||
elif id == 'SunOS': |
|||
return 'solaris' |
|||
+ elif id.find('CYGWIN') >= 0:
|
|||
+ return 'cygwin'
|
|||
else: |
|||
return None |
|||
|
@ -1,324 +0,0 @@ |
|||
# Copyright 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. |
|||
|
|||
import sys |
|||
from os.path import join, dirname, abspath |
|||
root_dir = dirname(File('SConstruct').rfile().abspath) |
|||
sys.path.append(join(root_dir, 'tools')) |
|||
import js2c |
|||
Import('context') |
|||
|
|||
|
|||
SOURCES = { |
|||
'all': Split(""" |
|||
accessors.cc |
|||
allocation.cc |
|||
api.cc |
|||
assembler.cc |
|||
ast.cc |
|||
bootstrapper.cc |
|||
builtins.cc |
|||
checks.cc |
|||
circular-queue.cc |
|||
code-stubs.cc |
|||
codegen.cc |
|||
compilation-cache.cc |
|||
compiler.cc |
|||
contexts.cc |
|||
conversions.cc |
|||
counters.cc |
|||
cpu-profiler.cc |
|||
data-flow.cc |
|||
dateparser.cc |
|||
debug-agent.cc |
|||
debug.cc |
|||
disassembler.cc |
|||
diy-fp.cc |
|||
dtoa.cc |
|||
execution.cc |
|||
factory.cc |
|||
flags.cc |
|||
flow-graph.cc |
|||
frame-element.cc |
|||
frames.cc |
|||
full-codegen.cc |
|||
func-name-inferrer.cc |
|||
global-handles.cc |
|||
fast-dtoa.cc |
|||
fixed-dtoa.cc |
|||
handles.cc |
|||
hashmap.cc |
|||
heap-profiler.cc |
|||
heap.cc |
|||
ic.cc |
|||
interpreter-irregexp.cc |
|||
jsregexp.cc |
|||
jump-target.cc |
|||
liveedit.cc |
|||
log-utils.cc |
|||
log.cc |
|||
mark-compact.cc |
|||
messages.cc |
|||
objects.cc |
|||
objects-visiting.cc |
|||
oprofile-agent.cc |
|||
parser.cc |
|||
profile-generator.cc |
|||
property.cc |
|||
regexp-macro-assembler-irregexp.cc |
|||
regexp-macro-assembler.cc |
|||
regexp-stack.cc |
|||
register-allocator.cc |
|||
rewriter.cc |
|||
runtime.cc |
|||
scanner.cc |
|||
scopeinfo.cc |
|||
scopes.cc |
|||
serialize.cc |
|||
snapshot-common.cc |
|||
spaces.cc |
|||
string-stream.cc |
|||
stub-cache.cc |
|||
token.cc |
|||
top.cc |
|||
type-info.cc |
|||
unicode.cc |
|||
utils.cc |
|||
v8-counters.cc |
|||
v8.cc |
|||
v8threads.cc |
|||
variables.cc |
|||
version.cc |
|||
virtual-frame.cc |
|||
vm-state.cc |
|||
zone.cc |
|||
"""), |
|||
'arch:arm': Split(""" |
|||
jump-target-light.cc |
|||
virtual-frame-light.cc |
|||
arm/builtins-arm.cc |
|||
arm/codegen-arm.cc |
|||
arm/constants-arm.cc |
|||
arm/cpu-arm.cc |
|||
arm/debug-arm.cc |
|||
arm/disasm-arm.cc |
|||
arm/frames-arm.cc |
|||
arm/full-codegen-arm.cc |
|||
arm/ic-arm.cc |
|||
arm/jump-target-arm.cc |
|||
arm/macro-assembler-arm.cc |
|||
arm/regexp-macro-assembler-arm.cc |
|||
arm/register-allocator-arm.cc |
|||
arm/stub-cache-arm.cc |
|||
arm/virtual-frame-arm.cc |
|||
arm/assembler-arm.cc |
|||
"""), |
|||
'arch:mips': Split(""" |
|||
mips/assembler-mips.cc |
|||
mips/builtins-mips.cc |
|||
mips/codegen-mips.cc |
|||
mips/constants-mips.cc |
|||
mips/cpu-mips.cc |
|||
mips/debug-mips.cc |
|||
mips/disasm-mips.cc |
|||
mips/full-codegen-mips.cc |
|||
mips/frames-mips.cc |
|||
mips/ic-mips.cc |
|||
mips/jump-target-mips.cc |
|||
mips/macro-assembler-mips.cc |
|||
mips/register-allocator-mips.cc |
|||
mips/stub-cache-mips.cc |
|||
mips/virtual-frame-mips.cc |
|||
"""), |
|||
'arch:ia32': Split(""" |
|||
jump-target-heavy.cc |
|||
virtual-frame-heavy.cc |
|||
ia32/assembler-ia32.cc |
|||
ia32/builtins-ia32.cc |
|||
ia32/codegen-ia32.cc |
|||
ia32/cpu-ia32.cc |
|||
ia32/debug-ia32.cc |
|||
ia32/disasm-ia32.cc |
|||
ia32/frames-ia32.cc |
|||
ia32/full-codegen-ia32.cc |
|||
ia32/ic-ia32.cc |
|||
ia32/jump-target-ia32.cc |
|||
ia32/macro-assembler-ia32.cc |
|||
ia32/regexp-macro-assembler-ia32.cc |
|||
ia32/register-allocator-ia32.cc |
|||
ia32/stub-cache-ia32.cc |
|||
ia32/virtual-frame-ia32.cc |
|||
"""), |
|||
'arch:x64': Split(""" |
|||
jump-target-heavy.cc |
|||
virtual-frame-heavy.cc |
|||
x64/assembler-x64.cc |
|||
x64/builtins-x64.cc |
|||
x64/codegen-x64.cc |
|||
x64/cpu-x64.cc |
|||
x64/debug-x64.cc |
|||
x64/disasm-x64.cc |
|||
x64/frames-x64.cc |
|||
x64/full-codegen-x64.cc |
|||
x64/ic-x64.cc |
|||
x64/jump-target-x64.cc |
|||
x64/macro-assembler-x64.cc |
|||
x64/regexp-macro-assembler-x64.cc |
|||
x64/register-allocator-x64.cc |
|||
x64/stub-cache-x64.cc |
|||
x64/virtual-frame-x64.cc |
|||
"""), |
|||
'simulator:arm': ['arm/simulator-arm.cc'], |
|||
'simulator:mips': ['mips/simulator-mips.cc'], |
|||
'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'], |
|||
'os:openbsd': ['platform-openbsd.cc', 'platform-posix.cc'], |
|||
'os:linux': ['platform-linux.cc', 'platform-posix.cc'], |
|||
'os:android': ['platform-linux.cc', 'platform-posix.cc'], |
|||
'os:macos': ['platform-macos.cc', 'platform-posix.cc'], |
|||
'os:solaris': ['platform-solaris.cc', 'platform-posix.cc'], |
|||
'os:nullos': ['platform-nullos.cc'], |
|||
'os:win32': ['platform-win32.cc'], |
|||
'mode:release': [], |
|||
'mode:debug': [ |
|||
'objects-debug.cc', 'prettyprinter.cc', 'regexp-macro-assembler-tracer.cc' |
|||
] |
|||
} |
|||
|
|||
|
|||
D8_FILES = { |
|||
'all': [ |
|||
'd8.cc', 'd8-debug.cc' |
|||
], |
|||
'os:linux': [ |
|||
'd8-posix.cc' |
|||
], |
|||
'os:macos': [ |
|||
'd8-posix.cc' |
|||
], |
|||
'os:android': [ |
|||
'd8-posix.cc' |
|||
], |
|||
'os:freebsd': [ |
|||
'd8-posix.cc' |
|||
], |
|||
'os:openbsd': [ |
|||
'd8-posix.cc' |
|||
], |
|||
'os:solaris': [ |
|||
'd8-posix.cc' |
|||
], |
|||
'os:win32': [ |
|||
'd8-windows.cc' |
|||
], |
|||
'os:nullos': [ |
|||
'd8-windows.cc' # Empty implementation at the moment. |
|||
], |
|||
'console:readline': [ |
|||
'd8-readline.cc' |
|||
] |
|||
} |
|||
|
|||
|
|||
LIBRARY_FILES = ''' |
|||
runtime.js |
|||
v8natives.js |
|||
array.js |
|||
string.js |
|||
uri.js |
|||
math.js |
|||
messages.js |
|||
apinatives.js |
|||
date.js |
|||
regexp.js |
|||
json.js |
|||
liveedit-debugger.js |
|||
mirror-debugger.js |
|||
debug-debugger.js |
|||
'''.split() |
|||
|
|||
|
|||
def Abort(message): |
|||
print message |
|||
sys.exit(1) |
|||
|
|||
|
|||
def ConfigureObjectFiles(): |
|||
env = Environment() |
|||
env.Replace(**context.flags['v8']) |
|||
context.ApplyEnvOverrides(env) |
|||
env['BUILDERS']['JS2C'] = Builder(action=js2c.JS2C) |
|||
env['BUILDERS']['Snapshot'] = Builder(action='$SOURCE $TARGET --logfile "$LOGFILE" --log-snapshot-positions') |
|||
|
|||
# Build the standard platform-independent source files. |
|||
source_files = context.GetRelevantSources(SOURCES) |
|||
|
|||
d8_files = context.GetRelevantSources(D8_FILES) |
|||
d8_js = env.JS2C('d8-js.cc', 'd8.js', TYPE='D8') |
|||
d8_js_obj = context.ConfigureObject(env, d8_js, CPPPATH=['.']) |
|||
d8_objs = [context.ConfigureObject(env, [d8_files]), d8_js_obj] |
|||
|
|||
# Combine the JavaScript library files into a single C++ file and |
|||
# compile it. |
|||
library_files = [s for s in LIBRARY_FILES] |
|||
library_files.append('macros.py') |
|||
libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files, TYPE='CORE') |
|||
libraries_obj = context.ConfigureObject(env, libraries_src, CPPPATH=['.']) |
|||
|
|||
# Build dtoa. |
|||
dtoa_env = env.Copy() |
|||
dtoa_env.Replace(**context.flags['dtoa']) |
|||
dtoa_files = ['dtoa-config.c'] |
|||
dtoa_obj = context.ConfigureObject(dtoa_env, dtoa_files) |
|||
|
|||
source_objs = context.ConfigureObject(env, source_files) |
|||
non_snapshot_files = [dtoa_obj, source_objs] |
|||
|
|||
# Create snapshot if necessary. For cross compilation you should either |
|||
# do without snapshots and take the performance hit or you should build a |
|||
# host VM with the simulator=arm and snapshot=on options and then take the |
|||
# resulting snapshot.cc file from obj/release and put it in the src |
|||
# directory. Then rebuild the VM with the cross compiler and specify |
|||
# snapshot=nobuild on the scons command line. |
|||
empty_snapshot_obj = context.ConfigureObject(env, 'snapshot-empty.cc') |
|||
mksnapshot_env = env.Copy() |
|||
mksnapshot_env.Replace(**context.flags['mksnapshot']) |
|||
mksnapshot_src = 'mksnapshot.cc' |
|||
mksnapshot = mksnapshot_env.Program('mksnapshot', [mksnapshot_src, libraries_obj, non_snapshot_files, empty_snapshot_obj], PDB='mksnapshot.exe.pdb') |
|||
if context.use_snapshot: |
|||
if context.build_snapshot: |
|||
snapshot_cc = env.Snapshot('snapshot.cc', mksnapshot, LOGFILE=File('snapshot.log').abspath) |
|||
else: |
|||
snapshot_cc = 'snapshot.cc' |
|||
snapshot_obj = context.ConfigureObject(env, snapshot_cc, CPPPATH=['.']) |
|||
else: |
|||
snapshot_obj = empty_snapshot_obj |
|||
library_objs = [non_snapshot_files, libraries_obj, snapshot_obj] |
|||
return (library_objs, d8_objs, [mksnapshot]) |
|||
|
|||
|
|||
(library_objs, d8_objs, mksnapshot) = ConfigureObjectFiles() |
|||
Return('library_objs d8_objs mksnapshot') |
File diff suppressed because it is too large
@ -0,0 +1,491 @@ |
|||
// 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_ARM_CODE_STUBS_ARM_H_ |
|||
#define V8_ARM_CODE_STUBS_ARM_H_ |
|||
|
|||
#include "ic-inl.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
|
|||
// Compute a transcendental math function natively, or call the
|
|||
// TranscendentalCache runtime function.
|
|||
class TranscendentalCacheStub: public CodeStub { |
|||
public: |
|||
explicit TranscendentalCacheStub(TranscendentalCache::Type type) |
|||
: type_(type) {} |
|||
void Generate(MacroAssembler* masm); |
|||
private: |
|||
TranscendentalCache::Type type_; |
|||
Major MajorKey() { return TranscendentalCache; } |
|||
int MinorKey() { return type_; } |
|||
Runtime::FunctionId RuntimeFunction(); |
|||
}; |
|||
|
|||
|
|||
class ToBooleanStub: public CodeStub { |
|||
public: |
|||
explicit ToBooleanStub(Register tos) : tos_(tos) { } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
private: |
|||
Register tos_; |
|||
Major MajorKey() { return ToBoolean; } |
|||
int MinorKey() { return tos_.code(); } |
|||
}; |
|||
|
|||
|
|||
class GenericBinaryOpStub : public CodeStub { |
|||
public: |
|||
static const int kUnknownIntValue = -1; |
|||
|
|||
GenericBinaryOpStub(Token::Value op, |
|||
OverwriteMode mode, |
|||
Register lhs, |
|||
Register rhs, |
|||
int constant_rhs = kUnknownIntValue) |
|||
: op_(op), |
|||
mode_(mode), |
|||
lhs_(lhs), |
|||
rhs_(rhs), |
|||
constant_rhs_(constant_rhs), |
|||
specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, constant_rhs)), |
|||
runtime_operands_type_(BinaryOpIC::DEFAULT), |
|||
name_(NULL) { } |
|||
|
|||
GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) |
|||
: op_(OpBits::decode(key)), |
|||
mode_(ModeBits::decode(key)), |
|||
lhs_(LhsRegister(RegisterBits::decode(key))), |
|||
rhs_(RhsRegister(RegisterBits::decode(key))), |
|||
constant_rhs_(KnownBitsForMinorKey(KnownIntBits::decode(key))), |
|||
specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op_, constant_rhs_)), |
|||
runtime_operands_type_(type_info), |
|||
name_(NULL) { } |
|||
|
|||
private: |
|||
Token::Value op_; |
|||
OverwriteMode mode_; |
|||
Register lhs_; |
|||
Register rhs_; |
|||
int constant_rhs_; |
|||
bool specialized_on_rhs_; |
|||
BinaryOpIC::TypeInfo runtime_operands_type_; |
|||
char* name_; |
|||
|
|||
static const int kMaxKnownRhs = 0x40000000; |
|||
static const int kKnownRhsKeyBits = 6; |
|||
|
|||
// Minor key encoding in 17 bits.
|
|||
class ModeBits: public BitField<OverwriteMode, 0, 2> {}; |
|||
class OpBits: public BitField<Token::Value, 2, 6> {}; |
|||
class TypeInfoBits: public BitField<int, 8, 2> {}; |
|||
class RegisterBits: public BitField<bool, 10, 1> {}; |
|||
class KnownIntBits: public BitField<int, 11, kKnownRhsKeyBits> {}; |
|||
|
|||
Major MajorKey() { return GenericBinaryOp; } |
|||
int MinorKey() { |
|||
ASSERT((lhs_.is(r0) && rhs_.is(r1)) || |
|||
(lhs_.is(r1) && rhs_.is(r0))); |
|||
// Encode the parameters in a unique 18 bit value.
|
|||
return OpBits::encode(op_) |
|||
| ModeBits::encode(mode_) |
|||
| KnownIntBits::encode(MinorKeyForKnownInt()) |
|||
| TypeInfoBits::encode(runtime_operands_type_) |
|||
| RegisterBits::encode(lhs_.is(r0)); |
|||
} |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
void HandleNonSmiBitwiseOp(MacroAssembler* masm, |
|||
Register lhs, |
|||
Register rhs); |
|||
void HandleBinaryOpSlowCases(MacroAssembler* masm, |
|||
Label* not_smi, |
|||
Register lhs, |
|||
Register rhs, |
|||
const Builtins::JavaScript& builtin); |
|||
void GenerateTypeTransition(MacroAssembler* masm); |
|||
|
|||
static bool RhsIsOneWeWantToOptimizeFor(Token::Value op, int constant_rhs) { |
|||
if (constant_rhs == kUnknownIntValue) return false; |
|||
if (op == Token::DIV) return constant_rhs >= 2 && constant_rhs <= 3; |
|||
if (op == Token::MOD) { |
|||
if (constant_rhs <= 1) return false; |
|||
if (constant_rhs <= 10) return true; |
|||
if (constant_rhs <= kMaxKnownRhs && IsPowerOf2(constant_rhs)) return true; |
|||
return false; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
int MinorKeyForKnownInt() { |
|||
if (!specialized_on_rhs_) return 0; |
|||
if (constant_rhs_ <= 10) return constant_rhs_ + 1; |
|||
ASSERT(IsPowerOf2(constant_rhs_)); |
|||
int key = 12; |
|||
int d = constant_rhs_; |
|||
while ((d & 1) == 0) { |
|||
key++; |
|||
d >>= 1; |
|||
} |
|||
ASSERT(key >= 0 && key < (1 << kKnownRhsKeyBits)); |
|||
return key; |
|||
} |
|||
|
|||
int KnownBitsForMinorKey(int key) { |
|||
if (!key) return 0; |
|||
if (key <= 11) return key - 1; |
|||
int d = 1; |
|||
while (key != 12) { |
|||
key--; |
|||
d <<= 1; |
|||
} |
|||
return d; |
|||
} |
|||
|
|||
Register LhsRegister(bool lhs_is_r0) { |
|||
return lhs_is_r0 ? r0 : r1; |
|||
} |
|||
|
|||
Register RhsRegister(bool lhs_is_r0) { |
|||
return lhs_is_r0 ? r1 : r0; |
|||
} |
|||
|
|||
bool ShouldGenerateSmiCode() { |
|||
return ((op_ != Token::DIV && op_ != Token::MOD) || specialized_on_rhs_) && |
|||
runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS && |
|||
runtime_operands_type_ != BinaryOpIC::STRINGS; |
|||
} |
|||
|
|||
bool ShouldGenerateFPCode() { |
|||
return runtime_operands_type_ != BinaryOpIC::STRINGS; |
|||
} |
|||
|
|||
virtual int GetCodeKind() { return Code::BINARY_OP_IC; } |
|||
|
|||
virtual InlineCacheState GetICState() { |
|||
return BinaryOpIC::ToState(runtime_operands_type_); |
|||
} |
|||
|
|||
const char* GetName(); |
|||
|
|||
#ifdef DEBUG |
|||
void Print() { |
|||
if (!specialized_on_rhs_) { |
|||
PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_)); |
|||
} else { |
|||
PrintF("GenericBinaryOpStub (%s by %d)\n", |
|||
Token::String(op_), |
|||
constant_rhs_); |
|||
} |
|||
} |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
// Flag that indicates how to generate code for the stub StringAddStub.
|
|||
enum StringAddFlags { |
|||
NO_STRING_ADD_FLAGS = 0, |
|||
NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub.
|
|||
}; |
|||
|
|||
|
|||
class StringAddStub: public CodeStub { |
|||
public: |
|||
explicit StringAddStub(StringAddFlags flags) { |
|||
string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0); |
|||
} |
|||
|
|||
private: |
|||
Major MajorKey() { return StringAdd; } |
|||
int MinorKey() { return string_check_ ? 0 : 1; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
// Should the stub check whether arguments are strings?
|
|||
bool string_check_; |
|||
}; |
|||
|
|||
|
|||
class SubStringStub: public CodeStub { |
|||
public: |
|||
SubStringStub() {} |
|||
|
|||
private: |
|||
Major MajorKey() { return SubString; } |
|||
int MinorKey() { return 0; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
}; |
|||
|
|||
|
|||
|
|||
class StringCompareStub: public CodeStub { |
|||
public: |
|||
StringCompareStub() { } |
|||
|
|||
// Compare two flat ASCII strings and returns result in r0.
|
|||
// Does not use the stack.
|
|||
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm, |
|||
Register left, |
|||
Register right, |
|||
Register scratch1, |
|||
Register scratch2, |
|||
Register scratch3, |
|||
Register scratch4); |
|||
|
|||
private: |
|||
Major MajorKey() { return StringCompare; } |
|||
int MinorKey() { return 0; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
}; |
|||
|
|||
|
|||
// This stub can do a fast mod operation without using fp.
|
|||
// It is tail called from the GenericBinaryOpStub and it always
|
|||
// returns an answer. It never causes GC so it doesn't need a real frame.
|
|||
//
|
|||
// The inputs are always positive Smis. This is never called
|
|||
// where the denominator is a power of 2. We handle that separately.
|
|||
//
|
|||
// If we consider the denominator as an odd number multiplied by a power of 2,
|
|||
// then:
|
|||
// * The exponent (power of 2) is in the shift_distance register.
|
|||
// * The odd number is in the odd_number register. It is always in the range
|
|||
// of 3 to 25.
|
|||
// * The bits from the numerator that are to be copied to the answer (there are
|
|||
// shift_distance of them) are in the mask_bits register.
|
|||
// * The other bits of the numerator have been shifted down and are in the lhs
|
|||
// register.
|
|||
class IntegerModStub : public CodeStub { |
|||
public: |
|||
IntegerModStub(Register result, |
|||
Register shift_distance, |
|||
Register odd_number, |
|||
Register mask_bits, |
|||
Register lhs, |
|||
Register scratch) |
|||
: result_(result), |
|||
shift_distance_(shift_distance), |
|||
odd_number_(odd_number), |
|||
mask_bits_(mask_bits), |
|||
lhs_(lhs), |
|||
scratch_(scratch) { |
|||
// We don't code these in the minor key, so they should always be the same.
|
|||
// We don't really want to fix that since this stub is rather large and we
|
|||
// don't want many copies of it.
|
|||
ASSERT(shift_distance_.is(r9)); |
|||
ASSERT(odd_number_.is(r4)); |
|||
ASSERT(mask_bits_.is(r3)); |
|||
ASSERT(scratch_.is(r5)); |
|||
} |
|||
|
|||
private: |
|||
Register result_; |
|||
Register shift_distance_; |
|||
Register odd_number_; |
|||
Register mask_bits_; |
|||
Register lhs_; |
|||
Register scratch_; |
|||
|
|||
// Minor key encoding in 16 bits.
|
|||
class ResultRegisterBits: public BitField<int, 0, 4> {}; |
|||
class LhsRegisterBits: public BitField<int, 4, 4> {}; |
|||
|
|||
Major MajorKey() { return IntegerMod; } |
|||
int MinorKey() { |
|||
// Encode the parameters in a unique 16 bit value.
|
|||
return ResultRegisterBits::encode(result_.code()) |
|||
| LhsRegisterBits::encode(lhs_.code()); |
|||
} |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
const char* GetName() { return "IntegerModStub"; } |
|||
|
|||
// Utility functions.
|
|||
void DigitSum(MacroAssembler* masm, |
|||
Register lhs, |
|||
int mask, |
|||
int shift, |
|||
Label* entry); |
|||
void DigitSum(MacroAssembler* masm, |
|||
Register lhs, |
|||
Register scratch, |
|||
int mask, |
|||
int shift1, |
|||
int shift2, |
|||
Label* entry); |
|||
void ModGetInRangeBySubtraction(MacroAssembler* masm, |
|||
Register lhs, |
|||
int shift, |
|||
int rhs); |
|||
void ModReduce(MacroAssembler* masm, |
|||
Register lhs, |
|||
int max, |
|||
int denominator); |
|||
void ModAnswer(MacroAssembler* masm, |
|||
Register result, |
|||
Register shift_distance, |
|||
Register mask_bits, |
|||
Register sum_of_digits); |
|||
|
|||
|
|||
#ifdef DEBUG |
|||
void Print() { PrintF("IntegerModStub\n"); } |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
// This stub can convert a signed int32 to a heap number (double). It does
|
|||
// not work for int32s that are in Smi range! No GC occurs during this stub
|
|||
// so you don't have to set up the frame.
|
|||
class WriteInt32ToHeapNumberStub : public CodeStub { |
|||
public: |
|||
WriteInt32ToHeapNumberStub(Register the_int, |
|||
Register the_heap_number, |
|||
Register scratch) |
|||
: the_int_(the_int), |
|||
the_heap_number_(the_heap_number), |
|||
scratch_(scratch) { } |
|||
|
|||
private: |
|||
Register the_int_; |
|||
Register the_heap_number_; |
|||
Register scratch_; |
|||
|
|||
// Minor key encoding in 16 bits.
|
|||
class IntRegisterBits: public BitField<int, 0, 4> {}; |
|||
class HeapNumberRegisterBits: public BitField<int, 4, 4> {}; |
|||
class ScratchRegisterBits: public BitField<int, 8, 4> {}; |
|||
|
|||
Major MajorKey() { return WriteInt32ToHeapNumber; } |
|||
int MinorKey() { |
|||
// Encode the parameters in a unique 16 bit value.
|
|||
return IntRegisterBits::encode(the_int_.code()) |
|||
| HeapNumberRegisterBits::encode(the_heap_number_.code()) |
|||
| ScratchRegisterBits::encode(scratch_.code()); |
|||
} |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
const char* GetName() { return "WriteInt32ToHeapNumberStub"; } |
|||
|
|||
#ifdef DEBUG |
|||
void Print() { PrintF("WriteInt32ToHeapNumberStub\n"); } |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
class NumberToStringStub: public CodeStub { |
|||
public: |
|||
NumberToStringStub() { } |
|||
|
|||
// Generate code to do a lookup in the number string cache. If the number in
|
|||
// the register object is found in the cache the generated code falls through
|
|||
// with the result in the result register. The object and the result register
|
|||
// can be the same. If the number is not found in the cache the code jumps to
|
|||
// the label not_found with only the content of register object unchanged.
|
|||
static void GenerateLookupNumberStringCache(MacroAssembler* masm, |
|||
Register object, |
|||
Register result, |
|||
Register scratch1, |
|||
Register scratch2, |
|||
Register scratch3, |
|||
bool object_is_smi, |
|||
Label* not_found); |
|||
|
|||
private: |
|||
Major MajorKey() { return NumberToString; } |
|||
int MinorKey() { return 0; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
const char* GetName() { return "NumberToStringStub"; } |
|||
}; |
|||
|
|||
|
|||
class RecordWriteStub : public CodeStub { |
|||
public: |
|||
RecordWriteStub(Register object, Register offset, Register scratch) |
|||
: object_(object), offset_(offset), scratch_(scratch) { } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
private: |
|||
Register object_; |
|||
Register offset_; |
|||
Register scratch_; |
|||
|
|||
// Minor key encoding in 12 bits. 4 bits for each of the three
|
|||
// registers (object, offset and scratch) OOOOAAAASSSS.
|
|||
class ScratchBits: public BitField<uint32_t, 0, 4> {}; |
|||
class OffsetBits: public BitField<uint32_t, 4, 4> {}; |
|||
class ObjectBits: public BitField<uint32_t, 8, 4> {}; |
|||
|
|||
Major MajorKey() { return RecordWrite; } |
|||
|
|||
int MinorKey() { |
|||
// Encode the registers.
|
|||
return ObjectBits::encode(object_.code()) | |
|||
OffsetBits::encode(offset_.code()) | |
|||
ScratchBits::encode(scratch_.code()); |
|||
} |
|||
|
|||
#ifdef DEBUG |
|||
void Print() { |
|||
PrintF("RecordWriteStub (object reg %d), (offset reg %d)," |
|||
" (scratch reg %d)\n", |
|||
object_.code(), offset_.code(), scratch_.code()); |
|||
} |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
// Enter C code from generated RegExp code in a way that allows
|
|||
// the C code to fix the return address in case of a GC.
|
|||
// Currently only needed on ARM.
|
|||
class RegExpCEntryStub: public CodeStub { |
|||
public: |
|||
RegExpCEntryStub() {} |
|||
virtual ~RegExpCEntryStub() {} |
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
private: |
|||
Major MajorKey() { return RegExpCEntry; } |
|||
int MinorKey() { return 0; } |
|||
const char* GetName() { return "RegExpCEntryStub"; } |
|||
}; |
|||
|
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // V8_ARM_CODE_STUBS_ARM_H_
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,763 +0,0 @@ |
|||
// 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 "flow-graph.h" |
|||
#include "scopes.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
void BasicBlock::BuildTraversalOrder(ZoneList<BasicBlock*>* preorder, |
|||
ZoneList<BasicBlock*>* postorder, |
|||
bool mark) { |
|||
if (mark_ == mark) return; |
|||
mark_ = mark; |
|||
preorder->Add(this); |
|||
if (right_successor_ != NULL) { |
|||
right_successor_->BuildTraversalOrder(preorder, postorder, mark); |
|||
} |
|||
if (left_successor_ != NULL) { |
|||
left_successor_->BuildTraversalOrder(preorder, postorder, mark); |
|||
} |
|||
postorder->Add(this); |
|||
} |
|||
|
|||
|
|||
FlowGraph* FlowGraphBuilder::Build(FunctionLiteral* lit) { |
|||
// Create new entry and exit nodes. These will not change during
|
|||
// construction.
|
|||
entry_ = new BasicBlock(NULL); |
|||
exit_ = new BasicBlock(NULL); |
|||
// Begin accumulating instructions in the entry block.
|
|||
current_ = entry_; |
|||
|
|||
VisitDeclarations(lit->scope()->declarations()); |
|||
VisitStatements(lit->body()); |
|||
// In the event of stack overflow or failure to handle a syntactic
|
|||
// construct, return an invalid flow graph.
|
|||
if (HasStackOverflow()) return new FlowGraph(NULL, NULL); |
|||
|
|||
// If current is not the exit, add a link to the exit.
|
|||
if (current_ != exit_) { |
|||
// If current already has a successor (i.e., will be a branch node) and
|
|||
// if the exit already has a predecessor, insert an empty block to
|
|||
// maintain edge split form.
|
|||
if (current_->HasSuccessor() && exit_->HasPredecessor()) { |
|||
current_ = new BasicBlock(current_); |
|||
} |
|||
Literal* undefined = new Literal(Factory::undefined_value()); |
|||
current_->AddInstruction(new ReturnStatement(undefined)); |
|||
exit_->AddPredecessor(current_); |
|||
} |
|||
|
|||
FlowGraph* graph = new FlowGraph(entry_, exit_); |
|||
bool mark = !entry_->GetMark(); |
|||
entry_->BuildTraversalOrder(graph->preorder(), graph->postorder(), mark); |
|||
|
|||
#ifdef DEBUG |
|||
// Number the nodes in reverse postorder.
|
|||
int n = 0; |
|||
for (int i = graph->postorder()->length() - 1; i >= 0; --i) { |
|||
graph->postorder()->at(i)->set_number(n++); |
|||
} |
|||
#endif |
|||
|
|||
return graph; |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitDeclaration(Declaration* decl) { |
|||
Variable* var = decl->proxy()->AsVariable(); |
|||
Slot* slot = var->slot(); |
|||
// We allow only declarations that do not require code generation.
|
|||
// The following all require code generation: global variables and
|
|||
// functions, variables with slot type LOOKUP, declarations with
|
|||
// mode CONST, and functions.
|
|||
|
|||
if (var->is_global() || |
|||
(slot != NULL && slot->type() == Slot::LOOKUP) || |
|||
decl->mode() == Variable::CONST || |
|||
decl->fun() != NULL) { |
|||
// Here and in the rest of the flow graph builder we indicate an
|
|||
// unsupported syntactic construct by setting the stack overflow
|
|||
// flag on the visitor. This causes bailout of the visitor.
|
|||
SetStackOverflow(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitBlock(Block* stmt) { |
|||
VisitStatements(stmt->statements()); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { |
|||
Visit(stmt->expression()); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) { |
|||
// Nothing to do.
|
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitIfStatement(IfStatement* stmt) { |
|||
// Build a diamond in the flow graph. First accumulate the instructions
|
|||
// of the test in the current basic block.
|
|||
Visit(stmt->condition()); |
|||
|
|||
// Remember the branch node and accumulate the true branch as its left
|
|||
// successor. This relies on the successors being added left to right.
|
|||
BasicBlock* branch = current_; |
|||
current_ = new BasicBlock(branch); |
|||
Visit(stmt->then_statement()); |
|||
|
|||
// Construct a join node and then accumulate the false branch in a fresh
|
|||
// successor of the branch node.
|
|||
BasicBlock* join = new BasicBlock(current_); |
|||
current_ = new BasicBlock(branch); |
|||
Visit(stmt->else_statement()); |
|||
join->AddPredecessor(current_); |
|||
|
|||
current_ = join; |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitBreakStatement(BreakStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitWhileStatement(WhileStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitForStatement(ForStatement* stmt) { |
|||
// Build a loop in the flow graph. First accumulate the instructions of
|
|||
// the initializer in the current basic block.
|
|||
if (stmt->init() != NULL) Visit(stmt->init()); |
|||
|
|||
// Create a new basic block for the test. This will be the join node.
|
|||
BasicBlock* join = new BasicBlock(current_); |
|||
current_ = join; |
|||
if (stmt->cond() != NULL) Visit(stmt->cond()); |
|||
|
|||
// The current node is the branch node. Create a new basic block to begin
|
|||
// the body.
|
|||
BasicBlock* branch = current_; |
|||
current_ = new BasicBlock(branch); |
|||
Visit(stmt->body()); |
|||
if (stmt->next() != NULL) Visit(stmt->next()); |
|||
|
|||
// Add the backward edge from the end of the body and continue with the
|
|||
// false arm of the branch.
|
|||
join->AddPredecessor(current_); |
|||
current_ = new BasicBlock(branch); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitForInStatement(ForInStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitSharedFunctionInfoLiteral( |
|||
SharedFunctionInfoLiteral* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitConditional(Conditional* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitSlot(Slot* expr) { |
|||
// Slots do not appear in the AST.
|
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitVariableProxy(VariableProxy* expr) { |
|||
current_->AddInstruction(expr); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitLiteral(Literal* expr) { |
|||
current_->AddInstruction(expr); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitAssignment(Assignment* expr) { |
|||
// There are three basic kinds of assignment: variable assignments,
|
|||
// property assignments, and invalid left-hand sides (which are translated
|
|||
// to "throw ReferenceError" by the parser).
|
|||
Variable* var = expr->target()->AsVariableProxy()->AsVariable(); |
|||
Property* prop = expr->target()->AsProperty(); |
|||
ASSERT(var == NULL || prop == NULL); |
|||
if (var != NULL) { |
|||
if (expr->is_compound() && !expr->target()->IsTrivial()) { |
|||
Visit(expr->target()); |
|||
} |
|||
if (!expr->value()->IsTrivial()) Visit(expr->value()); |
|||
current_->AddInstruction(expr); |
|||
|
|||
} else if (prop != NULL) { |
|||
if (!prop->obj()->IsTrivial()) Visit(prop->obj()); |
|||
if (!prop->key()->IsPropertyName() && !prop->key()->IsTrivial()) { |
|||
Visit(prop->key()); |
|||
} |
|||
if (!expr->value()->IsTrivial()) Visit(expr->value()); |
|||
current_->AddInstruction(expr); |
|||
|
|||
} else { |
|||
Visit(expr->target()); |
|||
} |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitThrow(Throw* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitProperty(Property* expr) { |
|||
if (!expr->obj()->IsTrivial()) Visit(expr->obj()); |
|||
if (!expr->key()->IsPropertyName() && !expr->key()->IsTrivial()) { |
|||
Visit(expr->key()); |
|||
} |
|||
current_->AddInstruction(expr); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitCall(Call* expr) { |
|||
Visit(expr->expression()); |
|||
VisitExpressions(expr->arguments()); |
|||
current_->AddInstruction(expr); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitCallNew(CallNew* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitCallRuntime(CallRuntime* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { |
|||
switch (expr->op()) { |
|||
case Token::NOT: |
|||
case Token::BIT_NOT: |
|||
case Token::DELETE: |
|||
case Token::TYPEOF: |
|||
case Token::VOID: |
|||
SetStackOverflow(); |
|||
break; |
|||
|
|||
case Token::ADD: |
|||
case Token::SUB: |
|||
Visit(expr->expression()); |
|||
current_->AddInstruction(expr); |
|||
break; |
|||
|
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitCountOperation(CountOperation* expr) { |
|||
Visit(expr->expression()); |
|||
current_->AddInstruction(expr); |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) { |
|||
switch (expr->op()) { |
|||
case Token::COMMA: |
|||
case Token::OR: |
|||
case Token::AND: |
|||
SetStackOverflow(); |
|||
break; |
|||
|
|||
case Token::BIT_OR: |
|||
case Token::BIT_XOR: |
|||
case Token::BIT_AND: |
|||
case Token::SHL: |
|||
case Token::SAR: |
|||
case Token::SHR: |
|||
case Token::ADD: |
|||
case Token::SUB: |
|||
case Token::MUL: |
|||
case Token::DIV: |
|||
case Token::MOD: |
|||
if (!expr->left()->IsTrivial()) Visit(expr->left()); |
|||
if (!expr->right()->IsTrivial()) Visit(expr->right()); |
|||
current_->AddInstruction(expr); |
|||
break; |
|||
|
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
|||
switch (expr->op()) { |
|||
case Token::EQ: |
|||
case Token::NE: |
|||
case Token::EQ_STRICT: |
|||
case Token::NE_STRICT: |
|||
case Token::INSTANCEOF: |
|||
case Token::IN: |
|||
SetStackOverflow(); |
|||
break; |
|||
|
|||
case Token::LT: |
|||
case Token::GT: |
|||
case Token::LTE: |
|||
case Token::GTE: |
|||
if (!expr->left()->IsTrivial()) Visit(expr->left()); |
|||
if (!expr->right()->IsTrivial()) Visit(expr->right()); |
|||
current_->AddInstruction(expr); |
|||
break; |
|||
|
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void FlowGraphBuilder::VisitThisFunction(ThisFunction* expr) { |
|||
SetStackOverflow(); |
|||
} |
|||
|
|||
|
|||
#ifdef DEBUG |
|||
|
|||
// Print a textual representation of an instruction in a flow graph.
|
|||
class InstructionPrinter: public AstVisitor { |
|||
public: |
|||
InstructionPrinter() {} |
|||
|
|||
private: |
|||
// Overridden from the base class.
|
|||
virtual void VisitExpressions(ZoneList<Expression*>* exprs); |
|||
|
|||
// AST node visit functions.
|
|||
#define DECLARE_VISIT(type) virtual void Visit##type(type* node); |
|||
AST_NODE_LIST(DECLARE_VISIT) |
|||
#undef DECLARE_VISIT |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(InstructionPrinter); |
|||
}; |
|||
|
|||
|
|||
static void PrintSubexpression(Expression* expr) { |
|||
if (!expr->IsTrivial()) { |
|||
PrintF("@%d", expr->num()); |
|||
} else if (expr->AsLiteral() != NULL) { |
|||
expr->AsLiteral()->handle()->Print(); |
|||
} else if (expr->AsVariableProxy() != NULL) { |
|||
PrintF("%s", *expr->AsVariableProxy()->name()->ToCString()); |
|||
} else { |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitExpressions(ZoneList<Expression*>* exprs) { |
|||
for (int i = 0; i < exprs->length(); ++i) { |
|||
if (i != 0) PrintF(", "); |
|||
PrintF("@%d", exprs->at(i)->num()); |
|||
} |
|||
} |
|||
|
|||
|
|||
// We only define printing functions for the node types that can occur as
|
|||
// instructions in a flow graph. The rest are unreachable.
|
|||
void InstructionPrinter::VisitDeclaration(Declaration* decl) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitBlock(Block* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitExpressionStatement(ExpressionStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitEmptyStatement(EmptyStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitIfStatement(IfStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitContinueStatement(ContinueStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitBreakStatement(BreakStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitReturnStatement(ReturnStatement* stmt) { |
|||
PrintF("return "); |
|||
PrintSubexpression(stmt->expression()); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitWithEnterStatement(WithEnterStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitWithExitStatement(WithExitStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitSwitchStatement(SwitchStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitDoWhileStatement(DoWhileStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitWhileStatement(WhileStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitForStatement(ForStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitForInStatement(ForInStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitTryCatchStatement(TryCatchStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitDebuggerStatement(DebuggerStatement* stmt) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitFunctionLiteral(FunctionLiteral* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitSharedFunctionInfoLiteral( |
|||
SharedFunctionInfoLiteral* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitConditional(Conditional* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitSlot(Slot* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitVariableProxy(VariableProxy* expr) { |
|||
Variable* var = expr->AsVariable(); |
|||
if (var != NULL) { |
|||
PrintF("%s", *var->name()->ToCString()); |
|||
} else { |
|||
ASSERT(expr->AsProperty() != NULL); |
|||
Visit(expr->AsProperty()); |
|||
} |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitLiteral(Literal* expr) { |
|||
expr->handle()->Print(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitRegExpLiteral(RegExpLiteral* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitObjectLiteral(ObjectLiteral* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitArrayLiteral(ArrayLiteral* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitCatchExtensionObject( |
|||
CatchExtensionObject* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitAssignment(Assignment* expr) { |
|||
Variable* var = expr->target()->AsVariableProxy()->AsVariable(); |
|||
Property* prop = expr->target()->AsProperty(); |
|||
|
|||
// Print the left-hand side.
|
|||
Visit(expr->target()); |
|||
if (var == NULL && prop == NULL) return; // Throw reference error.
|
|||
PrintF(" = "); |
|||
// For compound assignments, print the left-hand side again and the
|
|||
// corresponding binary operator.
|
|||
if (expr->is_compound()) { |
|||
PrintSubexpression(expr->target()); |
|||
PrintF(" %s ", Token::String(expr->binary_op())); |
|||
} |
|||
|
|||
// Print the right-hand side.
|
|||
PrintSubexpression(expr->value()); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitThrow(Throw* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitProperty(Property* expr) { |
|||
PrintSubexpression(expr->obj()); |
|||
if (expr->key()->IsPropertyName()) { |
|||
PrintF("."); |
|||
ASSERT(expr->key()->AsLiteral() != NULL); |
|||
expr->key()->AsLiteral()->handle()->Print(); |
|||
} else { |
|||
PrintF("["); |
|||
PrintSubexpression(expr->key()); |
|||
PrintF("]"); |
|||
} |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitCall(Call* expr) { |
|||
PrintF("@%d(", expr->expression()->num()); |
|||
VisitExpressions(expr->arguments()); |
|||
PrintF(")"); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitCallNew(CallNew* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitCallRuntime(CallRuntime* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitUnaryOperation(UnaryOperation* expr) { |
|||
PrintF("%s(@%d)", Token::String(expr->op()), expr->expression()->num()); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitCountOperation(CountOperation* expr) { |
|||
if (expr->is_prefix()) { |
|||
PrintF("%s@%d", Token::String(expr->op()), expr->expression()->num()); |
|||
} else { |
|||
PrintF("@%d%s", expr->expression()->num(), Token::String(expr->op())); |
|||
} |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitBinaryOperation(BinaryOperation* expr) { |
|||
PrintSubexpression(expr->left()); |
|||
PrintF(" %s ", Token::String(expr->op())); |
|||
PrintSubexpression(expr->right()); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitCompareOperation(CompareOperation* expr) { |
|||
PrintSubexpression(expr->left()); |
|||
PrintF(" %s ", Token::String(expr->op())); |
|||
PrintSubexpression(expr->right()); |
|||
} |
|||
|
|||
|
|||
void InstructionPrinter::VisitThisFunction(ThisFunction* expr) { |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
|
|||
int BasicBlock::PrintAsText(int instruction_number) { |
|||
// Print a label for all blocks except the entry.
|
|||
if (HasPredecessor()) { |
|||
PrintF("L%d:", number()); |
|||
} |
|||
|
|||
// Number and print the instructions. Since AST child nodes are visited
|
|||
// before their parents, the parent nodes can refer to them by number.
|
|||
InstructionPrinter printer; |
|||
for (int i = 0; i < instructions_.length(); ++i) { |
|||
PrintF("\n%d ", instruction_number); |
|||
instructions_[i]->set_num(instruction_number++); |
|||
instructions_[i]->Accept(&printer); |
|||
} |
|||
|
|||
// If this is the exit, print "exit". If there is a single successor,
|
|||
// print "goto" successor on a separate line. If there are two
|
|||
// successors, print "goto" successor on the same line as the last
|
|||
// instruction in the block. There is a blank line between blocks (and
|
|||
// after the last one).
|
|||
if (left_successor_ == NULL) { |
|||
PrintF("\nexit\n\n"); |
|||
} else if (right_successor_ == NULL) { |
|||
PrintF("\ngoto L%d\n\n", left_successor_->number()); |
|||
} else { |
|||
PrintF(", goto (L%d, L%d)\n\n", |
|||
left_successor_->number(), |
|||
right_successor_->number()); |
|||
} |
|||
|
|||
return instruction_number; |
|||
} |
|||
|
|||
|
|||
void FlowGraph::PrintAsText(Handle<String> name) { |
|||
PrintF("\n==== name = \"%s\" ====\n", *name->ToCString()); |
|||
// Print nodes in reverse postorder. Note that AST node numbers are used
|
|||
// during printing of instructions and thus their current values are
|
|||
// destroyed.
|
|||
int number = 0; |
|||
for (int i = postorder_.length() - 1; i >= 0; --i) { |
|||
number = postorder_[i]->PrintAsText(number); |
|||
} |
|||
} |
|||
|
|||
#endif // DEBUG
|
|||
|
|||
|
|||
} } // namespace v8::internal
|
@ -1,180 +0,0 @@ |
|||
// 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_FLOW_GRAPH_H_ |
|||
#define V8_FLOW_GRAPH_H_ |
|||
|
|||
#include "v8.h" |
|||
|
|||
#include "data-flow.h" |
|||
#include "zone.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
// The nodes of a flow graph are basic blocks. Basic blocks consist of
|
|||
// instructions represented as pointers to AST nodes in the order that they
|
|||
// would be visited by the code generator. A block can have arbitrarily many
|
|||
// (even zero) predecessors and up to two successors. Blocks with multiple
|
|||
// predecessors are "join nodes" and blocks with multiple successors are
|
|||
// "branch nodes". A block can be both a branch and a join node.
|
|||
//
|
|||
// Flow graphs are in edge split form: a branch node is never the
|
|||
// predecessor of a merge node. Empty basic blocks are inserted to maintain
|
|||
// edge split form.
|
|||
class BasicBlock: public ZoneObject { |
|||
public: |
|||
// Construct a basic block with a given predecessor. NULL indicates no
|
|||
// predecessor or that the predecessor will be set later.
|
|||
explicit BasicBlock(BasicBlock* predecessor) |
|||
: predecessors_(2), |
|||
instructions_(8), |
|||
left_successor_(NULL), |
|||
right_successor_(NULL), |
|||
mark_(false) { |
|||
if (predecessor != NULL) AddPredecessor(predecessor); |
|||
} |
|||
|
|||
bool HasPredecessor() { return !predecessors_.is_empty(); } |
|||
bool HasSuccessor() { return left_successor_ != NULL; } |
|||
|
|||
// Add a given basic block as a predecessor of this block. This function
|
|||
// also adds this block as a successor of the given block.
|
|||
void AddPredecessor(BasicBlock* predecessor) { |
|||
ASSERT(predecessor != NULL); |
|||
predecessors_.Add(predecessor); |
|||
predecessor->AddSuccessor(this); |
|||
} |
|||
|
|||
// Add an instruction to the end of this block. The block must be "open"
|
|||
// by not having a successor yet.
|
|||
void AddInstruction(AstNode* instruction) { |
|||
ASSERT(!HasSuccessor() && instruction != NULL); |
|||
instructions_.Add(instruction); |
|||
} |
|||
|
|||
// Perform a depth-first traversal of graph rooted at this node,
|
|||
// accumulating pre- and postorder traversal orders. Visited nodes are
|
|||
// marked with mark.
|
|||
void BuildTraversalOrder(ZoneList<BasicBlock*>* preorder, |
|||
ZoneList<BasicBlock*>* postorder, |
|||
bool mark); |
|||
bool GetMark() { return mark_; } |
|||
|
|||
#ifdef DEBUG |
|||
// In debug mode, blocks are numbered in reverse postorder to help with
|
|||
// printing.
|
|||
int number() { return number_; } |
|||
void set_number(int n) { number_ = n; } |
|||
|
|||
// Print a basic block, given the number of the first instruction.
|
|||
// Returns the next number after the number of the last instruction.
|
|||
int PrintAsText(int instruction_number); |
|||
#endif |
|||
|
|||
private: |
|||
// Add a given basic block as successor to this block. This function does
|
|||
// not add this block as a predecessor of the given block so as to avoid
|
|||
// circularity.
|
|||
void AddSuccessor(BasicBlock* successor) { |
|||
ASSERT(right_successor_ == NULL && successor != NULL); |
|||
if (HasSuccessor()) { |
|||
right_successor_ = successor; |
|||
} else { |
|||
left_successor_ = successor; |
|||
} |
|||
} |
|||
|
|||
ZoneList<BasicBlock*> predecessors_; |
|||
ZoneList<AstNode*> instructions_; |
|||
BasicBlock* left_successor_; |
|||
BasicBlock* right_successor_; |
|||
|
|||
// Support for graph traversal. Before traversal, all nodes in the graph
|
|||
// have the same mark (true or false). Traversal marks already-visited
|
|||
// nodes with the opposite mark. After traversal, all nodes again have
|
|||
// the same mark. Traversal of the same graph is not reentrant.
|
|||
bool mark_; |
|||
|
|||
#ifdef DEBUG |
|||
int number_; |
|||
#endif |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(BasicBlock); |
|||
}; |
|||
|
|||
|
|||
// A flow graph has distinguished entry and exit blocks. The entry block is
|
|||
// the only one with no predecessors and the exit block is the only one with
|
|||
// no successors.
|
|||
class FlowGraph: public ZoneObject { |
|||
public: |
|||
FlowGraph(BasicBlock* entry, BasicBlock* exit) |
|||
: entry_(entry), exit_(exit), preorder_(8), postorder_(8) { |
|||
} |
|||
|
|||
ZoneList<BasicBlock*>* preorder() { return &preorder_; } |
|||
ZoneList<BasicBlock*>* postorder() { return &postorder_; } |
|||
|
|||
#ifdef DEBUG |
|||
void PrintAsText(Handle<String> name); |
|||
#endif |
|||
|
|||
private: |
|||
BasicBlock* entry_; |
|||
BasicBlock* exit_; |
|||
ZoneList<BasicBlock*> preorder_; |
|||
ZoneList<BasicBlock*> postorder_; |
|||
}; |
|||
|
|||
|
|||
// The flow graph builder walks the AST adding reachable AST nodes to the
|
|||
// flow graph as instructions. It remembers the entry and exit nodes of the
|
|||
// graph, and keeps a pointer to the current block being constructed.
|
|||
class FlowGraphBuilder: public AstVisitor { |
|||
public: |
|||
FlowGraphBuilder() {} |
|||
|
|||
FlowGraph* Build(FunctionLiteral* lit); |
|||
|
|||
private: |
|||
// AST node visit functions.
|
|||
#define DECLARE_VISIT(type) virtual void Visit##type(type* node); |
|||
AST_NODE_LIST(DECLARE_VISIT) |
|||
#undef DECLARE_VISIT |
|||
|
|||
BasicBlock* entry_; |
|||
BasicBlock* exit_; |
|||
BasicBlock* current_; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(FlowGraphBuilder); |
|||
}; |
|||
|
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // V8_FLOW_GRAPH_H_
|
File diff suppressed because it is too large
@ -0,0 +1,376 @@ |
|||
// 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_IA32_CODE_STUBS_IA32_H_ |
|||
#define V8_IA32_CODE_STUBS_IA32_H_ |
|||
|
|||
#include "macro-assembler.h" |
|||
#include "code-stubs.h" |
|||
#include "ic-inl.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
|
|||
// Compute a transcendental math function natively, or call the
|
|||
// TranscendentalCache runtime function.
|
|||
class TranscendentalCacheStub: public CodeStub { |
|||
public: |
|||
explicit TranscendentalCacheStub(TranscendentalCache::Type type) |
|||
: type_(type) {} |
|||
void Generate(MacroAssembler* masm); |
|||
private: |
|||
TranscendentalCache::Type type_; |
|||
Major MajorKey() { return TranscendentalCache; } |
|||
int MinorKey() { return type_; } |
|||
Runtime::FunctionId RuntimeFunction(); |
|||
void GenerateOperation(MacroAssembler* masm); |
|||
}; |
|||
|
|||
|
|||
class ToBooleanStub: public CodeStub { |
|||
public: |
|||
ToBooleanStub() { } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
private: |
|||
Major MajorKey() { return ToBoolean; } |
|||
int MinorKey() { return 0; } |
|||
}; |
|||
|
|||
|
|||
// Flag that indicates how to generate code for the stub GenericBinaryOpStub.
|
|||
enum GenericBinaryFlags { |
|||
NO_GENERIC_BINARY_FLAGS = 0, |
|||
NO_SMI_CODE_IN_STUB = 1 << 0 // Omit smi code in stub.
|
|||
}; |
|||
|
|||
|
|||
class GenericBinaryOpStub: public CodeStub { |
|||
public: |
|||
GenericBinaryOpStub(Token::Value op, |
|||
OverwriteMode mode, |
|||
GenericBinaryFlags flags, |
|||
TypeInfo operands_type) |
|||
: op_(op), |
|||
mode_(mode), |
|||
flags_(flags), |
|||
args_in_registers_(false), |
|||
args_reversed_(false), |
|||
static_operands_type_(operands_type), |
|||
runtime_operands_type_(BinaryOpIC::DEFAULT), |
|||
name_(NULL) { |
|||
if (static_operands_type_.IsSmi()) { |
|||
mode_ = NO_OVERWRITE; |
|||
} |
|||
use_sse3_ = CpuFeatures::IsSupported(SSE3); |
|||
ASSERT(OpBits::is_valid(Token::NUM_TOKENS)); |
|||
} |
|||
|
|||
GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo runtime_operands_type) |
|||
: op_(OpBits::decode(key)), |
|||
mode_(ModeBits::decode(key)), |
|||
flags_(FlagBits::decode(key)), |
|||
args_in_registers_(ArgsInRegistersBits::decode(key)), |
|||
args_reversed_(ArgsReversedBits::decode(key)), |
|||
use_sse3_(SSE3Bits::decode(key)), |
|||
static_operands_type_(TypeInfo::ExpandedRepresentation( |
|||
StaticTypeInfoBits::decode(key))), |
|||
runtime_operands_type_(runtime_operands_type), |
|||
name_(NULL) { |
|||
} |
|||
|
|||
// Generate code to call the stub with the supplied arguments. This will add
|
|||
// code at the call site to prepare arguments either in registers or on the
|
|||
// stack together with the actual call.
|
|||
void GenerateCall(MacroAssembler* masm, Register left, Register right); |
|||
void GenerateCall(MacroAssembler* masm, Register left, Smi* right); |
|||
void GenerateCall(MacroAssembler* masm, Smi* left, Register right); |
|||
|
|||
bool ArgsInRegistersSupported() { |
|||
return op_ == Token::ADD || op_ == Token::SUB |
|||
|| op_ == Token::MUL || op_ == Token::DIV; |
|||
} |
|||
|
|||
private: |
|||
Token::Value op_; |
|||
OverwriteMode mode_; |
|||
GenericBinaryFlags flags_; |
|||
bool args_in_registers_; // Arguments passed in registers not on the stack.
|
|||
bool args_reversed_; // Left and right argument are swapped.
|
|||
bool use_sse3_; |
|||
|
|||
// Number type information of operands, determined by code generator.
|
|||
TypeInfo static_operands_type_; |
|||
|
|||
// Operand type information determined at runtime.
|
|||
BinaryOpIC::TypeInfo runtime_operands_type_; |
|||
|
|||
char* name_; |
|||
|
|||
const char* GetName(); |
|||
|
|||
#ifdef DEBUG |
|||
void Print() { |
|||
PrintF("GenericBinaryOpStub %d (op %s), " |
|||
"(mode %d, flags %d, registers %d, reversed %d, type_info %s)\n", |
|||
MinorKey(), |
|||
Token::String(op_), |
|||
static_cast<int>(mode_), |
|||
static_cast<int>(flags_), |
|||
static_cast<int>(args_in_registers_), |
|||
static_cast<int>(args_reversed_), |
|||
static_operands_type_.ToString()); |
|||
} |
|||
#endif |
|||
|
|||
// Minor key encoding in 18 bits RRNNNFRASOOOOOOOMM.
|
|||
class ModeBits: public BitField<OverwriteMode, 0, 2> {}; |
|||
class OpBits: public BitField<Token::Value, 2, 7> {}; |
|||
class SSE3Bits: public BitField<bool, 9, 1> {}; |
|||
class ArgsInRegistersBits: public BitField<bool, 10, 1> {}; |
|||
class ArgsReversedBits: public BitField<bool, 11, 1> {}; |
|||
class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {}; |
|||
class StaticTypeInfoBits: public BitField<int, 13, 3> {}; |
|||
class RuntimeTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 16, 2> {}; |
|||
|
|||
Major MajorKey() { return GenericBinaryOp; } |
|||
int MinorKey() { |
|||
// Encode the parameters in a unique 18 bit value.
|
|||
return OpBits::encode(op_) |
|||
| ModeBits::encode(mode_) |
|||
| FlagBits::encode(flags_) |
|||
| SSE3Bits::encode(use_sse3_) |
|||
| ArgsInRegistersBits::encode(args_in_registers_) |
|||
| ArgsReversedBits::encode(args_reversed_) |
|||
| StaticTypeInfoBits::encode( |
|||
static_operands_type_.ThreeBitRepresentation()) |
|||
| RuntimeTypeInfoBits::encode(runtime_operands_type_); |
|||
} |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
void GenerateSmiCode(MacroAssembler* masm, Label* slow); |
|||
void GenerateLoadArguments(MacroAssembler* masm); |
|||
void GenerateReturn(MacroAssembler* masm); |
|||
void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure); |
|||
void GenerateRegisterArgsPush(MacroAssembler* masm); |
|||
void GenerateTypeTransition(MacroAssembler* masm); |
|||
|
|||
bool IsOperationCommutative() { |
|||
return (op_ == Token::ADD) || (op_ == Token::MUL); |
|||
} |
|||
|
|||
void SetArgsInRegisters() { args_in_registers_ = true; } |
|||
void SetArgsReversed() { args_reversed_ = true; } |
|||
bool HasSmiCodeInStub() { return (flags_ & NO_SMI_CODE_IN_STUB) == 0; } |
|||
bool HasArgsInRegisters() { return args_in_registers_; } |
|||
bool HasArgsReversed() { return args_reversed_; } |
|||
|
|||
bool ShouldGenerateSmiCode() { |
|||
return HasSmiCodeInStub() && |
|||
runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS && |
|||
runtime_operands_type_ != BinaryOpIC::STRINGS; |
|||
} |
|||
|
|||
bool ShouldGenerateFPCode() { |
|||
return runtime_operands_type_ != BinaryOpIC::STRINGS; |
|||
} |
|||
|
|||
virtual int GetCodeKind() { return Code::BINARY_OP_IC; } |
|||
|
|||
virtual InlineCacheState GetICState() { |
|||
return BinaryOpIC::ToState(runtime_operands_type_); |
|||
} |
|||
|
|||
friend class CodeGenerator; |
|||
}; |
|||
|
|||
|
|||
class StringHelper : public AllStatic { |
|||
public: |
|||
// Generate code for copying characters using a simple loop. This should only
|
|||
// be used in places where the number of characters is small and the
|
|||
// additional setup and checking in GenerateCopyCharactersREP adds too much
|
|||
// overhead. Copying of overlapping regions is not supported.
|
|||
static void GenerateCopyCharacters(MacroAssembler* masm, |
|||
Register dest, |
|||
Register src, |
|||
Register count, |
|||
Register scratch, |
|||
bool ascii); |
|||
|
|||
// Generate code for copying characters using the rep movs instruction.
|
|||
// Copies ecx characters from esi to edi. Copying of overlapping regions is
|
|||
// not supported.
|
|||
static void GenerateCopyCharactersREP(MacroAssembler* masm, |
|||
Register dest, // Must be edi.
|
|||
Register src, // Must be esi.
|
|||
Register count, // Must be ecx.
|
|||
Register scratch, // Neither of above.
|
|||
bool ascii); |
|||
|
|||
// Probe the symbol table for a two character string. If the string
|
|||
// requires non-standard hashing a jump to the label not_probed is
|
|||
// performed and registers c1 and c2 are preserved. In all other
|
|||
// cases they are clobbered. If the string is not found by probing a
|
|||
// jump to the label not_found is performed. This jump does not
|
|||
// guarantee that the string is not in the symbol table. If the
|
|||
// string is found the code falls through with the string in
|
|||
// register eax.
|
|||
static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, |
|||
Register c1, |
|||
Register c2, |
|||
Register scratch1, |
|||
Register scratch2, |
|||
Register scratch3, |
|||
Label* not_probed, |
|||
Label* not_found); |
|||
|
|||
// Generate string hash.
|
|||
static void GenerateHashInit(MacroAssembler* masm, |
|||
Register hash, |
|||
Register character, |
|||
Register scratch); |
|||
static void GenerateHashAddCharacter(MacroAssembler* masm, |
|||
Register hash, |
|||
Register character, |
|||
Register scratch); |
|||
static void GenerateHashGetHash(MacroAssembler* masm, |
|||
Register hash, |
|||
Register scratch); |
|||
|
|||
private: |
|||
DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); |
|||
}; |
|||
|
|||
|
|||
// Flag that indicates how to generate code for the stub StringAddStub.
|
|||
enum StringAddFlags { |
|||
NO_STRING_ADD_FLAGS = 0, |
|||
// Omit left string check in stub (left is definitely a string).
|
|||
NO_STRING_CHECK_LEFT_IN_STUB = 1 << 0, |
|||
// Omit right string check in stub (right is definitely a string).
|
|||
NO_STRING_CHECK_RIGHT_IN_STUB = 1 << 1, |
|||
// Omit both string checks in stub.
|
|||
NO_STRING_CHECK_IN_STUB = |
|||
NO_STRING_CHECK_LEFT_IN_STUB | NO_STRING_CHECK_RIGHT_IN_STUB |
|||
}; |
|||
|
|||
|
|||
class StringAddStub: public CodeStub { |
|||
public: |
|||
explicit StringAddStub(StringAddFlags flags) : flags_(flags) {} |
|||
|
|||
private: |
|||
Major MajorKey() { return StringAdd; } |
|||
int MinorKey() { return flags_; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
void GenerateConvertArgument(MacroAssembler* masm, |
|||
int stack_offset, |
|||
Register arg, |
|||
Register scratch1, |
|||
Register scratch2, |
|||
Register scratch3, |
|||
Label* slow); |
|||
|
|||
const StringAddFlags flags_; |
|||
}; |
|||
|
|||
|
|||
class SubStringStub: public CodeStub { |
|||
public: |
|||
SubStringStub() {} |
|||
|
|||
private: |
|||
Major MajorKey() { return SubString; } |
|||
int MinorKey() { return 0; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
}; |
|||
|
|||
|
|||
class StringCompareStub: public CodeStub { |
|||
public: |
|||
explicit StringCompareStub() { |
|||
} |
|||
|
|||
// Compare two flat ascii strings and returns result in eax after popping two
|
|||
// arguments from the stack.
|
|||
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm, |
|||
Register left, |
|||
Register right, |
|||
Register scratch1, |
|||
Register scratch2, |
|||
Register scratch3); |
|||
|
|||
private: |
|||
Major MajorKey() { return StringCompare; } |
|||
int MinorKey() { return 0; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
}; |
|||
|
|||
|
|||
class NumberToStringStub: public CodeStub { |
|||
public: |
|||
NumberToStringStub() { } |
|||
|
|||
// Generate code to do a lookup in the number string cache. If the number in
|
|||
// the register object is found in the cache the generated code falls through
|
|||
// with the result in the result register. The object and the result register
|
|||
// can be the same. If the number is not found in the cache the code jumps to
|
|||
// the label not_found with only the content of register object unchanged.
|
|||
static void GenerateLookupNumberStringCache(MacroAssembler* masm, |
|||
Register object, |
|||
Register result, |
|||
Register scratch1, |
|||
Register scratch2, |
|||
bool object_is_smi, |
|||
Label* not_found); |
|||
|
|||
private: |
|||
Major MajorKey() { return NumberToString; } |
|||
int MinorKey() { return 0; } |
|||
|
|||
void Generate(MacroAssembler* masm); |
|||
|
|||
const char* GetName() { return "NumberToStringStub"; } |
|||
|
|||
#ifdef DEBUG |
|||
void Print() { |
|||
PrintF("NumberToStringStub\n"); |
|||
} |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // V8_IA32_CODE_STUBS_IA32_H_
|
File diff suppressed because it is too large
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue