[clang] [clang]Implement the c23 stdc bit builtins (PR #185978)
Eli Friedman via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 16 11:35:27 PDT 2026
================
@@ -3733,6 +3790,99 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI_rotr64:
return emitRotate(E, true);
+ case Builtin::BIstdc_leading_zeros:
+ case Builtin::BI__builtin_stdc_leading_zeros:
+ return emitStdcCountIntrinsic(E, Intrinsic::ctlz, /*InvertArg=*/false);
+ case Builtin::BIstdc_leading_ones:
+ case Builtin::BI__builtin_stdc_leading_ones:
+ return emitStdcCountIntrinsic(E, Intrinsic::ctlz, /*InvertArg=*/true);
+ case Builtin::BIstdc_trailing_zeros:
+ case Builtin::BI__builtin_stdc_trailing_zeros:
+ return emitStdcCountIntrinsic(E, Intrinsic::cttz, /*InvertArg=*/false);
+ case Builtin::BIstdc_trailing_ones:
+ case Builtin::BI__builtin_stdc_trailing_ones:
+ return emitStdcCountIntrinsic(E, Intrinsic::cttz, /*InvertArg=*/true);
+ case Builtin::BIstdc_first_leading_zero:
+ case Builtin::BI__builtin_stdc_first_leading_zero:
+ return emitStdcFirstBit(E, Intrinsic::ctlz, /*InvertArg=*/true);
+ case Builtin::BIstdc_first_leading_one:
+ case Builtin::BI__builtin_stdc_first_leading_one:
+ return emitStdcFirstBit(E, Intrinsic::ctlz, /*InvertArg=*/false);
+ case Builtin::BIstdc_first_trailing_zero:
+ case Builtin::BI__builtin_stdc_first_trailing_zero:
+ return emitStdcFirstBit(E, Intrinsic::cttz, /*InvertArg=*/true);
+ case Builtin::BIstdc_first_trailing_one:
+ case Builtin::BI__builtin_stdc_first_trailing_one:
+ return emitStdcFirstBit(E, Intrinsic::cttz, /*InvertArg=*/false);
+ case Builtin::BIstdc_count_zeros:
+ case Builtin::BI__builtin_stdc_count_zeros:
+ return emitStdcBitWidthMinus(E, Intrinsic::ctpop, /*IsPop=*/true);
+ case Builtin::BIstdc_count_ones:
+ case Builtin::BI__builtin_stdc_count_ones:
+ return emitStdcCountIntrinsic(E, Intrinsic::ctpop, /*InvertArg=*/false,
+ /*IsPop=*/true);
+ case Builtin::BIstdc_has_single_bit:
+ case Builtin::BI__builtin_stdc_has_single_bit: {
+ Value *ArgValue = EmitScalarExpr(E->getArg(0));
+ llvm::Type *ArgType = ArgValue->getType();
+ Value *One = ConstantInt::get(ArgType, 1);
+ Function *F = CGM.getIntrinsic(Intrinsic::ctpop, ArgType);
+ Value *PopCnt = Builder.CreateCall(F, ArgValue);
+ return RValue::get(Builder.CreateICmpEQ(PopCnt, One));
+ }
+ case Builtin::BIstdc_bit_width:
+ case Builtin::BI__builtin_stdc_bit_width:
+ return emitStdcBitWidthMinus(E, Intrinsic::ctlz, /*IsPop=*/false);
+ case Builtin::BIstdc_bit_floor:
+ case Builtin::BI__builtin_stdc_bit_floor: {
+ Value *ArgValue = EmitScalarExpr(E->getArg(0));
+ llvm::Type *ArgType = ArgValue->getType();
+ unsigned BitWidth = ArgType->getIntegerBitWidth();
+ Value *Zero = ConstantInt::get(ArgType, 0);
+ Value *One = ConstantInt::get(ArgType, 1);
+ Function *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType);
+ Value *LZ = Builder.CreateCall(F, {ArgValue, Builder.getTrue()});
+ Value *ShiftAmt =
+ Builder.CreateSub(ConstantInt::get(ArgType, BitWidth - 1), LZ);
+ Value *Shifted = Builder.CreateShl(One, ShiftAmt);
+ Value *IsZero = Builder.CreateICmpEQ(ArgValue, Zero);
+ Value *Result = Builder.CreateSelect(IsZero, Zero, Shifted);
+ return RValue::get(Result);
+ }
+ case Builtin::BIstdc_bit_ceil:
+ case Builtin::BI__builtin_stdc_bit_ceil: {
+ Value *ArgValue = EmitScalarExpr(E->getArg(0));
+ llvm::Type *ArgType = ArgValue->getType();
+ unsigned BitWidth = ArgType->getIntegerBitWidth();
+ Value *Zero = ConstantInt::get(ArgType, 0);
+ Value *One = ConstantInt::get(ArgType, 1);
+
+ Value *IsLEOne = Builder.CreateICmpULE(ArgValue, One, "isleone");
+
+ BasicBlock *EntryBB = Builder.GetInsertBlock();
+ BasicBlock *CalcBB = createBasicBlock("bitceil.calc", CurFn);
+ BasicBlock *MergeBB = createBasicBlock("bitceil.merge", CurFn);
+
+ Builder.CreateCondBr(IsLEOne, MergeBB, CalcBB);
+
+ Builder.SetInsertPoint(CalcBB);
+ Function *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType);
+ Value *ArgMinusOne = Builder.CreateSub(ArgValue, One);
+ Value *LZ = Builder.CreateCall(F, {ArgMinusOne, Builder.getFalse()});
+ Value *LZIsZero = Builder.CreateICmpEQ(LZ, Zero, "lzzero");
+ Value *ShiftAmt =
+ Builder.CreateSub(ConstantInt::get(ArgType, BitWidth), LZ);
+ Value *Tmp = Builder.CreateShl(One, ShiftAmt);
----------------
efriedma-quic wrote:
gcc uses a slightly different sequence, instead of `1<<ShiftAmt`, they do `2<<ShiftAmt`, and subtract one from the shift amount. This allows handling the overflow case without a conditional.
We might also want to add some backend test coverage for stdc_bit_floor and stdc_bit_ceil, so we can track how well optimized the assembly is. I think we have okay coverage for the sequences used for the other bit instructions, but I don't think we have coverage for floor and ceil specifically.
https://github.com/llvm/llvm-project/pull/185978
More information about the cfe-commits
mailing list