[clang] [CIR] Add rotate operation (PR #148426)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 14 08:29:17 PDT 2025
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/148426
>From 996ddacdb181385b5b26a54d884f00df38a835a7 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Sun, 13 Jul 2025 20:14:41 +0800
Subject: [PATCH] [CIR] Add rotate operation
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 39 +++++
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 21 +++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 2 +
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 16 ++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 10 ++
clang/test/CIR/CodeGen/builtin_bit.cpp | 138 ++++++++++++++++++
6 files changed, 226 insertions(+)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 8058e74968499..00d87a7eb2f21 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2847,6 +2847,45 @@ def ByteSwapOp : CIR_BitOpBase<"byte_swap", CIR_UIntOfWidths<[16, 32, 64]>> {
}];
}
+//===----------------------------------------------------------------------===//
+// RotateOp
+//===----------------------------------------------------------------------===//
+
+def RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
+ let summary = "Rotate the bits in the operand integer";
+ let description = [{
+ The `cir.rotate` rotates the bits in `input` by the given amount `amount`.
+ The rotate direction is specified by the `left` and `right` keyword.
+
+ `input` must be an unsigned integer and its with must be either 8, 16, 32,
+ or 64. The types of `input`, `amount`, and the result must all match.
+
+ Example:
+
+ ```mlir
+ %r = cir.rotate left %0, %1 : !u32i
+ %r = cir.rotate right %0, %1 : !u32i
+ ```
+ }];
+
+ let results = (outs CIR_IntType:$result);
+ let arguments = (ins
+ CIR_UIntOfWidths<[8, 16, 32, 64]>:$input,
+ CIR_IntType:$amount,
+ UnitAttr:$rotateLeft
+ );
+
+ let assemblyFormat = [{
+ (`left` $rotateLeft^) : (`right`)?
+ $input `,` $amount `:` type($result) attr-dict
+ }];
+
+ let extraClassDeclaration = [{
+ bool isRotateLeft() { return getRotateLeft(); }
+ bool isRotateRight() { return !getRotateLeft(); }
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Assume Operations
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 72e8d71c366d8..70473798d1514 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -57,6 +57,15 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
return RValue::get(result);
}
+RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) {
+ mlir::Value input = emitScalarExpr(e->getArg(0));
+ mlir::Value amount = emitScalarExpr(e->getArg(1));
+
+ auto r = builder.create<cir::RotateOp>(getLoc(e->getSourceRange()), input,
+ amount, isRotateLeft);
+ return RValue::get(r);
+}
+
RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
const CallExpr *e,
ReturnValueSlot returnValue) {
@@ -219,6 +228,18 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Value arg = emitScalarExpr(e->getArg(0));
return RValue::get(builder.create<cir::BitReverseOp>(loc, arg));
}
+
+ case Builtin::BI__builtin_rotateleft8:
+ case Builtin::BI__builtin_rotateleft16:
+ case Builtin::BI__builtin_rotateleft32:
+ case Builtin::BI__builtin_rotateleft64:
+ return emitRotate(e, /*isRotateLeft=*/true);
+
+ case Builtin::BI__builtin_rotateright8:
+ case Builtin::BI__builtin_rotateright16:
+ case Builtin::BI__builtin_rotateright32:
+ case Builtin::BI__builtin_rotateright64:
+ return emitRotate(e, /*isRotateLeft=*/false);
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 5feb5fc94d983..17a208d911fe1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1029,6 +1029,8 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s);
+ RValue emitRotate(const CallExpr *e, bool isRotateLeft);
+
mlir::Value emitScalarConstant(const ConstantEmission &constant, Expr *e);
/// Emit a conversion from the specified type to the specified destination
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 424ff969b3fd4..e40fc6d563bee 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -872,6 +872,21 @@ mlir::LogicalResult CIRToLLVMReturnOpLowering::matchAndRewrite(
return mlir::LogicalResult::success();
}
+mlir::LogicalResult CIRToLLVMRotateOpLowering::matchAndRewrite(
+ cir::RotateOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ // Note that LLVM intrinsic calls to @llvm.fsh{r,l}.i* have the same type as
+ // the operand.
+ auto input = adaptor.getInput();
+ if (op.isRotateLeft())
+ rewriter.replaceOpWithNewOp<mlir::LLVM::FshlOp>(op, input, input,
+ adaptor.getAmount());
+ else
+ rewriter.replaceOpWithNewOp<mlir::LLVM::FshrOp>(op, input, input,
+ adaptor.getAmount());
+ return mlir::LogicalResult::success();
+}
+
static mlir::LogicalResult
rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
mlir::ConversionPatternRewriter &rewriter,
@@ -2075,6 +2090,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMGetBitfieldOpLowering,
CIRToLLVMGetGlobalOpLowering,
CIRToLLVMGetMemberOpLowering,
+ CIRToLLVMRotateOpLowering,
CIRToLLVMSelectOpLowering,
CIRToLLVMSetBitfieldOpLowering,
CIRToLLVMShiftOpLowering,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 1716015a75882..7c81e2a292a90 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -160,6 +160,16 @@ class CIRToLLVMReturnOpLowering
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMRotateOpLowering
+ : public mlir::OpConversionPattern<cir::RotateOp> {
+public:
+ using mlir::OpConversionPattern<cir::RotateOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::RotateOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMCallOpLowering : public mlir::OpConversionPattern<cir::CallOp> {
public:
using mlir::OpConversionPattern<cir::CallOp>::OpConversionPattern;
diff --git a/clang/test/CIR/CodeGen/builtin_bit.cpp b/clang/test/CIR/CodeGen/builtin_bit.cpp
index 8ea7a69b3dd2a..de0e1b3cef260 100644
--- a/clang/test/CIR/CodeGen/builtin_bit.cpp
+++ b/clang/test/CIR/CodeGen/builtin_bit.cpp
@@ -416,3 +416,141 @@ unsigned long long test_builtin_bswap64(unsigned long long x) {
// OGCG-LABEL: @_Z20test_builtin_bswap64y
// OGCG: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
+
+unsigned char test_builtin_rotateleft8(unsigned char x, unsigned char y) {
+ return __builtin_rotateleft8(x, y);
+}
+
+// CIR-LABEL: @_Z24test_builtin_rotateleft8hh
+// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u8i
+
+// LLVM-LABEL: @_Z24test_builtin_rotateleft8hh
+// LLVM: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
+// LLVM-NEXT: %{{.+}} = call i8 @llvm.fshl.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z24test_builtin_rotateleft8hh
+// OGCG: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
+// OGCG-NEXT: %{{.+}} = call i8 @llvm.fshl.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
+
+unsigned short test_builtin_rotateleft16(unsigned short x, unsigned short y) {
+ return __builtin_rotateleft16(x, y);
+}
+
+// CIR-LABEL: @_Z25test_builtin_rotateleft16tt
+// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u16i
+
+// LLVM-LABEL: @_Z25test_builtin_rotateleft16tt
+// LLVM: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
+// LLVM-NEXT: %{{.+}} = call i16 @llvm.fshl.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z25test_builtin_rotateleft16tt
+// OGCG: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
+// OGCG-NEXT: %{{.+}} = call i16 @llvm.fshl.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
+
+unsigned test_builtin_rotateleft32(unsigned x, unsigned y) {
+ return __builtin_rotateleft32(x, y);
+}
+
+// CIR-LABEL: @_Z25test_builtin_rotateleft32jj
+// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u32i
+
+// LLVM-LABEL: @_Z25test_builtin_rotateleft32jj
+// LLVM: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT: %{{.+}} = call i32 @llvm.fshl.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z25test_builtin_rotateleft32jj
+// OGCG: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT: %{{.+}} = call i32 @llvm.fshl.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
+
+unsigned long long test_builtin_rotateleft64(unsigned long long x,
+ unsigned long long y) {
+ return __builtin_rotateleft64(x, y);
+}
+
+// CIR-LABEL: @_Z25test_builtin_rotateleft64yy
+// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u64i
+
+// LLVM-LABEL: @_Z25test_builtin_rotateleft64yy
+// LLVM: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
+// LLVM-NEXT: %{{.+}} = call i64 @llvm.fshl.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z25test_builtin_rotateleft64yy
+// OGCG: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
+// OGCG-NEXT: %{{.+}} = call i64 @llvm.fshl.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])
+
+unsigned char test_builtin_rotateright8(unsigned char x, unsigned char y) {
+ return __builtin_rotateright8(x, y);
+}
+
+// CIR-LABEL: @_Z25test_builtin_rotateright8hh
+// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u8i
+
+// LLVM-LABEL: @_Z25test_builtin_rotateright8hh
+// LLVM: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
+// LLVM-NEXT: %{{.+}} = call i8 @llvm.fshr.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z25test_builtin_rotateright8hh
+// OGCG: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
+// OGCG-NEXT: %{{.+}} = call i8 @llvm.fshr.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
+
+unsigned short test_builtin_rotateright16(unsigned short x, unsigned short y) {
+ return __builtin_rotateright16(x, y);
+}
+
+// CIR-LABEL: @_Z26test_builtin_rotateright16tt
+// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u16i
+
+// LLVM-LABEL: @_Z26test_builtin_rotateright16tt
+// LLVM: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
+// LLVM-NEXT: %{{.+}} = call i16 @llvm.fshr.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z26test_builtin_rotateright16tt
+// OGCG: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
+// OGCG-NEXT: %{{.+}} = call i16 @llvm.fshr.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
+
+unsigned test_builtin_rotateright32(unsigned x, unsigned y) {
+ return __builtin_rotateright32(x, y);
+}
+
+// CIR-LABEL: @_Z26test_builtin_rotateright32jj
+// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u32i
+
+// LLVM-LABEL: @_Z26test_builtin_rotateright32jj
+// LLVM: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT: %{{.+}} = call i32 @llvm.fshr.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z26test_builtin_rotateright32jj
+// OGCG: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT: %{{.+}} = call i32 @llvm.fshr.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
+
+unsigned long long test_builtin_rotateright64(unsigned long long x,
+ unsigned long long y) {
+ return __builtin_rotateright64(x, y);
+}
+
+// CIR-LABEL: @_Z26test_builtin_rotateright64yy
+// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u64i
+
+// LLVM-LABEL: @_Z26test_builtin_rotateright64yy
+// LLVM: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
+// LLVM-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
+// LLVM-NEXT: %{{.+}} = call i64 @llvm.fshr.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])
+
+// OGCG-LABEL: @_Z26test_builtin_rotateright64yy
+// OGCG: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
+// OGCG-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
+// OGCG-NEXT: %{{.+}} = call i64 @llvm.fshr.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])
More information about the cfe-commits
mailing list