https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/148426
This patch adds `cir.rotate` operation for the `__builtin_rotateleft` and `__builtin_rotateright` families of builtin calls. >From 1df58fbc1a0fa5aec67300eb240dd1ca21a30819 Mon Sep 17 00:00:00 2001 From: Sirui Mu <msrlanc...@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 | 31 ++++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 36 +++++ 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, 233 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 8058e74968499..f616210b6602c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2847,6 +2847,37 @@ 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. + + The width of the input integer must be either 8, 16, 32, or 64. `input`, + `amount`, and `result` must be of the same type. + + 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_IntType:$input, CIR_IntType:$amount, + UnitAttr:$isRotateLeft); + + let assemblyFormat = [{ + (`left` $isRotateLeft^) : (`right`)? + $input `,` $amount `:` type($result) attr-dict + }]; +} + //===----------------------------------------------------------------------===// // Assume Operations //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 72e8d71c366d8..1e80b029488a3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -57,6 +57,20 @@ 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)); + + // The builtin's amount parameter may have a different type than the input + // argument, but the CIR op uses the same type for all values. + mlir::Type ty = input.getType(); + amount = builder.createIntCast(amount, ty); + + 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 +233,28 @@ 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: + case Builtin::BI_rotl8: + case Builtin::BI_rotl16: + case Builtin::BI_rotl: + case Builtin::BI_lrotl: + case Builtin::BI_rotl64: + 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: + case Builtin::BI_rotr8: + case Builtin::BI_rotr16: + case Builtin::BI_rotr: + case Builtin::BI_lrotr: + case Builtin::BI_rotr64: + 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..15e0c0e109980 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.getIsRotateLeft()) + 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]]) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits