From 636e10d065f56bd85d756a6ac055aa26b9b6bc7c Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Wed, 7 Dec 2011 16:55:00 +0000 Subject: [PATCH] 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 --- src/arm/code-stubs-arm.cc | 240 ++++++++++++++++++++++----------- src/arm/full-codegen-arm.cc | 8 +- src/arm/lithium-arm.cc | 2 +- src/arm/lithium-codegen-arm.cc | 73 ++++------ 4 files changed, 194 insertions(+), 129 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 8b1d0c4b37..9baa911fa9 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -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, ¬_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(¬_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(); + } } diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 17289e8f0a..7e9a889116 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -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); } diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 31f608345b..da22cb6518 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -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, diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 8b3c1a4236..b35dc7c145 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -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); }