Skip to content

Commit 265fb36

Browse files
authored
[CIR] Add bit reverse and byte reverse operations (#147200)
This patch adds support for the following two builtin functions: - `__builtin_bswap`, represented by the `cir.byte_swap` operation. - `__builtin_bitreverse`, represented by the `cir.bit.reverse` operation.
1 parent d02c85a commit 265fb36

File tree

5 files changed

+191
-11
lines changed

5 files changed

+191
-11
lines changed

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2808,6 +2808,45 @@ def BitPopcountOp : CIR_BitOpBase<"bit.popcnt",
28082808
}];
28092809
}
28102810

2811+
def BitReverseOp : CIR_BitOpBase<"bit.reverse",
2812+
CIR_UIntOfWidths<[8, 16, 32, 64]>> {
2813+
let summary = "Reverse the bit pattern of the operand integer";
2814+
let description = [{
2815+
The `cir.bit.reverse` operation reverses the bits of the operand integer.
2816+
Its only argument must be of unsigned integer types of width 8, 16, 32, or
2817+
64.
2818+
2819+
This operation covers the C/C++ builtin function `__builtin_bitreverse`.
2820+
2821+
Example:
2822+
2823+
```mlir
2824+
%1 = cir.bit.reverse(%0 : !u32i): !u32i
2825+
```
2826+
}];
2827+
}
2828+
2829+
def ByteSwapOp : CIR_BitOpBase<"byte_swap", CIR_UIntOfWidths<[16, 32, 64]>> {
2830+
let summary = "Reverse the bytes in the object representation of the operand";
2831+
let description = [{
2832+
The `cir.byte_swap` operation takes an integer as operand, reverse the bytes
2833+
in the object representation of the operand integer, and returns the result.
2834+
2835+
The operand integer must be an unsigned integer. Its widths must be either
2836+
16, 32, or 64.
2837+
2838+
Example:
2839+
2840+
```mlir
2841+
// %0 = 0x12345678
2842+
%0 = cir.const #cir.int<305419896> : !u32i
2843+
2844+
// %1 should be 0x78563412
2845+
%1 = cir.byte_swap(%0 : !u32i) : !u32i
2846+
```
2847+
}];
2848+
}
2849+
28112850
//===----------------------------------------------------------------------===//
28122851
// Assume Operations
28132852
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,24 +60,23 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
6060
RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
6161
const CallExpr *e,
6262
ReturnValueSlot returnValue) {
63+
mlir::Location loc = getLoc(e->getSourceRange());
64+
6365
// See if we can constant fold this builtin. If so, don't emit it at all.
6466
// TODO: Extend this handling to all builtin calls that we can constant-fold.
6567
Expr::EvalResult result;
6668
if (e->isPRValue() && e->EvaluateAsRValue(result, cgm.getASTContext()) &&
6769
!result.hasSideEffects()) {
68-
if (result.Val.isInt()) {
69-
return RValue::get(builder.getConstInt(getLoc(e->getSourceRange()),
70-
result.Val.getInt()));
71-
}
70+
if (result.Val.isInt())
71+
return RValue::get(builder.getConstInt(loc, result.Val.getInt()));
7272
if (result.Val.isFloat()) {
7373
// Note: we are using result type of CallExpr to determine the type of
7474
// the constant. Classic codegen uses the result value to determine the
7575
// type. We feel it should be Ok to use expression type because it is
7676
// hard to imagine a builtin function evaluates to a value that
7777
// over/underflows its own defined type.
7878
mlir::Type type = convertType(e->getType());
79-
return RValue::get(builder.getConstFP(getLoc(e->getExprLoc()), type,
80-
result.Val.getFloat()));
79+
return RValue::get(builder.getConstFP(loc, type, result.Val.getFloat()));
8180
}
8281
}
8382

@@ -94,8 +93,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
9493
assert(!cir::MissingFeatures::builtinCallMathErrno());
9594
assert(!cir::MissingFeatures::builtinCall());
9695

97-
mlir::Location loc = getLoc(e->getExprLoc());
98-
9996
switch (builtinIDIfNoAsmLabel) {
10097
default:
10198
break;
@@ -200,11 +197,28 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
200197
probability);
201198
}
202199

203-
auto result = builder.create<cir::ExpectOp>(getLoc(e->getSourceRange()),
204-
argValue.getType(), argValue,
205-
expectedValue, probAttr);
200+
auto result = builder.create<cir::ExpectOp>(
201+
loc, argValue.getType(), argValue, expectedValue, probAttr);
206202
return RValue::get(result);
207203
}
204+
205+
case Builtin::BI__builtin_bswap16:
206+
case Builtin::BI__builtin_bswap32:
207+
case Builtin::BI__builtin_bswap64:
208+
case Builtin::BI_byteswap_ushort:
209+
case Builtin::BI_byteswap_ulong:
210+
case Builtin::BI_byteswap_uint64: {
211+
mlir::Value arg = emitScalarExpr(e->getArg(0));
212+
return RValue::get(builder.create<cir::ByteSwapOp>(loc, arg));
213+
}
214+
215+
case Builtin::BI__builtin_bitreverse8:
216+
case Builtin::BI__builtin_bitreverse16:
217+
case Builtin::BI__builtin_bitreverse32:
218+
case Builtin::BI__builtin_bitreverse64: {
219+
mlir::Value arg = emitScalarExpr(e->getArg(0));
220+
return RValue::get(builder.create<cir::BitReverseOp>(loc, arg));
221+
}
208222
}
209223

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

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,13 @@ mlir::LogicalResult CIRToLLVMBitPopcountOpLowering::matchAndRewrite(
535535
return mlir::LogicalResult::success();
536536
}
537537

538+
mlir::LogicalResult CIRToLLVMBitReverseOpLowering::matchAndRewrite(
539+
cir::BitReverseOp op, OpAdaptor adaptor,
540+
mlir::ConversionPatternRewriter &rewriter) const {
541+
rewriter.replaceOpWithNewOp<mlir::LLVM::BitReverseOp>(op, adaptor.getInput());
542+
return mlir::success();
543+
}
544+
538545
mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
539546
cir::BrCondOp brOp, OpAdaptor adaptor,
540547
mlir::ConversionPatternRewriter &rewriter) const {
@@ -551,6 +558,13 @@ mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
551558
return mlir::success();
552559
}
553560

561+
mlir::LogicalResult CIRToLLVMByteSwapOpLowering::matchAndRewrite(
562+
cir::ByteSwapOp op, OpAdaptor adaptor,
563+
mlir::ConversionPatternRewriter &rewriter) const {
564+
rewriter.replaceOpWithNewOp<mlir::LLVM::ByteSwapOp>(op, adaptor.getInput());
565+
return mlir::LogicalResult::success();
566+
}
567+
554568
mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const {
555569
return getTypeConverter()->convertType(ty);
556570
}
@@ -2044,8 +2058,10 @@ void ConvertCIRToLLVMPass::runOnOperation() {
20442058
CIRToLLVMBitCtzOpLowering,
20452059
CIRToLLVMBitParityOpLowering,
20462060
CIRToLLVMBitPopcountOpLowering,
2061+
CIRToLLVMBitReverseOpLowering,
20472062
CIRToLLVMBrCondOpLowering,
20482063
CIRToLLVMBrOpLowering,
2064+
CIRToLLVMByteSwapOpLowering,
20492065
CIRToLLVMCallOpLowering,
20502066
CIRToLLVMCmpOpLowering,
20512067
CIRToLLVMComplexAddOpLowering,

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ class CIRToLLVMBitPopcountOpLowering
9494
mlir::ConversionPatternRewriter &) const override;
9595
};
9696

97+
class CIRToLLVMBitReverseOpLowering
98+
: public mlir::OpConversionPattern<cir::BitReverseOp> {
99+
public:
100+
using mlir::OpConversionPattern<cir::BitReverseOp>::OpConversionPattern;
101+
102+
mlir::LogicalResult
103+
matchAndRewrite(cir::BitReverseOp op, OpAdaptor,
104+
mlir::ConversionPatternRewriter &) const override;
105+
};
106+
97107
class CIRToLLVMBrCondOpLowering
98108
: public mlir::OpConversionPattern<cir::BrCondOp> {
99109
public:
@@ -104,6 +114,16 @@ class CIRToLLVMBrCondOpLowering
104114
mlir::ConversionPatternRewriter &) const override;
105115
};
106116

