[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.
This commit is contained in:
parent
d02c85a29b
commit
265fb3605d
@ -2808,6 +2808,45 @@ def BitPopcountOp : CIR_BitOpBase<"bit.popcnt",
|
||||
}];
|
||||
}
|
||||
|
||||
def BitReverseOp : CIR_BitOpBase<"bit.reverse",
|
||||
CIR_UIntOfWidths<[8, 16, 32, 64]>> {
|
||||
let summary = "Reverse the bit pattern of the operand integer";
|
||||
let description = [{
|
||||
The `cir.bit.reverse` operation reverses the bits of the operand integer.
|
||||
Its only argument must be of unsigned integer types of width 8, 16, 32, or
|
||||
64.
|
||||
|
||||
This operation covers the C/C++ builtin function `__builtin_bitreverse`.
|
||||
|
||||
Example:
|
||||
|
||||
```mlir
|
||||
%1 = cir.bit.reverse(%0 : !u32i): !u32i
|
||||
```
|
||||
}];
|
||||
}
|
||||
|
||||
def ByteSwapOp : CIR_BitOpBase<"byte_swap", CIR_UIntOfWidths<[16, 32, 64]>> {
|
||||
let summary = "Reverse the bytes in the object representation of the operand";
|
||||
let description = [{
|
||||
The `cir.byte_swap` operation takes an integer as operand, reverse the bytes
|
||||
in the object representation of the operand integer, and returns the result.
|
||||
|
||||
The operand integer must be an unsigned integer. Its widths must be either
|
||||
16, 32, or 64.
|
||||
|
||||
Example:
|
||||
|
||||
```mlir
|
||||
// %0 = 0x12345678
|
||||
%0 = cir.const #cir.int<305419896> : !u32i
|
||||
|
||||
// %1 should be 0x78563412
|
||||
%1 = cir.byte_swap(%0 : !u32i) : !u32i
|
||||
```
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Assume Operations
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -60,15 +60,15 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
|
||||
RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
|
||||
const CallExpr *e,
|
||||
ReturnValueSlot returnValue) {
|
||||
mlir::Location loc = getLoc(e->getSourceRange());
|
||||
|
||||
// See if we can constant fold this builtin. If so, don't emit it at all.
|
||||
// TODO: Extend this handling to all builtin calls that we can constant-fold.
|
||||
Expr::EvalResult result;
|
||||
if (e->isPRValue() && e->EvaluateAsRValue(result, cgm.getASTContext()) &&
|
||||
!result.hasSideEffects()) {
|
||||
if (result.Val.isInt()) {
|
||||
return RValue::get(builder.getConstInt(getLoc(e->getSourceRange()),
|
||||
result.Val.getInt()));
|
||||
}
|
||||
if (result.Val.isInt())
|
||||
return RValue::get(builder.getConstInt(loc, result.Val.getInt()));
|
||||
if (result.Val.isFloat()) {
|
||||
// Note: we are using result type of CallExpr to determine the type of
|
||||
// the constant. Classic codegen uses the result value to determine the
|
||||
@ -76,8 +76,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
|
||||
// hard to imagine a builtin function evaluates to a value that
|
||||
// over/underflows its own defined type.
|
||||
mlir::Type type = convertType(e->getType());
|
||||
return RValue::get(builder.getConstFP(getLoc(e->getExprLoc()), type,
|
||||
result.Val.getFloat()));
|
||||
return RValue::get(builder.getConstFP(loc, type, result.Val.getFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,8 +93,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
|
||||
assert(!cir::MissingFeatures::builtinCallMathErrno());
|
||||
assert(!cir::MissingFeatures::builtinCall());
|
||||
|
||||
mlir::Location loc = getLoc(e->getExprLoc());
|
||||
|
||||
switch (builtinIDIfNoAsmLabel) {
|
||||
default:
|
||||
break;
|
||||
@ -200,11 +197,28 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
|
||||
probability);
|
||||
}
|
||||
|
||||
auto result = builder.create<cir::ExpectOp>(getLoc(e->getSourceRange()),
|
||||
argValue.getType(), argValue,
|
||||
expectedValue, probAttr);
|
||||
auto result = builder.create<cir::ExpectOp>(
|
||||
loc, argValue.getType(), argValue, expectedValue, probAttr);
|
||||
return RValue::get(result);
|
||||
}
|
||||
|
||||
case Builtin::BI__builtin_bswap16:
|
||||
case Builtin::BI__builtin_bswap32:
|
||||
case Builtin::BI__builtin_bswap64:
|
||||
case Builtin::BI_byteswap_ushort:
|
||||
case Builtin::BI_byteswap_ulong:
|
||||
case Builtin::BI_byteswap_uint64: {
|
||||
mlir::Value arg = emitScalarExpr(e->getArg(0));
|
||||
return RValue::get(builder.create<cir::ByteSwapOp>(loc, arg));
|
||||
}
|
||||
|
||||
case Builtin::BI__builtin_bitreverse8:
|
||||
case Builtin::BI__builtin_bitreverse16:
|
||||
case Builtin::BI__builtin_bitreverse32:
|
||||
case Builtin::BI__builtin_bitreverse64: {
|
||||
mlir::Value arg = emitScalarExpr(e->getArg(0));
|
||||
return RValue::get(builder.create<cir::BitReverseOp>(loc, arg));
|
||||
}
|
||||
}
|
||||
|
||||
// If this is an alias for a lib function (e.g. __builtin_sin), emit
|
||||
|
||||
@ -535,6 +535,13 @@ mlir::LogicalResult CIRToLLVMBitPopcountOpLowering::matchAndRewrite(
|
||||
return mlir::LogicalResult::success();
|
||||
}
|
||||
|
||||
mlir::LogicalResult CIRToLLVMBitReverseOpLowering::matchAndRewrite(
|
||||
cir::BitReverseOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
rewriter.replaceOpWithNewOp<mlir::LLVM::BitReverseOp>(op, adaptor.getInput());
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
|
||||
cir::BrCondOp brOp, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
@ -551,6 +558,13 @@ mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
mlir::LogicalResult CIRToLLVMByteSwapOpLowering::matchAndRewrite(
|
||||
cir::ByteSwapOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
rewriter.replaceOpWithNewOp<mlir::LLVM::ByteSwapOp>(op, adaptor.getInput());
|
||||
return mlir::LogicalResult::success();
|
||||
}
|
||||
|
||||
mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const {
|
||||
return getTypeConverter()->convertType(ty);
|
||||
}
|
||||
@ -2044,8 +2058,10 @@ void ConvertCIRToLLVMPass::runOnOperation() {
|
||||
CIRToLLVMBitCtzOpLowering,
|
||||
CIRToLLVMBitParityOpLowering,
|
||||
CIRToLLVMBitPopcountOpLowering,
|
||||
CIRToLLVMBitReverseOpLowering,
|
||||
CIRToLLVMBrCondOpLowering,
|
||||
CIRToLLVMBrOpLowering,
|
||||
CIRToLLVMByteSwapOpLowering,
|
||||
CIRToLLVMCallOpLowering,
|
||||
CIRToLLVMCmpOpLowering,
|
||||
CIRToLLVMComplexAddOpLowering,
|
||||
|
||||
@ -94,6 +94,16 @@ public:
|
||||
mlir::ConversionPatternRewriter &) const override;
|
||||
};
|
||||
|
||||
class CIRToLLVMBitReverseOpLowering
|
||||
: public mlir::OpConversionPattern<cir::BitReverseOp> {
|
||||
public:
|
||||
using mlir::OpConversionPattern<cir::BitReverseOp>::OpConversionPattern;
|
||||
|
||||
mlir::LogicalResult
|
||||
matchAndRewrite(cir::BitReverseOp op, OpAdaptor,
|
||||
mlir::ConversionPatternRewriter &) const override;
|
||||
};
|
||||
|
||||
class CIRToLLVMBrCondOpLowering
|
||||
: public mlir::OpConversionPattern<cir::BrCondOp> {
|
||||
public:
|
||||
@ -104,6 +114,16 @@ public:
|
||||
mlir::ConversionPatternRewriter &) const override;
|
||||
};
|
||||
|
||||
class CIRToLLVMByteSwapOpLowering
|
||||
: public mlir::OpConversionPattern<cir::ByteSwapOp> {
|
||||
public:
|
||||
using mlir::OpConversionPattern<cir::ByteSwapOp>::OpConversionPattern;
|
||||
|
||||
mlir::LogicalResult
|
||||
matchAndRewrite(cir::ByteSwapOp op, OpAdaptor,
|
||||
mlir::ConversionPatternRewriter &) const override;
|
||||
};
|
||||
|
||||
class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {
|
||||
mlir::DataLayout const &dataLayout;
|
||||
|
||||
|
||||
@ -325,3 +325,94 @@ int test_builtin_popcountg(unsigned x) {
|
||||
|
||||
// OGCG-LABEL: _Z22test_builtin_popcountgj
|
||||
// OGCG: %{{.+}} = call i32 @llvm.ctpop.i32(i32 %{{.+}})
|
||||
|
||||
unsigned char test_builtin_bitreverse8(unsigned char x) {
|
||||
return __builtin_bitreverse8(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: @_Z24test_builtin_bitreverse8h
|
||||
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u8i) : !u8i
|
||||
|
||||
// LLVM-LABEL: @_Z24test_builtin_bitreverse8h
|
||||
// LLVM: %{{.+}} = call i8 @llvm.bitreverse.i8(i8 %{{.+}})
|
||||
|
||||
// OGCG-LABEL: @_Z24test_builtin_bitreverse8h
|
||||
// OGCG: %{{.+}} = call i8 @llvm.bitreverse.i8(i8 %{{.+}})
|
||||
|
||||
unsigned short test_builtin_bitreverse16(unsigned short x) {
|
||||
return __builtin_bitreverse16(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: @_Z25test_builtin_bitreverse16t
|
||||
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u16i) : !u16i
|
||||
|
||||
// LLVM-LABEL: @_Z25test_builtin_bitreverse16t
|
||||
// LLVM: %{{.+}} = call i16 @llvm.bitreverse.i16(i16 %{{.+}})
|
||||
|
||||
// OGCG-LABEL: @_Z25test_builtin_bitreverse16t
|
||||
// OGCG: %{{.+}} = call i16 @llvm.bitreverse.i16(i16 %{{.+}})
|
||||
|
||||
unsigned test_builtin_bitreverse32(unsigned x) {
|
||||
return __builtin_bitreverse32(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: @_Z25test_builtin_bitreverse32j
|
||||
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u32i) : !u32i
|
||||
|
||||
// LLVM-LABEL: @_Z25test_builtin_bitreverse32j
|
||||
// LLVM: %{{.+}} = call i32 @llvm.bitreverse.i32(i32 %{{.+}})
|
||||
|
||||
// OGCG-LABEL: @_Z25test_builtin_bitreverse32j
|
||||
// OGCG: %{{.+}} = call i32 @llvm.bitreverse.i32(i32 %{{.+}})
|
||||
|
||||
unsigned long long test_builtin_bitreverse64(unsigned long long x) {
|
||||
return __builtin_bitreverse64(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: @_Z25test_builtin_bitreverse64y
|
||||
// CIR: %{{.+}} = cir.bit.reverse(%{{.+}} : !u64i) : !u64i
|
||||
|
||||
// LLVM-LABEL: @_Z25test_builtin_bitreverse64y
|
||||
// LLVM: %{{.+}} = call i64 @llvm.bitreverse.i64(i64 %{{.+}})
|
||||
|
||||
// OGCG-LABEL: @_Z25test_builtin_bitreverse64y
|
||||
// OGCG: %{{.+}} = call i64 @llvm.bitreverse.i64(i64 %{{.+}})
|
||||
|
||||
unsigned short test_builtin_bswap16(unsigned short x) {
|
||||
return __builtin_bswap16(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: @_Z20test_builtin_bswap16t
|
||||
// CIR: %{{.+}} = cir.byte_swap(%{{.+}} : !u16i) : !u16i
|
||||
|
||||
// LLVM-LABEL: @_Z20test_builtin_bswap16t
|
||||
// LLVM: %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
|
||||
|
||||
// OGCG-LABEL: @_Z20test_builtin_bswap16t
|
||||
// OGCG: %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
|
||||
|
||||
unsigned test_builtin_bswap32(unsigned x) {
|
||||
return __builtin_bswap32(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: @_Z20test_builtin_bswap32j
|
||||
// CIR: %{{.+}} = cir.byte_swap(%{{.+}} : !u32i) : !u32i
|
||||
|
||||
// LLVM-LABEL: @_Z20test_builtin_bswap32j
|
||||
// LLVM: %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
|
||||
|
||||
// OGCG-LABEL: @_Z20test_builtin_bswap32j
|
||||
// OGCG: %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
|
||||
|
||||
unsigned long long test_builtin_bswap64(unsigned long long x) {
|
||||
return __builtin_bswap64(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: @_Z20test_builtin_bswap64y
|
||||
// CIR: %{{.+}} = cir.byte_swap(%{{.+}} : !u64i) : !u64i
|
||||
|
||||
// LLVM-LABEL: @_Z20test_builtin_bswap64y
|
||||
// LLVM: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
|
||||
|
||||
// OGCG-LABEL: @_Z20test_builtin_bswap64y
|
||||
// OGCG: %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user