Port Math.pow inlining to ARM.

TEST=math-pow.js

Review URL: http://codereview.chromium.org/8840008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10210 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2011-12-07 16:55:00 +00:00
parent 30a2c00da5
commit 636e10d065
4 changed files with 194 additions and 129 deletions

View File

@ -3455,110 +3455,198 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
void MathPowStub::Generate(MacroAssembler* masm) {
Label call_runtime;
CpuFeatures::Scope vfp3_scope(VFP3);
const Register base = r1;
const Register exponent = r2;
const Register heapnumbermap = r5;
const Register heapnumber = r0;
const DoubleRegister double_base = d1;
const DoubleRegister double_exponent = d2;
const DoubleRegister double_result = d3;
const DoubleRegister double_scratch = d0;
const SwVfpRegister single_scratch = s0;
const Register scratch = r9;
const Register scratch2 = r7;
if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
Label base_not_smi;
Label exponent_not_smi;
Label convert_exponent;
const Register base = r0;
const Register exponent = r1;
const Register heapnumbermap = r5;
const Register heapnumber = r6;
const DoubleRegister double_base = d0;
const DoubleRegister double_exponent = d1;
const DoubleRegister double_result = d2;
const SwVfpRegister single_scratch = s0;
const Register scratch = r9;
const Register scratch2 = r7;
__ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex);
Label call_runtime, done, exponent_not_smi, int_exponent;
if (exponent_type_ == ON_STACK) {
Label base_is_smi, unpack_exponent;
// The exponent and base are supplied as arguments on the stack.
// This can only happen if the stub is called from non-optimized code.
// Load input parameters from stack to double registers.
__ ldr(base, MemOperand(sp, 1 * kPointerSize));
__ ldr(exponent, MemOperand(sp, 0 * kPointerSize));
// Convert base to double value and store it in d0.
__ JumpIfNotSmi(base, &base_not_smi);
// Base is a Smi. Untag and convert it.
__ SmiUntag(base);
__ vmov(single_scratch, base);
__ vcvt_f64_s32(double_base, single_scratch);
__ b(&convert_exponent);
__ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex);
__ bind(&base_not_smi);
__ JumpIfSmi(base, &base_is_smi);
__ ldr(scratch, FieldMemOperand(base, JSObject::kMapOffset));
__ cmp(scratch, heapnumbermap);
__ b(ne, &call_runtime);
// Base is a heapnumber. Load it into double register.
__ vldr(double_base, FieldMemOperand(base, HeapNumber::kValueOffset));
__ bind(&convert_exponent);
__ vldr(double_base, FieldMemOperand(base, HeapNumber::kValueOffset));
__ jmp(&unpack_exponent);
__ bind(&base_is_smi);
__ SmiUntag(base);
__ vmov(single_scratch, base);
__ vcvt_f64_s32(double_base, single_scratch);
__ bind(&unpack_exponent);
__ JumpIfNotSmi(exponent, &exponent_not_smi);
__ SmiUntag(exponent);
// The base is in a double register and the exponent is
// an untagged smi. Allocate a heap number and call a
// C function for integer exponents. The register containing
// the heap number is callee-saved.
__ AllocateHeapNumber(heapnumber,
scratch,
scratch2,
heapnumbermap,
&call_runtime);
__ push(lr);
__ PrepareCallCFunction(1, 1, scratch);
__ SetCallCDoubleArguments(double_base, exponent);
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(
ExternalReference::power_double_int_function(masm->isolate()),
1, 1);
__ pop(lr);
__ GetCFunctionDoubleResult(double_result);
}
__ vstr(double_result,
FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
__ mov(r0, heapnumber);
__ Ret(2 * kPointerSize);
__ jmp(&int_exponent);
__ bind(&exponent_not_smi);
__ ldr(scratch, FieldMemOperand(exponent, JSObject::kMapOffset));
__ cmp(scratch, heapnumbermap);
__ b(ne, &call_runtime);
// Exponent is a heapnumber. Load it into double register.
__ vldr(double_exponent,
FieldMemOperand(exponent, HeapNumber::kValueOffset));
} else if (exponent_type_ == TAGGED) {
// Base is already in double_base.
__ JumpIfNotSmi(exponent, &exponent_not_smi);
__ SmiUntag(exponent);
__ jmp(&int_exponent);
__ bind(&exponent_not_smi);
__ vldr(double_exponent,
FieldMemOperand(exponent, HeapNumber::kValueOffset));
}
if (exponent_type_ != INTEGER) {
// Detect integer exponents stored as double.
__ vcvt_u32_f64(single_scratch, double_exponent);
// We do not check for NaN or Infinity here because comparing numbers on
// ARM correctly distinguishes NaNs. We end up calling the built-in.
__ vcvt_f64_u32(double_scratch, single_scratch);
__ VFPCompareAndSetFlags(double_scratch, double_exponent);
__ vmov(exponent, single_scratch, eq);
__ b(eq, &int_exponent);
if (exponent_type_ == ON_STACK) {
// Detect square root case. Crankshaft detects constant +/-0.5 at
// compile time and uses DoMathPowHalf instead. We then skip this check
// for non-constant cases of +/-0.5 as these hardly occur.
Label not_plus_half;
// Test for 0.5.
__ vmov(double_scratch, 0.5);
__ VFPCompareAndSetFlags(double_exponent, double_scratch);
__ b(ne, &not_plus_half);
// Calculates square root of base. Check for the special case of
// Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13).
__ vmov(double_scratch, -V8_INFINITY);
__ VFPCompareAndSetFlags(double_base, double_scratch);
__ vneg(double_result, double_scratch, eq);
__ b(eq, &done);
// Add +0 to convert -0 to +0.
__ vadd(double_scratch, double_base, kDoubleRegZero);
__ vsqrt(double_result, double_scratch);
__ jmp(&done);
__ bind(&not_plus_half);
__ vmov(double_scratch, -0.5);
__ VFPCompareAndSetFlags(double_exponent, double_scratch);
__ b(ne, &call_runtime);
// Calculates square root of base. Check for the special case of
// Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13).
__ vmov(double_scratch, -V8_INFINITY);
__ VFPCompareAndSetFlags(double_base, double_scratch);
__ vmov(double_result, kDoubleRegZero, eq);
__ b(eq, &done);
// Add +0 to convert -0 to +0.
__ vadd(double_scratch, double_base, kDoubleRegZero);
__ vmov(double_result, 1);
__ vsqrt(double_scratch, double_scratch);
__ vdiv(double_result, double_result, double_scratch);
__ jmp(&done);
}
// The base and the exponent are in double registers.
// Allocate a heap number and call a C function for
// double exponents. The register containing
// the heap number is callee-saved.
__ AllocateHeapNumber(heapnumber,
scratch,
scratch2,
heapnumbermap,
&call_runtime);
__ push(lr);
__ PrepareCallCFunction(0, 2, scratch);
__ SetCallCDoubleArguments(double_base, double_exponent);
{
AllowExternalCallThatCantCauseGC scope(masm);
__ PrepareCallCFunction(0, 2, scratch);
__ SetCallCDoubleArguments(double_base, double_exponent);
__ CallCFunction(
ExternalReference::power_double_double_function(masm->isolate()),
0, 2);
__ pop(lr);
__ GetCFunctionDoubleResult(double_result);
}
__ vstr(double_result,
FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
__ mov(r0, heapnumber);
__ Ret(2 * kPointerSize);
__ pop(lr);
__ GetCFunctionDoubleResult(double_result);
__ jmp(&done);
}
__ bind(&call_runtime);
__ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
// Calculate power with integer exponent.
__ bind(&int_exponent);
__ mov(scratch, exponent); // Back up exponent.
__ vmov(double_scratch, double_base); // Back up base.
__ vmov(double_result, 1.0);
// Get absolute value of exponent.
__ cmp(scratch, Operand(0));
__ mov(scratch2, Operand(0), LeaveCC, mi);
__ sub(scratch, scratch2, scratch, LeaveCC, mi);
Label while_true;
__ bind(&while_true);
__ mov(scratch, Operand(scratch, ASR, 1), SetCC);
__ vmul(double_result, double_result, double_scratch, cs);
__ vmul(double_scratch, double_scratch, double_scratch, ne);
__ b(ne, &while_true);
__ cmp(exponent, Operand(0));
__ b(ge, &done);
__ vmov(double_scratch, 1.0);
__ vdiv(double_result, double_scratch, double_result);
// Test whether result is zero. Bail out to check for subnormal result.
// Due to subnormals, x^-y == (1/x)^y does not hold in all cases.
__ VFPCompareAndSetFlags(double_result, 0.0);
__ b(ne, &done);
// double_exponent may not containe the exponent value if the input was a
// smi. We set it with exponent value before bailing out.
__ vmov(single_scratch, exponent);
__ vcvt_f64_s32(double_exponent, single_scratch);
// Returning or bailing out.
Counters* counters = masm->isolate()->counters();
if (exponent_type_ == ON_STACK) {
// The arguments are still on the stack.
__ bind(&call_runtime);
__ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
// The stub is called from non-optimized code, which expects the result
// as heap number in exponent.
__ bind(&done);
__ AllocateHeapNumber(
heapnumber, scratch, scratch2, heapnumbermap, &call_runtime);
__ vstr(double_result,
FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
ASSERT(heapnumber.is(r0));
__ IncrementCounter(counters->math_pow(), 1, scratch, scratch2);
__ Ret(2 * kPointerSize);
} else {
__ push(lr);
{
AllowExternalCallThatCantCauseGC scope(masm);
__ PrepareCallCFunction(0, 2, scratch);
__ SetCallCDoubleArguments(double_base, double_exponent);
__ CallCFunction(
ExternalReference::power_double_double_function(masm->isolate()),
0, 2);
}
__ pop(lr);
__ GetCFunctionDoubleResult(double_result);
__ bind(&done);
__ IncrementCounter(counters->math_pow(), 1, scratch, scratch2);
__ Ret();
}
}

View File

@ -2938,8 +2938,12 @@ void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
ASSERT(args->length() == 2);
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
MathPowStub stub(MathPowStub::ON_STACK);
__ CallStub(&stub);
if (CpuFeatures::IsSupported(VFP3)) {
MathPowStub stub(MathPowStub::ON_STACK);
__ CallStub(&stub);
} else {
__ CallRuntime(Runtime::kMath_pow, 2);
}
context()->Plug(r0);
}

