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:
parent
539a29cbd3
commit
1e42e70021
@ -2175,7 +2175,6 @@ bool v8::V8::Initialize() {
|
||||
if (i::V8::HasBeenSetup()) return true;
|
||||
HandleScope scope;
|
||||
if (i::Snapshot::Initialize()) {
|
||||
i::Serializer::disable();
|
||||
return true;
|
||||
} else {
|
||||
return i::V8::Initialize(NULL);
|
||||
|
@ -69,27 +69,27 @@ XMMRegister xmm7 = { 7 };
|
||||
// Implementation of CpuFeatures
|
||||
|
||||
// Safe default is no features.
|
||||
uint32_t CpuFeatures::supported_ = 0;
|
||||
uint32_t CpuFeatures::enabled_ = 0;
|
||||
uint64_t CpuFeatures::supported_ = 0;
|
||||
uint64_t CpuFeatures::enabled_ = 0;
|
||||
|
||||
|
||||
typedef int (*F0)();
|
||||
|
||||
// The Probe method needs executable memory, so it uses Heap::CreateCode.
|
||||
// Allocation failure is silent and leads to safe default.
|
||||
void CpuFeatures::Probe() {
|
||||
supported_ = 0;
|
||||
ASSERT(Heap::HasBeenSetup());
|
||||
ASSERT(supported_ == 0);
|
||||
if (Serializer::enabled()) return; // No features if we might serialize.
|
||||
|
||||
Assembler assm(NULL, 0);
|
||||
Label done;
|
||||
Label cpuid, done;
|
||||
#define __ assm.
|
||||
// Save old esp, since we are going to modify the stack.
|
||||
__ push(ebp);
|
||||
__ pushfd();
|
||||
__ push(ecx);
|
||||
__ push(edx);
|
||||
__ push(ebx);
|
||||
__ mov(ebp, Operand(esp));
|
||||
|
||||
// If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
|
||||
__ pushfd();
|
||||
__ pop(eax);
|
||||
@ -100,34 +100,48 @@ void CpuFeatures::Probe() {
|
||||
__ pushfd();
|
||||
__ pop(eax);
|
||||
__ xor_(eax, Operand(edx)); // Different if CPUID is supported.
|
||||
__ j(zero, &done);
|
||||
// Invoke CPUID with 1 in eax to get feature information in edx.
|
||||
__ j(not_zero, &cpuid);
|
||||
|
||||
// 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);
|
||||
// Temporarily force CPUID support, since we know it is safe here.
|
||||
supported_ = (1 << CPUID);
|
||||
{ Scope fscope(CPUID);
|
||||
__ cpuid();
|
||||
}
|
||||
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));
|
||||
__ or_(eax, 1 << CPUID);
|
||||
__ mov(edx, Operand(ecx));
|
||||
|
||||
// Done.
|
||||
__ bind(&done);
|
||||
__ mov(esp, Operand(ebp));
|
||||
__ pop(ebx);
|
||||
__ pop(edx);
|
||||
__ pop(ecx);
|
||||
__ popfd();
|
||||
__ pop(ebp);
|
||||
__ ret(0);
|
||||
#undef __
|
||||
|
||||
CodeDesc desc;
|
||||
assm.GetCode(&desc);
|
||||
Object* code =
|
||||
Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL);
|
||||
if (!code->IsCode()) return;
|
||||
F0 f = FUNCTION_CAST<F0>(Code::cast(code)->entry());
|
||||
uint32_t res = f();
|
||||
supported_ = (res | (1 << CPUID));
|
||||
typedef uint64_t (*F0)();
|
||||
F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry());
|
||||
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) {
|
||||
EnsureSpace ensure_space(this);
|
||||
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() {
|
||||
EnsureSpace ensure_space(this);
|
||||
last_pc_ = pc_;
|
||||
|
@ -347,14 +347,18 @@ class CpuFeatures : public AllStatic {
|
||||
// Feature flags bit positions. They are mostly based on the CPUID spec.
|
||||
// (We assign CPUID itself to one of the currently reserved bits --
|
||||
// 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
|
||||
// is enabled (snapshots must be portable).
|
||||
static void Probe();
|
||||
// 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.
|
||||
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.
|
||||
class Scope BASE_EMBEDDED {
|
||||
#ifdef DEBUG
|
||||
@ -362,19 +366,19 @@ class CpuFeatures : public AllStatic {
|
||||
explicit Scope(Feature f) {
|
||||
ASSERT(CpuFeatures::IsSupported(f));
|
||||
old_enabled_ = CpuFeatures::enabled_;
|
||||
CpuFeatures::enabled_ |= (1 << f);
|
||||
CpuFeatures::enabled_ |= (static_cast<uint64_t>(1) << f);
|
||||
}
|
||||
~Scope() { CpuFeatures::enabled_ = old_enabled_; }
|
||||
private:
|
||||
uint32_t old_enabled_;
|
||||
uint64_t old_enabled_;
|
||||
#else
|
||||
public:
|
||||
explicit Scope(Feature f) {}
|
||||
#endif
|
||||
};
|
||||
private:
|
||||
static uint32_t supported_;
|
||||
static uint32_t enabled_;
|
||||
static uint64_t supported_;
|
||||
static uint64_t enabled_;
|
||||
};
|
||||
|
||||
|
||||
@ -635,6 +639,8 @@ class Assembler : public Malloced {
|
||||
void fistp_s(const Operand& adr);
|
||||
void fistp_d(const Operand& adr);
|
||||
|
||||
void fisttp_s(const Operand& adr);
|
||||
|
||||
void fabs();
|
||||
void fchs();
|
||||
|
||||
@ -663,6 +669,7 @@ class Assembler : public Malloced {
|
||||
void fcompp();
|
||||
void fnstsw_ax();
|
||||
void fwait();
|
||||
void fnclex();
|
||||
|
||||
void frndint();
|
||||
|
||||
|
@ -4507,31 +4507,44 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
|
||||
FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
|
||||
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.
|
||||
__ sub(Operand(esp), Immediate(2 * kPointerSize));
|
||||
|
||||
// Check if right operand is int32.
|
||||
__ fist_s(Operand(esp, 1 * kPointerSize));
|
||||
__ fild_s(Operand(esp, 1 * kPointerSize));
|
||||
__ fucompp();
|
||||
__ fnstsw_ax();
|
||||
__ sahf();
|
||||
__ j(not_zero, &non_int32_operands);
|
||||
__ j(parity_even, &non_int32_operands);
|
||||
bool use_sse3 = CpuFeatures::IsSupported(CpuFeatures::SSE3);
|
||||
if (use_sse3) {
|
||||
// Truncate the operands to 32-bit integers and check for
|
||||
// exceptions in doing so.
|
||||
CpuFeatures::Scope scope(CpuFeatures::SSE3);
|
||||
__ fisttp_s(Operand(esp, 0 * kPointerSize));
|
||||
__ fisttp_s(Operand(esp, 1 * kPointerSize));
|
||||
__ 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.
|
||||
__ fist_s(Operand(esp, 0 * kPointerSize));
|
||||
__ fild_s(Operand(esp, 0 * kPointerSize));
|
||||
__ fucompp();
|
||||
__ fnstsw_ax();
|
||||
__ sahf();
|
||||
__ j(not_zero, &non_int32_operands);
|
||||
__ j(parity_even, &non_int32_operands);
|
||||
// Check if left operand is int32.
|
||||
__ fist_s(Operand(esp, 1 * kPointerSize));
|
||||
__ fild_s(Operand(esp, 1 * kPointerSize));
|
||||
__ fucompp();
|
||||
__ fnstsw_ax();
|
||||
__ sahf();
|
||||
__ j(not_zero, &operand_conversion_failure);
|
||||
__ j(parity_even, &operand_conversion_failure);
|
||||
}
|
||||
|
||||
// Get int32 operands and perform bitop.
|
||||
__ pop(eax);
|
||||
__ pop(ecx);
|
||||
__ pop(eax);
|
||||
switch (op_) {
|
||||
case Token::BIT_OR: __ or_(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));
|
||||
__ ret(2 * kPointerSize);
|
||||
}
|
||||
__ bind(&non_int32_operands);
|
||||
// Restore stacks and operands before calling runtime.
|
||||
__ ffree(0);
|
||||
|
||||
// Clear the FPU exception flag and reset the stack before calling
|
||||
// the runtime system.
|
||||
__ bind(&operand_conversion_failure);
|
||||
__ 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.
|
||||
if (op_ == Token::SHR) __ bind(&non_smi_result);
|
||||
|
@ -30,11 +30,12 @@
|
||||
#include "v8.h"
|
||||
|
||||
#include "cpu.h"
|
||||
#include "macro-assembler.h"
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
void CPU::Setup() {
|
||||
// Nothing to do.
|
||||
CpuFeatures::Probe();
|
||||
}
|
||||
|
||||
|
||||
|
@ -162,6 +162,8 @@ int main(int argc, char** argv) {
|
||||
const int kExtensionCount = 1;
|
||||
const char* extension_list[kExtensionCount] = { "v8/gc" };
|
||||
v8::ExtensionConfiguration extensions(kExtensionCount, extension_list);
|
||||
|
||||
i::Serializer::Enable();
|
||||
v8::Context::New(&extensions);
|
||||
|
||||
// Make sure all builtin scripts are cached.
|
||||
|
@ -930,7 +930,7 @@ Serializer::~Serializer() {
|
||||
}
|
||||
|
||||
|
||||
bool Serializer::serialization_enabled_ = true;
|
||||
bool Serializer::serialization_enabled_ = false;
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -993,7 +993,7 @@ void Serializer::Serialize() {
|
||||
Heap::IterateRoots(this);
|
||||
PutLog();
|
||||
PutContextStack();
|
||||
disable();
|
||||
Disable();
|
||||
}
|
||||
|
||||
|
||||
|
@ -147,7 +147,8 @@ class Serializer: public ObjectVisitor {
|
||||
|
||||
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:
|
||||
friend class ReferenceUpdater;
|
||||
|
@ -51,9 +51,6 @@ bool V8::Initialize(Deserializer *des) {
|
||||
Logger::Setup();
|
||||
if (des) des->GetLog();
|
||||
|
||||
// Setup the CPU support.
|
||||
CPU::Setup();
|
||||
|
||||
// Setup the platform OS support.
|
||||
OS::Setup();
|
||||
|
||||
@ -83,6 +80,11 @@ bool V8::Initialize(Deserializer *des) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -164,8 +164,6 @@ TEST(AssemblerIa323) {
|
||||
v8::internal::byte buffer[256];
|
||||
Assembler assm(buffer, sizeof buffer);
|
||||
|
||||
Serializer::disable(); // Needed for Probe when running without snapshot.
|
||||
CpuFeatures::Probe();
|
||||
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
|
||||
{ CpuFeatures::Scope fscope(CpuFeatures::SSE2);
|
||||
__ cvttss2si(eax, Operand(esp, 4));
|
||||
@ -197,8 +195,6 @@ TEST(AssemblerIa324) {
|
||||
v8::internal::byte buffer[256];
|
||||
Assembler assm(buffer, sizeof buffer);
|
||||
|
||||
Serializer::disable(); // Needed for Probe when running without snapshot.
|
||||
CpuFeatures::Probe();
|
||||
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
|
||||
CpuFeatures::Scope fscope(CpuFeatures::SSE2);
|
||||
__ cvttsd2si(eax, Operand(esp, 4));
|
||||
@ -246,8 +242,6 @@ typedef double (*F5)(double x, double y);
|
||||
TEST(AssemblerIa326) {
|
||||
InitializeVM();
|
||||
v8::HandleScope scope;
|
||||
Serializer::disable(); // Needed for Probe when running without snapshot.
|
||||
CpuFeatures::Probe();
|
||||
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
|
||||
CpuFeatures::Scope fscope(CpuFeatures::SSE2);
|
||||
v8::internal::byte buffer[256];
|
||||
@ -290,8 +284,6 @@ typedef double (*F6)(int x);
|
||||
TEST(AssemblerIa328) {
|
||||
InitializeVM();
|
||||
v8::HandleScope scope;
|
||||
Serializer::disable(); // Needed for Probe when running without snapshot.
|
||||
CpuFeatures::Probe();
|
||||
CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
|
||||
CpuFeatures::Scope fscope(CpuFeatures::SSE2);
|
||||
v8::internal::byte buffer[256];
|
||||
|
@ -56,8 +56,6 @@ static void DummyStaticFunction(Object* result) {
|
||||
|
||||
TEST(DisasmIa320) {
|
||||
InitializeVM();
|
||||
Serializer::disable(); // Needed for Probe when running without snapshot.
|
||||
CpuFeatures::Probe();
|
||||
v8::HandleScope scope;
|
||||
v8::internal::byte buffer[1024];
|
||||
Assembler assm(buffer, sizeof buffer);
|
||||
|
@ -157,6 +157,7 @@ static void Serialize() {
|
||||
const int kExtensionCount = 1;
|
||||
const char* extension_list[kExtensionCount] = { "v8/gc" };
|
||||
v8::ExtensionConfiguration extensions(kExtensionCount, extension_list);
|
||||
Serializer::Enable();
|
||||
v8::Persistent<v8::Context> env = v8::Context::New(&extensions);
|
||||
env->Enter();
|
||||
|
||||
@ -187,6 +188,7 @@ TEST(SerializeNondestructive) {
|
||||
if (Snapshot::IsEnabled()) return;
|
||||
StatsTable::SetCounterFunction(counter_function);
|
||||
v8::HandleScope scope;
|
||||
Serializer::Enable();
|
||||
v8::Persistent<v8::Context> env = v8::Context::New();
|
||||
v8::Context::Scope context_scope(env);
|
||||
Serializer().Serialize();
|
||||
|
Loading…
Reference in New Issue
Block a user