[HWArith][HWArithToHW] Implement ICmp (#3637)

This commit is contained in:
7FM 2022-08-01 12:27:48 +02:00 committed by GitHub
parent 25314edcec
commit d4dbdb1fd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 289 additions and 34 deletions

View File

@ -192,7 +192,7 @@ operand's width.
We know that `a + 1 > b`, and derive from (S) that the result type must be We know that `a + 1 > b`, and derive from (S) that the result type must be
`si<(a+1) + 1>` = `si<a+2>`. `si<(a+1) + 1>` = `si<a+2>`.
* `a < b`, i.e. the unsigned operand's width is stricly less than the signed * `a < b`, i.e. the unsigned operand's width is strictly less than the signed
operand's width. operand's width.
As `a + 1 ≤ b`, the result via (S) is `si<b+1>`. As `a + 1 ≤ b`, the result via (S) is `si<b+1>`.
@ -233,7 +233,7 @@ Example:
Result value range: `[SI_MIN<a> - SI_MAX<b>, SI_MAX<a> - SI_MIN<b>]` Result value range: `[SI_MIN<a> - SI_MAX<b>, SI_MAX<a> - SI_MIN<b>]`
We rewrite the value range and then appy the same reasoning as for the We rewrite the value range and then apply the same reasoning as for the
[signed addition](#signed-addition-S): [signed addition](#signed-addition-S):
``` ```
[SI_MIN<a> - SI_MAX<b>, SI_MAX<a> - SI_MIN<b>] [SI_MIN<a> - SI_MAX<b>, SI_MAX<a> - SI_MIN<b>]
@ -370,7 +370,7 @@ Example:
Result value range: `[SI_MIN<a> / 1, SI_MIN<a> / -1]` Result value range: `[SI_MIN<a> / 1, SI_MIN<a> / -1]`
`- SI_MIN<a>` is not convered by `si<a>`, so we have to widen the result type to `- SI_MIN<a>` is not covered by `si<a>`, so we have to widen the result type to
`si<a+1>`. `si<a+1>`.
Example: Example:
@ -465,6 +465,45 @@ required.
%3 = hwarith.cast %13 : (si14) -> i4 // (S) %3 = hwarith.cast %13 : (si14) -> i4 // (S)
``` ```
### Comparison
In order to compare two sign-aware operands, a common type must first be found
that is able to preserve the values of the two operands. Once determined, the
operands are casted to this comparison type as specified in the
[casting section](#casting). To simplify the use as well as the IR generation
of the dialect, the necessary casting steps are hidden from the user and
applied during the lowering. Thus, the following code:
```mlir
%2 = hwarith.cast %0 : (si3) -> si6
%3 = hwarith.cast %1 : (ui5) -> si6
%4 = hwarith.icmp lt %2, %3 : si6, si6
```
can be simplified to:
```mlir
%4 = hwarith.icmp lt %0, %1 : si3, ui5
```
Note that the result of the comparison is *always* of type `ui1`, regardless of
the operands. So if the `i1` type is needed, the result must be cast
accordingly.
#### Overview
| | LHS type | RHS type | Comparison type | Result type |
| - | :------- | :------- | :--------------------------------------- | :---------- |
|(U)| `ui<a>` | `ui<b>` | `ui<r>`, *r* = max(*a*, *b*) | `ui1` |
|(S)| `si<a>` | `si<b>` | `si<r>`, *r* = max(*a*, *b*) | `ui1` |
|(M)| `ui<a>` | `si<b>` | `si<r>`, *r* = *a* + 1 **if** *a**b* | `ui1` |
| | | | `si<r>`, *r* = *b* **if** *a* < *b* | `ui1` |
|(M)| `si<a>` | `ui<b>` | Same as `ui<b> si<a>` | `ui1` |
#### Examples
```mlir
%0 = hwarith.icmp lt %10, %11 : ui5, ui6 // (U)
%1 = hwarith.icmp lt %12, %13 : si3, si4 // (S)
%2 = hwarith.icmp lt %12, %11 : si3, ui6 // (M)
```
### Additional operators ### Additional operators
**TODO**: elaborate once operators are provided. **TODO**: elaborate once operators are provided.
* `hwarith.mod %0, %1 : (#l0, #l1) -> (#l2)`: * `hwarith.mod %0, %1 : (#l0, #l1) -> (#l2)`:

View File

@ -2,3 +2,6 @@ add_circt_dialect(HWArith hwarith)
add_circt_dialect_doc(HWArith hwarith) add_circt_dialect_doc(HWArith hwarith)
set(LLVM_TARGET_DEFINITIONS HWArith.td) set(LLVM_TARGET_DEFINITIONS HWArith.td)
mlir_tablegen(HWArithEnums.h.inc -gen-enum-decls)
mlir_tablegen(HWArithEnums.cpp.inc -gen-enum-defs)
add_public_tablegen_target(MLIRHWArithEnumsIncGen)

View File

@ -19,4 +19,7 @@
// Pull in the Dialect definition. // Pull in the Dialect definition.
#include "circt/Dialect/HWArith/HWArithDialect.h.inc" #include "circt/Dialect/HWArith/HWArithDialect.h.inc"
// Pull in all enum type definitions and utility function declarations.
#include "circt/Dialect/HWArith/HWArithEnums.h.inc"
#endif // CIRCT_DIALECT_HWARITH_HWARITHDIALECT_H #endif // CIRCT_DIALECT_HWARITH_HWARITHDIALECT_H

View File

@ -22,4 +22,15 @@
#define GET_OP_CLASSES #define GET_OP_CLASSES
#include "circt/Dialect/HWArith/HWArith.h.inc" #include "circt/Dialect/HWArith/HWArith.h.inc"
namespace circt {
namespace hwarith {
// Infer the bitwidth as well as the signedness that is required to store the
// sum of two given integer types without over- or underflowing.
unsigned inferAddResultType(IntegerType::SignednessSemantics &signedness,
IntegerType lhs, IntegerType rhs);
} // namespace hwarith
} // namespace circt
#endif // CIRCT_DIALECT_HWARITH_OPS_H #endif // CIRCT_DIALECT_HWARITH_OPS_H

View File

@ -13,6 +13,7 @@
#ifndef CIRCT_DIALECT_HWARITH_HWARITHOPS_TD #ifndef CIRCT_DIALECT_HWARITH_HWARITHOPS_TD
#define CIRCT_DIALECT_HWARITH_HWARITHOPS_TD #define CIRCT_DIALECT_HWARITH_HWARITHOPS_TD
include "mlir/IR/EnumAttr.td"
include "mlir/IR/OpAsmInterface.td" include "mlir/IR/OpAsmInterface.td"
include "mlir/Interfaces/InferTypeOpInterface.td" include "mlir/Interfaces/InferTypeOpInterface.td"
include "mlir/Interfaces/SideEffectInterfaces.td" include "mlir/Interfaces/SideEffectInterfaces.td"
@ -201,4 +202,53 @@ def HWArith_CastOp : HWArithOp<"cast", [NoSideEffect]> {
let hasVerifier = 1; let hasVerifier = 1;
} }
// Comparison predicates
// `==` and `!=`
def ICmpPredicateEQ : I64EnumAttrCase<"eq", 0b000>;
def ICmpPredicateNE : I64EnumAttrCase<"ne", 0b001>;
// `<` and `>=`
def ICmpPredicateLT : I64EnumAttrCase<"lt", 0b010>;
def ICmpPredicateGE : I64EnumAttrCase<"ge", 0b011>;
// `<=` and `>`
def ICmpPredicateLE : I64EnumAttrCase<"le", 0b100>;
def ICmpPredicateGT : I64EnumAttrCase<"gt", 0b101>;
let cppNamespace = "circt::hwarith" in
def ICmpPredicate : I64EnumAttr<
"ICmpPredicate",
"hwarith.icmp comparison predicate",
[ICmpPredicateEQ, ICmpPredicateNE, ICmpPredicateLT,
ICmpPredicateGE, ICmpPredicateLE, ICmpPredicateGT]>;
def ICmpOp : HWArithOp<"icmp", [NoSideEffect]> {
let summary = "Sign- and bitwidth-aware integer comparison.";
let description = [{
The `icmp` operation compares two integers using a predicate. If the
predicate is true, returns 1, otherwise returns 0. This operation always
returns a one bit wide result of type `ui1`. Both operand types may be
signed or unsigned scalar integer types of arbitrary bitwidth.
| LHS type | RHS type | Comparison type | Result type |
| :------- | :------- | :--------------------------------------- | :---------- |
| `ui<a>` | `ui<b>` | `ui<r>`, *r* = max(*a*, *b*) | `ui1` |
| `si<a>` | `si<b>` | `si<r>`, *r* = max(*a*, *b*) | `ui1` |
| `ui<a>` | `si<b>` | `si<r>`, *r* = *a* + 1 **if** *a* *b* | `ui1` |
| | | `si<r>`, *r* = *b* **if** *a* < *b* | `ui1` |
| `si<a>` | `ui<b>` | Same as `ui<b> si<a>` | `ui1` |
Examples:
```mlir
%0 = hwarith.icmp lt %10, %11 : ui5, ui6
%1 = hwarith.icmp lt %12, %13 : si3, si4
%2 = hwarith.icmp lt %12, %11 : si3, ui6
```
}];
let arguments = (ins ICmpPredicate:$predicate,
HWArithIntegerType:$lhs, HWArithIntegerType:$rhs);
let results = (outs UI1:$result);
let assemblyFormat = "$predicate $lhs `,` $rhs attr-dict `:` type($lhs) `,` type($rhs)";
}
#endif // CIRCT_DIALECT_HWARITH_HWARITHOPS_TD #endif // CIRCT_DIALECT_HWARITH_HWARITHOPS_TD

View File

@ -19,6 +19,8 @@
namespace circt { namespace circt {
namespace hwarith { namespace hwarith {
// Check whether a specified type satisfies the constraints for the
// HWArithIntegerType
bool isHWArithIntegerType(::mlir::Type type); bool isHWArithIntegerType(::mlir::Type type);
} // namespace hwarith } // namespace hwarith

View File

@ -167,6 +167,65 @@ struct CastOpLowering : public OpConversionPattern<CastOp> {
}; };
} // namespace } // namespace
namespace {
// Utility lowering function that maps a hwarith::ICmpPredicate predicate and
// the information whether the comparison contains signed values to the
// corresponding comb::ICmpPredicate.
static comb::ICmpPredicate lowerPredicate(ICmpPredicate pred, bool isSigned) {
#define _CREATE_HWARITH_ICMP_CASE(x) \
case ICmpPredicate::x: \
return isSigned ? comb::ICmpPredicate::s##x : comb::ICmpPredicate::u##x
switch (pred) {
case ICmpPredicate::eq:
return comb::ICmpPredicate::eq;
case ICmpPredicate::ne:
return comb::ICmpPredicate::ne;
_CREATE_HWARITH_ICMP_CASE(lt);
_CREATE_HWARITH_ICMP_CASE(ge);
_CREATE_HWARITH_ICMP_CASE(le);
_CREATE_HWARITH_ICMP_CASE(gt);
}
#undef _CREATE_HWARITH_ICMP_CASE
llvm_unreachable(
"Missing hwarith::ICmpPredicate to comb::ICmpPredicate lowering");
return comb::ICmpPredicate::eq;
}
struct ICmpOpLowering : public OpConversionPattern<ICmpOp> {
using OpConversionPattern<ICmpOp>::OpConversionPattern;
LogicalResult
matchAndRewrite(ICmpOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto lhsType = op.lhs().getType().cast<IntegerType>();
auto rhsType = op.rhs().getType().cast<IntegerType>();
IntegerType::SignednessSemantics cmpSignedness;
const unsigned cmpWidth =
inferAddResultType(cmpSignedness, lhsType, rhsType) - 1;
ICmpPredicate pred = op.predicate();
comb::ICmpPredicate combPred = lowerPredicate(
pred, cmpSignedness == IntegerType::SignednessSemantics::Signed);
const auto loc = op.getLoc();
Value lhsValue = extendTypeWidth(rewriter, loc, adaptor.lhs(), cmpWidth,
lhsType.isSigned());
Value rhsValue = extendTypeWidth(rewriter, loc, adaptor.rhs(), cmpWidth,
rhsType.isSigned());
rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, combPred, lhsValue, rhsValue);
return success();
}
};
} // namespace
// Templated patterns // Templated patterns
namespace { namespace {
@ -212,7 +271,7 @@ public:
target.addIllegalDialect<HWArithDialect>(); target.addIllegalDialect<HWArithDialect>();
target.addLegalDialect<comb::CombDialect, hw::HWDialect>(); target.addLegalDialect<comb::CombDialect, hw::HWDialect>();
patterns.add<ConstantOpLowering, CastOpLowering, patterns.add<ConstantOpLowering, CastOpLowering, ICmpOpLowering,
BinaryOpLowering<AddOp, comb::AddOp>, BinaryOpLowering<AddOp, comb::AddOp>,
BinaryOpLowering<SubOp, comb::SubOp>, BinaryOpLowering<SubOp, comb::SubOp>,
BinaryOpLowering<MulOp, comb::MulOp>, DivOpLowering>( BinaryOpLowering<MulOp, comb::MulOp>, DivOpLowering>(

View File

@ -15,6 +15,7 @@ add_circt_dialect_library(CIRCTHWArith
DEPENDS DEPENDS
MLIRHWArithIncGen MLIRHWArithIncGen
MLIRHWArithEnumsIncGen
MLIRIR MLIRIR
LINK_COMPONENTS LINK_COMPONENTS

View File

@ -29,4 +29,7 @@ void HWArithDialect::initialize() {
>(); >();
} }
// Provide implementations for the enums we use.
#include "circt/Dialect/HWArith/HWArithEnums.cpp.inc"
#include "circt/Dialect/HWArith/HWArithDialect.cpp.inc" #include "circt/Dialect/HWArith/HWArithDialect.cpp.inc"

View File

@ -75,32 +75,6 @@ ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
// AddOp // AddOp
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
static unsigned inferAddResultType(IntegerType::SignednessSemantics &signedness,
IntegerType lhs, IntegerType rhs) {
// the result width is never less than max(w1, w2) + 1
unsigned resultWidth = std::max(lhs.getWidth(), rhs.getWidth()) + 1;
if (lhs.getSignedness() == rhs.getSignedness()) {
// max(w1, w2) + 1 in case both operands use the same signedness
// the signedness is also identical to the operands
signedness = lhs.getSignedness();
} else {
// For mixed signedness the result is always signed
signedness = IntegerType::Signed;
// Regarding the result width two case need to be considered:
if ((lhs.isUnsigned() && lhs.getWidth() >= rhs.getWidth()) ||
(rhs.isUnsigned() && rhs.getWidth() >= lhs.getWidth())) {
// 1. the unsigned width is >= the signed width,
// then the width needs to be increased by 1
++resultWidth;
}
// 2. the unsigned width is < the signed width,
// then no further adjustment is needed
}
return resultWidth;
}
LogicalResult AddOp::inferReturnTypes(MLIRContext *context, LogicalResult AddOp::inferReturnTypes(MLIRContext *context,
Optional<Location> loc, Optional<Location> loc,
ValueRange operands, DictionaryAttr attrs, ValueRange operands, DictionaryAttr attrs,
@ -200,6 +174,32 @@ LogicalResult DivOp::inferReturnTypes(MLIRContext *context,
namespace circt { namespace circt {
namespace hwarith { namespace hwarith {
unsigned inferAddResultType(IntegerType::SignednessSemantics &signedness,
IntegerType lhs, IntegerType rhs) {
// the result width is never less than max(w1, w2) + 1
unsigned resultWidth = std::max(lhs.getWidth(), rhs.getWidth()) + 1;
if (lhs.getSignedness() == rhs.getSignedness()) {
// max(w1, w2) + 1 in case both operands use the same signedness
// the signedness is also identical to the operands
signedness = lhs.getSignedness();
} else {
// For mixed signedness the result is always signed
signedness = IntegerType::Signed;
// Regarding the result width two case need to be considered:
if ((lhs.isUnsigned() && lhs.getWidth() >= rhs.getWidth()) ||
(rhs.isUnsigned() && rhs.getWidth() >= lhs.getWidth())) {
// 1. the unsigned width is >= the signed width,
// then the width needs to be increased by 1
++resultWidth;
}
// 2. the unsigned width is < the signed width,
// then no further adjustment is needed
}
return resultWidth;
}
static LogicalResult verifyBinOp(Operation *binOp) { static LogicalResult verifyBinOp(Operation *binOp) {
auto ops = binOp->getOperands(); auto ops = binOp->getOperands();
if (ops.size() != 2) if (ops.size() != 2)

View File

@ -179,14 +179,14 @@ hw.module @div(%op0: i32, %op1: i32) -> (sisi: i32, siui: i32, uisi: i32, uiui:
%op1Unsigned = hwarith.cast %op1 : (i32) -> ui32 %op1Unsigned = hwarith.cast %op1 : (i32) -> ui32
// CHECK: %[[SIGN_BIT_OP0:.*]] = comb.extract %op0 from 31 : (i32) -> i1 // CHECK: %[[SIGN_BIT_OP0:.*]] = comb.extract %op0 from 31 : (i32) -> i1
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %0, %op0 : i1, i32 // CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[SIGN_BIT_OP0]], %op0 : i1, i32
// CHECK: %[[SIGN_BIT_OP1:.*]] = comb.extract %op1 from 31 : (i32) -> i1 // CHECK: %[[SIGN_BIT_OP1:.*]] = comb.extract %op1 from 31 : (i32) -> i1
// CHECK: %[[OP1_PADDED:.*]] = comb.concat %2, %op1 : i1, i32 // CHECK: %[[OP1_PADDED:.*]] = comb.concat %[[SIGN_BIT_OP1]], %op1 : i1, i32
// CHECK: %[[SISI_RES:.*]] = comb.divs %[[OP0_PADDED]], %[[OP1_PADDED]] : i33 // CHECK: %[[SISI_RES:.*]] = comb.divs %[[OP0_PADDED]], %[[OP1_PADDED]] : i33
%sisi = hwarith.div %op0Signed, %op1Signed : (si32, si32) -> si33 %sisi = hwarith.div %op0Signed, %op1Signed : (si32, si32) -> si33
// CHECK: %[[SIGN_BIT_OP0:.*]] = comb.extract %op0 from 31 : (i32) -> i1 // CHECK: %[[SIGN_BIT_OP0:.*]] = comb.extract %op0 from 31 : (i32) -> i1
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %5, %op0 : i1, i32 // CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[SIGN_BIT_OP0]], %op0 : i1, i32
// CHECK: %[[ZERO_EXTEND:.*]] = hw.constant false // CHECK: %[[ZERO_EXTEND:.*]] = hw.constant false
// CHECK: %[[OP1_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op1 : i1, i32 // CHECK: %[[OP1_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op1 : i1, i32
// CHECK: %[[SIUI_RES_IMM:.*]] = comb.divs %[[OP0_PADDED]], %[[OP1_PADDED]] : i33 // CHECK: %[[SIUI_RES_IMM:.*]] = comb.divs %[[OP0_PADDED]], %[[OP1_PADDED]] : i33
@ -196,7 +196,7 @@ hw.module @div(%op0: i32, %op1: i32) -> (sisi: i32, siui: i32, uisi: i32, uiui:
// CHECK: %[[ZERO_EXTEND:.*]] = hw.constant false // CHECK: %[[ZERO_EXTEND:.*]] = hw.constant false
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op0 : i1, i32 // CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op0 : i1, i32
// CHECK: %[[SIGN_BIT_OP1:.*]] = comb.extract %op1 from 31 : (i32) -> i1 // CHECK: %[[SIGN_BIT_OP1:.*]] = comb.extract %op1 from 31 : (i32) -> i1
// CHECK: %[[OP1_PADDED:.*]] = comb.concat %11, %op1 : i1, i32 // CHECK: %[[OP1_PADDED:.*]] = comb.concat %[[SIGN_BIT_OP1]], %op1 : i1, i32
// CHECK: %[[UISI_RES:.*]] = comb.divs %[[OP0_PADDED]], %[[OP1_PADDED]] : i33 // CHECK: %[[UISI_RES:.*]] = comb.divs %[[OP0_PADDED]], %[[OP1_PADDED]] : i33
%uisi = hwarith.div %op0Unsigned, %op1Signed : (ui32, si32) -> si33 %uisi = hwarith.div %op0Unsigned, %op1Signed : (ui32, si32) -> si33
@ -213,3 +213,83 @@ hw.module @div(%op0: i32, %op1: i32) -> (sisi: i32, siui: i32, uisi: i32, uiui:
// CHECK: hw.output %[[SISI_OUT]], %[[SIUI_RES]], %[[UISI_OUT]], %[[UIUI_RES]] : i32, i32, i32, i32 // CHECK: hw.output %[[SISI_OUT]], %[[SIUI_RES]], %[[UISI_OUT]], %[[UIUI_RES]] : i32, i32, i32, i32
hw.output %sisiOut, %siuiOut, %uisiOut, %uiuiOut : i32, i32, i32, i32 hw.output %sisiOut, %siuiOut, %uisiOut, %uiuiOut : i32, i32, i32, i32
} }
// -----
// CHECK: hw.module @icmp(%op0: i32, %op1: i32) -> (sisi: i1, siui: i1, uisi: i1, uiui: i1) {
hw.module @icmp(%op0: i32, %op1: i32) -> (sisi: i1, siui: i1, uisi: i1, uiui: i1) {
%op0Signed = hwarith.cast %op0 : (i32) -> si32
%op0Unsigned = hwarith.cast %op0 : (i32) -> ui32
%op1Signed = hwarith.cast %op1 : (i32) -> si32
%op1Unsigned = hwarith.cast %op1 : (i32) -> ui32
// CHECK: %[[SISI_OUT:.*]] = comb.icmp slt %op0, %op1 : i32
%sisi = hwarith.icmp lt %op0Signed, %op1Signed : si32, si32
// CHECK: %[[SIGN_BIT_OP0:.*]] = comb.extract %op0 from 31 : (i32) -> i1
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[SIGN_BIT_OP0]], %op0 : i1, i32
// CHECK: %[[ZERO_EXTEND:.*]] = hw.constant false
// CHECK: %[[OP1_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op1 : i1, i32
// CHECK: %[[SIUI_OUT:.*]] = comb.icmp slt %[[OP0_PADDED]], %[[OP1_PADDED]] : i33
%siui = hwarith.icmp lt %op0Signed, %op1Unsigned : si32, ui32
// CHECK: %[[ZERO_EXTEND:.*]] = hw.constant false
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op0 : i1, i32
// CHECK: %[[SIGN_BIT_OP1:.*]] = comb.extract %op1 from 31 : (i32) -> i1
// CHECK: %[[OP1_PADDED:.*]] = comb.concat %[[SIGN_BIT_OP1]], %op1 : i1, i32
// CHECK: %[[UISI_OUT:.*]] = comb.icmp slt %[[OP0_PADDED]], %[[OP1_PADDED]] : i33
%uisi = hwarith.icmp lt %op0Unsigned, %op1Signed : ui32, si32
// CHECK: %[[UIUI_OUT:.*]] = comb.icmp ult %op0, %op1 : i32
%uiui = hwarith.icmp lt %op0Unsigned, %op1Unsigned : ui32, ui32
%sisiOut = hwarith.cast %sisi : (ui1) -> i1
%siuiOut = hwarith.cast %siui : (ui1) -> i1
%uisiOut = hwarith.cast %uisi : (ui1) -> i1
%uiuiOut = hwarith.cast %uiui : (ui1) -> i1
// CHECK: hw.output %[[SISI_OUT]], %[[SIUI_OUT]], %[[UISI_OUT]], %[[UIUI_OUT]] : i1, i1, i1, i1
hw.output %sisiOut, %siuiOut, %uisiOut, %uiuiOut : i1, i1, i1, i1
}
// -----
// CHECK: hw.module @icmp_mixed_width(%op0: i5, %op1: i7) -> (sisi: i1, siui: i1, uisi: i1, uiui: i1) {
hw.module @icmp_mixed_width(%op0: i5, %op1: i7) -> (sisi: i1, siui: i1, uisi: i1, uiui: i1) {
%op0Signed = hwarith.cast %op0 : (i5) -> si5
%op0Unsigned = hwarith.cast %op0 : (i5) -> ui5
%op1Signed = hwarith.cast %op1 : (i7) -> si7
%op1Unsigned = hwarith.cast %op1 : (i7) -> ui7
// CHECK: %[[SIGN_BIT_OP0:.*]] = comb.extract %op0 from 4 : (i5) -> i1
// CHECK: %[[SIGN_EXTEND:.*]] = comb.replicate %[[SIGN_BIT_OP0]] : (i1) -> i2
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[SIGN_EXTEND]], %op0 : i2, i5
// CHECK: %[[SISI_OUT:.*]] = comb.icmp slt %[[OP0_PADDED]], %op1 : i7
%sisi = hwarith.icmp lt %op0Signed, %op1Signed : si5, si7
// CHECK: %[[SIGN_BIT_OP0:.*]] = comb.extract %op0 from 4 : (i5) -> i1
// CHECK: %[[SIGN_EXTEND:.*]] = comb.replicate %[[SIGN_BIT_OP0]] : (i1) -> i3
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[SIGN_EXTEND]], %op0 : i3, i5
// CHECK: %[[ZERO_EXTEND:.*]] = hw.constant false
// CHECK: %[[OP1_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op1 : i1, i7
// CHECK: %[[SIUI_OUT:.*]] = comb.icmp slt %[[OP0_PADDED]], %[[OP1_PADDED]] : i8
%siui = hwarith.icmp lt %op0Signed, %op1Unsigned : si5, ui7
// CHECK: %[[ZERO_EXTEND:.*]] = hw.constant 0 : i2
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op0 : i2, i5
// CHECK: %[[UISI_OUT:.*]] = comb.icmp slt %[[OP0_PADDED]], %op1 : i7
%uisi = hwarith.icmp lt %op0Unsigned, %op1Signed : ui5, si7
// CHECK: %[[ZERO_EXTEND:.*]] = hw.constant 0 : i2
// CHECK: %[[OP0_PADDED:.*]] = comb.concat %[[ZERO_EXTEND]], %op0 : i2, i5
// CHECK: %[[UIUI_OUT:.*]] = comb.icmp ult %[[OP0_PADDED]], %op1 : i7
%uiui = hwarith.icmp lt %op0Unsigned, %op1Unsigned : ui5, ui7
%sisiOut = hwarith.cast %sisi : (ui1) -> i1
%siuiOut = hwarith.cast %siui : (ui1) -> i1
%uisiOut = hwarith.cast %uisi : (ui1) -> i1
%uiuiOut = hwarith.cast %uiui : (ui1) -> i1
// CHECK: hw.output %[[SISI_OUT]], %[[SIUI_OUT]], %[[UISI_OUT]], %[[UIUI_OUT]] : i1, i1, i1, i1
hw.output %sisiOut, %siuiOut, %uisiOut, %uiuiOut : i1, i1, i1, i1
}

View File

@ -31,4 +31,8 @@ hw.module @test1() {
%11 = hwarith.add %10, %6 : (si9, ui3) -> si10 %11 = hwarith.add %10, %6 : (si9, ui3) -> si10
// CHECK: %12 = hwarith.cast %11 : (si10) -> i9 // CHECK: %12 = hwarith.cast %11 : (si10) -> i9
%12 = hwarith.cast %11 : (si10) -> i9 %12 = hwarith.cast %11 : (si10) -> i9
// CHECK: %13 = hwarith.icmp eq %5, %10 : ui2, si9
%13 = hwarith.icmp eq %5, %10 : ui2, si9
// CHECK: %14 = hwarith.cast %13 : (ui1) -> i1
%14 = hwarith.cast %13 : (ui1) -> i1
} }