View File

@ -1405,7 +1405,7 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) {
LOperand* left = UseFixedDouble(instr->left(), d1);
LOperand* right = exponent_type.IsDouble() ?
UseFixedDouble(instr->right(), d2) :
UseFixed(instr->right(), r0);
UseFixed(instr->right(), r2);
LPower* result = new LPower(left, right);
return MarkAsCall(DefineFixedDouble(result, d3),
instr,

View File

@ -3122,61 +3122,34 @@ void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
void LCodeGen::DoPower(LPower* instr) {
LOperand* left = instr->InputAt(0);
LOperand* right = instr->InputAt(1);
Register scratch = scratch0();
DoubleRegister result_reg = ToDoubleRegister(instr->result());
Representation exponent_type = instr->hydrogen()->right()->representation();
if (exponent_type.IsDouble()) {
// Prepare arguments and call C function.
__ PrepareCallCFunction(0, 2, scratch);
__ SetCallCDoubleArguments(ToDoubleRegister(left),
ToDoubleRegister(right));
__ CallCFunction(
ExternalReference::power_double_double_function(isolate()), 0, 2);
} else if (exponent_type.IsInteger32()) {
ASSERT(ToRegister(right).is(r0));
// Prepare arguments and call C function.
__ PrepareCallCFunction(1, 1, scratch);
__ SetCallCDoubleArguments(ToDoubleRegister(left), ToRegister(right));
__ CallCFunction(
ExternalReference::power_double_int_function(isolate()), 1, 1);
} else {
ASSERT(exponent_type.IsTagged());
ASSERT(instr->hydrogen()->left()->representation().IsDouble());
// Having marked this as a call, we can use any registers.
// Just make sure that the input/output registers are the expected ones.
ASSERT(!instr->InputAt(1)->IsDoubleRegister() ||
ToDoubleRegister(instr->InputAt(1)).is(d2));
ASSERT(!instr->InputAt(1)->IsRegister() ||
ToRegister(instr->InputAt(1)).is(r2));
ASSERT(ToDoubleRegister(instr->InputAt(0)).is(d1));
ASSERT(ToDoubleRegister(instr->result()).is(d3));
Register right_reg = ToRegister(right);
// Check for smi on the right hand side.
Label non_smi, call;
__ JumpIfNotSmi(right_reg, &non_smi);
// Untag smi and convert it to a double.
__ SmiUntag(right_reg);
SwVfpRegister single_scratch = double_scratch0().low();
__ vmov(single_scratch, right_reg);
__ vcvt_f64_s32(result_reg, single_scratch);
__ jmp(&call);
// Heap number map check.
__ bind(&non_smi);
__ ldr(scratch, FieldMemOperand(right_reg, HeapObject::kMapOffset));
if (exponent_type.IsTagged()) {
Label no_deopt;
__ JumpIfSmi(r2, &no_deopt);
__ ldr(r7, FieldMemOperand(r2, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
__ cmp(scratch, Operand(ip));
__ cmp(r7, Operand(ip));
DeoptimizeIf(ne, instr->environment());
int32_t value_offset = HeapNumber::kValueOffset - kHeapObjectTag;
__ add(scratch, right_reg, Operand(value_offset));
__ vldr(result_reg, scratch, 0);
// Prepare arguments and call C function.
__ bind(&call);
__ PrepareCallCFunction(0, 2, scratch);
__ SetCallCDoubleArguments(ToDoubleRegister(left), result_reg);
__ CallCFunction(
ExternalReference::power_double_double_function(isolate()), 0, 2);
__ bind(&no_deopt);
MathPowStub stub(MathPowStub::TAGGED);
__ CallStub(&stub);
} else if (exponent_type.IsInteger32()) {
MathPowStub stub(MathPowStub::INTEGER);
__ CallStub(&stub);
} else {
ASSERT(exponent_type.IsDouble());
MathPowStub stub(MathPowStub::DOUBLE);
__ CallStub(&stub);
}
// Store the result in the result register.
__ GetCFunctionDoubleResult(result_reg);
}