Skip to content

Commit 1df58fb

Browse files
committed
[CIR] Add rotate operation
1 parent 265fb36 commit 1df58fb

File tree

6 files changed

+233
-0
lines changed

6 files changed

+233
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,37 @@ def ByteSwapOp : CIR_BitOpBase<"byte_swap", CIR_UIntOfWidths<[16, 32, 64]>> {
28472847
}];
28482848
}
28492849

2850+
//===----------------------------------------------------------------------===//
2851+
// RotateOp
2852+
//===----------------------------------------------------------------------===//
2853+
2854+
def RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
2855+
let summary = "Rotate the bits in the operand integer";
2856+
let description = [{
2857+
The `cir.rotate` rotates the bits in `input` by the given amount `amount`.
2858+
The rotate direction is specified by the `left` and `right` keyword.
2859+
2860+
The width of the input integer must be either 8, 16, 32, or 64. `input`,
2861+
`amount`, and `result` must be of the same type.
2862+
2863+
Example:
2864+
2865+
```mlir
2866+
%r = cir.rotate left %0, %1 -> !u32i
2867+
%r = cir.rotate right %0, %1 -> !u32i
2868+
```
2869+
}];
2870+
2871+
let results = (outs CIR_IntType:$result);
2872+
let arguments = (ins CIR_IntType:$input, CIR_IntType:$amount,
2873+
UnitAttr:$isRotateLeft);
2874+
2875+
let assemblyFormat = [{
2876+
(`left` $isRotateLeft^) : (`right`)?
2877+
$input `,` $amount `:` type($result) attr-dict
2878+
}];
2879+
}
2880+
28502881
//===----------------------------------------------------------------------===//
28512882
// Assume Operations
28522883
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
5757
return RValue::get(result);
5858
}
5959

60+
RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) {
61+
mlir::Value input = emitScalarExpr(e->getArg(0));
62+
mlir::Value amount = emitScalarExpr(e->getArg(1));
63+
64+
// The builtin's amount parameter may have a different type than the input
65+
// argument, but the CIR op uses the same type for all values.
66+
mlir::Type ty = input.getType();
67+
amount = builder.createIntCast(amount, ty);
68+
69+
auto r = builder.create<cir::RotateOp>(getLoc(e->getSourceRange()), input,
70+
amount, isRotateLeft);
71+
return RValue::get(r);
72+
}
73+
6074
RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
6175
const CallExpr *e,
6276
ReturnValueSlot returnValue) {
@@ -219,6 +233,28 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
219233
mlir::Value arg = emitScalarExpr(e->getArg(0));
220234
return RValue::get(builder.create<cir::BitReverseOp>(loc, arg));
221235
}
236+
237+
case Builtin::BI__builtin_rotateleft8:
238+
case Builtin::BI__builtin_rotateleft16:
239+
case Builtin::BI__builtin_rotateleft32:
240+
case Builtin::BI__builtin_rotateleft64:
241+
case Builtin::BI_rotl8:
242+
case Builtin::BI_rotl16:
243+
case Builtin::BI_rotl:
244+
case Builtin::BI_lrotl:
245+
case Builtin::BI_rotl64:
246+
return emitRotate(e, /*isRotateLeft=*/true);
247+
248+
case Builtin::BI__builtin_rotateright8:
249+
case Builtin::BI__builtin_rotateright16:
250+
case Builtin::BI__builtin_rotateright32:
251+
case Builtin::BI__builtin_rotateright64:
252+
case Builtin::BI_rotr8:
253+
case Builtin::BI_rotr16:
254+
case Builtin::BI_rotr:
255+
case Builtin::BI_lrotr:
256+
case Builtin::BI_rotr64:
257+
return emitRotate(e, /*isRotateLeft=*/false);
222258
}
223259

224260
// If this is an alias for a lib function (e.g. __builtin_sin), emit

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,8 @@ class CIRGenFunction : public CIRGenTypeCache {
10291029

10301030
mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s);
10311031

1032+
RValue emitRotate(const CallExpr *e, bool isRotateLeft);
1033+
10321034
mlir::Value emitScalarConstant(const ConstantEmission &constant, Expr *e);
10331035

10341036
/// Emit a conversion from the specified type to the specified destination

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,21 @@ mlir::LogicalResult CIRToLLVMReturnOpLowering::matchAndRewrite(
872872
return mlir::LogicalResult::success();
873873
}
874874

