Bug 772892 - Optimize Math.pow(). r=jandem,mjrosenb

This commit is contained in:
Sean Stangl 2012-07-31 20:04:42 -07:00
parent 104b645161
commit fe338dfe8b
29 changed files with 588 additions and 71 deletions

View File

@ -1530,6 +1530,48 @@ CodeGenerator::visitAbsI(LAbsI *ins)
return true;
}
bool
CodeGenerator::visitPowI(LPowI *ins)
{
FloatRegister input = ToFloatRegister(ins->input());
Register temp = ToRegister(ins->temp());
// In all implementations, setupUnalignedABICall() relinquishes use of
// its scratch register. We can therefore save an input register by
// reusing the scratch register to pass constants to callWithABI.
masm.setupUnalignedABICall(2, temp);
masm.passABIArg(input);
const LAllocation *power = ins->power();
if (power->isRegister()) {
masm.passABIArg(ToRegister(power));
} else {
masm.move32(Imm32(ToInt32(power)), temp);
masm.passABIArg(temp);
}
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::powi), MacroAssembler::DOUBLE);
JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
return true;
}
bool
CodeGenerator::visitPowD(LPowD *ins)
{
FloatRegister input = ToFloatRegister(ins->input());
FloatRegister power = ToFloatRegister(ins->power());
Register temp = ToRegister(ins->temp());
masm.setupUnalignedABICall(2, temp);
masm.passABIArg(input);
masm.passABIArg(power);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaPow), MacroAssembler::DOUBLE);
JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
return true;
}
bool
CodeGenerator::visitMathFunctionD(LMathFunctionD *ins)
{

View File

@ -105,6 +105,8 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitStoreFixedSlotV(LStoreFixedSlotV *ins);
bool visitStoreFixedSlotT(LStoreFixedSlotT *ins);
bool visitAbsI(LAbsI *lir);
bool visitPowI(LPowI *lir);
bool visitPowD(LPowD *lir);
bool visitMathFunctionD(LMathFunctionD *ins);
bool visitModD(LModD *ins);
bool visitBinaryV(LBinaryV *lir);

View File

@ -376,6 +376,7 @@ class IonBuilder : public MIRGenerator
InliningStatus inlineMathFloor(uint32 argc, bool constructing);
InliningStatus inlineMathRound(uint32 argc, bool constructing);
InliningStatus inlineMathSqrt(uint32 argc, bool constructing);
InliningStatus inlineMathPow(uint32 argc, bool constructing);
InliningStatus inlineMathFunction(MMathFunction::Function function, uint32 argc,
bool constructing);

View File

@ -1256,6 +1256,65 @@ class LSqrtD : public LInstructionHelper<1, 1, 0>
}
};
// Double raised to an integer power.
class LPowI : public LInstructionHelper<1, 2, 1>
{
public:
LIR_HEADER(PowI);
LPowI(const LAllocation &input, const LAllocation &power, const LDefinition &temp) {
setOperand(0, input);
setOperand(1, power);
setTemp(0, temp);
}
const LAllocation *input() {
return getOperand(0);
}
const LAllocation *power() {
return getOperand(1);
}
const LDefinition *temp() {
return getTemp(0);
}
const LDefinition *output() {
return getDef(0);
}
// Currently no inline path is implemented.
bool isCall() const {
return true;
}
};
// Double raised to a double power.
class LPowD : public LInstructionHelper<1, 2, 1>
{
public:
LIR_HEADER(PowD);
LPowD(const LAllocation &input, const LAllocation &power, const LDefinition &temp) {
setOperand(0, input);
setOperand(1, power);
setTemp(0, temp);
}
const LAllocation *input() {
return getOperand(0);
}
const LAllocation *power() {
return getOperand(1);
}
const LDefinition *temp() {
return getTemp(0);
}
const LDefinition *output() {
return getDef(0);
}
bool isCall() const {
return true;
}
};
class LMathFunctionD : public LInstructionHelper<1, 1, 1>
{
public:

View File

@ -62,6 +62,8 @@
_(AbsI) \
_(AbsD) \
_(SqrtD) \
_(PowI) \
_(PowD) \
_(MathFunctionD) \
_(NotI) \
_(NotD) \

View File

@ -675,6 +675,25 @@ LIRGenerator::visitSqrt(MSqrt *ins)
return define(lir, ins);
}
bool
LIRGenerator::visitPow(MPow *ins)
{
MDefinition *input = ins->input();
JS_ASSERT(input->type() == MIRType_Double);
MDefinition *power = ins->power();
JS_ASSERT(power->type() == MIRType_Int32 || power->type() == MIRType_Double);
if (power->type() == MIRType_Int32) {
LPowI *lir = new LPowI(useRegister(input), useRegisterOrConstant(power),
tempFixed(CallTempReg0));
return defineFixed(lir, ins, LAllocation(AnyRegister(ReturnFloatReg)));
}
LPowD *lir = new LPowD(useRegister(input), useRegister(power), tempFixed(CallTempReg0));
return defineFixed(lir, ins, LAllocation(AnyRegister(ReturnFloatReg)));
}
bool
LIRGenerator::visitMathFunction(MMathFunction *ins)
{

View File

@ -106,6 +106,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitRound(MRound *ins);
bool visitAbs(MAbs *ins);
bool visitSqrt(MSqrt *ins);
bool visitPow(MPow *ins);
bool visitMathFunction(MMathFunction *ins);
bool visitAdd(MAdd *ins);
bool visitSub(MSub *ins);

View File

@ -37,6 +37,8 @@ IonBuilder::inlineNativeCall(JSNative native, uint32 argc, bool constructing)
return inlineMathRound(argc, constructing);
if (native == js_math_sqrt)
return inlineMathSqrt(argc, constructing);
if (native == js_math_pow)
return inlineMathPow(argc, constructing);
if (native == js::math_sin)
return inlineMathFunction(MMathFunction::Sin, argc, constructing);
if (native == js::math_cos)
@ -124,9 +126,17 @@ IonBuilder::getInlineArgType(uint32 argc, uint32 arg)
IonBuilder::InliningStatus
IonBuilder::inlineMathFunction(MMathFunction::Function function, uint32 argc, bool constructing)
{
if (argc != 1 || constructing)
if (constructing)
return InliningStatus_NotInlined;
// Math.{$Function}() == NaN.
if (argc == 0) {
MConstant *nan = MConstant::New(GetIonContext()->cx->runtime->NaNValue);
current->add(nan);
current->push(nan);
return InliningStatus_Inlined;
}
if (getInlineReturnType() != MIRType_Double)
return InliningStatus_NotInlined;
if (!IsNumberType(getInlineArgType(argc, 1)))
@ -282,9 +292,17 @@ IonBuilder::inlineArrayPush(uint32 argc, bool constructing)
IonBuilder::InliningStatus
IonBuilder::inlineMathAbs(uint32 argc, bool constructing)
{
if (argc != 1 || constructing)
if (constructing)
return InliningStatus_NotInlined;
// Math.abs() == NaN.
if (argc == 0) {
MConstant *nan = MConstant::New(GetIonContext()->cx->runtime->NaNValue);
current->add(nan);
current->push(nan);
return InliningStatus_Inlined;
}
MIRType returnType = getInlineReturnType();
MIRType argType = getInlineArgType(argc, 1);
if (argType != MIRType_Int32 && argType != MIRType_Double)
@ -306,9 +324,18 @@ IonBuilder::inlineMathAbs(uint32 argc, bool constructing)
IonBuilder::InliningStatus
IonBuilder::inlineMathFloor(uint32 argc, bool constructing)
{
if (argc != 1 || constructing)
if (constructing)
return InliningStatus_NotInlined;
// Math.floor() == NaN.
if (argc == 0) {
MConstant *nan = MConstant::New(GetIonContext()->cx->runtime->NaNValue);
current->add(nan);
current->push(nan);
return InliningStatus_Inlined;
}
MIRType argType = getInlineArgType(argc, 1);
if (getInlineReturnType() != MIRType_Int32)
return InliningStatus_NotInlined;
@ -338,9 +365,17 @@ IonBuilder::inlineMathFloor(uint32 argc, bool constructing)
IonBuilder::InliningStatus
IonBuilder::inlineMathRound(uint32 argc, bool constructing)
{
if (argc != 1 || constructing)
if (constructing)
return InliningStatus_NotInlined;
// Math.round() == NaN.
if (argc == 0) {
MConstant *nan = MConstant::New(GetIonContext()->cx->runtime->NaNValue);
current->add(nan);
current->push(nan);
return InliningStatus_Inlined;
}
MIRType returnType = getInlineReturnType();
MIRType argType = getInlineArgType(argc, 1);
@ -369,9 +404,17 @@ IonBuilder::inlineMathRound(uint32 argc, bool constructing)
IonBuilder::InliningStatus
IonBuilder::inlineMathSqrt(uint32 argc, bool constructing)
{
if (argc != 1 || constructing)
if (constructing)
return InliningStatus_NotInlined;
// Math.sqrt() == NaN.
if (argc == 0) {
MConstant *nan = MConstant::New(GetIonContext()->cx->runtime->NaNValue);
current->add(nan);
current->push(nan);
return InliningStatus_Inlined;
}
MIRType argType = getInlineArgType(argc, 1);
if (getInlineReturnType() != MIRType_Double)
return InliningStatus_NotInlined;
@ -388,6 +431,119 @@ IonBuilder::inlineMathSqrt(uint32 argc, bool constructing)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineMathPow(uint32 argc, bool constructing)
{
if (constructing)
return InliningStatus_NotInlined;
// Math.pow() == Math.pow(x) == NaN.
if (argc < 2) {
MConstant *nan = MConstant::New(GetIonContext()->cx->runtime->NaNValue);
current->add(nan);
current->push(nan);
return InliningStatus_Inlined;
}
// Typechecking.
if (getInlineReturnType() != MIRType_Double)
return InliningStatus_NotInlined;
MIRType arg1Type = getInlineArgType(argc, 1);
MIRType arg2Type = getInlineArgType(argc, 2);
if (arg1Type != MIRType_Int32 && arg1Type != MIRType_Double)
return InliningStatus_NotInlined;
if (arg2Type != MIRType_Int32 && arg2Type != MIRType_Double)
return InliningStatus_NotInlined;
MDefinitionVector argv;
if (!discardCall(argc, argv, current))
return InliningStatus_Error;
// If the non-power input is integer, convert it to a Double.
// Safe since the output must be a Double.
if (arg1Type == MIRType_Int32) {
MToDouble *conv = MToDouble::New(argv[1]);
current->add(conv);
argv[1] = conv;
}
// Optimize some constant powers.
if (argv[2]->isConstant()) {
double pow;
if (!ToNumber(GetIonContext()->cx, argv[2]->toConstant()->value(), &pow))
return InliningStatus_Error;
// Math.pow(x, +-0) == 1, even for x = NaN.
if (pow == 0.0) {
MConstant *nan = MConstant::New(GetIonContext()->cx->runtime->NaNValue);
current->add(nan);
current->push(nan);
return InliningStatus_Inlined;
}
// Math.pow(x, 0.5) is a sqrt with edge-case detection.
if (pow == 0.5) {
MPowHalf *half = MPowHalf::New(argv[1]);
current->add(half);
current->push(half);
return InliningStatus_Inlined;
}
// Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
if (pow == -0.5) {
MPowHalf *half = MPowHalf::New(argv[1]);
current->add(half);
MConstant *one = MConstant::New(DoubleValue(1.0));
current->add(one);
MDiv *div = MDiv::New(one, half, MIRType_Double);
current->add(div);
current->push(div);
return InliningStatus_Inlined;
}
// Math.pow(x, 1) == x.
if (pow == 1.0) {
current->push(argv[1]);
return InliningStatus_Inlined;
}
// Math.pow(x, 2) == x*x.
if (pow == 2.0) {
MMul *mul = MMul::New(argv[1], argv[1], MIRType_Double);
current->add(mul);
current->push(mul);
return InliningStatus_Inlined;
}
// Math.pow(x, 3) == x*x*x.
if (pow == 3.0) {
MMul *mul1 = MMul::New(argv[1], argv[1], MIRType_Double);
current->add(mul1);
MMul *mul2 = MMul::New(argv[1], mul1, MIRType_Double);
current->add(mul2);
current->push(mul2);
return InliningStatus_Inlined;
}
// Math.pow(x, 4) == y*y, where y = x*x.
if (pow == 4.0) {
MMul *y = MMul::New(argv[1], argv[1], MIRType_Double);
current->add(y);
MMul *mul = MMul::New(y, y, MIRType_Double);
current->add(mul);
current->push(mul);
return InliningStatus_Inlined;
}
}
MPow *ins = MPow::New(argv[1], argv[2], arg2Type);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineStrCharCodeAt(uint32 argc, bool constructing)
{

View File

@ -2252,6 +2252,7 @@ class MAbs
}
};
// Inline implementation of Math.sqrt().
class MSqrt
: public MUnaryInstruction,
public DoublePolicy<0>
@ -2282,6 +2283,73 @@ class MSqrt
}
};
// Inline implementation of Math.pow().
class MPow
: public MBinaryInstruction,
public PowPolicy
{
MPow(MDefinition *input, MDefinition *power, MIRType powerType)
: MBinaryInstruction(input, power),
PowPolicy(powerType)
{
setResultType(MIRType_Double);
setMovable();
}
public:
INSTRUCTION_HEADER(Pow);
static MPow *New(MDefinition *input, MDefinition *power, MIRType powerType) {
return new MPow(input, power, powerType);
}
MDefinition *input() const {
return lhs();
}
MDefinition *power() const {
return rhs();
}
bool congruentTo(MDefinition *const &ins) const {
return congruentIfOperandsEqual(ins);
}
TypePolicy *typePolicy() {
return this;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
};
// Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x).
class MPowHalf
: public MUnaryInstruction,
public DoublePolicy<0>
{
MPowHalf(MDefinition *input)
: MUnaryInstruction(input)
{
setResultType(MIRType_Double);
setMovable();
}
public:
INSTRUCTION_HEADER(PowHalf);
static MPowHalf *New(MDefinition *input) {
return new MPowHalf(input);
}
MDefinition *input() const {
return getOperand(0);
}
bool congruentTo(MDefinition *const &ins) const {
return congruentIfOperandsEqual(ins);
}
TypePolicy *typePolicy() {
return this;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
};
class MMathFunction
: public MUnaryInstruction,
public DoublePolicy<0>
@ -2419,17 +2487,22 @@ class MMul : public MBinaryArithInstruction
{
bool canBeNegativeZero_;
MMul(MDefinition *left, MDefinition *right)
MMul(MDefinition *left, MDefinition *right, MIRType type)
: MBinaryArithInstruction(left, right),
canBeNegativeZero_(true)
{
setResultType(MIRType_Value);
if (type != MIRType_Value)
specialization_ = type;
setResultType(type);
}
public:
INSTRUCTION_HEADER(Mul);
static MMul *New(MDefinition *left, MDefinition *right) {
return new MMul(left, right);
return new MMul(left, right, MIRType_Value);
}
static MMul *New(MDefinition *left, MDefinition *right, MIRType type) {
return new MMul(left, right, type);
}
MDefinition *foldsTo(bool useValueNumbers);
@ -2471,20 +2544,26 @@ class MDiv : public MBinaryArithInstruction
bool canBeDivideByZero_;
bool implicitTruncate_;
MDiv(MDefinition *left, MDefinition *right)
MDiv(MDefinition *left, MDefinition *right, MIRType type)
: MBinaryArithInstruction(left, right),
canBeNegativeZero_(true),
canBeNegativeOverflow_(true),
canBeDivideByZero_(true),
implicitTruncate_(false)
{
setResultType(MIRType_Value);
if (type != MIRType_Value)
specialization_ = type;
setResultType(type);
}
public:
INSTRUCTION_HEADER(Div);
static MDiv *New(MDefinition *left, MDefinition *right) {
return new MDiv(left, right);
return new MDiv(left, right, MIRType_Value);
}
static MDiv *New(MDefinition *left, MDefinition *right, MIRType type) {
return new MDiv(left, right, type);
}
MDefinition *foldsTo(bool useValueNumbers);

View File

@ -45,6 +45,8 @@ namespace ion {
_(Ursh) \
_(Abs) \
_(Sqrt) \
_(Pow) \
_(PowHalf) \
_(MathFunction) \
_(Add) \
_(Sub) \

View File

@ -34,15 +34,6 @@ BoxInputsPolicy::adjustInputs(MInstruction *ins)
return true;
}
bool
SimplePolicy::adjustInputs(MInstruction *def)
{
if (specialized())
return true;
return BoxInputsPolicy::adjustInputs(def);
}
bool
ArithPolicy::adjustInputs(MInstruction *ins)
{
@ -250,6 +241,37 @@ TableSwitchPolicy::adjustInputs(MInstruction *ins)
return true;
}
bool
PowPolicy::adjustInputs(MInstruction *ins)
{
JS_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Double);
MDefinition *input = ins->getOperand(0);
MDefinition *power = ins->getOperand(1);
// Input must be a double.
if (input->type() != MIRType_Double) {
MToDouble *replace = MToDouble::New(input);
ins->block()->insertBefore(ins, replace);
ins->replaceOperand(0, replace);
}
// Power may be an int32 or a double. Integers receive a faster path.
if (power->type() != specialization_) {
if (specialization_ == MIRType_Double) {
MToDouble *replace = MToDouble::New(power);
ins->block()->insertBefore(ins, replace);
ins->replaceOperand(1, replace);
} else {
MUnbox *replace = MUnbox::New(power, MIRType_Int32, MUnbox::Fallible);
ins->block()->insertBefore(ins, replace);
ins->replaceOperand(1, replace);
}
}
return true;
}
bool
StringPolicy::staticAdjustInputs(MInstruction *def)
{

View File

@ -85,8 +85,7 @@ class ComparePolicy : public BoxInputsPolicy
public:
ComparePolicy()
: specialization_(MIRType_None)
{
}
{ }
bool adjustInputs(MInstruction *def);
};
@ -104,6 +103,19 @@ class CallPolicy : public BoxInputsPolicy
bool adjustInputs(MInstruction *def);
};
// Policy for MPow. First operand Double; second Double or Int32.
class PowPolicy : public BoxInputsPolicy
{
MIRType specialization_;
public:
PowPolicy(MIRType specialization)
: specialization_(specialization)
{ }
bool adjustInputs(MInstruction *ins);
};
// Single-string input. If the input is a Value, it is unboxed.
class StringPolicy : public BoxInputsPolicy
{
@ -161,36 +173,16 @@ class BoxPolicy : public BoxInputsPolicy
}
};
// Ignore the input, unless unspecialized, and then use BoxInputsPolicy.
class SimplePolicy : public BoxInputsPolicy
{
bool specialized_;
public:
SimplePolicy()
: specialized_(true)
{ }
bool adjustInputs(MInstruction *def);
bool specialized() const {
return specialized_;
}
void unspecialize() {
specialized_ = false;
}
};
// Combine multiple policies.
template <class Lhs, class Rhs>
class MixPolicy
: public BoxInputsPolicy
class MixPolicy : public TypePolicy
{
public:
static bool staticAdjustInputs(MInstruction *def) {
return Lhs::staticAdjustInputs(def) && Rhs::staticAdjustInputs(def);
static bool staticAdjustInputs(MInstruction *ins) {
return Lhs::staticAdjustInputs(ins) && Rhs::staticAdjustInputs(ins);
}
virtual bool adjustInputs(MInstruction *def) {
return staticAdjustInputs(def);
virtual bool adjustInputs(MInstruction *ins) {
return staticAdjustInputs(ins);
}
};

View File

@ -694,6 +694,29 @@ CodeGeneratorARM::visitShiftOp(LShiftOp *ins)
return true;
}
bool
CodeGeneratorARM::visitPowHalfD(LPowHalfD *ins)
{
FloatRegister input = ToFloatRegister(ins->input());
FloatRegister output = ToFloatRegister(ins->output());
Label done;
// Masm.pow(-Infinity, 0.5) == Infinity.
masm.ma_vimm(js_NegativeInfinity, ScratchFloatReg);
masm.compareDouble(input, ScratchFloatReg);
masm.as_vneg(ScratchFloatReg, output, Assembler::Equal);
masm.ma_b(&done, Assembler::Equal);
// Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0.
masm.ma_vimm(0.0, ScratchFloatReg);
masm.ma_vadd(ScratchFloatReg, input, output);
masm.as_vsqrt(output, output);
masm.bind(&done);
return true;
}
typedef MoveResolver::MoveOperand MoveOperand;
MoveOperand

View File

@ -79,6 +79,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
virtual bool visitModI(LModI *ins);
virtual bool visitModPowTwoI(LModPowTwoI *ins);
virtual bool visitModMaskI(LModMaskI *ins);
virtual bool visitPowHalfD(LPowHalfD *ins);
virtual bool visitMoveGroup(LMoveGroup *group);
virtual bool visitShiftOp(LShiftOp *ins);

View File

@ -131,7 +131,7 @@ class LModI : public LBinaryMath<3>
}
};
class LModPowTwoI : public LInstructionHelper<1,1,0>
class LModPowTwoI : public LInstructionHelper<1, 1, 0>
{
const int32 shift_;
@ -149,7 +149,7 @@ class LModPowTwoI : public LInstructionHelper<1,1,0>
}
};
class LModMaskI : public LInstructionHelper<1,1,1>
class LModMaskI : public LInstructionHelper<1, 1, 1>
{
const int32 shift_;
@ -167,6 +167,23 @@ class LModMaskI : public LInstructionHelper<1,1,1>
setTemp(0, temp1);
}
};
class LPowHalfD : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(PowHalfD);
LPowHalfD(const LAllocation &input) {
setOperand(0, input);
}
const LAllocation *input() {
return getOperand(0);
}
const LDefinition *output() {
return getDef(0);
}
};
// Takes a tableswitch with an integer to decide
class LTableSwitch : public LInstructionHelper<0, 1, 1>
{
@ -246,4 +263,3 @@ class LMulI : public LBinaryMath<0>
} // namespace js
#endif // jsion_lir_arm_h__

