Improve code generation for the HRandom instruction.

Since the per-context random number generator is now
properly seeded upon context creation, we do not need
to check for lazy-initialization anymore, and so we
can implement the HRandom instruction w/o having to
call into the C function (which means we don't need
to MarkAsCall anymore).

TEST=cctest/test-random
R=yangguo@chromium.org

Review URL: https://codereview.chromium.org/23478031

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16684 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
bmeurer@chromium.org 2013-09-12 12:12:01 +00:00
parent dc901fae7f
commit 0bdbadcfca
12 changed files with 170 additions and 195 deletions

View File

@ -1724,9 +1724,13 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) {
LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
ASSERT(instr->representation().IsDouble());
ASSERT(instr->global_object()->representation().IsTagged());
LOperand* global_object = UseFixed(instr->global_object(), r0);
LRandom* result = new(zone()) LRandom(global_object);
return MarkAsCall(DefineFixedDouble(result, d7), instr);
LOperand* global_object = UseTempRegister(instr->global_object());
LOperand* scratch = TempRegister();
LOperand* scratch2 = TempRegister();
LOperand* scratch3 = TempRegister();
LRandom* result = new(zone()) LRandom(
global_object, scratch, scratch2, scratch3);
return DefineFixedDouble(result, d7);
}

View File