117+
class CIRToLLVMByteSwapOpLowering
118+
: public mlir::OpConversionPattern<cir::ByteSwapOp> {
119+
public:
120+
using mlir::OpConversionPattern<cir::ByteSwapOp>::OpConversionPattern;
121+
122+
mlir::LogicalResult
123+
matchAndRewrite(cir::ByteSwapOp op, OpAdaptor,
124+
mlir::ConversionPatternRewriter &) const override;
125+
};
126+
107127
class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {
108128
mlir::DataLayout const &dataLayout;
109129

clang/test/CIR/CodeGen/builtin_bit.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,94 @@ int test_builtin_popcountg(unsigned x) {
325325

326326
// OGCG-LABEL: _Z22test_builtin_popcountgj
327327
// OGCG: %{{.+}} = call i32 @llvm.ctpop.i32(i32 %{{.+}})
328+
329+
unsigned char test_builtin_bitreverse8(unsigned char x) {
330+
return __builtin_bitreverse8(x);
331+
}
332+
333+
// CIR-LABEL: @_Z24test_builtin_bitreverse8h
334+
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u8i) : !u8i
335+
336+
// LLVM-LABEL: @_Z24test_builtin_bitreverse8h
337+
// LLVM: %{{.+}} = call i8 @llvm.bitreverse.i8(i8 %{{.+}})
338+
339+
// OGCG-LABEL: @_Z24test_builtin_bitreverse8h
340+
// OGCG: %{{.+}} = call i8 @llvm.bitreverse.i8(i8 %{{.+}})
341+
342+
unsigned short test_builtin_bitreverse16(unsigned short x) {
343+
return __builtin_bitreverse16(x);
344+
}
345+
346+
// CIR-LABEL: @_Z25test_builtin_bitreverse16t
347+
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u16i) : !u16i
348+
349+
// LLVM-LABEL: @_Z25test_builtin_bitreverse16t
350+
// LLVM: %{{.+}} = call i16 @llvm.bitreverse.i16(i16 %{{.+}})
351+
352+
// OGCG-LABEL: @_Z25test_builtin_bitreverse16t
353+
// OGCG: %{{.+}} = call i16 @llvm.bitreverse.i16(i16 %{{.+}})
354+
355+
unsigned test_builtin_bitreverse32(unsigned x) {
356+
return __builtin_bitreverse32(x);
357+
}
358+
359+
// CIR-LABEL: @_Z25test_builtin_bitreverse32j
360+
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u32i) : !u32i
361+
362+
// LLVM-LABEL: @_Z25test_builtin_bitreverse32j
363+
// LLVM: %{{.+}} = call i32 @llvm.bitreverse.i32(i32 %{{.+}})
364+
365+
// OGCG-LABEL: @_Z25test_builtin_bitreverse32j
366+
// OGCG: %{{.+}} = call i32 @llvm.bitreverse.i32(i32 %{{.+}})
367+
368+
unsigned long long test_builtin_bitreverse64(unsigned long long x) {
369+
return __builtin_bitreverse64(x);
370+
}
371+
372+
// CIR-LABEL: @_Z25test_builtin_bitreverse64y
373+
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u64i) : !u64i
374+
375+
// LLVM-LABEL: @_Z25test_builtin_bitreverse64y
376+
// LLVM: %{{.+}} = call i64 @llvm.bitreverse.i64(i64 %{{.+}})
377+
378+
// OGCG-LABEL: @_Z25test_builtin_bitreverse64y
379+
// OGCG: %{{.+}} = call i64 @llvm.bitreverse.i64(i64 %{{.+}})
380+
381+
unsigned short test_builtin_bswap16(unsigned short x) {
382+
return __builtin_bswap16(x);
383+
}
384+
385+
// CIR-LABEL: @_Z20test_builtin_bswap16t
386+
// CIR: %{{.+}} = cir.byte_swap(%{{.+}} : !u16i) : !u16i
387+
388+
// LLVM-LABEL: @_Z20test_builtin_bswap16t
389+
// LLVM: %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
390+
391+
// OGCG-LABEL: @_Z20test_builtin_bswap16t
392+
// OGCG: %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
393+
394+
unsigned test_builtin_bswap32(unsigned x) {
395+
return __builtin_bswap32(x);
396+
}
397+
398+
// CIR-LABEL: @_Z20test_builtin_bswap32j
399+
// CIR: %{{.+}} = cir.byte_swap(%{{.+}} : !u32i) : !u32i
400+
401+
// LLVM-LABEL: @_Z20test_builtin_bswap32j
402+
// LLVM: %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
403+
404+
// OGCG-LABEL: @_Z20test_builtin_bswap32j
405+
// OGCG: %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
406+
407+
unsigned long long test_builtin_bswap64(unsigned long long x) {
408+
return __builtin_bswap64(x);
409+
}
410+
411+
// CIR-LABEL: @_Z20test_builtin_bswap64y
412+
// CIR: %{{.+}} = cir.byte_swap(%{{.+}} : !u64i) : !u64i
413+
414+
// LLVM-LABEL: @_Z20test_builtin_bswap64y
415+
// LLVM: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
416+
417+
// OGCG-LABEL: @_Z20test_builtin_bswap64y
418+
// OGCG: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})

0 commit comments

Comments
 (0)