View File

@ -16,6 +16,8 @@
_(DivI) \
_(ModI) \
_(ModPowTwoI) \
_(ModMaskI)
_(ModMaskI) \
_(PowHalfD)
#endif // jsion_lir_opcodes_arm_h__

View File

@ -278,6 +278,15 @@ LIRGeneratorARM::lowerModI(MMod *mod)
return assignSnapshot(lir) && defineFixed(lir, mod, LAllocation(AnyRegister(r1)));
}
bool
LIRGeneratorARM::visitPowHalf(MPowHalf *ins)
{
MDefinition *input = ins->input();
JS_ASSERT(input->type() == MIRType_Double);
LPowHalfD *lir = new LPowHalfD(useRegisterAtStart(input));
return defineReuseInput(lir, ins, 0);
}
bool
LIRGeneratorARM::visitTableSwitch(MTableSwitch *tableswitch)
{

View File

@ -45,6 +45,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
bool lowerDivI(MDiv *div);
bool lowerModI(MMod *mod);
bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs);
bool visitPowHalf(MPowHalf *ins);
bool visitTableSwitch(MTableSwitch *tableswitch);
public:

View File

@ -1894,10 +1894,13 @@ MacroAssemblerARMCompat::addPtr(Imm32 imm, const Address &dest)
void
MacroAssemblerARMCompat::compareDouble(FloatRegister lhs, FloatRegister rhs)
{
// Compare the doubles, setting vector status flags.
if (rhs == InvalidFloatReg)
ma_vcmpz(lhs);
else
ma_vcmp(lhs, rhs);
// Move vector status bits to normal status flags.
as_vmrs(pc);
}

