From fe338dfe8b41a83976a013501d90414fd96b36dd Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Tue, 31 Jul 2012 20:04:42 -0700 Subject: [PATCH] Bug 772892 - Optimize Math.pow(). r=jandem,mjrosenb --- js/src/ion/CodeGenerator.cpp | 42 +++++ js/src/ion/CodeGenerator.h | 2 + js/src/ion/IonBuilder.h | 1 + js/src/ion/LIR-Common.h | 59 ++++++ js/src/ion/LOpcodes.h | 2 + js/src/ion/Lowering.cpp | 19 ++ js/src/ion/Lowering.h | 1 + js/src/ion/MCallOptimize.cpp | 168 +++++++++++++++++- js/src/ion/MIR.h | 91 +++++++++- js/src/ion/MOpcodes.h | 2 + js/src/ion/TypePolicy.cpp | 40 ++++- js/src/ion/TypePolicy.h | 46 ++--- js/src/ion/arm/CodeGenerator-arm.cpp | 25 ++- js/src/ion/arm/CodeGenerator-arm.h | 1 + js/src/ion/arm/LIR-arm.h | 22 ++- js/src/ion/arm/LOpcodes-arm.h | 4 +- js/src/ion/arm/Lowering-arm.cpp | 9 + js/src/ion/arm/Lowering-arm.h | 1 + js/src/ion/arm/MacroAssembler-arm.cpp | 3 + .../ion/shared/CodeGenerator-x86-shared.cpp | 31 ++++ js/src/ion/shared/CodeGenerator-x86-shared.h | 1 + js/src/ion/shared/LIR-x86-shared.h | 21 +++ js/src/ion/shared/Lowering-x86-shared.cpp | 9 + js/src/ion/shared/Lowering-x86-shared.h | 1 + js/src/ion/shared/MacroAssembler-x86-shared.h | 17 +- js/src/ion/x64/LOpcodes-x64.h | 3 +- js/src/ion/x86/LOpcodes-x86.h | 3 +- js/src/jsmath.cpp | 29 +-- js/src/jsmath.h | 6 + 29 files changed, 588 insertions(+), 71 deletions(-) diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index aade16467cf0..84f5eeb3c7fc 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -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) { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index fb0ca53c52f8..d32b648ff687 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -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); diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index 624d60e7ad90..f33f64c5f8ec 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -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); diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index 301a3a178b8a..25e24ede70e0 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -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: diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index 001d25c70890..0d824ef2c847 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -62,6 +62,8 @@ _(AbsI) \ _(AbsD) \ _(SqrtD) \ + _(PowI) \ + _(PowD) \ _(MathFunctionD) \ _(NotI) \ _(NotD) \ diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index 0a24b28fd079..3d59f4376f71 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -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) { diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index f3706f4d37d8..e2fffdeacbd4 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -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); diff --git a/js/src/ion/MCallOptimize.cpp b/js/src/ion/MCallOptimize.cpp index e8688b87976f..a8f797005820 100644 --- a/js/src/ion/MCallOptimize.cpp +++ b/js/src/ion/MCallOptimize.cpp @@ -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) @@ -305,10 +323,19 @@ 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) { diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 0d27caae0056..d5b4dc839858 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -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); diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index f2e8b6b25f40..ecfc77d1515e 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -45,6 +45,8 @@ namespace ion { _(Ursh) \ _(Abs) \ _(Sqrt) \ + _(Pow) \ + _(PowHalf) \ _(MathFunction) \ _(Add) \ _(Sub) \ diff --git a/js/src/ion/TypePolicy.cpp b/js/src/ion/TypePolicy.cpp index ed2ad6296818..7c03524a2916 100644 --- a/js/src/ion/TypePolicy.cpp +++ b/js/src/ion/TypePolicy.cpp @@ -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) { diff --git a/js/src/ion/TypePolicy.h b/js/src/ion/TypePolicy.h index a08a7f381cf2..d5f409c90e29 100644 --- a/js/src/ion/TypePolicy.h +++ b/js/src/ion/TypePolicy.h @@ -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 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); } }; diff --git a/js/src/ion/arm/CodeGenerator-arm.cpp b/js/src/ion/arm/CodeGenerator-arm.cpp index eaf27373204a..624a3189acd1 100644 --- a/js/src/ion/arm/CodeGenerator-arm.cpp +++ b/js/src/ion/arm/CodeGenerator-arm.cpp @@ -31,7 +31,7 @@ class DeferredJumpTable : public DeferredData MacroAssembler *masm; public: DeferredJumpTable(LTableSwitch *lswitch, BufferOffset off_, MacroAssembler *masm_) - : lswitch(lswitch), off(off_), masm(masm_) + : lswitch(lswitch), off(off_), masm(masm_) { } void copy(IonCode *code, uint8 *ignore__) const { @@ -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 diff --git a/js/src/ion/arm/CodeGenerator-arm.h b/js/src/ion/arm/CodeGenerator-arm.h index e4ff1fcd7288..a6342e114097 100644 --- a/js/src/ion/arm/CodeGenerator-arm.h +++ b/js/src/ion/arm/CodeGenerator-arm.h @@ -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); diff --git a/js/src/ion/arm/LIR-arm.h b/js/src/ion/arm/LIR-arm.h index 44ad5cfd38de..b87e9802e611 100644 --- a/js/src/ion/arm/LIR-arm.h +++ b/js/src/ion/arm/LIR-arm.h @@ -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__ - diff --git a/js/src/ion/arm/LOpcodes-arm.h b/js/src/ion/arm/LOpcodes-arm.h index 34bb201dadee..fe4c8ba4beef 100644 --- a/js/src/ion/arm/LOpcodes-arm.h +++ b/js/src/ion/arm/LOpcodes-arm.h @@ -16,6 +16,8 @@ _(DivI) \ _(ModI) \ _(ModPowTwoI) \ - _(ModMaskI) + _(ModMaskI) \ + _(PowHalfD) + #endif // jsion_lir_opcodes_arm_h__ diff --git a/js/src/ion/arm/Lowering-arm.cpp b/js/src/ion/arm/Lowering-arm.cpp index b83c388ab673..9fc312e1ba33 100644 --- a/js/src/ion/arm/Lowering-arm.cpp +++ b/js/src/ion/arm/Lowering-arm.cpp @@ -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) { diff --git a/js/src/ion/arm/Lowering-arm.h b/js/src/ion/arm/Lowering-arm.h index 7d7878c6c62a..ee8d763393ec 100644 --- a/js/src/ion/arm/Lowering-arm.h +++ b/js/src/ion/arm/Lowering-arm.h @@ -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: diff --git a/js/src/ion/arm/MacroAssembler-arm.cpp b/js/src/ion/arm/MacroAssembler-arm.cpp index 2de7af4667c6..4c3ce6898169 100644 --- a/js/src/ion/arm/MacroAssembler-arm.cpp +++ b/js/src/ion/arm/MacroAssembler-arm.cpp @@ -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); } diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.cpp b/js/src/ion/shared/CodeGenerator-x86-shared.cpp index 6348fca4338f..852311a22b84 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp @@ -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) { diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.h b/js/src/ion/shared/CodeGenerator-x86-shared.h index d4d3f5c501b1..e9d0f9c742d8 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.h +++ b/js/src/ion/shared/CodeGenerator-x86-shared.h @@ -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); diff --git a/js/src/ion/shared/LIR-x86-shared.h b/js/src/ion/shared/LIR-x86-shared.h index 60a54db343b2..a010f7d19c34 100644 --- a/js/src/ion/shared/LIR-x86-shared.h +++ b/js/src/ion/shared/LIR-x86-shared.h @@ -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> { diff --git a/js/src/ion/shared/Lowering-x86-shared.cpp b/js/src/ion/shared/Lowering-x86-shared.cpp index 5c468abeccea..5360822d7bb8 100644 --- a/js/src/ion/shared/Lowering-x86-shared.cpp +++ b/js/src/ion/shared/Lowering-x86-shared.cpp @@ -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) { diff --git a/js/src/ion/shared/Lowering-x86-shared.h b/js/src/ion/shared/Lowering-x86-shared.h index 1475dd99b812..7d8428a5b90a 100644 --- a/js/src/ion/shared/Lowering-x86-shared.h +++ b/js/src/ion/shared/Lowering-x86-shared.h @@ -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); }; diff --git a/js/src/ion/shared/MacroAssembler-x86-shared.h b/js/src/ion/shared/MacroAssembler-x86-shared.h index 270364c3f288..862400221682 100644 --- a/js/src/ion/shared/MacroAssembler-x86-shared.h +++ b/js/src/ion/shared/MacroAssembler-x86-shared.h @@ -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); diff --git a/js/src/ion/x64/LOpcodes-x64.h b/js/src/ion/x64/LOpcodes-x64.h index 9e5571792efe..157d0a1a51eb 100644 --- a/js/src/ion/x64/LOpcodes-x64.h +++ b/js/src/ion/x64/LOpcodes-x64.h @@ -14,7 +14,8 @@ _(UnboxDouble) \ _(DivI) \ _(ModI) \ - _(ModPowTwoI) + _(ModPowTwoI) \ + _(PowHalfD) #endif // jsion_lir_opcodes_x64_h__ diff --git a/js/src/ion/x86/LOpcodes-x86.h b/js/src/ion/x86/LOpcodes-x86.h index b4ba0287a747..5bc9f5fa8ad9 100644 --- a/js/src/ion/x86/LOpcodes-x86.h +++ b/js/src/ion/x86/LOpcodes-x86.h @@ -15,7 +15,8 @@ _(BoxDouble) \ _(DivI) \ _(ModI) \ - _(ModPowTwoI) + _(ModPowTwoI) \ + _(PowHalfD) #endif // jsion_lir_opcodes_x86_h__ diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 274100b00772..0af16ade50c5 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -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; diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 6f650468be42..33dd2d41e75d 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -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___ */