[PATCH] D50168: [Builtins] Implement __builtin_clrsb to be compatible with gcc

Craig Topper via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 1 18:25:22 PDT 2018

craig.topper created this revision.
craig.topper added reviewers: bkramer, efriedma, spatel.
Herald added a reviewer: javed.absar.
Herald added a subscriber: kristof.beyls.

gcc defines an intrinsic called __builtin_clrsb which counts the number of extra sign bits on a number. This is equivalent to counting the number of leading zeros on a positive number or the number of leading ones on a negative number and subtracting one from the result. Since we can't count leading ones we need to invert negative numbers to count zeros.

The emitted sequence contains a bit of trickery stolen from an LLVM AArch64 test arm64-clrsb.ll to prevent passing a value of 0 to ctlz. I used a icmp slt and a select to conditionally negate, but InstCombine will turn that into an ashr+xor. I can emit that directly if that's prefered. I know @spatel has been trying to remove some of the bit tricks from InstCombine so I'm not sure if the ashr+xor form will be canonical going forward.

This patch will cause the builtin to be expanded inline while gcc uses a call to a function like __clrsbdi2 that is implemented in libgcc. But this is similar to what we already do for popcnt. And I don't think compiler-rt supports __clrsbdi2.



Index: lib/CodeGen/CGBuiltin.cpp
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -1537,6 +1537,34 @@
     return RValue::get(ComplexVal.second);
+  case Builtin::BI__builtin_clrsb:
+  case Builtin::BI__builtin_clrsbl:
+  case Builtin::BI__builtin_clrsbll: {
+    // clrsb(x) -> clz(x < 0 ? ~x : x) - 1 or
+    //          -> clz(((x < 0 ? ~x : x) << 1) | 1)
+    Value *ArgValue = EmitScalarExpr(E->getArg(0));
+    llvm::Type *ArgType = ArgValue->getType();
+    Value *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType);
+    llvm::Type *ResultType = ConvertType(E->getType());
+    Value *Zero = llvm::Constant::getNullValue(ArgType);
+    Value *IsNeg = Builder.CreateICmpSLT(ArgValue, Zero, "isneg");
+    Value *Inverse = Builder.CreateNot(ArgValue, "not");
+    Value *Tmp = Builder.CreateSelect(IsNeg, Inverse, ArgValue);
+    // Now we need to calculate ctlz(Tmp)-1, but Tmp might be zero. We know
+    // the sign bit is zero, so we can shift it out. Then put a 1 in the LSB.
+    // This removes one leading zero like the subtract does, and replaces it
+    // with a guaranteed one to prevent the value being 0.
+    Value *One = llvm::ConstantInt::get(ArgType, 1);
+    Tmp = Builder.CreateShl(Tmp, One);
+    Tmp = Builder.CreateOr(Tmp, One);
+    Value *Result = Builder.CreateCall(F, {Tmp, Builder.getTrue()});
+    if (Result->getType() != ResultType)
+      Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
+                                     "cast");
+    return RValue::get(Result);
+  }
   case Builtin::BI__builtin_ctzs:
   case Builtin::BI__builtin_ctz:
   case Builtin::BI__builtin_ctzl:
Index: include/clang/Basic/Builtins.def
--- include/clang/Basic/Builtins.def
+++ include/clang/Basic/Builtins.def
@@ -413,6 +413,9 @@
 BUILTIN(__builtin_popcount  , "iUi"  , "nc")
 BUILTIN(__builtin_popcountl , "iULi" , "nc")
 BUILTIN(__builtin_popcountll, "iULLi", "nc")
+BUILTIN(__builtin_clrsb  , "ii"  , "nc")
+BUILTIN(__builtin_clrsbl , "iLi" , "nc")
+BUILTIN(__builtin_clrsbll, "iLLi", "nc")
 // FIXME: These type signatures are not correct for targets with int != 32-bits
 // or with ULL != 64-bits.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: D50168.158674.patch
Type: text/x-patch
Size: 2301 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20180802/c07fd3b0/attachment.bin>

More information about the cfe-commits mailing list