-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[CIR] Add rotate operation #148426
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?
[CIR] Add rotate operation #148426
Conversation
@llvm/pr-subscribers-clangir Author: Sirui Mu (Lancern) ChangesThis patch adds Full diff: https://github.com/llvm/llvm-project/pull/148426.diff 6 Files Affected:
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]])
|
@llvm/pr-subscribers-clang Author: Sirui Mu (Lancern) ChangesThis patch adds Full diff: https://github.com/llvm/llvm-project/pull/148426.diff 6 Files Affected:
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]])
|
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, with a few nits
// RotateOp | ||
//===----------------------------------------------------------------------===// | ||
|
||
def RotateOp : CIR_Op<"rotate", [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.
Why doesn't this derive from CIR_BitOpBase? I'm not clear what we gain by having anything derive from that, but it seems like this should if other do.
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 believe the goal was to unify the assembly format and traits for all pure unary operations of the form T -> T
in a single place. However, the name BitOpBase
might misleadingly suggest that it specifically involves some bit-level semantics.
Formally, such operations are endomorphisms. That said, it's unclear to me whether names like EndomorphismOpBase
or EndoOpBase
are desirable or intuitive enough for this purpose.
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.
Formally, such operations are endomorphisms. That said, it's unclear to me whether names like EndomorphismOpBase or EndoOpBase are desirable or intuitive enough for this purpose.
The LLVM dialect could be a good reference in their LLVM intrinsic op definitions, see https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td .
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.
Yeah, though with LLVM dialect one needs to be cautious that it was written at the beginnings of MLIR, and many parts have not been updated since.
Maybe UnaryTypePreservingOp
might be a good name candidate?
// RotateOp | ||
//===----------------------------------------------------------------------===// | ||
|
||
def RotateOp : CIR_Op<"rotate", [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 RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> { | |
def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> { |
To mirror changes from llvm/clangir#1741
This patch adds
cir.rotate
operation for the__builtin_rotateleft
and__builtin_rotateright
families of builtin calls.