mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 20:47:44 +00:00
Bug 772892 - Optimize Math.pow(). r=jandem,mjrosenb
This commit is contained in:
parent
104b645161
commit
fe338dfe8b
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -62,6 +62,8 @@
|
||||
_(AbsI) \
|
||||
_(AbsD) \
|
||||
_(SqrtD) \
|
||||
_(PowI) \
|
||||
_(PowD) \
|
||||
_(MathFunctionD) \
|
||||
_(NotI) \
|
||||
_(NotD) \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -45,6 +45,8 @@ namespace ion {
|
||||
_(Ursh) \
|
||||
_(Abs) \
|
||||
_(Sqrt) \
|
||||
_(Pow) \
|
||||
_(PowHalf) \
|
||||
_(MathFunction) \
|
||||
_(Add) \
|
||||
_(Sub) \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
_(DivI) \
|
||||
_(ModI) \
|
||||
_(ModPowTwoI) \
|
||||
_(ModMaskI)
|
||||
_(ModMaskI) \
|
||||
_(PowHalfD)
|
||||
|
||||
#endif // jsion_lir_opcodes_arm_h__
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -14,7 +14,8 @@
|
||||
_(UnboxDouble) \
|
||||
_(DivI) \
|
||||
_(ModI) \
|
||||
_(ModPowTwoI)
|
||||
_(ModPowTwoI) \
|
||||
_(PowHalfD)
|
||||
|
||||
#endif // jsion_lir_opcodes_x64_h__
|
||||
|
||||
|
@ -15,7 +15,8 @@
|
||||
_(BoxDouble) \
|
||||
_(DivI) \
|
||||
_(ModI) \
|
||||
_(ModPowTwoI)
|
||||
_(ModPowTwoI) \
|
||||
_(PowHalfD)
|
||||
|
||||
#endif // jsion_lir_opcodes_x86_h__
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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___ */
|
||||
|
Loading…
Reference in New Issue
Block a user