Commit 3ef0c5dd authored by whesse@chromium.org's avatar whesse@chromium.org

Allow the optimizing code generator to call Math.pow with untagged doubles.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5949 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 967a0e30
...@@ -66,6 +66,7 @@ namespace internal { ...@@ -66,6 +66,7 @@ namespace internal {
const double DoubleConstant::min_int = kMinInt; const double DoubleConstant::min_int = kMinInt;
const double DoubleConstant::one_half = 0.5; const double DoubleConstant::one_half = 0.5;
const double DoubleConstant::negative_infinity = -V8_INFINITY;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
...@@ -722,6 +723,12 @@ ExternalReference ExternalReference::address_of_one_half() { ...@@ -722,6 +723,12 @@ ExternalReference ExternalReference::address_of_one_half() {
} }
ExternalReference ExternalReference::address_of_negative_infinity() {
return ExternalReference(reinterpret_cast<void*>(
const_cast<double*>(&DoubleConstant::negative_infinity)));
}
#ifndef V8_INTERPRETED_REGEXP #ifndef V8_INTERPRETED_REGEXP
ExternalReference ExternalReference::re_check_stack_guard_state() { ExternalReference ExternalReference::re_check_stack_guard_state() {
...@@ -793,6 +800,51 @@ static double mod_two_doubles(double x, double y) { ...@@ -793,6 +800,51 @@ static double mod_two_doubles(double x, double y) {
} }
// Helper function to compute x^y, where y is known to be an
// integer. Uses binary decomposition to limit the number of
// multiplications; see the discussion in "Hacker's Delight" by Henry
// S. Warren, Jr., figure 11-6, page 213.
double power_double_int(double x, int y) {
double m = (y < 0) ? 1 / x : x;
unsigned n = (y < 0) ? -y : y;
double p = 1;
while (n != 0) {
if ((n & 1) != 0) p *= m;
m *= m;
if ((n & 2) != 0) p *= m;
m *= m;
n >>= 2;
}
return p;
}
double power_double_double(double x, double y) {
int y_int = static_cast<int>(y);
if (y == y_int) {
return power_double_int(x, y_int); // Returns 1.0 for exponent 0.
}
if (!isinf(x)) {
if (y == 0.5) return sqrt(x);
if (y == -0.5) return 1.0 / sqrt(x);
}
if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
return OS::nan_value();
}
return pow(x, y);
}
ExternalReference ExternalReference::power_double_double_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(power_double_double)));
}
ExternalReference ExternalReference::power_double_int_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(power_double_int)));
}
static int native_compare_doubles(double y, double x) { static int native_compare_doubles(double y, double x) {
if (x == y) return EQUAL; if (x == y) return EQUAL;
return x < y ? LESS : GREATER; return x < y ? LESS : GREATER;
......
...@@ -50,6 +50,7 @@ class DoubleConstant: public AllStatic { ...@@ -50,6 +50,7 @@ class DoubleConstant: public AllStatic {
public: public:
static const double min_int; static const double min_int;
static const double one_half; static const double one_half;
static const double negative_infinity;
}; };
...@@ -539,6 +540,8 @@ class ExternalReference BASE_EMBEDDED { ...@@ -539,6 +540,8 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference double_fp_operation(Token::Value operation); static ExternalReference double_fp_operation(Token::Value operation);
static ExternalReference compare_doubles(); static ExternalReference compare_doubles();
static ExternalReference power_double_double_function();
static ExternalReference power_double_int_function();
static ExternalReference handle_scope_next_address(); static ExternalReference handle_scope_next_address();
static ExternalReference handle_scope_limit_address(); static ExternalReference handle_scope_limit_address();
...@@ -549,6 +552,7 @@ class ExternalReference BASE_EMBEDDED { ...@@ -549,6 +552,7 @@ class ExternalReference BASE_EMBEDDED {
// Static variables containing common double constants. // Static variables containing common double constants.
static ExternalReference address_of_min_int(); static ExternalReference address_of_min_int();
static ExternalReference address_of_one_half(); static ExternalReference address_of_one_half();
static ExternalReference address_of_negative_infinity();
Address address() const {return reinterpret_cast<Address>(address_);} Address address() const {return reinterpret_cast<Address>(address_);}
...@@ -710,6 +714,10 @@ static inline int NumberOfBitsSet(uint32_t x) { ...@@ -710,6 +714,10 @@ static inline int NumberOfBitsSet(uint32_t x) {
return num_bits_set; return num_bits_set;
} }
// Computes pow(x, y) with the special cases in the spec for Math.pow.
double power_double_int(double x, int y);
double power_double_double(double x, double y);
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_ASSEMBLER_H_ #endif // V8_ASSEMBLER_H_
...@@ -77,6 +77,7 @@ class LChunkBuilder; ...@@ -77,6 +77,7 @@ class LChunkBuilder;
// HLoadKeyedFastElement // HLoadKeyedFastElement
// HLoadKeyedGeneric // HLoadKeyedGeneric
// HLoadNamedGeneric // HLoadNamedGeneric
// HPower
// HStoreNamed // HStoreNamed
// HStoreNamedField // HStoreNamedField
// HStoreNamedGeneric // HStoreNamedGeneric
...@@ -223,6 +224,7 @@ class LChunkBuilder; ...@@ -223,6 +224,7 @@ class LChunkBuilder;
V(ObjectLiteral) \ V(ObjectLiteral) \
V(OsrEntry) \ V(OsrEntry) \
V(Parameter) \ V(Parameter) \
V(Power) \
V(PushArgument) \ V(PushArgument) \
V(RegExpLiteral) \ V(RegExpLiteral) \
V(Return) \ V(Return) \
...@@ -1377,6 +1379,7 @@ class HUnaryMathOperation: public HUnaryOperation { ...@@ -1377,6 +1379,7 @@ class HUnaryMathOperation: public HUnaryOperation {
SetFlag(kFlexibleRepresentation); SetFlag(kFlexibleRepresentation);
break; break;
case kMathSqrt: case kMathSqrt:
case kMathPowHalf:
default: default:
set_representation(Representation::Double()); set_representation(Representation::Double());
} }
...@@ -1395,6 +1398,7 @@ class HUnaryMathOperation: public HUnaryOperation { ...@@ -1395,6 +1398,7 @@ class HUnaryMathOperation: public HUnaryOperation {
case kMathRound: case kMathRound:
case kMathCeil: case kMathCeil:
case kMathSqrt: case kMathSqrt:
case kMathPowHalf:
return Representation::Double(); return Representation::Double();
break; break;
case kMathAbs: case kMathAbs:
...@@ -2184,6 +2188,22 @@ class HInstanceOf: public HBinaryOperation { ...@@ -2184,6 +2188,22 @@ class HInstanceOf: public HBinaryOperation {
}; };
class HPower: public HBinaryOperation {
public:
HPower(HValue* left, HValue* right)
: HBinaryOperation(left, right) {
set_representation(Representation::Double());
SetFlag(kUseGVN);
}
virtual Representation RequiredInputRepresentation(int index) const {
return (index == 1) ? Representation::None() : Representation::Double();
}
DECLARE_CONCRETE_INSTRUCTION(Power, "power")
};
class HAdd: public HArithmeticBinaryOperation { class HAdd: public HArithmeticBinaryOperation {
public: public:
HAdd(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) { HAdd(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) {
......
...@@ -4093,6 +4093,42 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { ...@@ -4093,6 +4093,42 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
return true; return true;
} }
break; break;
case kMathPow:
if (argument_count == 3) {
HValue* right = Pop();
HValue* left = Pop();
Pop(); // Pop receiver.
// Use sqrt() if exponent is 0.5 or -0.5.
if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
double exponent = HConstant::cast(right)->DoubleValue();
if (exponent == 0.5) {
PushAndAdd(new HUnaryMathOperation(left, kMathPowHalf));
return true;
} else if (exponent == -0.5) {
HConstant* double_one =
new HConstant(Handle<Object>(Smi::FromInt(1)),
Representation::Double());
AddInstruction(double_one);
HUnaryMathOperation* square_root =
new HUnaryMathOperation(left, kMathPowHalf);
AddInstruction(square_root);
PushAndAdd(new HDiv(double_one, square_root));
return true;
} else if (exponent == 2.0) {
PushAndAdd(new HMul(left, left));
return true;
}
} else if (right->IsConstant() &&
HConstant::cast(right)->HasInteger32Value() &&
HConstant::cast(right)->Integer32Value() == 2) {
PushAndAdd(new HMul(left, left));
return true;
}
PushAndAdd(new HPower(left, right));
return true;
}
break;
default: default:
// Either not a special math function or not yet supported for inlining. // Either not a special math function or not yet supported for inlining.
break; break;
...@@ -5020,9 +5056,9 @@ void HGraphBuilder::GenerateCallFunction(int argument_count) { ...@@ -5020,9 +5056,9 @@ void HGraphBuilder::GenerateCallFunction(int argument_count) {
// Fast call to math functions. // Fast call to math functions.
void HGraphBuilder::GenerateMathPow(int argument_count) { void HGraphBuilder::GenerateMathPow(int argument_count) {
ASSERT_EQ(2, argument_count); ASSERT_EQ(2, argument_count);
PushArgumentsForStubCall(argument_count); HValue* right = Pop();
PushAndAdd(new HCallStub(CodeStub::MathPow, argument_count), HValue* left = Pop();
RelocInfo::kNoPosition); PushAndAdd(new HPower(left, right));
} }
......
...@@ -2174,6 +2174,67 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { ...@@ -2174,6 +2174,67 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
} }
void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
XMMRegister xmm_scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->input());
ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
ExternalReference negative_infinity =
ExternalReference::address_of_negative_infinity();
__ movdbl(xmm_scratch, Operand::StaticVariable(negative_infinity));
__ ucomisd(xmm_scratch, input_reg);
DeoptimizeIf(equal, instr->environment());
__ sqrtsd(input_reg, input_reg);
}
void LCodeGen::DoPower(LPower* instr) {
LOperand* left = instr->left();
LOperand* right = instr->right();
Representation exponent_type = instr->hydrogen()->right()->representation();
if (exponent_type.IsDouble()) {
// Pass two doubles as arguments on the stack.
__ PrepareCallCFunction(4, eax);
__ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left));
__ movdbl(Operand(esp, 1 * kDoubleSize), ToDoubleRegister(right));
__ CallCFunction(ExternalReference::power_double_double_function(), 4);
} else if (exponent_type.IsInteger32()) {
__ PrepareCallCFunction(4, ebx);
__ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left));
__ mov(Operand(esp, 1 * kDoubleSize), ToRegister(right));
__ CallCFunction(ExternalReference::power_double_int_function(), 4);
} else {
ASSERT(exponent_type.IsTagged());
__ PrepareCallCFunction(4, ebx);
__ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left));
Register right_reg = ToRegister(right);
Label non_smi;
Label done;
__ test(right_reg, Immediate(kSmiTagMask));
__ j(not_zero, &non_smi);
__ SmiUntag(right_reg);
__ mov(Operand(esp, 1 * kDoubleSize), ToRegister(right));
__ CallCFunction(ExternalReference::power_double_int_function(), 4);
__ jmp(&done);
__ bind(&non_smi);
__ CmpObjectType(right_reg, HEAP_NUMBER_TYPE , ebx);
DeoptimizeIf(not_equal, instr->environment());
__ movdbl(xmm1, FieldOperand(right_reg, HeapNumber::kValueOffset));
__ movdbl(Operand(esp, 1 * kDoubleSize), xmm1);
__ CallCFunction(ExternalReference::power_double_double_function(), 4);
__ bind(&done);
}
// Return value is in st(0) on ia32.
// Store it into the (fixed) result register.
__ sub(Operand(esp), Immediate(kDoubleSize));
__ fstp_d(Operand(esp, 0));
__ movdbl(ToDoubleRegister(instr->result()), Operand(esp, 0));
__ add(Operand(esp), Immediate(kDoubleSize));
}
void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
switch (instr->op()) { switch (instr->op()) {
case kMathAbs: case kMathAbs:
...@@ -2188,6 +2249,9 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { ...@@ -2188,6 +2249,9 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
case kMathSqrt: case kMathSqrt:
DoMathSqrt(instr); DoMathSqrt(instr);
break; break;
case kMathPowHalf:
DoMathPowHalf(instr);
break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -175,6 +175,7 @@ class LCodeGen BASE_EMBEDDED { ...@@ -175,6 +175,7 @@ class LCodeGen BASE_EMBEDDED {
void DoMathFloor(LUnaryMathOperation* instr); void DoMathFloor(LUnaryMathOperation* instr);
void DoMathRound(LUnaryMathOperation* instr); void DoMathRound(LUnaryMathOperation* instr);
void DoMathSqrt(LUnaryMathOperation* instr); void DoMathSqrt(LUnaryMathOperation* instr);
void DoMathPowHalf(LUnaryMathOperation* instr);
// Support for recording safepoint and position information. // Support for recording safepoint and position information.
void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); void RecordSafepoint(LPointerMap* pointers, int deoptimization_index);
......
...@@ -1366,6 +1366,8 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { ...@@ -1366,6 +1366,8 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
return AssignEnvironment(DefineAsRegister(result)); return AssignEnvironment(DefineAsRegister(result));
case kMathSqrt: case kMathSqrt:
return DefineSameAsFirst(result); return DefineSameAsFirst(result);
case kMathPowHalf:
return AssignEnvironment(DefineSameAsFirst(result));
default: default:
UNREACHABLE(); UNREACHABLE();
return NULL; return NULL;
...@@ -1566,6 +1568,22 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { ...@@ -1566,6 +1568,22 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
} }
LInstruction* LChunkBuilder::DoPower(HPower* instr) {
ASSERT(instr->representation().IsDouble());
// We call a C function for double power. It can't trigger a GC.
// We need to use fixed result register for the call.
Representation exponent_type = instr->right()->representation();
ASSERT(instr->left()->representation().IsDouble());
LOperand* left = UseFixedDouble(instr->left(), xmm1);
LOperand* right = exponent_type.IsDouble() ?
UseFixedDouble(instr->right(), xmm2) :
UseFixed(instr->right(), eax);
LPower* result = new LPower(left, right);
return MarkAsCall(DefineFixedDouble(result, xmm1), instr,
CAN_DEOPTIMIZE_EAGERLY);
}
LInstruction* LChunkBuilder::DoCompare(HCompare* instr) { LInstruction* LChunkBuilder::DoCompare(HCompare* instr) {
Token::Value op = instr->token(); Token::Value op = instr->token();
if (instr->left()->representation().IsInteger32()) { if (instr->left()->representation().IsInteger32()) {
......
...@@ -67,6 +67,7 @@ class LGapNode; ...@@ -67,6 +67,7 @@ class LGapNode;
// LLoadKeyedGeneric // LLoadKeyedGeneric
// LModI // LModI
// LMulI // LMulI
// LPower
// LShiftI // LShiftI
// LSubI // LSubI
// LCallConstantFunction // LCallConstantFunction
...@@ -229,6 +230,7 @@ class LGapNode; ...@@ -229,6 +230,7 @@ class LGapNode;
V(ObjectLiteral) \ V(ObjectLiteral) \
V(OsrEntry) \ V(OsrEntry) \
V(Parameter) \ V(Parameter) \
V(Power) \
V(PushArgument) \ V(PushArgument) \
V(RegExpLiteral) \ V(RegExpLiteral) \
V(Return) \ V(Return) \
...@@ -1154,6 +1156,16 @@ class LAddI: public LBinaryOperation { ...@@ -1154,6 +1156,16 @@ class LAddI: public LBinaryOperation {
}; };
class LPower: public LBinaryOperation {
public:
LPower(LOperand* left, LOperand* right)
: LBinaryOperation(left, right) { }
DECLARE_CONCRETE_INSTRUCTION(Power, "power")
DECLARE_HYDROGEN_ACCESSOR(Power)
};
class LArithmeticD: public LBinaryOperation { class LArithmeticD: public LBinaryOperation {
public: public:
LArithmeticD(Token::Value op, LOperand* left, LOperand* right) LArithmeticD(Token::Value op, LOperand* left, LOperand* right)
......
...@@ -264,6 +264,7 @@ function SetupMath() { ...@@ -264,6 +264,7 @@ function SetupMath() {
%SetMathFunctionId($Math.round, 2); %SetMathFunctionId($Math.round, 2);
%SetMathFunctionId($Math.abs, 4); %SetMathFunctionId($Math.abs, 4);
%SetMathFunctionId($Math.sqrt, 0xd); %SetMathFunctionId($Math.sqrt, 0xd);
%SetMathFunctionId($Math.pow, 0xe);
// TODO(erikcorry): Set the id of the other functions so they can be // TODO(erikcorry): Set the id of the other functions so they can be
// optimized. // optimized.
}; };
......
...@@ -3729,7 +3729,9 @@ enum MathFunctionId { ...@@ -3729,7 +3729,9 @@ enum MathFunctionId {
kMathACos = 0xa, kMathACos = 0xa,
kMathATan = 0xb, kMathATan = 0xb,
kMathExp = 0xc, kMathExp = 0xc,
kMathSqrt = 0xd kMathSqrt = 0xd,
kMathPow = 0xe,
kMathPowHalf = 0xf
}; };
...@@ -4154,11 +4156,11 @@ class SharedFunctionInfo: public HeapObject { ...@@ -4154,11 +4156,11 @@ class SharedFunctionInfo: public HeapObject {
static const int kTryFullCodegen = 1; static const int kTryFullCodegen = 1;
static const int kAllowLazyCompilation = 2; static const int kAllowLazyCompilation = 2;
static const int kMathFunctionShift = 3; static const int kMathFunctionShift = 3;
static const int kMathFunctionMask = 0xf; static const int kMathFunctionMask = 0x1f;
static const int kLiveObjectsMayExist = 7; static const int kLiveObjectsMayExist = 8;
static const int kCodeAgeShift = 8; static const int kCodeAgeShift = 9;
static const int kCodeAgeMask = 0x7; static const int kCodeAgeMask = 0x7;
static const int kOptimizationDisabled = 11; static const int kOptimizationDisabled = 12;
DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo); DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
}; };
......
...@@ -5977,37 +5977,6 @@ static MaybeObject* Runtime_Math_log(Arguments args) { ...@@ -5977,37 +5977,6 @@ static MaybeObject* Runtime_Math_log(Arguments args) {
} }
// Helper function to compute x^y, where y is known to be an
// integer. Uses binary decomposition to limit the number of
// multiplications; see the discussion in "Hacker's Delight" by Henry
// S. Warren, Jr., figure 11-6, page 213.
static double powi(double x, int y) {
ASSERT(y != kMinInt);
unsigned n = (y < 0) ? -y : y;
double m = x;
double p = 1;
while (true) {
if ((n & 1) != 0) p *= m;
n >>= 1;
if (n == 0) {
if (y < 0) {
// Unfortunately, we have to be careful when p has reached
// infinity in the computation, because sometimes the higher
// internal precision in the pow() implementation would have
// given us a finite p. This happens very rarely.
double result = 1.0 / p;
return (result == 0 && isinf(p))
? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
: result;
} else {
return p;
}
}
m *= m;
}
}
static MaybeObject* Runtime_Math_pow(Arguments args) { static MaybeObject* Runtime_Math_pow(Arguments args) {
NoHandleAllocation ha; NoHandleAllocation ha;
ASSERT(args.length() == 2); ASSERT(args.length() == 2);
...@@ -6019,31 +5988,11 @@ static MaybeObject* Runtime_Math_pow(Arguments args) { ...@@ -6019,31 +5988,11 @@ static MaybeObject* Runtime_Math_pow(Arguments args) {
// custom powi() function than the generic pow(). // custom powi() function than the generic pow().
if (args[1]->IsSmi()) { if (args[1]->IsSmi()) {
int y = Smi::cast(args[1])->value(); int y = Smi::cast(args[1])->value();
return Heap::NumberFromDouble(powi(x, y)); return Heap::NumberFromDouble(power_double_int(x, y));
} }
CONVERT_DOUBLE_CHECKED(y, args[1]); CONVERT_DOUBLE_CHECKED(y, args[1]);
return Heap::AllocateHeapNumber(power_double_double(x, y));
if (!isinf(x)) {
if (y == 0.5) {
// It's not uncommon to use Math.pow(x, 0.5) to compute the
// square root of a number. To speed up such computations, we
// explictly check for this case and use the sqrt() function
// which is faster than pow().
return Heap::AllocateHeapNumber(sqrt(x));
} else if (y == -0.5) {
// Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
return Heap::AllocateHeapNumber(1.0 / sqrt(x));
}
}
if (y == 0) {
return Smi::FromInt(1);
} else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
return Heap::nan_value();
} else {
return Heap::AllocateHeapNumber(pow(x, y));
}
} }
// Fast version of Math.pow if we know that y is not an integer and // Fast version of Math.pow if we know that y is not an integer and
......
...@@ -486,6 +486,18 @@ void ExternalReferenceTable::PopulateTable() { ...@@ -486,6 +486,18 @@ void ExternalReferenceTable::PopulateTable() {
UNCLASSIFIED, UNCLASSIFIED,
36, 36,
"LDoubleConstant::one_half"); "LDoubleConstant::one_half");
Add(ExternalReference::address_of_negative_infinity().address(),
UNCLASSIFIED,
37,
"LDoubleConstant::negative_infinity");
Add(ExternalReference::power_double_double_function().address(),
UNCLASSIFIED,
38,
"power_double_double_function");
Add(ExternalReference::power_double_int_function().address(),
UNCLASSIFIED,
39,
"power_double_int_function");
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment