https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/148426
>From 4d0e8a07d8503187f00a57e688e5711c1dc73aea 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 | 39 +++++ clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 26 ++++ 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 ++++++++++++++++++ 7 files changed, 232 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 676ff76dff661..187891a8c58aa 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2898,6 +2898,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 width 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/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 48e309063d38b..182e4b6784d2f 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -254,6 +254,7 @@ struct MissingFeatures { static bool dtorCleanups() { return false; } static bool completeDtors() { return false; } static bool vtableInitialization() { return false; } + static bool msvcBuiltins() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 72e8d71c366d8..476f994959285 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)); + + // TODO(cir): MSVC flavor bit rotate builtins use different types for input + // and amount, but cir.rotate requires them to have the same type. Cast amount + // to the type of input when necessary. + assert(!cir::MissingFeatures::msvcBuiltins()); + + 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,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 1346333739bc1..3baabba5adfe1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1030,6 +1030,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 7dcea0c8eb529..840e856ba0cf8 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. + mlir::Value 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, @@ -2077,6 +2092,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 3c30b1bc5b072..3faf1e900848e 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 f017b6eb51971..4ac82bd749e8a 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