Bug 1251140 - Baldr: Implement CtzI. r=jandem
authorMichael Bebenita <mbebenita@gmail.com>
Wed, 24 Feb 2016 21:11:14 -0800
changeset 322712 b1766ac255d5df660ac56b0421a82dc3a5fcd977
parent 322711 bfd9d073c88e13080e8b92b36751cdd148408363
child 322713 eacf24b301ac0207e1dd1283f11133904cacbfb8
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1251140
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1251140 - Baldr: Implement CtzI. r=jandem
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/jit-test/tests/wasm/basic-integer.js
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/RangeAnalysis.cpp
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm64/CodeGenerator-arm64.cpp
js/src/jit/arm64/CodeGenerator-arm64.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/x86-shared/Assembler-x86-shared.h
js/src/jit/x86-shared/BaseAssembler-x86-shared.h
js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
js/src/jit/x86-shared/CodeGenerator-x86-shared.h
js/src/jit/x86-shared/Encoding-x86-shared.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -422,20 +422,19 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeSetLocal(f, expected);
       case Expr::Block:
         return DecodeBlock(f, expected);
       case Expr::If:
         return DecodeIfElse(f, /* hasElse */ false, expected);
       case Expr::IfElse:
         return DecodeIfElse(f, /* hasElse */ true, expected);
       case Expr::I32Clz:
+      case Expr::I32Ctz:
       case Expr::I32Popcnt:
         return DecodeUnaryOperator(f, expected, ExprType::I32);
-      case Expr::I32Ctz:
-        return f.fail("NYI: ctz");
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
         return f.fail("NYI: i64") &&
                DecodeUnaryOperator(f, expected, ExprType::I64);
       case Expr::F32Abs:
       case Expr::F32Neg:
       case Expr::F32Ceil:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -2816,16 +2816,18 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I32TruncSF32:
       case Expr::I32TruncUF32:
         return EmitUnary<MTruncateToInt32>(f, ExprType::F32, def);
       case Expr::I32TruncSF64:
       case Expr::I32TruncUF64:
         return EmitUnary<MTruncateToInt32>(f, ExprType::F64, def);
       case Expr::I32Clz:
         return EmitUnary<MClz>(f, ExprType::I32, def);
+      case Expr::I32Ctz:
+        return EmitUnary<MCtz>(f, ExprType::I32, def);
       case Expr::I32Popcnt:
         return EmitUnary<MPopcnt>(f, ExprType::I32, def);
       case Expr::I32Abs:
         return EmitUnaryMir<MAbs>(f, ExprType::I32, def);
       case Expr::I32Neg:
         return EmitUnaryMir<MAsmJSNeg>(f, ExprType::I32, def);
       case Expr::I32Or:
         return EmitBitwise<MBitOr>(f, ExprType::I32, def);
@@ -3054,17 +3056,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
         return EmitSimdOp(f, ExprType::I32x4, SimdOperation::Fn_fromFloat32x4,
                           SimdSign::Unsigned, def);
 
       // Future opcodes
       case Expr::Loop:
       case Expr::Select:
       case Expr::Br:
       case Expr::BrIf:
-      case Expr::I32Ctz:
       case Expr::F32CopySign:
       case Expr::F32Trunc:
       case Expr::F32Nearest:
       case Expr::F64CopySign:
       case Expr::F64Nearest:
       case Expr::F64Trunc:
       case Expr::I32WrapI64:
       case Expr::I64ExtendSI32:
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -46,17 +46,20 @@ function testComparison(type, opcode, lh
                             (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
                             (export "" 1))`)(), expect);
   } else {
     assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
   }
 }
 
 testUnary('i32', 'clz', 40, 26);
-//testUnary('i32', 'ctz', 40, 0); // TODO: NYI
+testUnary('i32', 'ctz', 40, 3);
+testUnary('i32', 'ctz', 0, 32);
+testUnary('i32', 'ctz', -2147483648, 31);
+
 testUnary('i32', 'popcnt', 40, 2);
 testUnary('i32', 'popcnt', 0, 0);
 testUnary('i32', 'popcnt', 0xFFFFFFFF, 32);
 
 testBinary('i32', 'add', 40, 2, 42);
 testBinary('i32', 'sub', 40, 2, 38);
 testBinary('i32', 'mul', 40, 2, 80);
 testBinary('i32', 'div_s', -40, 2, -20);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1389,16 +1389,25 @@ LIRGenerator::visitClz(MClz* ins)
 {
     MDefinition* num = ins->num();
 
     LClzI* lir = new(alloc()) LClzI(useRegisterAtStart(num));
     define(lir, ins);
 }
 
 void
+LIRGenerator::visitCtz(MCtz* ins)
+{
+    MDefinition* num = ins->num();
+
+    LCtzI* lir = new(alloc()) LCtzI(useRegisterAtStart(num));
+    define(lir, ins);
+}
+
+void
 LIRGenerator::visitPopcnt(MPopcnt* ins)
 {
     MDefinition* num = ins->num();
 
     LPopcntI* lir = new(alloc()) LPopcntI(useRegisterAtStart(num), temp());
     define(lir, ins);
 }
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -124,16 +124,17 @@ class LIRGenerator : public LIRGenerator
     void visitRsh(MRsh* ins);
     void visitUrsh(MUrsh* ins);
     void visitFloor(MFloor* ins);
     void visitCeil(MCeil* ins);
     void visitRound(MRound* ins);
     void visitMinMax(MMinMax* ins);
     void visitAbs(MAbs* ins);
     void visitClz(MClz* ins);
+    void visitCtz(MCtz* ins);
     void visitSqrt(MSqrt* ins);
     void visitPopcnt(MPopcnt* ins);
     void visitAtan2(MAtan2* ins);
     void visitHypot(MHypot* ins);
     void visitPow(MPow* ins);
     void visitMathFunction(MMathFunction* ins);
     void visitAdd(MAdd* ins);
     void visitSub(MSub* ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5098,16 +5098,29 @@ MClz::foldsTo(TempAllocator& alloc)
             return MConstant::New(alloc, Int32Value(32));
         return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n)));
     }
 
     return this;
 }
 
 MDefinition*
+MCtz::foldsTo(TempAllocator& alloc)
+{
+    if (num()->isConstant()) {
+        int32_t n = num()->toConstant()->toInt32();
+        if (n == 0)
+            return MConstant::New(alloc, Int32Value(32));
+        return MConstant::New(alloc, Int32Value(mozilla::CountTrailingZeroes32(n)));
+    }
+
+    return this;
+}
+
+MDefinition*
 MPopcnt::foldsTo(TempAllocator& alloc)
 {
     if (num()->isConstant()) {
         int32_t n = num()->toConstant()->toInt32();
         return MConstant::New(alloc, Int32Value(mozilla::CountPopulation32(n)));
     }
 
     return this;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5975,18 +5975,18 @@ class MAbs
     bool canRecoverOnBailout() const override {
         return true;
     }
 
     ALLOW_CLONE(MAbs)
 };
 
 class MClz
-    : public MUnaryInstruction
-    , public BitwisePolicy::Data
+  : public MUnaryInstruction
+  , public BitwisePolicy::Data
 {
     bool operandIsNeverZero_;
 
     explicit MClz(MDefinition* num)
       : MUnaryInstruction(num),
         operandIsNeverZero_(false)
     {
         MOZ_ASSERT(IsNumberType(num->type()));
@@ -6018,19 +6018,63 @@ class MClz
         return operandIsNeverZero_;
     }
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
     void computeRange(TempAllocator& alloc) override;
     void collectRangeInfoPreTrunc() override;
 };
 
+class MCtz
+  : public MUnaryInstruction
+  , public BitwisePolicy::Data
+{
+    bool operandIsNeverZero_;
+
+    explicit MCtz(MDefinition* num)
+      : MUnaryInstruction(num),
+        operandIsNeverZero_(false)
+    {
+        MOZ_ASSERT(IsNumberType(num->type()));
+        specialization_ = MIRType_Int32;
+        setResultType(MIRType_Int32);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(Ctz)
+    static MCtz* New(TempAllocator& alloc, MDefinition* num) {
+        return new(alloc) MCtz(num);
+    }
+    static MCtz* NewAsmJS(TempAllocator& alloc, MDefinition* num) {
+        return new(alloc) MCtz(num);
+    }
+    MDefinition* num() const {
+        return getOperand(0);
+    }
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins);
+    }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    bool operandIsNeverZero() const {
+        return operandIsNeverZero_;
+    }
+
+    MDefinition* foldsTo(TempAllocator& alloc) override;
+    void computeRange(TempAllocator& alloc) override;
+    void collectRangeInfoPreTrunc() override;
+};
+
 class MPopcnt
-    : public MUnaryInstruction
-    , public BitwisePolicy::Data
+  : public MUnaryInstruction
+  , public BitwisePolicy::Data
 {
     explicit MPopcnt(MDefinition* num)
       : MUnaryInstruction(num)
     {
         MOZ_ASSERT(IsNumberType(num->type()));
         specialization_ = MIRType_Int32;
         setResultType(MIRType_Int32);
         setMovable();
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -81,16 +81,17 @@ namespace jit {
     _(BitOr)                                                                \
     _(BitXor)                                                               \
     _(Lsh)                                                                  \
     _(Rsh)                                                                  \
     _(Ursh)                                                                 \
     _(MinMax)                                                               \
     _(Abs)                                                                  \
     _(Clz)                                                                  \
+    _(Ctz)                                                                  \
     _(Popcnt)                                                               \
     _(Sqrt)                                                                 \
     _(Atan2)                                                                \
     _(Hypot)                                                                \
     _(Pow)                                                                  \
     _(PowHalf)                                                              \
     _(Random)                                                               \
     _(MathFunction)                                                         \
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1468,16 +1468,22 @@ MCeil::computeRange(TempAllocator& alloc
 
 void
 MClz::computeRange(TempAllocator& alloc)
 {
     setRange(Range::NewUInt32Range(alloc, 0, 32));
 }
 
 void
+MCtz::computeRange(TempAllocator& alloc)
+{
+    setRange(Range::NewUInt32Range(alloc, 0, 32));
+}
+
+void
 MPopcnt::computeRange(TempAllocator& alloc)
 {
     setRange(Range::NewUInt32Range(alloc, 0, 32));
 }
 
 void
 MMinMax::computeRange(TempAllocator& alloc)
 {
@@ -3214,16 +3220,24 @@ void
 MClz::collectRangeInfoPreTrunc()
 {
     Range inputRange(input());
     if (!inputRange.canBeZero())
         operandIsNeverZero_ = true;
 }
 
 void
+MCtz::collectRangeInfoPreTrunc()
+{
+    Range inputRange(input());
+    if (!inputRange.canBeZero())
+        operandIsNeverZero_ = true;
+}
+
+void
 MDiv::collectRangeInfoPreTrunc()
 {
     Range lhsRange(lhs());
     Range rhsRange(rhs());
 
     // Test if Dividend is non-negative.
     if (lhsRange.isFiniteNonNegative())
         canBeNegativeDividend_ = false;
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -950,16 +950,29 @@ CodeGeneratorARM::visitClzI(LClzI* ins)
 {
     Register input = ToRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     masm.ma_clz(input, output);
 }
 
 void
+CodeGeneratorARM::visitCtzI(LCtzI* ins)
+{
+    Register input = ToRegister(ins->input());
+    Register output = ToRegister(ins->output());
+    ScratchRegisterScope scratch(masm);
+
+    masm.ma_rsb(input, Imm32(0), scratch, SetCC);
+    masm.ma_and(input, scratch, input);
+    masm.ma_clz(input, output);
+    masm.ma_rsb(input, Imm32(0x1f), output, LeaveCC, Assembler::NotEqual);
+}
+
+void
 CodeGeneratorARM::visitPopcntI(LPopcntI* ins)
 {
     Register input = ToRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // Equivalent to GCC output of mozilla::CountPopulation32()
     Register tmp = ToRegister(ins->temp());
 
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -118,16 +118,17 @@ class CodeGeneratorARM : public CodeGene
     virtual void visitSoftModI(LSoftModI* ins);
     virtual void visitModPowTwoI(LModPowTwoI* ins);
     virtual void visitModMaskI(LModMaskI* ins);
     virtual void visitPowHalfD(LPowHalfD* ins);
     virtual void visitShiftI(LShiftI* ins);
     virtual void visitUrshD(LUrshD* ins);
 
     virtual void visitClzI(LClzI* ins);
+    virtual void visitCtzI(LCtzI* ins);
     virtual void visitPopcntI(LPopcntI* ins);
 
     virtual void visitTestIAndBranch(LTestIAndBranch* test);
     virtual void visitCompare(LCompare* comp);
     virtual void visitCompareAndBranch(LCompareAndBranch* comp);
     virtual void visitTestDAndBranch(LTestDAndBranch* test);
     virtual void visitTestFAndBranch(LTestFAndBranch* test);
     virtual void visitCompareD(LCompareD* comp);
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -348,16 +348,22 @@ CodeGeneratorARM64::visitRoundF(LRoundF*
 
 void
 CodeGeneratorARM64::visitClzI(LClzI* lir)
 {
     MOZ_CRASH("visitClzI");
 }
 
 void
+CodeGeneratorARM64::visitCtzI(LCtzI* lir)
+{
+    MOZ_CRASH("visitCtzI");
+}
+
+void
 CodeGeneratorARM64::emitRoundDouble(FloatRegister src, Register dest, Label* fail)
 {
     MOZ_CRASH("CodeGeneratorARM64::emitRoundDouble");
 }
 
 void
 CodeGeneratorARM64::visitTruncateDToInt32(LTruncateDToInt32* ins)
 {
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -158,16 +158,17 @@ class CodeGeneratorARM64 : public CodeGe
     virtual void visitCeil(LCeil* lir);
     virtual void visitCeilF(LCeilF* lir);
     virtual void visitRound(LRound* lir);
     virtual void visitRoundF(LRoundF* lir);
     virtual void visitTruncateDToInt32(LTruncateDToInt32* ins);
     virtual void visitTruncateFToInt32(LTruncateFToInt32* ins);
 
     virtual void visitClzI(LClzI* lir);
+    virtual void visitCtzI(LCtzI* lir);
     // Out of line visitors.
     void visitOutOfLineBailout(OutOfLineBailout* ool);
     void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
 
   protected:
     ValueOperand ToValue(LInstruction* ins, size_t pos);
     ValueOperand ToOutValue(LInstruction* ins);
     ValueOperand ToTempValue(LInstruction* ins, size_t pos);
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3203,16 +3203,30 @@ class LClzI : public LInstructionHelper<
         setOperand(0, num);
     }
 
     MClz* mir() const {
         return mir_->toClz();
     }
 };
 
+// Count trailing zeroes
+class LCtzI : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(CtzI)
+    explicit LCtzI(const LAllocation& num) {
+        setOperand(0, num);
+    }
+
+    MCtz* mir() const {
+        return mir_->toCtz();
+    }
+};
+
 // Count population
 class LPopcntI : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(PopcntI)
     explicit LPopcntI(const LAllocation& num, const LDefinition& temp) {
         setOperand(0, num);
         setTemp(0, temp);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -137,16 +137,17 @@
     _(MinMaxF)                      \
     _(NegI)                         \
     _(NegD)                         \
     _(NegF)                         \
     _(AbsI)                         \
     _(AbsD)                         \
     _(AbsF)                         \
     _(ClzI)                         \
+    _(CtzI)                         \
     _(PopcntI)                      \
     _(SqrtD)                        \
     _(SqrtF)                        \
     _(Atan2D)                       \
     _(Hypot)                        \
     _(PowI)                         \
     _(PowD)                         \
     _(PowHalfD)                     \
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -1543,16 +1543,19 @@ class AssemblerX86Shared : public Assemb
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
     void bsr(const Register& src, const Register& dest) {
         masm.bsr_rr(src.encoding(), dest.encoding());
     }
+    void bsf(const Register& src, const Register& dest) {
+        masm.bsf_rr(src.encoding(), dest.encoding());
+    }
     void popcnt(const Register& src, const Register& dest) {
         masm.popcnt_rr(src.encoding(), dest.encoding());
     }
     void imull(Register multiplier) {
         masm.imull_r(multiplier.encoding());
     }
     void umull(Register multiplier) {
         masm.mull_r(multiplier.encoding());
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -1280,16 +1280,22 @@ public:
     }
 
     void bsr_rr(RegisterID src, RegisterID dst)
     {
         spew("bsr        %s, %s", GPReg32Name(src), GPReg32Name(dst));
         m_formatter.twoByteOp(OP2_BSR_GvEv, src, dst);
     }
 
+    void bsf_rr(RegisterID src, RegisterID dst)
+    {
+        spew("bsf        %s, %s", GPReg32Name(src), GPReg32Name(dst));
+        m_formatter.twoByteOp(OP2_BSF_GvEv, src, dst);
+    }
+
     void popcnt_rr(RegisterID src, RegisterID dst)
     {
         spew("popcnt     %s, %s", GPReg32Name(src), GPReg32Name(dst));
         m_formatter.legacySSEPrefix(VEX_SS);
         m_formatter.twoByteOp(OP2_POPCNT_GvEv, src, dst);
     }
 
     void imull_rr(RegisterID src, RegisterID dst)
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -720,16 +720,36 @@ CodeGeneratorX86Shared::visitClzI(LClzI*
 
     masm.bind(&nonzero);
     masm.bsr(input, output);
     masm.xor32(Imm32(0x1F), output);
     masm.bind(&done);
 }
 
 void
+CodeGeneratorX86Shared::visitCtzI(LCtzI* ins)
+{
+    Register input = ToRegister(ins->input());
+    Register output = ToRegister(ins->output());
+
+    // bsf is undefined on 0
+    Label done, nonzero;
+    if (!ins->mir()->operandIsNeverZero()) {
+        masm.test32(input, input);
+        masm.j(Assembler::NonZero, &nonzero);
+        masm.move32(Imm32(32), output);
+        masm.jump(&done);
+    }
+
+    masm.bind(&nonzero);
+    masm.bsf(input, output);
+    masm.bind(&done);
+}
+
+void
 CodeGeneratorX86Shared::visitPopcntI(LPopcntI* ins)
 {
     Register input = ToRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     if (AssemblerX86Shared::HasPOPCNT()) {
         masm.popcnt(input, output);
         return;
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -211,16 +211,17 @@ class CodeGeneratorX86Shared : public Co
     // Instruction visitors.
     virtual void visitDouble(LDouble* ins);
     virtual void visitFloat32(LFloat32* ins);
     virtual void visitMinMaxD(LMinMaxD* ins);
     virtual void visitMinMaxF(LMinMaxF* ins);
     virtual void visitAbsD(LAbsD* ins);
     virtual void visitAbsF(LAbsF* ins);
     virtual void visitClzI(LClzI* ins);
+    virtual void visitCtzI(LCtzI* ins);
     virtual void visitPopcntI(LPopcntI* ins);
     virtual void visitSqrtD(LSqrtD* ins);
     virtual void visitSqrtF(LSqrtF* ins);
     virtual void visitPowHalfD(LPowHalfD* ins);
     virtual void visitAddI(LAddI* ins);
     virtual void visitSubI(LSubI* ins);
     virtual void visitMulI(LMulI* ins);
     virtual void visitDivI(LDivI* ins);
--- a/js/src/jit/x86-shared/Encoding-x86-shared.h
+++ b/js/src/jit/x86-shared/Encoding-x86-shared.h
@@ -197,16 +197,17 @@ enum TwoByteOpcodeID {
     OP_SETCC            = 0x90,
     OP2_SHLD            = 0xA4,
     OP2_SHRD            = 0xAC,
     OP_FENCE            = 0xAE,
     OP2_IMUL_GvEv       = 0xAF,
     OP2_CMPXCHG_GvEb    = 0xB0,
     OP2_CMPXCHG_GvEw    = 0xB1,
     OP2_POPCNT_GvEv     = 0xB8,
+    OP2_BSF_GvEv        = 0xBC,
     OP2_BSR_GvEv        = 0xBD,
     OP2_MOVSX_GvEb      = 0xBE,
     OP2_MOVSX_GvEw      = 0xBF,
     OP2_MOVZX_GvEb      = 0xB6,
     OP2_MOVZX_GvEw      = 0xB7,
     OP2_XADD_EbGb       = 0xC0,
     OP2_XADD_EvGv       = 0xC1,
     OP2_CMPPS_VpsWps    = 0xC2,