Skip to content

Commit 06a18f7

Browse files
committed
[CIR] Add rotate operation
1 parent dbb1210 commit 06a18f7

File tree

7 files changed

+232
-0
lines changed

7 files changed

+232
-0
lines changed

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,45 @@ def CIR_ByteSwapOp : CIR_BitOpBase<"byte_swap",
29342934
}];
29352935
}
29362936

2937+
//===----------------------------------------------------------------------===//
2938+
// RotateOp
2939+
//===----------------------------------------------------------------------===//
2940+
2941+
def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
2942+
let summary = "Rotate the bits in the operand integer";
2943+
let description = [{
2944+
The `cir.rotate` rotates the bits in `input` by the given amount `amount`.
2945+
The rotate direction is specified by the `left` and `right` keyword.
2946+
2947+
`input` must be an unsigned integer and its width must be either 8, 16, 32,
2948+
or 64. The types of `input`, `amount`, and the result must all match.
2949+
2950+
Example:
2951+
2952+
```mlir
2953+
%r = cir.rotate left %0, %1 : !u32i
2954+
%r = cir.rotate right %0, %1 : !u32i
2955+
```
2956+
}];
2957+
2958+
let results = (outs CIR_IntType:$result);
2959+
let arguments = (ins
2960+
CIR_UIntOfWidths<[8, 16, 32, 64]>:$input,
2961+
CIR_IntType:$amount,
2962+
UnitAttr:$rotateLeft
2963+
);
2964+
2965+
let assemblyFormat = [{
2966+
(`left` $rotateLeft^) : (`right`)?
2967+
$input `,` $amount `:` type($result) attr-dict
2968+
}];
2969+
2970+
let extraClassDeclaration = [{
2971+
bool isRotateLeft() { return getRotateLeft(); }
2972+
bool isRotateRight() { return !getRotateLeft(); }
2973+
}];
2974+
}
2975+
29372976
//===----------------------------------------------------------------------===//
29382977
// Assume Operations
29392978
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ struct MissingFeatures {
254254
static bool dtorCleanups() { return false; }
255255
static bool completeDtors() { return false; }
256256
static bool vtableInitialization() { return false; }
257+
static bool msvcBuiltins() { return false; }
257258

258259
// Missing types
259260
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 26 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+
// TODO(cir): MSVC flavor bit rotate builtins use different types for input
65+
// and amount, but cir.rotate requires them to have the same type. Cast amount
66+
// to the type of input when necessary.
67+
assert(!cir::MissingFeatures::msvcBuiltins());
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,18 @@ 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+
return emitRotate(e, /*isRotateLeft=*/true);
242+
243+
case Builtin::BI__builtin_rotateright8:
244+
case Builtin::BI__builtin_rotateright16:
245+
case Builtin::BI__builtin_rotateright32:
246+
case Builtin::BI__builtin_rotateright64:
247+
return emitRotate(e, /*isRotateLeft=*/false);
222248
}
223249

224250
// 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
@@ -1030,6 +1030,8 @@ class CIRGenFunction : public CIRGenTypeCache {
10301030

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

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

10351037
/// 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+
mlir::Value input = adaptor.getInput();
881+
if (op.isRotateLeft())
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,
@@ -2077,6 +2092,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
20772092
CIRToLLVMGetBitfieldOpLowering,
20782093
CIRToLLVMGetGlobalOpLowering,
20792094
CIRToLLVMGetMemberOpLowering,
2095+
CIRToLLVMRotateOpLowering,
20802096
CIRToLLVMSelectOpLowering,
20812097
CIRToLLVMSetBitfieldOpLowering,
20822098
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)