Author: Sirui Mu Date: 2025-07-26T00:07:36+08:00 New Revision: 2b3ca68401ef74c75d6d5e9cc2957cdbdab2a22d
URL: https://github.com/llvm/llvm-project/commit/2b3ca68401ef74c75d6d5e9cc2957cdbdab2a22d DIFF: https://github.com/llvm/llvm-project/commit/2b3ca68401ef74c75d6d5e9cc2957cdbdab2a22d.diff LOG: [CIR] Add folders for bit manipulation operations (#150235) This patch adds folders for the bit manipulation operations, namely: `clrsb`, `clz`, `ctz`, `parity`, `popcount`, `bitreverse`, `byte_swap`, and `rotate`. Added: clang/test/CIR/Transforms/bit.cir Modified: clang/include/clang/CIR/Dialect/IR/CIROps.td clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 32bb9009aeec9..e2ddbd12c77bd 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2817,6 +2817,8 @@ class CIR_BitOpBase<string mnemonic, TypeConstraint operandTy> let assemblyFormat = [{ $input `:` type($result) attr-dict }]; + + let hasFolder = 1; } class CIR_BitZeroCountOpBase<string mnemonic, TypeConstraint operandTy> @@ -3024,6 +3026,8 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> { bool isRotateLeft() { return getRotateLeft(); } bool isRotateRight() { return !getRotateLeft(); } }]; + + let hasFolder = 1; } //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index e1a5c3d9ca337..d17c85f89d589 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -287,6 +287,7 @@ struct MissingFeatures { // Future CIR attributes static bool optInfoAttr() { return false; } + static bool poisonAttr() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index cd77166622fac..9c36a43c663cf 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -17,6 +17,7 @@ #include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/FunctionImplementation.h" +#include "mlir/Support/LLVM.h" #include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc" #include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc" @@ -2230,6 +2231,126 @@ LogicalResult cir::ComplexImagPtrOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// Bit manipulation operations +//===----------------------------------------------------------------------===// + +static OpFoldResult +foldUnaryBitOp(mlir::Attribute inputAttr, + llvm::function_ref<llvm::APInt(const llvm::APInt &)> func, + bool poisonZero = false) { + auto input = mlir::dyn_cast_if_present<IntAttr>(inputAttr); + if (!input) + return nullptr; + + llvm::APInt inputValue = input.getValue(); + if (poisonZero && inputValue.isZero()) { + // TODO(cir): maybe we should return a poison value here? + assert(!MissingFeatures::poisonAttr()); + return nullptr; + } + + llvm::APInt resultValue = func(inputValue); + return IntAttr::get(input.getType(), resultValue); +} + +OpFoldResult BitClrsbOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + unsigned resultValue = + inputValue.getBitWidth() - inputValue.getSignificantBits(); + return llvm::APInt(inputValue.getBitWidth(), resultValue); + }); +} + +OpFoldResult BitClzOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp( + adaptor.getInput(), + [](const llvm::APInt &inputValue) { + unsigned resultValue = inputValue.countLeadingZeros(); + return llvm::APInt(inputValue.getBitWidth(), resultValue); + }, + getPoisonZero()); +} + +OpFoldResult BitCtzOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp( + adaptor.getInput(), + [](const llvm::APInt &inputValue) { + return llvm::APInt(inputValue.getBitWidth(), + inputValue.countTrailingZeros()); + }, + getPoisonZero()); +} + +OpFoldResult BitParityOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount() % 2); + }); +} + +OpFoldResult BitPopcountOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount()); + }); +} + +OpFoldResult BitReverseOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return inputValue.reverseBits(); + }); +} + +OpFoldResult ByteSwapOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return inputValue.byteSwap(); + }); +} + +OpFoldResult RotateOp::fold(FoldAdaptor adaptor) { + auto input = mlir::dyn_cast_if_present<IntAttr>(adaptor.getInput()); + auto amount = mlir::dyn_cast_if_present<IntAttr>(adaptor.getAmount()); + if (!input && !amount) + return nullptr; + + // We could fold cir.rotate even if one of its two operands is not a constant: + // - `cir.rotate left/right %0, 0` could be folded into just %0 even if %0 + // is not a constant. + // - `cir.rotate left/right 0/0b111...111, %0` could be folded into 0 or + // 0b111...111 even if %0 is not a constant. + + llvm::APInt inputValue; + if (input) { + inputValue = input.getValue(); + if (inputValue.isZero() || inputValue.isAllOnes()) { + // An input value of all 0s or all 1s will not change after rotation + return input; + } + } + + uint64_t amountValue; + if (amount) { + amountValue = amount.getValue().urem(getInput().getType().getWidth()); + if (amountValue == 0) { + // A shift amount of 0 will not change the input value + return getInput(); + } + } + + if (!input || !amount) + return nullptr; + + assert(inputValue.getBitWidth() == getInput().getType().getWidth() && + "input value must have the same bit width as the input type"); + + llvm::APInt resultValue; + if (isRotateLeft()) + resultValue = inputValue.rotl(amountValue); + else + resultValue = inputValue.rotr(amountValue); + + return IntAttr::get(input.getContext(), input.getType(), resultValue); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp index e505db50d3609..2143f167ba2c8 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -143,7 +143,8 @@ void CIRCanonicalizePass::runOnOperation() { if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp, ComplexCreateOp, ComplexImagOp, ComplexRealOp, VecCmpOp, VecCreateOp, VecExtractOp, VecShuffleOp, VecShuffleDynamicOp, - VecTernaryOp>(op)) + VecTernaryOp, BitClrsbOp, BitClzOp, BitCtzOp, BitParityOp, + BitPopcountOp, BitReverseOp, ByteSwapOp, RotateOp>(op)) ops.push_back(op); }); diff --git a/clang/test/CIR/Transforms/bit.cir b/clang/test/CIR/Transforms/bit.cir new file mode 100644 index 0000000000000..c85b05de4d9ea --- /dev/null +++ b/clang/test/CIR/Transforms/bit.cir @@ -0,0 +1,141 @@ +// RUN: cir-opt -cir-canonicalize -cir-simplify -o %t.cir %s +// RUN: FileCheck --input-file=%t.cir %s + +!s32i = !cir.int<s, 32> +!u32i = !cir.int<u, 32> + +module { + cir.func @fold_clrsb() -> !s32i { + %0 = cir.const #cir.int<1> : !s32i + %1 = cir.clrsb %0 : !s32i + cir.return %1 : !s32i + } + // CHECK-LABEL: @fold_clrsb + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<30> : !s32i + // CHECK-NEXT: cir.return %[[R]] : !s32i + // CHECK-NEXT: } + + cir.func @fold_clz() -> !u32i { + %0 = cir.const #cir.int<1> : !u32i + %1 = cir.clz %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_clz + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<31> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_ctz() -> !u32i { + %0 = cir.const #cir.int<2> : !u32i + %1 = cir.ctz %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_ctz + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<1> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_parity() -> !u32i { + // 0xdeadbeef is 0b1101_1110_1010_1101_1011_1110_1110_1111 + // 0xdeadbeef contains 24 ones + %0 = cir.const #cir.int<0xdeadbeef> : !u32i + %1 = cir.parity %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_parity + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<0> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_popcount() -> !u32i { + // 0xdeadbeef is 0b1101_1110_1010_1101_1011_1110_1110_1111 + // 0xdeadbeef contains 24 ones + %0 = cir.const #cir.int<0xdeadbeef> : !u32i + %1 = cir.popcount %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_popcount + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<24> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_bitreverse() -> !u32i { + // 0xdeadbeef is 0b1101_1110_1010_1101_1011_1110_1110_1111 + %0 = cir.const #cir.int<0xdeadbeef> : !u32i + %1 = cir.bitreverse %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_bitreverse + // 4152210811 is 0b1111_0111_0111_1101_1011_0101_0111_1011 + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<4152210811> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_byte_swap() -> !u32i { + %0 = cir.const #cir.int<0xdeadbeef> : !u32i + %1 = cir.byte_swap %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_byte_swap + // 4022250974 is 0xefbeadde + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<4022250974> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_rotate_input_all_zeros(%arg0 : !u32i) -> !u32i { + %0 = cir.const #cir.int<0> : !u32i + %1 = cir.rotate left %0, %arg0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_rotate_input_all_zeros + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<0> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_rotate_input_all_ones(%arg0 : !u32i) -> !u32i { + // 4294967295 is 0b1111_1111_1111_1111_1111_1111_1111_1111 + %0 = cir.const #cir.int<4294967295> : !u32i + %1 = cir.rotate left %0, %arg0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_rotate_input_all_ones + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<4294967295> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_rotate_zero_amount(%arg0 : !u32i) -> !u32i { + %0 = cir.const #cir.int<32> : !u32i + %1 = cir.rotate left %arg0, %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_rotate_zero_amount + // CHECK-SAME: (%[[R:.+]]: !u32i) + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_rotate_left() -> !u32i { + // 0xdeadbeef is 0b1101_1110_1010_1101_1011_1110_1110_1111 + %0 = cir.const #cir.int<0xdeadbeef> : !u32i + %1 = cir.const #cir.int<8> : !u32i + %2 = cir.rotate left %0, %1 : !u32i + cir.return %2 : !u32i + } + // CHECK-LABEL: @fold_rotate_left + // 2914971614 is 0b1010_1101_1011_1110_1110_1111_1101_1110 + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<2914971614> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_rotate_right() -> !u32i { + // 0xdeadbeef is 0b1101_1110_1010_1101_1011_1110_1110_1111 + %0 = cir.const #cir.int<0xdeadbeef> : !u32i + %1 = cir.const #cir.int<8> : !u32i + %2 = cir.rotate right %0, %1 : !u32i + cir.return %2 : !u32i + } + // CHECK-LABEL: @fold_rotate_right + // 4260027374 is 0b1110_1111_1101_1110_1010_1101_1011_1110 + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<4024348094> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits