Use SSE3 instructions - if available - for faster To(U)Int32

conversion in bit operation stubs. Disable serialization
support by default to allow us to use SSE3 instructions and
faster write barrier code when running without snapshot.
Review URL: http://codereview.chromium.org/27046

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1364 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kasperl@chromium.org 2009-02-25 14:57:46 +00:00
parent 539a29cbd3
commit 1e42e70021
12 changed files with 121 additions and 61 deletions

View File

@ -2175,7 +2175,6 @@ bool v8::V8::Initialize() {
if (i::V8::HasBeenSetup()) return true; if (i::V8::HasBeenSetup()) return true;
HandleScope scope; HandleScope scope;
if (i::Snapshot::Initialize()) { if (i::Snapshot::Initialize()) {
i::Serializer::disable();
return true; return true;
} else { } else {
return i::V8::Initialize(NULL); return i::V8::Initialize(NULL);

View File

@ -69,27 +69,27 @@ XMMRegister xmm7 = { 7 };
// Implementation of CpuFeatures // Implementation of CpuFeatures
// Safe default is no features. // Safe default is no features.
uint32_t CpuFeatures::supported_ = 0; uint64_t CpuFeatures::supported_ = 0;
uint32_t CpuFeatures::enabled_ = 0; uint64_t CpuFeatures::enabled_ = 0;
typedef int (*F0)();
// The Probe method needs executable memory, so it uses Heap::CreateCode. // The Probe method needs executable memory, so it uses Heap::CreateCode.
// Allocation failure is silent and leads to safe default. // Allocation failure is silent and leads to safe default.
void CpuFeatures::Probe() { void CpuFeatures::Probe() {
supported_ = 0; ASSERT(Heap::HasBeenSetup());
ASSERT(supported_ == 0);
if (Serializer::enabled()) return; // No features if we might serialize. if (Serializer::enabled()) return; // No features if we might serialize.
Assembler assm(NULL, 0); Assembler assm(NULL, 0);
Label done; Label cpuid, done;
#define __ assm. #define __ assm.
// Save old esp, since we are going to modify the stack. // Save old esp, since we are going to modify the stack.
__ push(ebp); __ push(ebp);
__ pushfd(); __ pushfd();
__ push(ecx); __ push(ecx);
__ push(edx);
__ push(ebx); __ push(ebx);
__ mov(ebp, Operand(esp)); __ mov(ebp, Operand(esp));
// If we can modify bit 21 of the EFLAGS register, then CPUID is supported. // If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
__ pushfd(); __ pushfd();
__ pop(eax); __ pop(eax);
@ -100,34 +100,48 @@ void CpuFeatures::Probe() {
__ pushfd(); __ pushfd();
__ pop(eax); __ pop(eax);
__ xor_(eax, Operand(edx)); // Different if CPUID is supported. __ xor_(eax, Operand(edx)); // Different if CPUID is supported.
__ j(zero, &done); __ j(not_zero, &cpuid);
// Invoke CPUID with 1 in eax to get feature information in edx.
// CPUID not supported. Clear the supported features in edx:eax.
__ xor_(eax, Operand(eax));
__ xor_(edx, Operand(edx));
__ jmp(&done);
// Invoke CPUID with 1 in eax to get feature information in
// ecx:edx. Temporarily enable CPUID support because we know it's
// safe here.
__ bind(&cpuid);
__ mov(eax, 1); __ mov(eax, 1);
// Temporarily force CPUID support, since we know it is safe here.
supported_ = (1 << CPUID); supported_ = (1 << CPUID);
{ Scope fscope(CPUID); { Scope fscope(CPUID);
__ cpuid(); __ cpuid();
} }
supported_ = 0; supported_ = 0;
// Return result in eax.
// Move the result from ecx:edx to edx:eax and make sure to mark the
// CPUID feature as supported.
__ mov(eax, Operand(edx)); __ mov(eax, Operand(edx));
__ or_(eax, 1 << CPUID);
__ mov(edx, Operand(ecx));
// Done.
__ bind(&done); __ bind(&done);
__ mov(esp, Operand(ebp)); __ mov(esp, Operand(ebp));
__ pop(ebx); __ pop(ebx);
__ pop(edx);
__ pop(ecx); __ pop(ecx);
__ popfd(); __ popfd();
__ pop(ebp); __ pop(ebp);
__ ret(0); __ ret(0);
#undef __ #undef __
CodeDesc desc; CodeDesc desc;
assm.GetCode(&desc); assm.GetCode(&desc);
Object* code = Object* code =
Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL); Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL);
if (!code->IsCode()) return; if (!code->IsCode()) return;
F0 f = FUNCTION_CAST<F0>(Code::cast(code)->entry()); typedef uint64_t (*F0)();
uint32_t res = f(); F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry());
supported_ = (res | (1 << CPUID)); supported_ = probe();
} }
@ -1614,6 +1628,15 @@ void Assembler::fistp_s(const Operand& adr) {
} }
void Assembler::fisttp_s(const Operand& adr) {
ASSERT(CpuFeatures::IsEnabled(CpuFeatures::SSE3));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xDB);
emit_operand(ecx, adr);
}
void Assembler::fist_s(const Operand& adr) { void Assembler::fist_s(const Operand& adr) {
EnsureSpace ensure_space(this); EnsureSpace ensure_space(this);
last_pc_ = pc_; last_pc_ = pc_;
@ -1809,6 +1832,14 @@ void Assembler::frndint() {
} }
void Assembler::fnclex() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xDB);
EMIT(0xE2);
}
void Assembler::sahf() { void Assembler::sahf() {
EnsureSpace ensure_space(this); EnsureSpace ensure_space(this);
last_pc_ = pc_; last_pc_ = pc_;

View File

@ -347,14 +347,18 @@ class CpuFeatures : public AllStatic {
// Feature flags bit positions. They are mostly based on the CPUID spec. // Feature flags bit positions. They are mostly based on the CPUID spec.
// (We assign CPUID itself to one of the currently reserved bits -- // (We assign CPUID itself to one of the currently reserved bits --
// feel free to change this if needed.) // feel free to change this if needed.)
enum Feature { SSE2 = 26, CMOV = 15, RDTSC = 4, CPUID = 10 }; enum Feature { SSE3 = 32, SSE2 = 26, CMOV = 15, RDTSC = 4, CPUID = 10 };
// Detect features of the target CPU. Set safe defaults if the serializer // Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable). // is enabled (snapshots must be portable).
static void Probe(); static void Probe();
// Check whether a feature is supported by the target CPU. // Check whether a feature is supported by the target CPU.
static bool IsSupported(Feature f) { return supported_ & (1 << f); } static bool IsSupported(Feature f) {
return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
}
// Check whether a feature is currently enabled. // Check whether a feature is currently enabled.
static bool IsEnabled(Feature f) { return enabled_ & (1 << f); } static bool IsEnabled(Feature f) {
return (enabled_ & (static_cast<uint64_t>(1) << f)) != 0;
}
// Enable a specified feature within a scope. // Enable a specified feature within a scope.
class Scope BASE_EMBEDDED { class Scope BASE_EMBEDDED {
#ifdef DEBUG #ifdef DEBUG
@ -362,19 +366,19 @@ class CpuFeatures : public AllStatic {
explicit Scope(Feature f) { explicit Scope(Feature f) {
ASSERT(CpuFeatures::IsSupported(f)); ASSERT(CpuFeatures::IsSupported(f));
old_enabled_ = CpuFeatures::enabled_; old_enabled_ = CpuFeatures::enabled_;
CpuFeatures::enabled_ |= (1 << f); CpuFeatures::enabled_ |= (static_cast<uint64_t>(1) << f);
} }
~Scope() { CpuFeatures::enabled_ = old_enabled_; } ~Scope() { CpuFeatures::enabled_ = old_enabled_; }
private: private:
uint32_t old_enabled_; uint64_t old_enabled_;
#else #else
public: public:
explicit Scope(Feature f) {} explicit Scope(Feature f) {}
#endif #endif
}; };
private: private:
static uint32_t supported_; static uint64_t supported_;
static uint32_t enabled_; static uint64_t enabled_;
}; };
@ -635,6 +639,8 @@ class Assembler : public Malloced {
void fistp_s(const Operand& adr); void fistp_s(const Operand& adr);
void fistp_d(const Operand& adr); void fistp_d(const Operand& adr);
void fisttp_s(const Operand& adr);
void fabs(); void fabs();
void fchs(); void fchs();
@ -663,6 +669,7 @@ class Assembler : public Malloced {
void fcompp(); void fcompp();
void fnstsw_ax(); void fnstsw_ax();
void fwait(); void fwait();
void fnclex();
void frndint(); void frndint();

View File

@ -4507,31 +4507,44 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx); FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
FloatingPointHelper::LoadFloatOperands(masm, ecx); FloatingPointHelper::LoadFloatOperands(masm, ecx);
Label non_int32_operands, non_smi_result, skip_allocation; Label skip_allocation, non_smi_result, operand_conversion_failure;
// Reserve space for converted numbers. // Reserve space for converted numbers.
__ sub(Operand(esp), Immediate(2 * kPointerSize)); __ sub(Operand(esp), Immediate(2 * kPointerSize));
// Check if right operand is int32. bool use_sse3 = CpuFeatures::IsSupported(CpuFeatures::SSE3);
__ fist_s(Operand(esp, 1 * kPointerSize)); if (use_sse3) {
__ fild_s(Operand(esp, 1 * kPointerSize)); // Truncate the operands to 32-bit integers and check for
__ fucompp(); // exceptions in doing so.
__ fnstsw_ax(); CpuFeatures::Scope scope(CpuFeatures::SSE3);
__ sahf(); __ fisttp_s(Operand(esp, 0 * kPointerSize));
__ j(not_zero, &non_int32_operands); __ fisttp_s(Operand(esp, 1 * kPointerSize));
__ j(parity_even, &non_int32_operands); __ fnstsw_ax();
__ test(eax, Immediate(1));
__ j(not_zero, &operand_conversion_failure);
} else {
// Check if right operand is int32.
__ fist_s(Operand(esp, 0 * kPointerSize));
__ fild_s(Operand(esp, 0 * kPointerSize));
__ fucompp();
__ fnstsw_ax();
__ sahf();
__ j(not_zero, &operand_conversion_failure);
__ j(parity_even, &operand_conversion_failure);
// Check if left operand is int32. // Check if left operand is int32.
__ fist_s(Operand(esp, 0 * kPointerSize)); __ fist_s(Operand(esp, 1 * kPointerSize));
__ fild_s(Operand(esp, 0 * kPointerSize)); __ fild_s(Operand(esp, 1 * kPointerSize));
__ fucompp(); __ fucompp();
__ fnstsw_ax(); __ fnstsw_ax();
__ sahf(); __ sahf();
__ j(not_zero, &non_int32_operands); __ j(not_zero, &operand_conversion_failure);
__ j(parity_even, &non_int32_operands); __ j(parity_even, &operand_conversion_failure);
}
// Get int32 operands and perform bitop. // Get int32 operands and perform bitop.
__ pop(eax);
__ pop(ecx); __ pop(ecx);
__ pop(eax);
switch (op_) { switch (op_) {
case Token::BIT_OR: __ or_(eax, Operand(ecx)); break; case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
case Token::BIT_AND: __ and_(eax, Operand(ecx)); break; case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
@ -4579,10 +4592,22 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ ret(2 * kPointerSize); __ ret(2 * kPointerSize);
} }
__ bind(&non_int32_operands);
// Restore stacks and operands before calling runtime. // Clear the FPU exception flag and reset the stack before calling
__ ffree(0); // the runtime system.
__ bind(&operand_conversion_failure);
__ add(Operand(esp), Immediate(2 * kPointerSize)); __ add(Operand(esp), Immediate(2 * kPointerSize));
if (use_sse3) {
// If we've used the SSE3 instructions for truncating the
// floating point values to integers and it failed, we have a
// pending #IA exception. Clear it.
__ fnclex();
} else {
// The non-SSE3 variant does early bailout if the right
// operand isn't a 32-bit integer, so we may have a single
// value on the FPU stack we need to get rid of.
__ ffree(0);
}
// SHR should return uint32 - go to runtime for non-smi/negative result. // SHR should return uint32 - go to runtime for non-smi/negative result.
if (op_ == Token::SHR) __ bind(&non_smi_result); if (op_ == Token::SHR) __ bind(&non_smi_result);

View File

@ -30,11 +30,12 @@
#include "v8.h" #include "v8.h"
#include "cpu.h" #include "cpu.h"
#include "macro-assembler.h"
namespace v8 { namespace internal { namespace v8 { namespace internal {
void CPU::Setup() { void CPU::Setup() {
// Nothing to do. CpuFeatures::Probe();
} }

View File

@ -162,6 +162,8 @@ int main(int argc, char** argv) {
const int kExtensionCount = 1; const int kExtensionCount = 1;
const char* extension_list[kExtensionCount] = { "v8/gc" }; const char* extension_list[kExtensionCount] = { "v8/gc" };
v8::ExtensionConfiguration extensions(kExtensionCount, extension_list); v8::ExtensionConfiguration extensions(kExtensionCount, extension_list);
i::Serializer::Enable();
v8::Context::New(&extensions); v8::Context::New(&extensions);
// Make sure all builtin scripts are cached. // Make sure all builtin scripts are cached.

View File

@ -930,7 +930,7 @@ Serializer::~Serializer() {
} }
bool Serializer::serialization_enabled_ = true; bool Serializer::serialization_enabled_ = false;
#ifdef DEBUG #ifdef DEBUG
@ -993,7 +993,7 @@ void Serializer::Serialize() {
Heap::IterateRoots(this); Heap::IterateRoots(this);
PutLog(); PutLog();
PutContextStack(); PutContextStack();
disable(); Disable();
} }

View File

@ -147,7 +147,8 @@ class Serializer: public ObjectVisitor {
static bool enabled() { return serialization_enabled_; } static bool enabled() { return serialization_enabled_; }
static void disable() { serialization_enabled_ = false; } static void Enable() { serialization_enabled_ = true; }
static void Disable() { serialization_enabled_ = false; }
private: private:
friend class ReferenceUpdater; friend class ReferenceUpdater;

View File

@ -51,9 +51,6 @@ bool V8::Initialize(Deserializer *des) {
Logger::Setup(); Logger::Setup();
if (des) des->GetLog(); if (des) des->GetLog();
// Setup the CPU support.
CPU::Setup();
// Setup the platform OS support. // Setup the platform OS support.
OS::Setup(); OS::Setup();
@ -83,6 +80,11 @@ bool V8::Initialize(Deserializer *des) {
StubCache::Clear(); StubCache::Clear();
} }
// Setup the CPU support. Must be done after heap setup and after
// any deserialization because we have to have the initial heap
// objects in place for creating the code object used for probing.
CPU::Setup();
return true; return true;
} }

View File

@ -164,8 +164,6 @@ TEST(AssemblerIa323) {
v8::internal::byte buffer[256]; v8::internal::byte buffer[256];
Assembler assm(buffer, sizeof buffer); Assembler assm(buffer, sizeof buffer);
Serializer::disable(); // Needed for Probe when running without snapshot.
CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2)); CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
{ CpuFeatures::Scope fscope(CpuFeatures::SSE2); { CpuFeatures::Scope fscope(CpuFeatures::SSE2);
__ cvttss2si(eax, Operand(esp, 4)); __ cvttss2si(eax, Operand(esp, 4));
@ -197,8 +195,6 @@ TEST(AssemblerIa324) {
v8::internal::byte buffer[256]; v8::internal::byte buffer[256];
Assembler assm(buffer, sizeof buffer); Assembler assm(buffer, sizeof buffer);
Serializer::disable(); // Needed for Probe when running without snapshot.
CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2)); CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
CpuFeatures::Scope fscope(CpuFeatures::SSE2); CpuFeatures::Scope fscope(CpuFeatures::SSE2);
__ cvttsd2si(eax, Operand(esp, 4)); __ cvttsd2si(eax, Operand(esp, 4));
@ -246,8 +242,6 @@ typedef double (*F5)(double x, double y);
TEST(AssemblerIa326) { TEST(AssemblerIa326) {
InitializeVM(); InitializeVM();
v8::HandleScope scope; v8::HandleScope scope;
Serializer::disable(); // Needed for Probe when running without snapshot.
CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2)); CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
CpuFeatures::Scope fscope(CpuFeatures::SSE2); CpuFeatures::Scope fscope(CpuFeatures::SSE2);
v8::internal::byte buffer[256]; v8::internal::byte buffer[256];
@ -290,8 +284,6 @@ typedef double (*F6)(int x);
TEST(AssemblerIa328) { TEST(AssemblerIa328) {
InitializeVM(); InitializeVM();
v8::HandleScope scope; v8::HandleScope scope;
Serializer::disable(); // Needed for Probe when running without snapshot.
CpuFeatures::Probe();
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2)); CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
CpuFeatures::Scope fscope(CpuFeatures::SSE2); CpuFeatures::Scope fscope(CpuFeatures::SSE2);
v8::internal::byte buffer[256]; v8::internal::byte buffer[256];

View File

@ -56,8 +56,6 @@ static void DummyStaticFunction(Object* result) {
TEST(DisasmIa320) { TEST(DisasmIa320) {
InitializeVM(); InitializeVM();
Serializer::disable(); // Needed for Probe when running without snapshot.
CpuFeatures::Probe();
v8::HandleScope scope; v8::HandleScope scope;
v8::internal::byte buffer[1024]; v8::internal::byte buffer[1024];
Assembler assm(buffer, sizeof buffer); Assembler assm(buffer, sizeof buffer);

View File

@ -157,6 +157,7 @@ static void Serialize() {
const int kExtensionCount = 1; const int kExtensionCount = 1;
const char* extension_list[kExtensionCount] = { "v8/gc" }; const char* extension_list[kExtensionCount] = { "v8/gc" };
v8::ExtensionConfiguration extensions(kExtensionCount, extension_list); v8::ExtensionConfiguration extensions(kExtensionCount, extension_list);
Serializer::Enable();
v8::Persistent<v8::Context> env = v8::Context::New(&extensions); v8::Persistent<v8::Context> env = v8::Context::New(&extensions);
env->Enter(); env->Enter();
@ -187,6 +188,7 @@ TEST(SerializeNondestructive) {
if (Snapshot::IsEnabled()) return; if (Snapshot::IsEnabled()) return;
StatsTable::SetCounterFunction(counter_function); StatsTable::SetCounterFunction(counter_function);
v8::HandleScope scope; v8::HandleScope scope;
Serializer::Enable();
v8::Persistent<v8::Context> env = v8::Context::New(); v8::Persistent<v8::Context> env = v8::Context::New();
v8::Context::Scope context_scope(env); v8::Context::Scope context_scope(env);
Serializer().Serialize(); Serializer().Serialize();