View File

@ -7,6 +7,7 @@
#include "jscntxt.h"
#include "jscompartment.h"
#include "jsmath.h"
#include "CodeGenerator-x86-shared.h"
#include "CodeGenerator-shared-inl.h"
#include "ion/IonFrames.h"
@ -420,6 +421,36 @@ CodeGeneratorX86Shared::visitSqrtD(LSqrtD *ins)
return true;
}
bool
CodeGeneratorX86Shared::visitPowHalfD(LPowHalfD *ins)
{
FloatRegister input = ToFloatRegister(ins->input());
Register scratch = ToRegister(ins->temp());
JS_ASSERT(input == ToFloatRegister(ins->output()));
const uint32_t NegInfinityFloatBits = 0xFF800000;
Label done, sqrt;
// Branch if not -Infinity.
masm.move32(Imm32(NegInfinityFloatBits), scratch);
masm.loadFloatAsDouble(scratch, ScratchFloatReg);
masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, ScratchFloatReg, &sqrt);
// Math.pow(-Infinity, 0.5) == Infinity.
masm.xorpd(input, input);
masm.subsd(ScratchFloatReg, input);
masm.jump(&done);
// Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0.
masm.bind(&sqrt);
masm.xorpd(ScratchFloatReg, ScratchFloatReg);
masm.addsd(ScratchFloatReg, input);
masm.sqrtsd(input, input);
masm.bind(&done);
return true;
}
bool
CodeGeneratorX86Shared::visitAddI(LAddI *ins)
{

View File

@ -87,6 +87,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
// Instruction visitors.
virtual bool visitAbsD(LAbsD *ins);
virtual bool visitSqrtD(LSqrtD *ins);
virtual bool visitPowHalfD(LPowHalfD *ins);
virtual bool visitAddI(LAddI *ins);
virtual bool visitSubI(LSubI *ins);
virtual bool visitMulI(LMulI *ins);

View File

@ -67,6 +67,27 @@ class LModPowTwoI : public LInstructionHelper<1,1,0>
}
};
// Double raised to a half power.
class LPowHalfD : public LInstructionHelper<1, 1, 1>
{
public:
LIR_HEADER(PowHalfD);
LPowHalfD(const LAllocation &input, const LDefinition &temp) {
setOperand(0, input);
setTemp(0, temp);
}
const LAllocation *input() {
return getOperand(0);
}
const LDefinition *temp() {
return getTemp(0);
}
const LDefinition *output() {
return getDef(0);
}
};
// Takes a tableswitch with an integer to decide
class LTableSwitch : public LInstructionHelper<0, 1, 2>
{

View File

@ -67,6 +67,15 @@ LIRGeneratorX86Shared::visitGuardShape(MGuardShape *ins)
return assignSnapshot(guard, Bailout_Invalidate) && add(guard, ins);
}
bool
LIRGeneratorX86Shared::visitPowHalf(MPowHalf *ins)
{
MDefinition *input = ins->input();
JS_ASSERT(input->type() == MIRType_Double);
LPowHalfD *lir = new LPowHalfD(useRegisterAtStart(input), temp());
return defineReuseInput(lir, ins, 0);
}
bool
LIRGeneratorX86Shared::lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs)
{

View File

@ -24,6 +24,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared
bool visitRecompileCheck(MRecompileCheck *ins);
bool visitInterruptCheck(MInterruptCheck *ins);
bool visitGuardShape(MGuardShape *ins);
bool visitPowHalf(MPowHalf *ins);
bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs);
bool lowerModI(MMod *mod);
};

View File

@ -44,8 +44,9 @@ class MacroAssemblerX86Shared : public Assembler
else
ucomisd(lhs, rhs);
}
void branchDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs,
Label *label) {
void branchDouble(DoubleCondition cond, const FloatRegister &lhs,
const FloatRegister &rhs, Label *label)
{
compareDouble(cond, lhs, rhs);
if (cond == DoubleEqual) {
@ -56,8 +57,8 @@ class MacroAssemblerX86Shared : public Assembler
return;
}
if (cond == DoubleNotEqualOrUnordered) {
j(Parity, label);
j(NotEqual, label);
j(Parity, label);
return;
}
@ -255,6 +256,10 @@ class MacroAssemblerX86Shared : public Assembler
void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest) {
cvtsd2ss(src, dest);
}
void loadFloatAsDouble(const Register &src, FloatRegister dest) {
movd(src, dest);
cvtss2sd(dest, dest);
}
void loadFloatAsDouble(const Address &src, FloatRegister dest) {
movss(Operand(src), dest);
cvtss2sd(dest, dest);
@ -296,9 +301,13 @@ class MacroAssemblerX86Shared : public Assembler
// This implements parts of "13.4 Generating constants" of
// "2. Optimizing subroutines in assembly language" by Agner Fog.
switch (u) {
case 0ULL:
case 0x0000000000000000ULL: // 0.0
xorpd(dest, dest);
break;
case 0x8000000000000000ULL: // -0.0
pcmpeqw(dest, dest);
psllq(Imm32(63), dest);
break;
case 0x3fe0000000000000ULL: // 0.5
pcmpeqw(dest, dest);
psllq(Imm32(55), dest);

View File

@ -14,7 +14,8 @@
_(UnboxDouble) \
_(DivI) \
_(ModI) \
_(ModPowTwoI)
_(ModPowTwoI) \
_(PowHalfD)
#endif // jsion_lir_opcodes_x64_h__

View File

@ -15,7 +15,8 @@
_(BoxDouble) \
_(DivI) \
_(ModI) \
_(ModPowTwoI)
_(ModPowTwoI) \
_(PowHalfD)
#endif // jsion_lir_opcodes_x86_h__

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -418,8 +419,8 @@ js_math_min(JSContext *cx, unsigned argc, Value *vp)
return JS_TRUE;
}
static double
powi(double x, int y)
double
js::powi(double x, int y)
{
unsigned n = (y < 0) ? -y : y;
double m = x;
@ -446,6 +447,18 @@ powi(double x, int y)
}
}
double
js::ecmaPow(double x, double y)
{
/*
* Because C99 and ECMA specify different behavior for pow(),
* we need to wrap the libm call to make it ECMA compliant.
*/
if (!MOZ_DOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0))
return js_NaN;
return pow(x, y);
}
JSBool
js_math_pow(JSContext *cx, unsigned argc, Value *vp)
{
@ -471,14 +484,6 @@ js_math_pow(JSContext *cx, unsigned argc, Value *vp)
return JS_TRUE;
}
}
/*
* Because C99 and ECMA specify different behavior for pow(),
* we need to wrap the libm call to make it ECMA compliant.
*/
if (!MOZ_DOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) {
vp->setDouble(js_NaN);
return JS_TRUE;
}
/* pow(x, +-0) is always 1, even for x = NaN. */
if (y == 0) {
vp->setInt32(1);
@ -493,7 +498,7 @@ js_math_pow(JSContext *cx, unsigned argc, Value *vp)
if (int32_t(y) == y)
z = powi(x, int32_t(y));
else
z = pow(x, y);
z = ecmaPow(x, y);
vp->setNumber(z);
return JS_TRUE;

View File

@ -113,6 +113,12 @@ math_tan(JSContext *cx, unsigned argc, js::Value *vp);
extern double
math_tan_impl(MathCache *cache, double x);
extern double
powi(double x, int y);
extern double
ecmaPow(double x, double y);
} /* namespace js */
#endif /* jsmath_h___ */