-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[CIR] Add bit reverse and byte reverse operations #147200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Sirui Mu (Lancern) ChangesThis patch adds support for the following two builtin functions:
Full diff: https://github.com/llvm/llvm-project/pull/147200.diff 5 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 6529f1386599c..e26942d5cf511 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2661,6 +2661,55 @@ def BitPopcountOp : CIR_BitOpBase<"bit.popcnt",
}];
}
+def BitReverseOp : CIR_BitOpBase<"bit.reverse", CIR_UIntOfWidths<[8, 16, 32, 64]>> {
+ let summary = "Reverse the bit pattern of the operand integer";
+ let description = [{
+ The `cir.bit.reverse` operation reverses the bits of the operand integer.
+ Its only argument must be of unsigned integer types of width 8, 16, 32, or
+ 64.
+
+ This operation covers the C/C++ builtin function `__builtin_bitreverse`.
+
+ Example:
+
+ ```mlir
+ %1 = cir.bit.reverse %0 : !u32i
+ ```
+ }];
+}
+
+//===----------------------------------------------------------------------===//
+// ByteswapOp
+//===----------------------------------------------------------------------===//
+
+def ByteSwapOp : CIR_Op<"bswap", [Pure, SameOperandsAndResultType]> {
+ let summary = "Reverse the bytes in the object representation of the operand";
+ let description = [{
+ The `cir.bswap` operation takes an integer as operand, reverse the bytes in
+ the object representation of the operand integer, and returns the result.
+
+ The operand integer must be an unsigned integer. Its widths must be either
+ 16, 32, or 64.
+
+ Example:
+
+ ```mlir
+ // %0 = 0x12345678
+ %0 = cir.const #cir.int<305419896> : !u32i
+
+ // %1 should be 0x78563412
+ %1 = cir.bswap(%0 : !u32i) : !u32i
+ ```
+ }];
+
+ let results = (outs CIR_IntType:$result);
+ let arguments = (ins CIR_UIntOfWidths<[16, 32, 64]>:$input);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` `:` type($result) attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Assume Operations
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fb046533a91b8..6c90bb7975630 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -190,6 +190,26 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
expectedValue, probAttr);
return RValue::get(result);
}
+
+ case Builtin::BI__builtin_bswap16:
+ case Builtin::BI__builtin_bswap32:
+ case Builtin::BI__builtin_bswap64:
+ case Builtin::BI_byteswap_ushort:
+ case Builtin::BI_byteswap_ulong:
+ case Builtin::BI_byteswap_uint64: {
+ mlir::Value arg = emitScalarExpr(e->getArg(0));
+ return RValue::get(
+ builder.create<cir::ByteSwapOp>(getLoc(e->getSourceRange()), arg));
+ }
+
+ case Builtin::BI__builtin_bitreverse8:
+ case Builtin::BI__builtin_bitreverse16:
+ case Builtin::BI__builtin_bitreverse32:
+ case Builtin::BI__builtin_bitreverse64: {
+ mlir::Value arg = emitScalarExpr(e->getArg(0));
+ return RValue::get(
+ builder.create<cir::BitReverseOp>(getLoc(e->getSourceRange()), arg));
+ }
}
cgm.errorNYI(e->getSourceRange(), "unimplemented builtin call");
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5ac42b6a63b09..acbaae1d227d0 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -535,6 +535,13 @@ mlir::LogicalResult CIRToLLVMBitPopcountOpLowering::matchAndRewrite(
return mlir::LogicalResult::success();
}
+mlir::LogicalResult CIRToLLVMBitReverseOpLowering::matchAndRewrite(
+ cir::BitReverseOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ rewriter.replaceOpWithNewOp<mlir::LLVM::BitReverseOp>(op, adaptor.getInput());
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
cir::BrCondOp brOp, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -551,6 +558,13 @@ mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMByteSwapOpLowering::matchAndRewrite(
+ cir::ByteSwapOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ rewriter.replaceOpWithNewOp<mlir::LLVM::ByteSwapOp>(op, adaptor.getInput());
+ return mlir::LogicalResult::success();
+}
+
mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const {
return getTypeConverter()->convertType(ty);
}
@@ -2035,8 +2049,10 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMBitCtzOpLowering,
CIRToLLVMBitParityOpLowering,
CIRToLLVMBitPopcountOpLowering,
+ CIRToLLVMBitReverseOpLowering,
CIRToLLVMBrCondOpLowering,
CIRToLLVMBrOpLowering,
+ CIRToLLVMByteSwapOpLowering,
CIRToLLVMCallOpLowering,
CIRToLLVMCmpOpLowering,
CIRToLLVMComplexCreateOpLowering,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index d9fb91066317b..83e692d6dcbac 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -94,6 +94,16 @@ class CIRToLLVMBitPopcountOpLowering
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMBitReverseOpLowering
+ : public mlir::OpConversionPattern<cir::BitReverseOp> {
+public:
+ using mlir::OpConversionPattern<cir::BitReverseOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::BitReverseOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMBrCondOpLowering
: public mlir::OpConversionPattern<cir::BrCondOp> {
public:
@@ -104,6 +114,16 @@ class CIRToLLVMBrCondOpLowering
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMByteSwapOpLowering
+ : public mlir::OpConversionPattern<cir::ByteSwapOp> {
+public:
+ using mlir::OpConversionPattern<cir::ByteSwapOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::ByteSwapOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {
mlir::DataLayout const &dataLayout;
diff --git a/clang/test/CIR/CodeGen/builtin_bit.cpp b/clang/test/CIR/CodeGen/builtin_bit.cpp
index ba56c91ce7401..55377ce3b80fa 100644
--- a/clang/test/CIR/CodeGen/builtin_bit.cpp
+++ b/clang/test/CIR/CodeGen/builtin_bit.cpp
@@ -325,3 +325,94 @@ int test_builtin_popcountg(unsigned x) {
// OGCG-LABEL: _Z22test_builtin_popcountgj
// OGCG: %{{.+}} = call i32 @llvm.ctpop.i32(i32 %{{.+}})
+
+unsigned char test_builtin_bitreverse8(unsigned char x) {
+ return __builtin_bitreverse8(x);
+}
+
+// CIR-LABEL: @_Z24test_builtin_bitreverse8h
+// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u8i) : !u8i
+
+// LLVM-LABEL: @_Z24test_builtin_bitreverse8h
+// LLVM: %{{.+}} = call i8 @llvm.bitreverse.i8(i8 %{{.+}})
+
+// OGCG-LABEL: @_Z24test_builtin_bitreverse8h
+// OGCG: %{{.+}} = call i8 @llvm.bitreverse.i8(i8 %{{.+}})
+
+unsigned short test_builtin_bitreverse16(unsigned short x) {
+ return __builtin_bitreverse16(x);
+}
+
+// CIR-LABEL: @_Z25test_builtin_bitreverse16t
+// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u16i) : !u16i
+
+// LLVM-LABEL: @_Z25test_builtin_bitreverse16t
+// LLVM: %{{.+}} = call i16 @llvm.bitreverse.i16(i16 %{{.+}})
+
+// OGCG-LABEL: @_Z25test_builtin_bitreverse16t
+// OGCG: %{{.+}} = call i16 @llvm.bitreverse.i16(i16 %{{.+}})
+
+unsigned test_builtin_bitreverse32(unsigned x) {
+ return __builtin_bitreverse32(x);
+}
+
+// CIR-LABEL: @_Z25test_builtin_bitreverse32j
+// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u32i) : !u32i
+
+// LLVM-LABEL: @_Z25test_builtin_bitreverse32j
+// LLVM: %{{.+}} = call i32 @llvm.bitreverse.i32(i32 %{{.+}})
+
+// OGCG-LABEL: @_Z25test_builtin_bitreverse32j
+// OGCG: %{{.+}} = call i32 @llvm.bitreverse.i32(i32 %{{.+}})
+
+unsigned long long test_builtin_bitreverse64(unsigned long long x) {
+ return __builtin_bitreverse64(x);
+}
+
+// CIR-LABEL: @_Z25test_builtin_bitreverse64y
+// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u64i) : !u64i
+
+// LLVM-LABEL: @_Z25test_builtin_bitreverse64y
+// LLVM: %{{.+}} = call i64 @llvm.bitreverse.i64(i64 %{{.+}})
+
+// OGCG-LABEL: @_Z25test_builtin_bitreverse64y
+// OGCG: %{{.+}} = call i64 @llvm.bitreverse.i64(i64 %{{.+}})
+
+unsigned short test_builtin_bswap16(unsigned short x) {
+ return __builtin_bswap16(x);
+}
+
+// CIR-LABEL: @_Z20test_builtin_bswap16t
+// CIR: %{{.+}} = cir.bswap(%{{.+}} : !u16i) : !u16i
+
+// LLVM-LABEL: @_Z20test_builtin_bswap16t
+// LLVM: %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
+
+// OGCG-LABEL: @_Z20test_builtin_bswap16t
+// OGCG: %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
+
+unsigned test_builtin_bswap32(unsigned x) {
+ return __builtin_bswap32(x);
+}
+
+// CIR-LABEL: @_Z20test_builtin_bswap32j
+// CIR: %{{.+}} = cir.bswap(%{{.+}} : !u32i) : !u32i
+
+// LLVM-LABEL: @_Z20test_builtin_bswap32j
+// LLVM: %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
+
+// OGCG-LABEL: @_Z20test_builtin_bswap32j
+// OGCG: %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
+
+unsigned long long test_builtin_bswap64(unsigned long long x) {
+ return __builtin_bswap64(x);
+}
+
+// CIR-LABEL: @_Z20test_builtin_bswap64y
+// CIR: %{{.+}} = cir.bswap(%{{.+}} : !u64i) : !u64i
+
+// LLVM-LABEL: @_Z20test_builtin_bswap64y
+// LLVM: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
+
+// OGCG-LABEL: @_Z20test_builtin_bswap64y
+// OGCG: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, one minor change needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, with one question
@@ -2661,6 +2661,55 @@ def BitPopcountOp : CIR_BitOpBase<"bit.popcnt", | |||
}]; | |||
} | |||
|
|||
def BitReverseOp : CIR_BitOpBase<"bit.reverse", CIR_UIntOfWidths<[8, 16, 32, 64]>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this have the same constraints ([Pure, SameOperandsAndResultType]
) as the operation below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are inherited from CIR_BitOpBase
.
@@ -2661,6 +2661,55 @@ def BitPopcountOp : CIR_BitOpBase<"bit.popcnt", | |||
}]; | |||
} | |||
|
|||
def BitReverseOp : CIR_BitOpBase<"bit.reverse", CIR_UIntOfWidths<[8, 16, 32, 64]>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def BitReverseOp : CIR_BitOpBase<"bit.reverse", CIR_UIntOfWidths<[8, 16, 32, 64]>> { | |
def CIR_BitReverseOp : CIR_BitOpBase<"bit.reverse", | |
CIR_UIntOfWidths<[8, 16, 32, 64]> | |
> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also suggest to rename to bitreverse
to mirror builtins name. Also dot slightly implies some "namespace".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well I think we need to think about what the exact scope of CIR_BitOpBase
is and whether the bit reverse operation should be one of it. I'm naming it bit.reverse
because I think it should follow the CIR_BitOpBase
convention.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, ok. Lets keep it then for now with bit.
.
@@ -2661,6 +2661,55 @@ def BitPopcountOp : CIR_BitOpBase<"bit.popcnt", | |||
}]; | |||
} | |||
|
|||
def BitReverseOp : CIR_BitOpBase<"bit.reverse", CIR_UIntOfWidths<[8, 16, 32, 64]>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are inherited from CIR_BitOpBase
.
// ByteswapOp | ||
//===----------------------------------------------------------------------===// | ||
|
||
def ByteSwapOp : CIR_Op<"bswap", [Pure, SameOperandsAndResultType]> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def ByteSwapOp : CIR_Op<"bswap", [Pure, SameOperandsAndResultType]> { | |
def CIR_ByteSwapOp : CIR_Op<"bswap", [Pure, SameOperandsAndResultType]> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This operation has same format results and arguments as CIR_BitOpBase
.
It can be used here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. Also the mnemonic is updated to cir.bit.bswap
to match the naming convention for CIR_BitOpBase
.
35ca858
to
2dc9775
Compare
2dc9775
to
cf2bf0b
Compare
}]; | ||
} | ||
|
||
def ByteSwapOp : CIR_BitOpBase<"bit.bswap", CIR_UIntOfWidths<[16, 32, 64]>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like bit.bswap
. I know this is lumped in as a bit-manipulation operation, but it makes the referent of the b
very unclear. Can we maybe spell it out, bit.byte_swap
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here — I’d also prefer to keep the names as close as possible to Clang builtins. Using similar names reduces cognitive load and makes it easier to trace things through the entire pipeline.
For the same reason, I’d like to eliminate the bit.
prefix entirely and use the builtin names directly, like bitreverse
(This can be, and probably should be done as separate PR). I tend to interpret dot-separated names as namespaces, so using them just for readability feels a bit off. Grouping things like complex.
or libc.
makes sense, as they are in a way subdialects, but when the goal is simply improved readability, I’d lean toward using underscores instead in general.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the same reason, I’d like to eliminate the bit. prefix entirely and use the builtin names directly, like bitreverse (This can be, and probably should be done as separate PR).
I'll send a separate PR for this after this PR lands.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we maybe spell it out,
bit.byte_swap
?
Updated.
This patch adds support for the following two builtin functions:
__builtin_bswap
, represented by thecir.bswap
operation.__builtin_bitreverse
, represented by thecir.bit.reverse
operation.