[turbofan] Turn Math.clz32 into an inlinable builtin.
authorBenedikt Meurer <bmeurer@chromium.org>
Fri, 20 Mar 2015 08:37:20 +0000 (09:37 +0100)
committerBenedikt Meurer <bmeurer@chromium.org>
Fri, 20 Mar 2015 08:37:34 +0000 (08:37 +0000)
R=dcarney@chromium.org, yangguo@chromium.org
BUG=v8:3952
LOG=n

Review URL: https://codereview.chromium.org/1021183002

Cr-Commit-Position: refs/heads/master@{#27329}

46 files changed:
src/compiler/arm/code-generator-arm.cc
src/compiler/arm/instruction-codes-arm.h
src/compiler/arm/instruction-selector-arm.cc
src/compiler/arm64/code-generator-arm64.cc
src/compiler/arm64/instruction-codes-arm64.h
src/compiler/arm64/instruction-selector-arm64.cc
src/compiler/ia32/code-generator-ia32.cc
src/compiler/ia32/instruction-codes-ia32.h
src/compiler/ia32/instruction-selector-ia32.cc
src/compiler/instruction-selector.cc
src/compiler/js-intrinsic-lowering.cc
src/compiler/js-intrinsic-lowering.h
src/compiler/machine-operator.cc
src/compiler/machine-operator.h
src/compiler/opcodes.h
src/compiler/raw-machine-assembler.h
src/compiler/simplified-lowering.cc
src/compiler/typer.cc
src/compiler/verifier.cc
src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-codes-x64.h
src/compiler/x64/instruction-selector-x64.cc
src/counters.h
src/hydrogen.cc
src/hydrogen.h
src/ia32/lithium-codegen-ia32.cc
src/ia32/macro-assembler-ia32.cc
src/ia32/macro-assembler-ia32.h
src/math.js
src/runtime/runtime-maths.cc
src/runtime/runtime.h
src/x64/assembler-x64.cc
src/x64/assembler-x64.h
src/x64/lithium-codegen-x64.cc
src/x64/macro-assembler-x64.cc
src/x64/macro-assembler-x64.h
test/cctest/test-disasm-x64.cc
test/mjsunit/asm/math-clz32.js [new file with mode: 0644]
test/unittests/compiler/arm/instruction-selector-arm-unittest.cc
test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc
test/unittests/compiler/ia32/instruction-selector-ia32-unittest.cc
test/unittests/compiler/js-intrinsic-lowering-unittest.cc
test/unittests/compiler/machine-operator-unittest.cc
test/unittests/compiler/node-test-utils.cc
test/unittests/compiler/node-test-utils.h
test/unittests/compiler/x64/instruction-selector-x64-unittest.cc

index b9c3476128cd098df3927211c99d483d1e6a9d22..26ad5b03179d06c680e71f9e16ca521c33ab7c26 100644 (file)
@@ -489,6 +489,10 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
                i.InputInt32(2));
       DCHECK_EQ(LeaveCC, i.OutputSBit());
       break;
+    case kArmClz:
+      __ clz(i.OutputRegister(), i.InputRegister(0));
+      DCHECK_EQ(LeaveCC, i.OutputSBit());
+      break;
     case kArmCmp:
       __ cmp(i.InputRegister(0), i.InputOperand2(1));
       DCHECK_EQ(SetCC, i.OutputSBit());
index 5cb08e4f7d173bd70252842b09865ab857fd02ca..404b1e0acad64a2324e143d6e1d111059a838bed 100644 (file)
@@ -15,6 +15,7 @@ namespace compiler {
   V(ArmAdd)                        \
   V(ArmAnd)                        \
   V(ArmBic)                        \
+  V(ArmClz)                        \
   V(ArmCmp)                        \
   V(ArmCmn)                        \
   V(ArmTst)                        \
index 20276ede7e62364fb5c4f6bff3f6780007224f32..9f788a5bfa8be5d3c359ff67d0e718aedb4d03ea 100644 (file)
@@ -645,6 +645,12 @@ void InstructionSelector::VisitWord32Ror(Node* node) {
 }
 
 
+void InstructionSelector::VisitWord32Clz(Node* node) {
+  ArmOperandGenerator g(this);
+  Emit(kArmClz, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitInt32Add(Node* node) {
   ArmOperandGenerator g(this);
   Int32BinopMatcher m(node);
index b494090c47d7cad72bd91cd38ff4217d912b4496..3122225085ef44332675121d9234b6e1032ed1a9 100644 (file)
@@ -631,6 +631,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ PokePair(i.InputRegister(1), i.InputRegister(0), slot * kPointerSize);
       break;
     }
+    case kArm64Clz32:
+      __ Clz(i.OutputRegister32(), i.InputRegister32(0));
+      break;
     case kArm64Cmp:
       __ Cmp(i.InputRegister(0), i.InputOperand(1));
       break;
index dfd90da44aad0a562aabac8c95000376432cd49f..3e3f5c37d927413189e64ff3fef5839d9a8f448a 100644 (file)
@@ -18,6 +18,7 @@ namespace compiler {
   V(Arm64And32)                    \
   V(Arm64Bic)                      \
   V(Arm64Bic32)                    \
+  V(Arm64Clz32)                    \
   V(Arm64Cmp)                      \
   V(Arm64Cmp32)                    \
   V(Arm64Cmn)                      \
index edef43837b3fe264c646b7da37fe99b61e04904e..97099e2253702cc91aacf0335c6015b355eb99b8 100644 (file)
@@ -738,6 +738,12 @@ void InstructionSelector::VisitWord64Ror(Node* node) {
 }
 
 
+void InstructionSelector::VisitWord32Clz(Node* node) {
+  Arm64OperandGenerator g(this);
+  Emit(kArm64Clz32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitInt32Add(Node* node) {
   Arm64OperandGenerator g(this);
   Int32BinopMatcher m(node);
index fe86e3aaac635e3d2b91e695ed30d94f245cd434..25bf0331c1557ceb7cab8a659cbe0ad817c2d041 100644 (file)
@@ -447,6 +447,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
         __ ror_cl(i.OutputOperand());
       }
       break;
+    case kIA32Lzcnt:
+      __ Lzcnt(i.OutputRegister(), i.InputOperand(0));
+      break;
     case kSSEFloat64Cmp:
       __ ucomisd(i.InputDoubleRegister(0), i.InputOperand(1));
       break;
index e3a7ac1f83ca7ec6b1fd7f864516e64126e1bea9..b7a9e82f6b66dd5abf4106e9c5afe8c0dcca0d6f 100644 (file)
@@ -30,6 +30,7 @@ namespace compiler {
   V(IA32Shr)                       \
   V(IA32Sar)                       \
   V(IA32Ror)                       \
+  V(IA32Lzcnt)                     \
   V(SSEFloat64Cmp)                 \
   V(SSEFloat64Add)                 \
   V(SSEFloat64Sub)                 \
index 8ec091a0f6b4776bb981d727507320c4e47ac64d..ba21921dbb38c164f94df96c12074c93225fd7f2 100644 (file)
@@ -503,6 +503,12 @@ void InstructionSelector::VisitWord32Ror(Node* node) {
 }
 
 
+void InstructionSelector::VisitWord32Clz(Node* node) {
+  IA32OperandGenerator g(this);
+  Emit(kIA32Lzcnt, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitInt32Add(Node* node) {
   IA32OperandGenerator g(this);
 
index 0d0314dda328d26c78b9bf3f5cf6fcd2c38d69c2..c3837ad8b602e99fba10055f61cdbb5b81d6dd00 100644 (file)
@@ -658,6 +658,8 @@ void InstructionSelector::VisitNode(Node* node) {
       return VisitWord32Ror(node);
     case IrOpcode::kWord32Equal:
       return VisitWord32Equal(node);
+    case IrOpcode::kWord32Clz:
+      return VisitWord32Clz(node);
     case IrOpcode::kWord64And:
       return VisitWord64And(node);
     case IrOpcode::kWord64Or:
index a3d27ee1a5b98a5e7e9d61c47b8a39682ad1be74..8879ebc8d92ff2a646da5550ee53af8988f61baf 100644 (file)
@@ -42,6 +42,8 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
       return ReduceInlineDoubleHi(node);
     case Runtime::kInlineIsRegExp:
       return ReduceInlineIsInstanceType(node, JS_REGEXP_TYPE);
+    case Runtime::kInlineMathClz32:
+      return ReduceInlineMathClz32(node);
     case Runtime::kInlineMathFloor:
       return ReduceInlineMathFloor(node);
     case Runtime::kInlineMathSqrt:
@@ -179,6 +181,11 @@ Reduction JSIntrinsicLowering::ReduceInlineIsInstanceType(
 }
 
 
+Reduction JSIntrinsicLowering::ReduceInlineMathClz32(Node* node) {
+  return Change(node, machine()->Word32Clz());
+}
+
+
 Reduction JSIntrinsicLowering::ReduceInlineMathFloor(Node* node) {
   if (!machine()->HasFloat64RoundDown()) return NoChange();
   return Change(node, machine()->Float64RoundDown());
index 21982ac701329a5f57271dbcd56a379ad33efc58..6bc0c7d26f16d57bce6e471b20c3232b0fb58cb7 100644 (file)
@@ -35,6 +35,7 @@ class JSIntrinsicLowering FINAL : public Reducer {
   Reduction ReduceInlineConstructDouble(Node* node);
   Reduction ReduceInlineDoubleLo(Node* node);
   Reduction ReduceInlineDoubleHi(Node* node);
+  Reduction ReduceInlineMathClz32(Node* node);
   Reduction ReduceInlineMathFloor(Node* node);
   Reduction ReduceInlineMathSqrt(Node* node);
   Reduction ReduceInlineStringGetLength(Node* node);
index 2351223890e7d266c55c18883795e48a2406daec..fa8979c2fa4b3b5ad0b60bfb15b7b6825a1ca852 100644 (file)
@@ -73,6 +73,7 @@ CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const* op) {
   V(Word32Sar, Operator::kNoProperties, 2, 0, 1)                              \
   V(Word32Ror, Operator::kNoProperties, 2, 0, 1)                              \
   V(Word32Equal, Operator::kCommutative, 2, 0, 1)                             \
+  V(Word32Clz, Operator::kNoProperties, 1, 0, 1)                              \
   V(Word64And, Operator::kAssociative | Operator::kCommutative, 2, 0, 1)      \
   V(Word64Or, Operator::kAssociative | Operator::kCommutative, 2, 0, 1)       \
   V(Word64Xor, Operator::kAssociative | Operator::kCommutative, 2, 0, 1)      \
index 409f033cc8cb6594e7069cc537724e89336bdd44..d428562517d7930a3948d160f6709825865605b9 100644 (file)
@@ -96,6 +96,7 @@ class MachineOperatorBuilder FINAL : public ZoneObject {
   const Operator* Word32Sar();
   const Operator* Word32Ror();
   const Operator* Word32Equal();
+  const Operator* Word32Clz();
   bool Word32ShiftIsSafe() const { return flags_ & kWord32ShiftIsSafe; }
 
   const Operator* Word64And();
index 9114c8d139f90a5810f1fc1d2cf85c7d4f563dcf..8426fd8c18edf1e0adb6de2aff4a8b3ae38cfce0 100644 (file)
   V(Word32Shr)                  \
   V(Word32Sar)                  \
   V(Word32Ror)                  \
+  V(Word32Clz)                  \
   V(Word64And)                  \
   V(Word64Or)                   \
   V(Word64Xor)                  \
index 82ef3fabf2297490045baa116d15fdc78554081a..bcc96803f766869d65984a005751c705d864a68b 100644 (file)
@@ -175,6 +175,7 @@ class RawMachineAssembler : public GraphBuilder {
   Node* Word32Ror(Node* a, Node* b) {
     return NewNode(machine()->Word32Ror(), a, b);
   }
+  Node* Word32Clz(Node* a) { return NewNode(machine()->Word32Clz(), a); }
   Node* Word32Equal(Node* a, Node* b) {
     return NewNode(machine()->Word32Equal(), a, b);
   }
index ef65c9bc73e2331b40d563142ce2a8fd477992ff..939cd38d1461bd1ea0f8835891ea755d8658cb76 100644 (file)
@@ -939,6 +939,9 @@ class RepresentationSelector {
       case IrOpcode::kWord32Equal:
         return VisitBinop(node, kRepWord32, kRepBit);
 
+      case IrOpcode::kWord32Clz:
+        return VisitUnop(node, kMachUint32, kMachUint32);
+
       case IrOpcode::kInt32Add:
       case IrOpcode::kInt32Sub:
       case IrOpcode::kInt32Mul:
index 67e2c55b29aa59c5d4c873f8af238e588753e41d..e044450b37f988f1c9f127aae2e4e5ca29009e96 100644 (file)
@@ -1518,6 +1518,8 @@ Bounds Typer::Visitor::TypeJSCallRuntime(Node* node) {
     case Runtime::kInlineMathFloor:
     case Runtime::kInlineMathSqrt:
       return Bounds(Type::None(zone()), Type::Number());
+    case Runtime::kInlineMathClz32:
+      return Bounds(Type::None(), Type::Range(0, 32, zone()));
     default:
       break;
   }
@@ -1819,6 +1821,11 @@ Bounds Typer::Visitor::TypeWord32Equal(Node* node) {
 }
 
 
+Bounds Typer::Visitor::TypeWord32Clz(Node* node) {
+  return Bounds(Type::Integral32());
+}
+
+
 Bounds Typer::Visitor::TypeWord64And(Node* node) {
   return Bounds(Type::Internal());
 }
index 3cc6424c6c25de1e020f94f623109ff885c211df..bd8b971df89900e837518c88c6047dc611be2d8f 100644 (file)
@@ -751,6 +751,7 @@ void Verifier::Visitor::Check(Node* node) {
     case IrOpcode::kWord32Sar:
     case IrOpcode::kWord32Ror:
     case IrOpcode::kWord32Equal:
+    case IrOpcode::kWord32Clz:
     case IrOpcode::kWord64And:
     case IrOpcode::kWord64Or:
     case IrOpcode::kWord64Xor:
index 0027d49bec5a6f443568e4392ac761ca4c1bd6d3..b3285dea9462547686514dcee76fb0eb57ff636f 100644 (file)
@@ -694,6 +694,13 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kX64Ror:
       ASSEMBLE_SHIFT(rorq, 6);
       break;
+    case kX64Lzcnt32:
+      if (instr->InputAt(0)->IsRegister()) {
+        __ Lzcntl(i.OutputRegister(), i.InputRegister(0));
+      } else {
+        __ Lzcntl(i.OutputRegister(), i.InputOperand(0));
+      }
+      break;
     case kSSEFloat64Cmp:
       ASSEMBLE_DOUBLE_BINOP(ucomisd);
       break;
index 4c9d335353585deae9b5d2620e8f0361de471216..9416017af8ebb5856c3f1375f57e96a4fae5806a 100644 (file)
@@ -46,6 +46,7 @@ namespace compiler {
   V(X64Sar32)                      \
   V(X64Ror)                        \
   V(X64Ror32)                      \
+  V(X64Lzcnt32)                    \
   V(SSEFloat64Cmp)                 \
   V(SSEFloat64Add)                 \
   V(SSEFloat64Sub)                 \
index ac06da397a52751445addbca8144eeaaac2c60a7..f5f1264aa50500fbca5faf3bc9c7b906b329861f 100644 (file)
@@ -555,6 +555,12 @@ void InstructionSelector::VisitWord64Ror(Node* node) {
 }
 
 
+void InstructionSelector::VisitWord32Clz(Node* node) {
+  X64OperandGenerator g(this);
+  Emit(kX64Lzcnt32, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitInt32Add(Node* node) {
   X64OperandGenerator g(this);
 
index e20a49fa3ff5cde490244bbc50d627cb78b40ba8..af8e8b4553a77b989cf3f0936c72fd197a763ecf 100644 (file)
@@ -570,6 +570,7 @@ class AggregatedHistogramTimerScope {
   SC(math_asin, V8.MathAsin)                                                   \
   SC(math_atan, V8.MathAtan)                                                   \
   SC(math_atan2, V8.MathAtan2)                                                 \
+  SC(math_clz32, V8.MathClz32)                                                 \
   SC(math_exp, V8.MathExp)                                                     \
   SC(math_floor, V8.MathFloor)                                                 \
   SC(math_log, V8.MathLog)                                                     \
index 5153897fbf8148068e287d023eb7cb3ecd8c880e..6f349b79c4a1eac2e3b9ec332f49d63bd93071e7 100644 (file)
@@ -12030,6 +12030,15 @@ void HOptimizedGraphBuilder::GenerateMathPow(CallRuntime* call) {
 }
 
 
+void HOptimizedGraphBuilder::GenerateMathClz32(CallRuntime* call) {
+  DCHECK(call->arguments()->length() == 1);
+  CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+  HValue* value = Pop();
+  HInstruction* result = NewUncasted<HUnaryMathOperation>(value, kMathClz32);
+  return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
 void HOptimizedGraphBuilder::GenerateMathFloor(CallRuntime* call) {
   DCHECK(call->arguments()->length() == 1);
   CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
index 483c721e79f0497c703d44735a0691b231781039..b253d600c30ed416fd238aca08725d35156ad2e1 100644 (file)
@@ -2212,6 +2212,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
   F(ConstructDouble)                   \
   F(DoubleHi)                          \
   F(DoubleLo)                          \
+  F(MathClz32)                         \
   F(MathFloor)                         \
   F(MathSqrt)                          \
   F(MathLogRT)                         \
index bc0859aef3d6afef9bc4e769596070040f55b3ca..8dde18d2535aef1413fa3f26349d2fa69f264fbc 100644 (file)
@@ -3914,14 +3914,8 @@ void LCodeGen::DoMathLog(LMathLog* instr) {
 void LCodeGen::DoMathClz32(LMathClz32* instr) {
   Register input = ToRegister(instr->value());
   Register result = ToRegister(instr->result());
-  Label not_zero_input;
-  __ bsr(result, input);
 
-  __ j(not_zero, &not_zero_input);
-  __ Move(result, Immediate(63));  // 63^31 == 32
-
-  __ bind(&not_zero_input);
-  __ xor_(result, Immediate(31));  // for x in [0..31], 31^x == 31-x.
+  __ Lzcnt(result, input);
 }
 
 
index 52e674f9dc035a991b0356ab72148c2a60447f56..365ad3cc0ef40c58313058bca7d9949967196414 100644 (file)
@@ -2466,6 +2466,17 @@ void MacroAssembler::Pinsrd(XMMRegister dst, const Operand& src, int8_t imm8) {
 }
 
 
+void MacroAssembler::Lzcnt(Register dst, const Operand& src) {
+  // TODO(intel): Add support for LZCNT (with ABM/BMI1).
+  Label not_zero_src;
+  bsr(dst, src);
+  j(not_zero, &not_zero_src, Label::kNear);
+  Move(dst, Immediate(63));  // 63^31 == 32
+  bind(&not_zero_src);
+  xor_(dst, Immediate(31));  // for x in [0..31], 31^x == 31-x.
+}
+
+
 void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
   if (FLAG_native_code_counters && counter->Enabled()) {
     mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value));
index 8c135f2b6021c9b71e5dbf3eea44138fedb7c7a3..8a8ee2a30e722b369cef852cdd2977f35e307e5c 100644 (file)
@@ -820,6 +820,9 @@ class MacroAssembler: public Assembler {
   }
   void Pinsrd(XMMRegister dst, const Operand& src, int8_t imm8);
 
+  void Lzcnt(Register dst, Register src) { Lzcnt(dst, Operand(src)); }
+  void Lzcnt(Register dst, const Operand& src);
+
   // Emit call to the code we are currently generating.
   void CallSelf() {
     Handle<Code> self(reinterpret_cast<Code**>(CodeObject().location()));
index dff9390dee6b62e3a6c0191aa53eb9c672f7df94..f2eaa14bf9bd547817497651e859f045b57276cc 100644 (file)
@@ -252,17 +252,8 @@ function MathFroundJS(x) {
 }
 
 // ES6 draft 07-18-14, section 20.2.2.11
-function MathClz32(x) {
-  x = ToUint32(TO_NUMBER_INLINE(x));
-  if (x == 0) return 32;
-  var result = 0;
-  // Binary search.
-  if ((x & 0xFFFF0000) === 0) { x <<= 16; result += 16; };
-  if ((x & 0xFF000000) === 0) { x <<=  8; result +=  8; };
-  if ((x & 0xF0000000) === 0) { x <<=  4; result +=  4; };
-  if ((x & 0xC0000000) === 0) { x <<=  2; result +=  2; };
-  if ((x & 0x80000000) === 0) { x <<=  1; result +=  1; };
-  return result;
+function MathClz32JS(x) {
+  return %_MathClz32(x >>> 0);
 }
 
 // ES6 draft 09-27-13, section 20.2.2.9.
@@ -345,12 +336,13 @@ InstallFunctions(Math, DONT_ENUM, GlobalArray(
   "atanh", MathAtanh,
   "hypot", MathHypot,
   "fround", MathFroundJS,
-  "clz32", MathClz32,
+  "clz32", MathClz32JS,
   "cbrt", MathCbrt
 ));
 
 %SetInlineBuiltinFlag(MathAbs);
 %SetInlineBuiltinFlag(MathCeil);
+%SetInlineBuiltinFlag(MathClz32JS);
 %SetInlineBuiltinFlag(MathFloorJS);
 %SetInlineBuiltinFlag(MathRandom);
 %SetInlineBuiltinFlag(MathSqrtJS);
index a4435e09e98c63d229b7aac4a3b0943af2efed03..2941b580f57b5c3388eac94d7232c863de744e91 100644 (file)
@@ -112,6 +112,17 @@ RUNTIME_FUNCTION(Runtime_MathExpRT) {
 }
 
 
+RUNTIME_FUNCTION(Runtime_MathClz32) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 1);
+  isolate->counters()->math_clz32()->Increment();
+
+  CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
+  return *isolate->factory()->NewNumberFromUint(
+      base::bits::CountLeadingZeros32(x));
+}
+
+
 RUNTIME_FUNCTION(Runtime_MathFloor) {
   HandleScope scope(isolate);
   DCHECK(args.length() == 1);
index d4c5c6b2275711d76c678cba4e8a4539ee820e53..9604a2a79ff6545b207de37ea6d24ba45ab3dfc4 100644 (file)
@@ -694,6 +694,7 @@ namespace internal {
   F(ConstructDouble, 2, 1)                \
   F(DoubleHi, 1, 1)                       \
   F(DoubleLo, 1, 1)                       \
+  F(MathClz32, 1, 1)                      \
   F(MathFloor, 1, 1)                      \
   F(MathSqrt, 1, 1)                       \
   F(MathLogRT, 1, 1)                      \
index 1eb054772356d76e87092a3a0f3fda0ca3ed927b..abdf7a5d9c9fac54006a24e2cec39efc4e7d55b5 100644 (file)
@@ -742,6 +742,15 @@ void Assembler::bsrl(Register dst, Register src) {
 }
 
 
+void Assembler::bsrl(Register dst, const Operand& src) {
+  EnsureSpace ensure_space(this);
+  emit_optional_rex_32(dst, src);
+  emit(0x0F);
+  emit(0xBD);
+  emit_operand(dst, src);
+}
+
+
 void Assembler::call(Label* L) {
   positions_recorder()->WriteRecordedPositions();
   EnsureSpace ensure_space(this);
index 56c99fd43f4a6bc915bfecaa4b67b7152e4b0fa4..a83c5518b9370bb2f70f31bc49b54d75040f9ed5 100644 (file)
@@ -899,6 +899,7 @@ class Assembler : public AssemblerBase {
   void bt(const Operand& dst, Register src);
   void bts(const Operand& dst, Register src);
   void bsrl(Register dst, Register src);
+  void bsrl(Register dst, const Operand& src);
 
   // Miscellaneous
   void clc();
index 98a76ad58e6f2c4440684e0679b3292e910f4437..d1683e6ae7ab9685d64c9801a78891f2f56a2cb1 100644 (file)
@@ -4001,14 +4001,8 @@ void LCodeGen::DoMathLog(LMathLog* instr) {
 void LCodeGen::DoMathClz32(LMathClz32* instr) {
   Register input = ToRegister(instr->value());
   Register result = ToRegister(instr->result());
-  Label not_zero_input;
-  __ bsrl(result, input);
 
-  __ j(not_zero, &not_zero_input);
-  __ Set(result, 63);  // 63^31 == 32
-
-  __ bind(&not_zero_input);
-  __ xorl(result, Immediate(31));  // for x in [0..31], 31^x == 31-x.
+  __ Lzcntl(result, input);
 }
 
 
index 293b064a448ed7b96cfc87a0999260d9b598139b..12c471c2fface1519c1141ef9ee701a9fb7c80da 100644 (file)
@@ -2886,6 +2886,28 @@ void MacroAssembler::Pinsrd(XMMRegister dst, const Operand& src, int8_t imm8) {
 }
 
 
+void MacroAssembler::Lzcntl(Register dst, Register src) {
+  // TODO(intel): Add support for LZCNT (BMI1/ABM).
+  Label not_zero_src;
+  bsrl(dst, src);
+  j(not_zero, &not_zero_src, Label::kNear);
+  Set(dst, 63);  // 63^31 == 32
+  bind(&not_zero_src);
+  xorl(dst, Immediate(31));  // for x in [0..31], 31^x == 31 - x
+}
+
+
+void MacroAssembler::Lzcntl(Register dst, const Operand& src) {
+  // TODO(intel): Add support for LZCNT (BMI1/ABM).
+  Label not_zero_src;
+  bsrl(dst, src);
+  j(not_zero, &not_zero_src, Label::kNear);
+  Set(dst, 63);  // 63^31 == 32
+  bind(&not_zero_src);
+  xorl(dst, Immediate(31));  // for x in [0..31], 31^x == 31 - x
+}
+
+
 void MacroAssembler::Pushad() {
   Push(rax);
   Push(rcx);
index 1eb7f6effefc7c32bbc6ae96ad6f25bb5e80c68a..8db4df346c7f51d36ad20e46b784c6e94ed0134f 100644 (file)
@@ -927,6 +927,9 @@ class MacroAssembler: public Assembler {
   void Pinsrd(XMMRegister dst, Register src, int8_t imm8);
   void Pinsrd(XMMRegister dst, const Operand& src, int8_t imm8);
 
+  void Lzcntl(Register dst, Register src);
+  void Lzcntl(Register dst, const Operand& src);
+
   // Non-x64 instructions.
   // Push/pop all general purpose registers.
   // Does not push rsp/rbp nor any of the assembler's special purpose registers
index 8986ba76a0d2a7df3289754f0f4517bdb61e7ade..0eb934829abefed1c411a935fb7109373e5df5c8 100644 (file)
@@ -89,6 +89,9 @@ TEST(DisasmX64) {
   __ addq(rdi, Operand(rbp, rcx, times_4, -3999));
   __ addq(Operand(rbp, rcx, times_4, 12), Immediate(12));
 
+  __ bsrl(rax, r15);
+  __ bsrl(r9, Operand(rcx, times_8, 91919));
+
   __ nop();
   __ addq(rbx, Immediate(12));
   __ nop();
diff --git a/test/mjsunit/asm/math-clz32.js b/test/mjsunit/asm/math-clz32.js
new file mode 100644 (file)
index 0000000..004aa65
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+var stdlib = { Math: Math };
+
+var f = (function Module(stdlib) {
+  "use asm";
+
+  var clz32 = stdlib.Math.clz32;
+
+  function f(a) {
+    a = a >>> 0;
+    return clz32(a)|0;
+  }
+
+  return f;
+})(stdlib);
+
+assertEquals(32, f(0));
+assertEquals(32, f(NaN));
+assertEquals(32, f(undefined));
+for (var i = 0; i < 32; ++i) {
+  assertEquals(i, f((-1) >>> i));
+}
+for (var i = -2147483648; i < 2147483648; i += 3999773) {
+  assertEquals(%MathClz32(i), f(i));
+  assertEquals(%_MathClz32(i), f(i));
+}
index 1fa0b10842f849e2a51b88c3ade8c39938f3e7c4..85e52488b478411160bc8dccfc5a055692ce21d5 100644 (file)
@@ -2426,6 +2426,21 @@ TEST_F(InstructionSelectorTest, Word32AndWithWord32ShrWithImmediateForARMv7) {
   }
 }
 
+
+TEST_F(InstructionSelectorTest, Word32Clz) {
+  StreamBuilder m(this, kMachUint32, kMachUint32);
+  Node* const p0 = m.Parameter(0);
+  Node* const n = m.Word32Clz(p0);
+  m.Return(n);
+  Stream s = m.Build();
+  ASSERT_EQ(1U, s.size());
+  EXPECT_EQ(kArmClz, s[0]->arch_opcode());
+  ASSERT_EQ(1U, s[0]->InputCount());
+  EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+  ASSERT_EQ(1U, s[0]->OutputCount());
+  EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index 163175bcb896573e1c967c204d222bd5e6bffebb..02c8d2e06dd86156c4b04b90e97bd982818aed10 100644 (file)
@@ -2242,6 +2242,21 @@ TEST_F(InstructionSelectorTest, Word32SarWithWord32Shl) {
   }
 }
 
+
+TEST_F(InstructionSelectorTest, Word32Clz) {
+  StreamBuilder m(this, kMachUint32, kMachUint32);
+  Node* const p0 = m.Parameter(0);
+  Node* const n = m.Word32Clz(p0);
+  m.Return(n);
+  Stream s = m.Build();
+  ASSERT_EQ(1U, s.size());
+  EXPECT_EQ(kArm64Clz32, s[0]->arch_opcode());
+  ASSERT_EQ(1U, s[0]->InputCount());
+  EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+  ASSERT_EQ(1U, s[0]->OutputCount());
+  EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index a93b3fcafa21ae15fc7a666f71ef5174693703e6..9138ab2ca671503f3f43ece746ead4c028533c74 100644 (file)
@@ -11,9 +11,9 @@ namespace compiler {
 namespace {
 
 // Immediates (random subset).
-static const int32_t kImmediates[] = {
-    kMinInt, -42, -1, 0,  1,  2,    3,      4,          5,
-    6,       7,   8,  16, 42, 0xff, 0xffff, 0x0f0f0f0f, kMaxInt};
+const int32_t kImmediates[] = {kMinInt, -42, -1,   0,      1,          2,
+                               3,       4,   5,    6,      7,          8,
+                               16,      42,  0xff, 0xffff, 0x0f0f0f0f, kMaxInt};
 
 }  // namespace
 
@@ -689,6 +689,21 @@ TEST_F(InstructionSelectorTest, Uint32LessThanWithLoadAndLoadStackPointer) {
   EXPECT_EQ(kUnsignedGreaterThan, s[0]->flags_condition());
 }
 
+
+TEST_F(InstructionSelectorTest, Word32Clz) {
+  StreamBuilder m(this, kMachUint32, kMachUint32);
+  Node* const p0 = m.Parameter(0);
+  Node* const n = m.Word32Clz(p0);
+  m.Return(n);
+  Stream s = m.Build();
+  ASSERT_EQ(1U, s.size());
+  EXPECT_EQ(kIA32Lzcnt, s[0]->arch_opcode());
+  ASSERT_EQ(1U, s[0]->InputCount());
+  EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+  ASSERT_EQ(1U, s[0]->OutputCount());
+  EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index 96331b509af2e98f77bbd88377e19e8e3f2d7096..8adbc54ac2d9c530ae3c018cc99a014b46815dba 100644 (file)
@@ -295,6 +295,23 @@ TEST_F(JSIntrinsicLoweringTest, InlineStringGetLength) {
 }
 
 
+// -----------------------------------------------------------------------------
+// %_MathClz32
+
+
+TEST_F(JSIntrinsicLoweringTest, InlineMathClz32) {
+  Node* const input = Parameter(0);
+  Node* const context = Parameter(1);
+  Node* const effect = graph()->start();
+  Node* const control = graph()->start();
+  Reduction const r = Reduce(
+      graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineMathClz32, 1),
+                       input, context, effect, control));
+  ASSERT_TRUE(r.Changed());
+  EXPECT_THAT(r.replacement(), IsWord32Clz(input));
+}
+
+
 // -----------------------------------------------------------------------------
 // %_ValueOf
 
index 8244c3791412b35df3e9250f892491e4ac49548c..71b3c0edd9a5ed2cb083f07672fc5736d9033494 100644 (file)
@@ -180,24 +180,24 @@ const PureOperator kPureOperators[] = {
     PURE(Word32And, 2, 0, 1), PURE(Word32Or, 2, 0, 1), PURE(Word32Xor, 2, 0, 1),
     PURE(Word32Shl, 2, 0, 1), PURE(Word32Shr, 2, 0, 1),
     PURE(Word32Sar, 2, 0, 1), PURE(Word32Ror, 2, 0, 1),
-    PURE(Word32Equal, 2, 0, 1), PURE(Word64And, 2, 0, 1),
-    PURE(Word64Or, 2, 0, 1), PURE(Word64Xor, 2, 0, 1), PURE(Word64Shl, 2, 0, 1),
-    PURE(Word64Shr, 2, 0, 1), PURE(Word64Sar, 2, 0, 1),
-    PURE(Word64Ror, 2, 0, 1), PURE(Word64Equal, 2, 0, 1),
-    PURE(Int32Add, 2, 0, 1), PURE(Int32AddWithOverflow, 2, 0, 2),
-    PURE(Int32Sub, 2, 0, 1), PURE(Int32SubWithOverflow, 2, 0, 2),
-    PURE(Int32Mul, 2, 0, 1), PURE(Int32MulHigh, 2, 0, 1),
-    PURE(Int32Div, 2, 1, 1), PURE(Uint32Div, 2, 1, 1), PURE(Int32Mod, 2, 1, 1),
-    PURE(Uint32Mod, 2, 1, 1), PURE(Int32LessThan, 2, 0, 1),
-    PURE(Int32LessThanOrEqual, 2, 0, 1), PURE(Uint32LessThan, 2, 0, 1),
-    PURE(Uint32LessThanOrEqual, 2, 0, 1), PURE(Int64Add, 2, 0, 1),
-    PURE(Int64Sub, 2, 0, 1), PURE(Int64Mul, 2, 0, 1), PURE(Int64Div, 2, 0, 1),
-    PURE(Uint64Div, 2, 0, 1), PURE(Int64Mod, 2, 0, 1), PURE(Uint64Mod, 2, 0, 1),
-    PURE(Int64LessThan, 2, 0, 1), PURE(Int64LessThanOrEqual, 2, 0, 1),
-    PURE(Uint64LessThan, 2, 0, 1), PURE(ChangeFloat32ToFloat64, 1, 0, 1),
-    PURE(ChangeFloat64ToInt32, 1, 0, 1), PURE(ChangeFloat64ToUint32, 1, 0, 1),
-    PURE(ChangeInt32ToInt64, 1, 0, 1), PURE(ChangeUint32ToFloat64, 1, 0, 1),
-    PURE(ChangeUint32ToUint64, 1, 0, 1),
+    PURE(Word32Equal, 2, 0, 1), PURE(Word32Clz, 1, 0, 1),
+    PURE(Word64And, 2, 0, 1), PURE(Word64Or, 2, 0, 1), PURE(Word64Xor, 2, 0, 1),
+    PURE(Word64Shl, 2, 0, 1), PURE(Word64Shr, 2, 0, 1),
+    PURE(Word64Sar, 2, 0, 1), PURE(Word64Ror, 2, 0, 1),
+    PURE(Word64Equal, 2, 0, 1), PURE(Int32Add, 2, 0, 1),
+    PURE(Int32AddWithOverflow, 2, 0, 2), PURE(Int32Sub, 2, 0, 1),
+    PURE(Int32SubWithOverflow, 2, 0, 2), PURE(Int32Mul, 2, 0, 1),
+    PURE(Int32MulHigh, 2, 0, 1), PURE(Int32Div, 2, 1, 1),
+    PURE(Uint32Div, 2, 1, 1), PURE(Int32Mod, 2, 1, 1), PURE(Uint32Mod, 2, 1, 1),
+    PURE(Int32LessThan, 2, 0, 1), PURE(Int32LessThanOrEqual, 2, 0, 1),
+    PURE(Uint32LessThan, 2, 0, 1), PURE(Uint32LessThanOrEqual, 2, 0, 1),
+    PURE(Int64Add, 2, 0, 1), PURE(Int64Sub, 2, 0, 1), PURE(Int64Mul, 2, 0, 1),
+    PURE(Int64Div, 2, 0, 1), PURE(Uint64Div, 2, 0, 1), PURE(Int64Mod, 2, 0, 1),
+    PURE(Uint64Mod, 2, 0, 1), PURE(Int64LessThan, 2, 0, 1),
+    PURE(Int64LessThanOrEqual, 2, 0, 1), PURE(Uint64LessThan, 2, 0, 1),
+    PURE(ChangeFloat32ToFloat64, 1, 0, 1), PURE(ChangeFloat64ToInt32, 1, 0, 1),
+    PURE(ChangeFloat64ToUint32, 1, 0, 1), PURE(ChangeInt32ToInt64, 1, 0, 1),
+    PURE(ChangeUint32ToFloat64, 1, 0, 1), PURE(ChangeUint32ToUint64, 1, 0, 1),
     PURE(TruncateFloat64ToFloat32, 1, 0, 1),
     PURE(TruncateFloat64ToInt32, 1, 0, 1), PURE(TruncateInt64ToInt32, 1, 0, 1),
     PURE(Float64Add, 2, 0, 1), PURE(Float64Sub, 2, 0, 1),
index 5c4f81dd6896f17b8d55ee198ef635731fdd378f..5890b49b02e9de2ab75135bb6d6329cedb661053 100644 (file)
@@ -1634,6 +1634,7 @@ IS_UNOP_MATCHER(NumberToInt32)
 IS_UNOP_MATCHER(NumberToUint32)
 IS_UNOP_MATCHER(ObjectIsSmi)
 IS_UNOP_MATCHER(ObjectIsNonNegativeSmi)
+IS_UNOP_MATCHER(Word32Clz)
 #undef IS_UNOP_MATCHER
 
 }  // namespace compiler
index b10e358bb5fc419909321d949e542a4a25438d23..7c306a2c9d963b13565d6b6eb2bab58f24895b58 100644 (file)
@@ -169,6 +169,7 @@ Matcher<Node*> IsWord32Ror(const Matcher<Node*>& lhs_matcher,
                            const Matcher<Node*>& rhs_matcher);
 Matcher<Node*> IsWord32Equal(const Matcher<Node*>& lhs_matcher,
                              const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsWord32Clz(const Matcher<Node*>& value_matcher);
 Matcher<Node*> IsWord64And(const Matcher<Node*>& lhs_matcher,
                            const Matcher<Node*>& rhs_matcher);
 Matcher<Node*> IsWord64Shl(const Matcher<Node*>& lhs_matcher,
index 13899c5c3ce89221e1ada0d98520f254f0353ed0..a83deafe7d9a7e257dd8ab36347744d4a01c0985 100644 (file)
@@ -1146,6 +1146,21 @@ TEST_F(InstructionSelectorTest, Word32AndWith0xffff) {
   }
 }
 
+
+TEST_F(InstructionSelectorTest, Word32Clz) {
+  StreamBuilder m(this, kMachUint32, kMachUint32);
+  Node* const p0 = m.Parameter(0);
+  Node* const n = m.Word32Clz(p0);
+  m.Return(n);
+  Stream s = m.Build();
+  ASSERT_EQ(1U, s.size());
+  EXPECT_EQ(kX64Lzcnt32, s[0]->arch_opcode());
+  ASSERT_EQ(1U, s[0]->InputCount());
+  EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+  ASSERT_EQ(1U, s[0]->OutputCount());
+  EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8