diff --git a/js/src/jit-test/tests/basic/testMathClz32.js b/js/src/jit-test/tests/basic/testMathClz32.js new file mode 100644 index 000000000000..0cc4aae890cb --- /dev/null +++ b/js/src/jit-test/tests/basic/testMathClz32.js @@ -0,0 +1,28 @@ +function f() { + var x = 0; + for (var i = 1; i < 1e6; i++) { + if (i > 0) + x += Math.clz32(i); + } + return x; +} + +function g() { + var x = 0; + for (var i = 1; i < 1e6; i++) { + x += Math.clz32(i); + } + return x; +} + +function h() { + var x = 0; + for (var i = 0; i < 1e6; i++) { + x += Math.clz32(i); + } + return x; +} + +assertEq(f(), 13048543); +assertEq(g(), 13048543); +assertEq(h(), 13048575); \ No newline at end of file diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 558dd74b2047..894fdcf3388d 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -684,6 +684,7 @@ class IonBuilder : public MIRGenerator InliningStatus inlineMathAbs(CallInfo &callInfo); InliningStatus inlineMathFloor(CallInfo &callInfo); InliningStatus inlineMathCeil(CallInfo &callInfo); + InliningStatus inlineMathClz32(CallInfo &callInfo); InliningStatus inlineMathRound(CallInfo &callInfo); InliningStatus inlineMathSqrt(CallInfo &callInfo); InliningStatus inlineMathAtan2(CallInfo &callInfo); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 5b4a33aa7449..71afda7466ea 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -2677,6 +2677,20 @@ class LAbsF : public LInstructionHelper<1, 1, 0> } }; +// Count leading zeroes +class LClzI : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(ClzI) + LClzI(const LAllocation &num) { + setOperand(0, num); + } + + MClz *mir() const { + return mir_->toClz(); + } +}; + // Square root of a double. class LSqrtD : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index a2c9256de91c..6e4600820bbe 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -115,6 +115,7 @@ _(AbsI) \ _(AbsD) \ _(AbsF) \ + _(ClzI) \ _(SqrtD) \ _(SqrtF) \ _(Atan2D) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 33f443815cac..86ce64517d1b 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1296,6 +1296,15 @@ LIRGenerator::visitAbs(MAbs *ins) return defineReuseInput(lir, ins, 0); } +bool +LIRGenerator::visitClz(MClz *ins) +{ + MDefinition *num = ins->num(); + + LClzI *lir = new(alloc()) LClzI(useRegisterAtStart(num)); + return define(lir, ins); +} + bool LIRGenerator::visitSqrt(MSqrt *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index e3320f92bede..71d8cc972926 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -125,6 +125,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitRound(MRound *ins); bool visitMinMax(MMinMax *ins); bool visitAbs(MAbs *ins); + bool visitClz(MClz *ins); bool visitSqrt(MSqrt *ins); bool visitAtan2(MAtan2 *ins); bool visitHypot(MHypot *ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 4883045bcd0e..230a729772e5 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -54,6 +54,8 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target) return inlineMathFloor(callInfo); if (native == js::math_ceil) return inlineMathCeil(callInfo); + if (native == js::math_clz32) + return inlineMathClz32(callInfo); if (native == js::math_round) return inlineMathRound(callInfo); if (native == js::math_sqrt) @@ -791,6 +793,31 @@ IonBuilder::inlineMathCeil(CallInfo &callInfo) return InliningStatus_NotInlined; } +IonBuilder::InliningStatus +IonBuilder::inlineMathClz32(CallInfo &callInfo) +{ + if (callInfo.constructing()) + return InliningStatus_NotInlined; + + if (callInfo.argc() != 1) + return InliningStatus_NotInlined; + + MIRType returnType = getInlineReturnType(); + if (returnType != MIRType_Int32) + return InliningStatus_NotInlined; + + if (!IsNumberType(callInfo.getArg(0)->type())) + return InliningStatus_NotInlined; + + callInfo.setImplicitlyUsedUnchecked(); + + MClz *ins = MClz::New(alloc(), callInfo.getArg(0)); + current->add(ins); + current->push(ins); + return InliningStatus_Inlined; + +} + IonBuilder::InliningStatus IonBuilder::inlineMathRound(CallInfo &callInfo) { diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 751c32e3de70..81110ffb20d7 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3424,6 +3424,19 @@ MSqrt::trySpecializeFloat32(TempAllocator &alloc) { setPolicyType(MIRType_Float32); } +MDefinition * +MClz::foldsTo(TempAllocator &alloc) +{ + if (num()->isConstant()) { + int32_t n = num()->toConstant()->value().toInt32(); + if (n == 0) + return MConstant::New(alloc, Int32Value(32)); + return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n))); + } + + return this; +} + MDefinition * MBoundsCheck::foldsTo(TempAllocator &alloc) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 4080d0f29e72..611df7ac5090 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -4531,6 +4531,50 @@ class MAbs ALLOW_CLONE(MAbs) }; +class MClz + : public MUnaryInstruction + , public BitwisePolicy +{ + bool operandIsNeverZero_; + + MClz(MDefinition *num) + : MUnaryInstruction(num), + operandIsNeverZero_(false) + { + JS_ASSERT(IsNumberType(num->type())); + specialization_ = MIRType_Int32; + setResultType(MIRType_Int32); + setMovable(); + } + + public: + INSTRUCTION_HEADER(Clz) + static MClz *New(TempAllocator &alloc, MDefinition *num) { + return new(alloc) MClz(num); + } + MDefinition *num() const { + return getOperand(0); + } + TypePolicy *typePolicy() { + return this; + } + bool congruentTo(const MDefinition *ins) const { + return congruentIfOperandsEqual(ins); + } + + AliasSet getAliasSet() const { + return AliasSet::None(); + } + + bool operandIsNeverZero() const { + return operandIsNeverZero_; + } + + MDefinition *foldsTo(TempAllocator &alloc); + void computeRange(TempAllocator &alloc); + void collectRangeInfoPreTrunc(); +}; + // Inline implementation of Math.sqrt(). class MSqrt : public MUnaryInstruction, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 4becf6c00c76..342ab3d88867 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -63,6 +63,7 @@ namespace jit { _(Ursh) \ _(MinMax) \ _(Abs) \ + _(Clz) \ _(Sqrt) \ _(Atan2) \ _(Hypot) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index 573f283174f1..0db3b0015dbf 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -161,6 +161,7 @@ class ParallelSafetyVisitor : public MDefinitionVisitor SAFE_OP(Ursh) SPECIALIZED_OP(MinMax, PERMIT_NUMERIC) SAFE_OP(Abs) + SAFE_OP(Clz) SAFE_OP(Sqrt) UNSAFE_OP(Atan2) UNSAFE_OP(Hypot) diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 57c030915c7c..6d5af745b836 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1228,6 +1228,12 @@ MCeil::computeRange(TempAllocator &alloc) setRange(Range::ceil(alloc, &other)); } +void +MClz::computeRange(TempAllocator &alloc) +{ + setRange(Range::NewUInt32Range(alloc, 0, 32)); +} + void MMinMax::computeRange(TempAllocator &alloc) { @@ -2667,6 +2673,14 @@ MLoadElementHole::collectRangeInfoPreTrunc() needsNegativeIntCheck_ = false; } +void +MClz::collectRangeInfoPreTrunc() +{ + Range inputRange(input()); + if (!inputRange.canBeZero()) + operandIsNeverZero_ = true; +} + void MDiv::collectRangeInfoPreTrunc() { diff --git a/js/src/jit/shared/Assembler-x86-shared.h b/js/src/jit/shared/Assembler-x86-shared.h index 8254d881abf6..b56df8d2b013 100644 --- a/js/src/jit/shared/Assembler-x86-shared.h +++ b/js/src/jit/shared/Assembler-x86-shared.h @@ -1160,6 +1160,9 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + void bsr(const Register &src, const Register &dest) { + masm.bsr_rr(src.code(), dest.code()); + } void imull(Register multiplier) { masm.imull_r(multiplier.code()); } diff --git a/js/src/jit/shared/BaseAssembler-x86-shared.h b/js/src/jit/shared/BaseAssembler-x86-shared.h index df7968b2c9b1..76ae77fb7d4a 100644 --- a/js/src/jit/shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/shared/BaseAssembler-x86-shared.h @@ -324,6 +324,7 @@ private: OP_SETCC = 0x90, OP2_IMUL_GvEv = 0xAF, OP2_CMPXCHG_GvEw = 0xB1, + OP2_BSR_GvEv = 0xBD, OP2_MOVSX_GvEb = 0xBE, OP2_MOVSX_GvEw = 0xBF, OP2_MOVZX_GvEb = 0xB6, @@ -1301,6 +1302,12 @@ public: } #endif + void bsr_rr(RegisterID src, RegisterID dst) + { + spew("bsr %s, %s", nameIReg(4, src), nameIReg(4, dst)); + m_formatter.twoByteOp(OP2_BSR_GvEv, dst, src); + } + void imull_rr(RegisterID src, RegisterID dst) { spew("imull %s, %s", nameIReg(4,src), nameIReg(4, dst)); diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index d72491711215..92c7bc8d0986 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -544,6 +544,28 @@ CodeGeneratorX86Shared::visitAbsF(LAbsF *ins) return true; } +bool +CodeGeneratorX86Shared::visitClzI(LClzI *ins) +{ + Register input = ToRegister(ins->input()); + Register output = ToRegister(ins->output()); + + // bsr is undefined on 0 + Label done, nonzero; + if (!ins->mir()->operandIsNeverZero()) { + masm.testl(input, input); + masm.j(Assembler::NonZero, &nonzero); + masm.move32(Imm32(32), output); + masm.jump(&done); + } + + masm.bind(&nonzero); + masm.bsr(input, output); + masm.xor32(Imm32(0x1F), output); + masm.bind(&done); + return true; +} + bool CodeGeneratorX86Shared::visitSqrtD(LSqrtD *ins) { diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.h b/js/src/jit/shared/CodeGenerator-x86-shared.h index 9b2bc6a74caf..9f37a6572335 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/shared/CodeGenerator-x86-shared.h @@ -153,6 +153,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared virtual bool visitMinMaxD(LMinMaxD *ins); virtual bool visitAbsD(LAbsD *ins); virtual bool visitAbsF(LAbsF *ins); + virtual bool visitClzI(LClzI *ins); virtual bool visitSqrtD(LSqrtD *ins); virtual bool visitSqrtF(LSqrtF *ins); virtual bool visitPowHalfD(LPowHalfD *ins);