@ -1462,13 +1462,22 @@ class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> {
};
class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 0> {
class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 3> {
public:
explicit LRandom(LOperand* global_object) {
LRandom(LOperand* global_object,
LOperand* scratch,
LOperand* scratch2,
LOperand* scratch3) {
inputs_[0] = global_object;
temps_[0] = scratch;
temps_[1] = scratch2;
temps_[2] = scratch3;
}
LOperand* global_object() { return inputs_[0]; }
LOperand* global_object() const { return inputs_[0]; }
LOperand* scratch() const { return temps_[0]; }
LOperand* scratch2() const { return temps_[1]; }
LOperand* scratch3() const { return temps_[2]; }
DECLARE_CONCRETE_INSTRUCTION(Random, "random")
DECLARE_HYDROGEN_ACCESSOR(Random)

View File

@ -3891,80 +3891,64 @@ void LCodeGen::DoPower(LPower* instr) {
void LCodeGen::DoRandom(LRandom* instr) {
class DeferredDoRandom V8_FINAL : public LDeferredCode {
public:
DeferredDoRandom(LCodeGen* codegen, LRandom* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() V8_OVERRIDE { codegen()->DoDeferredRandom(instr_); }
virtual LInstruction* instr() V8_OVERRIDE { return instr_; }
private:
LRandom* instr_;
};
DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, instr);
// Having marked this instruction as a call we can use any
// registers.
ASSERT(ToDoubleRegister(instr->result()).is(d7));
ASSERT(ToRegister(instr->global_object()).is(r0));
// Assert that the register size is indeed the size of each seed.
static const int kSeedSize = sizeof(uint32_t);
STATIC_ASSERT(kPointerSize == kSeedSize);
__ ldr(r0, FieldMemOperand(r0, GlobalObject::kNativeContextOffset));
// Load native context
Register global_object = ToRegister(instr->global_object());
Register native_context = global_object;
__ ldr(native_context, FieldMemOperand(
global_object, GlobalObject::kNativeContextOffset));
// Load state (FixedArray of the native context's random seeds)
static const int kRandomSeedOffset =
FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
__ ldr(r2, FieldMemOperand(r0, kRandomSeedOffset));
// r2: FixedArray of the native context's random seeds
Register state = native_context;
__ ldr(state, FieldMemOperand(native_context, kRandomSeedOffset));
// Load state[0].
__ ldr(r1, FieldMemOperand(r2, ByteArray::kHeaderSize));
__ cmp(r1, Operand::Zero());
__ b(eq, deferred->entry());
Register state0 = ToRegister(instr->scratch());
__ ldr(state0, FieldMemOperand(state, ByteArray::kHeaderSize));
// Load state[1].
__ ldr(r0, FieldMemOperand(r2, ByteArray::kHeaderSize + kSeedSize));
// r1: state[0].
// r0: state[1].
Register state1 = ToRegister(instr->scratch2());
__ ldr(state1, FieldMemOperand(state, ByteArray::kHeaderSize + kSeedSize));
// state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
__ and_(r3, r1, Operand(0xFFFF));
__ mov(r4, Operand(18273));
__ mul(r3, r3, r4);
__ add(r1, r3, Operand(r1, LSR, 16));
Register scratch3 = ToRegister(instr->scratch3());
Register scratch4 = scratch0();
__ and_(scratch3, state0, Operand(0xFFFF));
__ mov(scratch4, Operand(18273));
__ mul(scratch3, scratch3, scratch4);
__ add(state0, scratch3, Operand(state0, LSR, 16));
// Save state[0].
__ str(r1, FieldMemOperand(r2, ByteArray::kHeaderSize));
__ str(state0, FieldMemOperand(state, ByteArray::kHeaderSize));
// state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
__ and_(r3, r0, Operand(0xFFFF));
__ mov(r4, Operand(36969));
__ mul(r3, r3, r4);
__ add(r0, r3, Operand(r0, LSR, 16));
__ and_(scratch3, state1, Operand(0xFFFF));
__ mov(scratch4, Operand(36969));
__ mul(scratch3, scratch3, scratch4);
__ add(state1, scratch3, Operand(state1, LSR, 16));
// Save state[1].
__ str(r0, FieldMemOperand(r2, ByteArray::kHeaderSize + kSeedSize));
__ str(state1, FieldMemOperand(state, ByteArray::kHeaderSize + kSeedSize));
// Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
__ and_(r0, r0, Operand(0x3FFFF));
__ add(r0, r0, Operand(r1, LSL, 14));
Register random = scratch4;
__ and_(random, state1, Operand(0x3FFFF));
__ add(random, random, Operand(state0, LSL, 14));
__ bind(deferred->exit());
// 0x41300000 is the top half of 1.0 x 2^20 as a double.
// Create this constant using mov/orr to avoid PC relative load.
__ mov(r1, Operand(0x41000000));
__ orr(r1, r1, Operand(0x300000));
__ mov(scratch3, Operand(0x41000000));
__ orr(scratch3, scratch3, Operand(0x300000));
// Move 0x41300000xxxxxxxx (x = random bits) to VFP.
__ vmov(d7, r0, r1);
DwVfpRegister result = ToDoubleRegister(instr->result());
__ vmov(result, random, scratch3);
// Move 0x4130000000000000 to VFP.
__ mov(r0, Operand::Zero());
__ vmov(d8, r0, r1);
// Subtract and store the result in the heap number.
__ vsub(d7, d7, d8);
}
void LCodeGen::DoDeferredRandom(LRandom* instr) {
__ PrepareCallCFunction(1, scratch0());
__ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
// Return value is in r0.
__ mov(scratch4, Operand::Zero());
DwVfpRegister scratch5 = double_scratch0();
__ vmov(scratch5, scratch4, scratch3);
__ vsub(result, result, scratch5);
}

View File

@ -149,7 +149,6 @@ class LCodeGen V8_FINAL BASE_EMBEDDED {
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
void DoDeferredStackCheck(LStackCheck* instr);
void DoDeferredRandom(LRandom* instr);
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);

View File

@ -4126,85 +4126,66 @@ void LCodeGen::DoPower(LPower* instr) {
void LCodeGen::DoRandom(LRandom* instr) {
class DeferredDoRandom V8_FINAL : public LDeferredCode {
public:
DeferredDoRandom(LCodeGen* codegen,
LRandom* instr,
const X87Stack& x87_stack)
: LDeferredCode(codegen, x87_stack), instr_(instr) { }
virtual void Generate() V8_OVERRIDE { codegen()->DoDeferredRandom(instr_); }
virtual LInstruction* instr() V8_OVERRIDE { return instr_; }
private:
LRandom* instr_;
};
DeferredDoRandom* deferred =
new(zone()) DeferredDoRandom(this, instr, x87_stack_);
CpuFeatureScope scope(masm(), SSE2);
// Having marked this instruction as a call we can use any
// registers.
ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
ASSERT(ToRegister(instr->global_object()).is(eax));
// Assert that the register size is indeed the size of each seed.
static const int kSeedSize = sizeof(uint32_t);
STATIC_ASSERT(kPointerSize == kSeedSize);
__ mov(eax, FieldOperand(eax, GlobalObject::kNativeContextOffset));
// Load native context
Register global_object = ToRegister(instr->global_object());
Register native_context = global_object;
__ mov(native_context, FieldOperand(
global_object, GlobalObject::kNativeContextOffset));
// Load state (FixedArray of the native context's random seeds)
static const int kRandomSeedOffset =
FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
__ mov(ebx, FieldOperand(eax, kRandomSeedOffset));
// ebx: FixedArray of the native context's random seeds
Register state = native_context;
__ mov(state, FieldOperand(native_context, kRandomSeedOffset));
// Load state[0].
__ mov(ecx, FieldOperand(ebx, ByteArray::kHeaderSize));
// If state[0] == 0, call runtime to initialize seeds.
__ test(ecx, ecx);
__ j(zero, deferred->entry());
Register state0 = ToRegister(instr->scratch());
__ mov(state0, FieldOperand(state, ByteArray::kHeaderSize));
// Load state[1].
__ mov(eax, FieldOperand(ebx, ByteArray::kHeaderSize + kSeedSize));
// ecx: state[0]
// eax: state[1]
Register state1 = ToRegister(instr->scratch2());
__ mov(state1, FieldOperand(state, ByteArray::kHeaderSize + kSeedSize));
// state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
__ movzx_w(edx, ecx);
__ imul(edx, edx, 18273);
__ shr(ecx, 16);
__ add(ecx, edx);
Register scratch3 = ToRegister(instr->scratch3());
__ movzx_w(scratch3, state0);
__ imul(scratch3, scratch3, 18273);
__ shr(state0, 16);
__ add(state0, scratch3);
// Save state[0].
__ mov(FieldOperand(ebx, ByteArray::kHeaderSize), ecx);
__ mov(FieldOperand(state, ByteArray::kHeaderSize), state0);
// state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
__ movzx_w(edx, eax);
__ imul(edx, edx, 36969);
__ shr(eax, 16);
__ add(eax, edx);
__ movzx_w(scratch3, state1);
__ imul(scratch3, scratch3, 36969);
__ shr(state1, 16);
__ add(state1, scratch3);
// Save state[1].
__ mov(FieldOperand(ebx, ByteArray::kHeaderSize + kSeedSize), eax);
__ mov(FieldOperand(state, ByteArray::kHeaderSize + kSeedSize), state1);
// Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
__ shl(ecx, 14);
__ and_(eax, Immediate(0x3FFFF));
__ add(eax, ecx);
Register random = state0;
__ shl(random, 14);
__ and_(state1, Immediate(0x3FFFF));
__ add(random, state1);
__ bind(deferred->exit());
// Convert 32 random bits in eax to 0.(32 random bits) in a double
// Convert 32 random bits in random to 0.(32 random bits) in a double
// by computing:
// ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
__ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
__ movd(xmm2, ebx);
__ movd(xmm1, eax);
__ cvtss2sd(xmm2, xmm2);
__ xorps(xmm1, xmm2);
__ subsd(xmm1, xmm2);
}
void LCodeGen::DoDeferredRandom(LRandom* instr) {
__ PrepareCallCFunction(1, ebx);
__ mov(Operand(esp, 0), eax);
__ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
// Return value is in eax.
XMMRegister result = ToDoubleRegister(instr->result());
// We use xmm0 as fixed scratch register here.
XMMRegister scratch4 = xmm0;
__ mov(scratch3, Immediate(0x49800000)); // 1.0 x 2^20 as single.
__ movd(scratch4, scratch3);
__ movd(result, random);
__ cvtss2sd(scratch4, scratch4);
__ xorps(result, scratch4);
__ subsd(result, scratch4);
}

View File

@ -166,7 +166,6 @@ class LCodeGen V8_FINAL BASE_EMBEDDED {
void DoDeferredTaggedToI(LTaggedToI* instr, Label* done);
void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
void DoDeferredStackCheck(LStackCheck* instr);
void DoDeferredRandom(LRandom* instr);
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);

View File

@ -1715,10 +1715,14 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) {
LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
ASSERT(instr->representation().IsDouble());
ASSERT(instr->global_object()->representation().IsSmiOrTagged());
LOperand* global_object = UseFixed(instr->global_object(), eax);
LRandom* result = new(zone()) LRandom(global_object);
return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
ASSERT(instr->global_object()->representation().IsTagged());
LOperand* global_object = UseTempRegister(instr->global_object());
LOperand* scratch = TempRegister();
LOperand* scratch2 = TempRegister();
LOperand* scratch3 = TempRegister();
LRandom* result = new(zone()) LRandom(
global_object, scratch, scratch2, scratch3);
return DefineFixedDouble(result, xmm1);
}

View File

@ -1462,13 +1462,22 @@ class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> {
};
class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 0> {
class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 3> {
public:
explicit LRandom(LOperand* global_object) {
LRandom(LOperand* global_object,
LOperand* scratch,
LOperand* scratch2,
LOperand* scratch3) {
inputs_[0] = global_object;
temps_[0] = scratch;
temps_[1] = scratch2;
temps_[2] = scratch3;
}
LOperand* global_object() { return inputs_[0]; }
LOperand* global_object() const { return inputs_[0]; }
LOperand* scratch() const { return temps_[0]; }
LOperand* scratch2() const { return temps_[1]; }
LOperand* scratch3() const { return temps_[2]; }
DECLARE_CONCRETE_INSTRUCTION(Random, "random")
DECLARE_HYDROGEN_ACCESSOR(Random)

View File

@ -3667,90 +3667,64 @@ void LCodeGen::DoPower(LPower* instr) {
void LCodeGen::DoRandom(LRandom* instr) {
class DeferredDoRandom V8_FINAL : public LDeferredCode {
public:
DeferredDoRandom(LCodeGen* codegen, LRandom* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() V8_OVERRIDE { codegen()->DoDeferredRandom(instr_); }
virtual LInstruction* instr() V8_OVERRIDE { return instr_; }
private:
LRandom* instr_;
};
DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, instr);
// Having marked this instruction as a call we can use any
// registers.
ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
// Choose the right register for the first argument depending on
// calling convention.
#ifdef _WIN64
ASSERT(ToRegister(instr->global_object()).is(rcx));
Register global_object = rcx;
#else
ASSERT(ToRegister(instr->global_object()).is(rdi));
Register global_object = rdi;
#endif
// Assert that register size is twice the size of each seed.
static const int kSeedSize = sizeof(uint32_t);
STATIC_ASSERT(kPointerSize == 2 * kSeedSize);
__ movq(global_object,
FieldOperand(global_object, GlobalObject::kNativeContextOffset));
// Load native context
Register global_object = ToRegister(instr->global_object());
Register native_context = global_object;
__ movq(native_context, FieldOperand(
global_object, GlobalObject::kNativeContextOffset));
// Load state (FixedArray of the native context's random seeds)
static const int kRandomSeedOffset =
FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
__ movq(rbx, FieldOperand(global_object, kRandomSeedOffset));
// rbx: FixedArray of the native context's random seeds
Register state = native_context;
__ movq(state, FieldOperand(native_context, kRandomSeedOffset));
// Load state[0].
__ movl(rax, FieldOperand(rbx, ByteArray::kHeaderSize));
// If state[0] == 0, call runtime to initialize seeds.
__ testl(rax, rax);
__ j(zero, deferred->entry());
Register state0 = ToRegister(instr->scratch());
__ movl(state0, FieldOperand(state, ByteArray::kHeaderSize));
// Load state[1].
__ movl(rcx, FieldOperand(rbx, ByteArray::kHeaderSize + kSeedSize));
Register state1 = ToRegister(instr->scratch2());
__ movl(state1, FieldOperand(state, ByteArray::kHeaderSize + kSeedSize));
// state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
// Only operate on the lower 32 bit of rax.
__ movzxwl(rdx, rax);
__ imull(rdx, rdx, Immediate(18273));
__ shrl(rax, Immediate(16));
__ addl(rax, rdx);
Register scratch3 = ToRegister(instr->scratch3());
__ movzxwl(scratch3, state0);
__ imull(scratch3, scratch3, Immediate(18273));
__ shrl(state0, Immediate(16));
__ addl(state0, scratch3);
// Save state[0].
__ movl(FieldOperand(rbx, ByteArray::kHeaderSize), rax);
__ movl(FieldOperand(state, ByteArray::kHeaderSize), state0);
// state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
__ movzxwl(rdx, rcx);
__ imull(rdx, rdx, Immediate(36969));
__ shrl(rcx, Immediate(16));
__ addl(rcx, rdx);
__ movzxwl(scratch3, state1);
__ imull(scratch3, scratch3, Immediate(36969));
__ shrl(state1, Immediate(16));
__ addl(state1, scratch3);
// Save state[1].
__ movl(FieldOperand(rbx, ByteArray::kHeaderSize + kSeedSize), rcx);
__ movl(FieldOperand(state, ByteArray::kHeaderSize + kSeedSize), state1);
// Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
__ shll(rax, Immediate(14));
__ andl(rcx, Immediate(0x3FFFF));
__ addl(rax, rcx);
Register random = state0;
__ shll(random, Immediate(14));
__ andl(state1, Immediate(0x3FFFF));
__ addl(random, state1);
__ bind(deferred->exit());
// Convert 32 random bits in rax to 0.(32 random bits) in a double
// by computing:
// ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
__ movq(rcx, V8_INT64_C(0x4130000000000000),
XMMRegister result = ToDoubleRegister(instr->result());
// We use xmm0 as fixed scratch register here.
XMMRegister scratch4 = xmm0;
__ movq(scratch3, V8_INT64_C(0x4130000000000000),
RelocInfo::NONE64); // 1.0 x 2^20 as double
__ movq(xmm2, rcx);
__ movd(xmm1, rax);
__ xorps(xmm1, xmm2);
__ subsd(xmm1, xmm2);
}
void LCodeGen::DoDeferredRandom(LRandom* instr) {
__ PrepareCallCFunction(1);
__ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// Return value is in rax.
__ movq(scratch4, scratch3);
__ movd(result, random);
__ xorps(result, scratch4);
__ subsd(result, scratch4);
}

View File

@ -126,7 +126,6 @@ class LCodeGen V8_FINAL BASE_EMBEDDED {
void DoDeferredTaggedToI(LTaggedToI* instr, Label* done);
void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
void DoDeferredStackCheck(LStackCheck* instr);
void DoDeferredRandom(LRandom* instr);
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
void DoDeferredAllocate(LAllocate* instr);

View File

@ -1613,9 +1613,13 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) {
LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
ASSERT(instr->representation().IsDouble());
ASSERT(instr->global_object()->representation().IsTagged());
LOperand* global_object = UseFixed(instr->global_object(), arg_reg_1);
LRandom* result = new(zone()) LRandom(global_object);
return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
LOperand* global_object = UseTempRegister(instr->global_object());
LOperand* scratch = TempRegister();
LOperand* scratch2 = TempRegister();
LOperand* scratch3 = TempRegister();
LRandom* result = new(zone()) LRandom(
global_object, scratch, scratch2, scratch3);
return DefineFixedDouble(result, xmm1);
}

View File

@ -1404,13 +1404,22 @@ class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> {
};
class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 0> {
class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 3> {
public:
explicit LRandom(LOperand* global_object) {
LRandom(LOperand* global_object,
LOperand* scratch,
LOperand* scratch2,
LOperand* scratch3) {
inputs_[0] = global_object;
temps_[0] = scratch;
temps_[1] = scratch2;
temps_[2] = scratch3;
}
LOperand* global_object() { return inputs_[0]; }
LOperand* scratch() const { return temps_[0]; }
LOperand* scratch2() const { return temps_[1]; }
LOperand* scratch3() const { return temps_[2]; }
DECLARE_CONCRETE_INSTRUCTION(Random, "random")
DECLARE_HYDROGEN_ACCESSOR(Random)