875+
mlir::LogicalResult CIRToLLVMRotateOpLowering::matchAndRewrite(
876+
cir::RotateOp op, OpAdaptor adaptor,
877+
mlir::ConversionPatternRewriter &rewriter) const {
878+
// Note that LLVM intrinsic calls to @llvm.fsh{r,l}.i* have the same type as
879+
// the operand.
880+
auto input = adaptor.getInput();
881+
if (op.getIsRotateLeft())
882+
rewriter.replaceOpWithNewOp<mlir::LLVM::FshlOp>(op, input, input,
883+
adaptor.getAmount());
884+
else
885+
rewriter.replaceOpWithNewOp<mlir::LLVM::FshrOp>(op, input, input,
886+
adaptor.getAmount());
887+
return mlir::LogicalResult::success();
888+
}
889+
875890
static mlir::LogicalResult
876891
rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
877892
mlir::ConversionPatternRewriter &rewriter,
@@ -2075,6 +2090,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
20752090
CIRToLLVMGetBitfieldOpLowering,
20762091
CIRToLLVMGetGlobalOpLowering,
20772092
CIRToLLVMGetMemberOpLowering,
2093+
CIRToLLVMRotateOpLowering,
20782094
CIRToLLVMSelectOpLowering,
20792095
CIRToLLVMSetBitfieldOpLowering,
20802096
CIRToLLVMShiftOpLowering,

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,16 @@ class CIRToLLVMReturnOpLowering
160160
mlir::ConversionPatternRewriter &) const override;
161161
};
162162

163+
class CIRToLLVMRotateOpLowering
164+
: public mlir::OpConversionPattern<cir::RotateOp> {
165+
public:
166+
using mlir::OpConversionPattern<cir::RotateOp>::OpConversionPattern;
167+
168+
mlir::LogicalResult
169+
matchAndRewrite(cir::RotateOp op, OpAdaptor,
170+
mlir::ConversionPatternRewriter &) const override;
171+
};
172+
163173
class CIRToLLVMCallOpLowering : public mlir::OpConversionPattern<cir::CallOp> {
164174
public:
165175
using mlir::OpConversionPattern<cir::CallOp>::OpConversionPattern;

clang/test/CIR/CodeGen/builtin_bit.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,141 @@ unsigned long long test_builtin_bswap64(unsigned long long x) {
416416

417417
// OGCG-LABEL: @_Z20test_builtin_bswap64y
418418
// OGCG: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
419+
420+
unsigned char test_builtin_rotateleft8(unsigned char x, unsigned char y) {
421+
return __builtin_rotateleft8(x, y);
422+
}
423+
424+
// CIR-LABEL: @_Z24test_builtin_rotateleft8hh
425+
// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u8i
426+
427+
// LLVM-LABEL: @_Z24test_builtin_rotateleft8hh
428+
// LLVM: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
429+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
430+
// LLVM-NEXT: %{{.+}} = call i8 @llvm.fshl.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
431+
432+
// OGCG-LABEL: @_Z24test_builtin_rotateleft8hh
433+
// OGCG: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
434+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
435+
// OGCG-NEXT: %{{.+}} = call i8 @llvm.fshl.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
436+
437+
unsigned short test_builtin_rotateleft16(unsigned short x, unsigned short y) {
438+
return __builtin_rotateleft16(x, y);
439+
}
440+
441+
// CIR-LABEL: @_Z25test_builtin_rotateleft16tt
442+
// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u16i
443+
444+
// LLVM-LABEL: @_Z25test_builtin_rotateleft16tt
445+
// LLVM: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
446+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
447+
// LLVM-NEXT: %{{.+}} = call i16 @llvm.fshl.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
448+
449+
// OGCG-LABEL: @_Z25test_builtin_rotateleft16tt
450+
// OGCG: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
451+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
452+
// OGCG-NEXT: %{{.+}} = call i16 @llvm.fshl.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
453+
454+
unsigned test_builtin_rotateleft32(unsigned x, unsigned y) {
455+
return __builtin_rotateleft32(x, y);
456+
}
457+
458+
// CIR-LABEL: @_Z25test_builtin_rotateleft32jj
459+
// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u32i
460+
461+
// LLVM-LABEL: @_Z25test_builtin_rotateleft32jj
462+
// LLVM: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
463+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
464+
// LLVM-NEXT: %{{.+}} = call i32 @llvm.fshl.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
465+
466+
// OGCG-LABEL: @_Z25test_builtin_rotateleft32jj
467+
// OGCG: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
468+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
469+
// OGCG-NEXT: %{{.+}} = call i32 @llvm.fshl.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
470+
471+
unsigned long long test_builtin_rotateleft64(unsigned long long x,
472+
unsigned long long y) {
473+
return __builtin_rotateleft64(x, y);
474+
}
475+
476+
// CIR-LABEL: @_Z25test_builtin_rotateleft64yy
477+
// CIR: %{{.+}} = cir.rotate left %{{.+}}, %{{.+}} : !u64i
478+
479+
// LLVM-LABEL: @_Z25test_builtin_rotateleft64yy
480+
// LLVM: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
481+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
482+
// LLVM-NEXT: %{{.+}} = call i64 @llvm.fshl.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])
483+
484+
// OGCG-LABEL: @_Z25test_builtin_rotateleft64yy
485+
// OGCG: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
486+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
487+
// OGCG-NEXT: %{{.+}} = call i64 @llvm.fshl.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])
488+
489+
unsigned char test_builtin_rotateright8(unsigned char x, unsigned char y) {
490+
return __builtin_rotateright8(x, y);
491+
}
492+
493+
// CIR-LABEL: @_Z25test_builtin_rotateright8hh
494+
// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u8i
495+
496+
// LLVM-LABEL: @_Z25test_builtin_rotateright8hh
497+
// LLVM: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
498+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
499+
// LLVM-NEXT: %{{.+}} = call i8 @llvm.fshr.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
500+
501+
// OGCG-LABEL: @_Z25test_builtin_rotateright8hh
502+
// OGCG: %[[INPUT:.+]] = load i8, ptr %{{.+}}, align 1
503+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i8, ptr %{{.+}}, align 1
504+
// OGCG-NEXT: %{{.+}} = call i8 @llvm.fshr.i8(i8 %[[INPUT]], i8 %[[INPUT]], i8 %[[AMOUNT]])
505+
506+
unsigned short test_builtin_rotateright16(unsigned short x, unsigned short y) {
507+
return __builtin_rotateright16(x, y);
508+
}
509+
510+
// CIR-LABEL: @_Z26test_builtin_rotateright16tt
511+
// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u16i
512+
513+
// LLVM-LABEL: @_Z26test_builtin_rotateright16tt
514+
// LLVM: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
515+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
516+
// LLVM-NEXT: %{{.+}} = call i16 @llvm.fshr.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
517+
518+
// OGCG-LABEL: @_Z26test_builtin_rotateright16tt
519+
// OGCG: %[[INPUT:.+]] = load i16, ptr %{{.+}}, align 2
520+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i16, ptr %{{.+}}, align 2
521+
// OGCG-NEXT: %{{.+}} = call i16 @llvm.fshr.i16(i16 %[[INPUT]], i16 %[[INPUT]], i16 %[[AMOUNT]])
522+
523+
unsigned test_builtin_rotateright32(unsigned x, unsigned y) {
524+
return __builtin_rotateright32(x, y);
525+
}
526+
527+
// CIR-LABEL: @_Z26test_builtin_rotateright32jj
528+
// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u32i
529+
530+
// LLVM-LABEL: @_Z26test_builtin_rotateright32jj
531+
// LLVM: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
532+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
533+
// LLVM-NEXT: %{{.+}} = call i32 @llvm.fshr.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
534+
535+
// OGCG-LABEL: @_Z26test_builtin_rotateright32jj
536+
// OGCG: %[[INPUT:.+]] = load i32, ptr %{{.+}}, align 4
537+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i32, ptr %{{.+}}, align 4
538+
// OGCG-NEXT: %{{.+}} = call i32 @llvm.fshr.i32(i32 %[[INPUT]], i32 %[[INPUT]], i32 %[[AMOUNT]])
539+
540+
unsigned long long test_builtin_rotateright64(unsigned long long x,
541+
unsigned long long y) {
542+
return __builtin_rotateright64(x, y);
543+
}
544+
545+
// CIR-LABEL: @_Z26test_builtin_rotateright64yy
546+
// CIR: %{{.+}} = cir.rotate right %{{.+}}, %{{.+}} : !u64i
547+
548+
// LLVM-LABEL: @_Z26test_builtin_rotateright64yy
549+
// LLVM: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
550+
// LLVM-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
551+
// LLVM-NEXT: %{{.+}} = call i64 @llvm.fshr.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])
552+
553+
// OGCG-LABEL: @_Z26test_builtin_rotateright64yy
554+
// OGCG: %[[INPUT:.+]] = load i64, ptr %{{.+}}, align 8
555+
// OGCG-NEXT: %[[AMOUNT:.+]] = load i64, ptr %{{.+}}, align 8
556+
// OGCG-NEXT: %{{.+}} = call i64 @llvm.fshr.i64(i64 %[[INPUT]], i64 %[[INPUT]], i64 %[[AMOUNT]])

0 commit comments

Comments
 (0)