[FIRRTL] Improve canonicalization involving `invalidvalue` (#2251)

This extends the FIRRTL fold and canonicalization patterns to treat
`invalidvalue` as a zero if it is an input to a primary operation. This
is inline with what the Scala implementation of the FIRRTL compiler
does.

Doing this removes quite a few of the special cases where certain ops
are aware of `invalidvalue`s and will fold as if it was a zero. The
addition of the `getConstant`, `isConstantZero`, and
`canonicalizePrimOp` helpers allow for most folders and canonicalizers
to not having to bother with `invalidvalue`s, as these helpers
implicitly map `invalidvalue`s to zero constants.

This also subsumes the work done in #2278 and #2198, which selectively
added handling for `invalidvalue`s to the `PadPrimOp`.

The extension of the folders and canonicalizers now makes all the cases
in `invalid-reg-fail.fir` pass, albeit some only after accepting that
the MLIR implementation folds *additional* cases where the Scala one
might just leave an operation untouched. These are marked with a
`<-- fixed; upstream to Scala FIRRTL impl?` and should probably be
upstreamed as additional canonicalizations on the Scala side as well.
This commit is contained in:
Fabian Schuiki 2021-12-04 09:22:04 +01:00 committed by GitHub
parent 18868ce742
commit c351dc6017
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1333 additions and 1135 deletions

View File

@ -64,16 +64,9 @@ def ZeroConstantOp : Constraint<Or<[
"$0.getDefiningOp<SpecialConstantOp>().value() == false">
]>>;
def IsInvalid : Constraint<
CPred<"$0.getDefiningOp<InvalidValueOp>()">
>;
def GetEmptyString : NativeCodeCall<
"StringAttr::get($_builder.getContext(), {}) ">;
def GetZeroConstant : NativeCodeCall<
"$_builder.create<ConstantOp>($0.getLoc(), APSInt($0.getType().cast<FIRRTLType>().getBitWidthOrSentinel()))">;
// leq(const, x) -> geq(x, const)
def LEQWithConstLHS : Pat<
(LEQPrimOp $lhs, $rhs),
@ -142,33 +135,6 @@ def GetWidthAsIntAttr : NativeCodeCall<
"IntegerAttr::get(IntegerType::get($_builder.getContext(), 32, IntegerType::Signless), "
"$0.getType().cast<FIRRTLType>().getBitWidthOrSentinel())">;
// add(x, invalid) -> pad(x, width)
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation.
def AddWithInvalidOp : Pat<
(AddPrimOp:$result $x, $y),
(PadPrimOp $x, (GetWidthAsIntAttr $result)), [
(KnownWidth $x), (IsInvalid $y)
]>;
// sub(x, invalid) -> pad(x, width)
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation.
def SubWithInvalidOp : Pat<
(SubPrimOp:$result $x, $y),
(PadPrimOp $x, (GetWidthAsIntAttr $result)), [
(KnownWidth $x), (IsInvalid $y)
]>;
// pad(invalid, width) -> zero<width>
def PadInvalid : Pat<
(PadPrimOp:$result (InvalidValueOp), $_),
(GetZeroConstant $result), []>;
////////////////////////////////////////////////////////////////////////////////
// DontTouch application
////////////////////////////////////////////////////////////////////////////////

View File

@ -296,7 +296,7 @@ class IntBinaryPrimOp<string mnemonic, Type resultType,
traits # [SameOperandsIntTypeKind]>;
let inferType = "impl::inferAddSubResult" in {
let hasCanonicalizer = true in {
let hasCanonicalizeMethod = true in {
def AddPrimOp : IntBinaryPrimOp<"add", IntType, [Commutative]>;
def SubPrimOp : IntBinaryPrimOp<"sub", IntType>;
}
@ -480,7 +480,6 @@ def PadPrimOp : PrimOp<"pad"> {
}];
let parseValidator = "impl::validateOneOperandOneConst";
let hasCanonicalizer = true;
}
class ShiftPrimOp<string mnemonic> : PrimOp<mnemonic> {

View File

@ -18,6 +18,7 @@
#include "mlir/IR/PatternMatch.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/TypeSwitch.h"
// Forward Decl for patterns.
static bool isUselessName(circt::StringRef name);
@ -93,11 +94,12 @@ static bool isUselessName(StringRef name) {
}
/// Implicitly replace the operand to a constant folding operation with a const
/// 0 in case the operand is non-constant but has a bit width 0.
/// 0 in case the operand is non-constant but has a bit width 0, or if the
/// operand is an invalid value.
///
/// This makes constant folding significantly easier, as we can simply pass the
/// operands to an operation through this function to appropriately replace any
/// zero-width dynamic values with a constant of value 0.
/// zero-width dynamic values or invalid values with a constant of value 0.
static Optional<APSInt> getExtendedConstant(Value operand, Attribute constant,
int32_t destWidth) {
assert(operand.getType().isa<IntType>() &&
@ -107,6 +109,10 @@ static Optional<APSInt> getExtendedConstant(Value operand, Attribute constant,
if (destWidth < 0)
return {};
// InvalidValue inputs simply read as zero.
if (auto result = constant.dyn_cast_or_null<InvalidValueAttr>())
return APSInt(destWidth, operand.getType().cast<IntType>().isUnsigned());
// Extension signedness follows the operand sign.
if (IntegerAttr result = constant.dyn_cast_or_null<IntegerAttr>())
return extOrTruncZeroWidth(result.getAPSInt(), destWidth);
@ -118,6 +124,34 @@ static Optional<APSInt> getExtendedConstant(Value operand, Attribute constant,
return {};
}
/// Determine the value of a constant operand for the sake of constant folding.
/// This will map `invalidvalue` to a zero value of the corresopnding type,
/// which aligns with how the Scala FIRRTL compiler handles invalids in most
/// cases. For a full discussion of this see the FIRRTL Rationale document.
static Optional<APSInt> getConstant(Attribute operand) {
if (!operand)
return {};
if (auto attr = operand.dyn_cast<InvalidValueAttr>()) {
if (auto type = attr.getType().dyn_cast<IntType>())
return APSInt(type.getWidth().getValueOr(1), type.isUnsigned());
if (attr.getType().isa<ClockType, ResetType, AsyncResetType>())
return APSInt(1);
}
if (auto attr = operand.dyn_cast<IntegerAttr>())
return attr.getAPSInt();
if (auto attr = operand.dyn_cast<BoolAttr>())
return APSInt(APInt(1, attr.getValue()));
return {};
}
/// Determine whether a constant operand is a zero value for the sake of
/// constant folding. This considers `invalidvalue` to be zero.
static bool isConstantZero(Attribute operand) {
if (auto cst = getConstant(operand))
return cst->isZero();
return false;
}
/// This is the policy for folding, which depends on the sort of operator we're
/// processing.
enum class BinOpKind {
@ -192,6 +226,63 @@ constFoldFIRRTLBinaryOp(Operation *op, ArrayRef<Attribute> operands,
return getIntAttr(resultType, resultValue);
}
/// Applies the canonicalization function `canonicalize` to the given operation.
///
/// Determines which (if any) of the operation's operands are constants, and
/// provides them as arguments to the callback function. Any `invalidvalue` in
/// the input is mapped to a constant zero. The value returned from the callback
/// is used as the replacement for `op`, and an additional pad operation is
/// inserted if necessary. Does nothing if the result of `op` is of unknown
/// width, in which case the necessity of a pad cannot be determined.
static LogicalResult canonicalizePrimOp(
Operation *op, PatternRewriter &rewriter,
const function_ref<OpFoldResult(ArrayRef<Attribute>)> &canonicalize) {
// Can only operate on FIRRTL primitive operations.
if (op->getNumResults() != 1)
return failure();
auto type = op->getResult(0).getType().dyn_cast<FIRRTLType>();
if (!type)
return failure();
// Can only operate on operations with a known result width.
auto width = type.getBitWidthOrSentinel();
if (width < 0)
return failure();
// Determine which of the operands are constants.
SmallVector<Attribute, 3> constOperands;
constOperands.reserve(op->getNumOperands());
for (auto operand : op->getOperands()) {
Attribute attr;
if (auto *defOp = operand.getDefiningOp())
TypeSwitch<Operation *>(defOp)
.Case<ConstantOp, SpecialConstantOp, InvalidValueOp>(
[&](auto op) { attr = op.fold({}).template get<Attribute>(); });
constOperands.push_back(attr);
}
// Perform the canonicalization and materialize the result if it is a
// constant.
auto result = canonicalize(constOperands);
if (!result)
return failure();
Value resultValue;
if (auto cst = result.dyn_cast<Attribute>())
resultValue = op->getDialect()
->materializeConstant(rewriter, cst, type, op->getLoc())
->getResult(0);
else
resultValue = result.get<Value>();
// Insert a pad if the type widths disagree.
if (width != resultValue.getType().cast<FIRRTLType>().getBitWidthOrSentinel())
resultValue = rewriter.create<PadPrimOp>(op->getLoc(), resultValue, width);
assert(type == resultValue.getType() && "canonicalization changed type");
rewriter.replaceOp(op, resultValue);
return success();
}
/// Get the largest unsigned value of a given bit width. Returns a 1-bit zero
/// value if `bitWidth` is 0.
static APInt getMaxUnsignedValue(unsigned bitWidth) {
@ -237,9 +328,17 @@ OpFoldResult AddPrimOp::fold(ArrayRef<Attribute> operands) {
[=](APSInt a, APSInt b) { return a + b; });
}
void AddPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
MLIRContext *context) {
results.insert<patterns::AddWithInvalidOp>(context);
LogicalResult AddPrimOp::canonicalize(AddPrimOp op, PatternRewriter &rewriter) {
return canonicalizePrimOp(op, rewriter,
[&](ArrayRef<Attribute> operands) -> OpFoldResult {
// add(x, 0) -> x
if (isConstantZero(operands[1]))
return op.getOperand(0);
// add(0, x) -> x
if (isConstantZero(operands[0]))
return op.getOperand(1);
return {};
});
}
OpFoldResult SubPrimOp::fold(ArrayRef<Attribute> operands) {
@ -247,20 +346,33 @@ OpFoldResult SubPrimOp::fold(ArrayRef<Attribute> operands) {
[=](APSInt a, APSInt b) { return a - b; });
}
void SubPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
MLIRContext *context) {
results.insert<patterns::SubWithInvalidOp>(context);
LogicalResult SubPrimOp::canonicalize(SubPrimOp op, PatternRewriter &rewriter) {
return canonicalizePrimOp(
op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
// sub(x, 0) -> x
if (isConstantZero(operands[1]))
return op.getOperand(0);
// sub(0, x) -> neg(x) if x is signed
// sub(0, x) -> asUInt(neg(x)) if x is unsigned
if (isConstantZero(operands[0])) {
Value value =
rewriter.create<NegPrimOp>(op.getLoc(), op.getOperand(1));
if (op.getType().isa<UIntType>())
value = rewriter.create<AsUIntPrimOp>(op.getLoc(), value);
return value;
}
return {};
});
}
OpFoldResult MulPrimOp::fold(ArrayRef<Attribute> operands) {
// mul(x, invalid) -> 0
// mul(x, 0) -> 0
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation. Note: the Scala FIRRTL Compiler does NOT currently optimize
// multiplication this way and will emit "x * 0".
if (operands[1].dyn_cast_or_null<InvalidValueAttr>() ||
operands[0].dyn_cast_or_null<InvalidValueAttr>())
if (isConstantZero(operands[1]) || isConstantZero(operands[0]))
return getIntZerosAttr(getType());
return constFoldFIRRTLBinaryOp(*this, operands, BinOpKind::Normal,
@ -283,14 +395,13 @@ OpFoldResult DivPrimOp::fold(ArrayRef<Attribute> operands) {
return getIntAttr(getType(), APInt(width, 1));
}
// div(invalid, x) -> 0
// div(0, x) -> 0
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation. Note: the Scala FIRRTL Compiler does NOT currently optimize
// division this way and will emit "0 / x".
if (operands[0].dyn_cast_or_null<InvalidValueAttr>() &&
!operands[1].dyn_cast_or_null<InvalidValueAttr>())
if (isConstantZero(operands[0]) && !isConstantZero(operands[1]))
return getIntZerosAttr(getType());
/// div(x, 1) -> x : (uint, uint) -> uint
@ -311,14 +422,22 @@ OpFoldResult DivPrimOp::fold(ArrayRef<Attribute> operands) {
}
OpFoldResult RemPrimOp::fold(ArrayRef<Attribute> operands) {
// rem(invalid, x) -> 0
// rem(x, x) -> 0
//
// Division by zero is undefined in the FIRRTL specification. This fold
// exploits that fact to optimize self division remainder to zero. Note:
// this should supersede any division with invalid or zero. Remainder of
// division of invalid by invalid should be zero.
if (lhs() == rhs())
return getIntZerosAttr(getType());
// rem(0, x) -> 0
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation. Note: the Scala FIRRTL Compiler does NOT currently optimize
// division this way and will emit "0 % x".
if (operands[0].dyn_cast_or_null<InvalidValueAttr>() &&
!operands[1].dyn_cast_or_null<InvalidValueAttr>())
if (isConstantZero(operands[0]))
return getIntZerosAttr(getType());
return constFoldFIRRTLBinaryOp(*this, operands, BinOpKind::DivideOrShift,
@ -353,22 +472,13 @@ OpFoldResult DShrPrimOp::fold(ArrayRef<Attribute> operands) {
// TODO: Move to DRR.
OpFoldResult AndPrimOp::fold(ArrayRef<Attribute> operands) {
// and(x, invalid) -> 0
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation.
if (operands[1].dyn_cast_or_null<InvalidValueAttr>() ||
operands[0].dyn_cast_or_null<InvalidValueAttr>())
return getIntZerosAttr(getType());
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
/// and(x, 0) -> 0
if (rhsCst.getValue().isZero() && rhs().getType() == getType())
return rhs();
if (rhsCst->isZero() && rhs().getType() == getType())
return getIntZerosAttr(getType());
/// and(x, -1) -> x
if (rhsCst.getValue().isAllOnes() && lhs().getType() == getType() &&
if (rhsCst->isAllOnes() && lhs().getType() == getType() &&
rhs().getType() == getType())
return lhs();
}
@ -383,20 +493,7 @@ OpFoldResult AndPrimOp::fold(ArrayRef<Attribute> operands) {
}
OpFoldResult OrPrimOp::fold(ArrayRef<Attribute> operands) {
// or(x, invalid) -> x
// or(invalid, x) -> x
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation.
if (operands[0].dyn_cast_or_null<InvalidValueAttr>() &&
rhs().getType() == getType())
return rhs();
if (operands[1].dyn_cast_or_null<InvalidValueAttr>() &&
lhs().getType() == getType())
return lhs();
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
/// or(x, 0) -> x
if (rhsCst.getValue().isZero() && lhs().getType() == getType())
return lhs();
@ -417,21 +514,8 @@ OpFoldResult OrPrimOp::fold(ArrayRef<Attribute> operands) {
}
OpFoldResult XorPrimOp::fold(ArrayRef<Attribute> operands) {
// xor(x, invalid) -> x
// xor(invalid, x) -> x
//
// This is legal because it aligns with the Scala FIRRTL Compiler
// interpretation of lowering invalid to constant zero before constant
// propagation.
if (operands[0].dyn_cast_or_null<InvalidValueAttr>() &&
rhs().getType() == getType())
return rhs();
if (operands[1].dyn_cast_or_null<InvalidValueAttr>() &&
lhs().getType() == getType())
return lhs();
/// xor(x, 0) -> x
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>())
if (auto rhsCst = getConstant(operands[1]))
if (rhsCst.getValue().isZero() && lhs().getType() == getType())
return lhs();
@ -461,7 +545,7 @@ OpFoldResult LEQPrimOp::fold(ArrayRef<Attribute> operands) {
// Comparison against constant outside type bounds.
if (auto width = lhs().getType().cast<IntType>().getWidth()) {
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
auto commonWidth =
std::max<int32_t>(*width, rhsCst.getValue().getBitWidth());
commonWidth = std::max(commonWidth, 0);
@ -508,14 +592,14 @@ OpFoldResult LTPrimOp::fold(ArrayRef<Attribute> operands) {
return getIntAttr(getType(), APInt(1, 0));
// lt(x, 0) -> 0 when x is unsigned
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
if (rhsCst.getValue().isZero() && lhs().getType().isa<UIntType>())
return getIntAttr(getType(), APInt(1, 0));
}
// Comparison against constant outside type bounds.
if (auto width = lhs().getType().cast<IntType>().getWidth()) {
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
auto commonWidth =
std::max<int32_t>(*width, rhsCst.getValue().getBitWidth());
commonWidth = std::max(commonWidth, 0);
@ -561,14 +645,14 @@ OpFoldResult GEQPrimOp::fold(ArrayRef<Attribute> operands) {
return getIntAttr(getType(), APInt(1, 1));
// geq(x, 0) -> 1 when x is unsigned
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
if (rhsCst.getValue().isZero() && lhs().getType().isa<UIntType>())
return getIntAttr(getType(), APInt(1, 1));
}
// Comparison against constant outside type bounds.
if (auto width = lhs().getType().cast<IntType>().getWidth()) {
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
auto commonWidth =
std::max<int32_t>(*width, rhsCst.getValue().getBitWidth());
commonWidth = std::max(commonWidth, 0);
@ -616,7 +700,7 @@ OpFoldResult GTPrimOp::fold(ArrayRef<Attribute> operands) {
// Comparison against constant outside type bounds.
if (auto width = lhs().getType().cast<IntType>().getWidth()) {
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
auto commonWidth =
std::max<int32_t>(*width, rhsCst.getValue().getBitWidth());
commonWidth = std::max(commonWidth, 0);
@ -655,7 +739,7 @@ OpFoldResult EQPrimOp::fold(ArrayRef<Attribute> operands) {
if (lhs() == rhs())
return getIntAttr(getType(), APInt(1, 1));
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
/// eq(x, 1) -> x when x is 1 bit.
/// TODO: Support SInt<1> on the LHS etc.
if (rhsCst.getValue().isAllOnes() && lhs().getType() == getType() &&
@ -669,33 +753,35 @@ OpFoldResult EQPrimOp::fold(ArrayRef<Attribute> operands) {
}
LogicalResult EQPrimOp::canonicalize(EQPrimOp op, PatternRewriter &rewriter) {
return canonicalizePrimOp(
op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
if (auto rhsCst = getConstant(operands[1])) {
auto width =
op.lhs().getType().cast<IntType>().getBitWidthOrSentinel();
if (auto rhsCst = dyn_cast_or_null<ConstantOp>(op.rhs().getDefiningOp())) {
auto width = op.lhs().getType().cast<IntType>().getBitWidthOrSentinel();
// eq(x, 0) -> not(x) when x is 1 bit.
if (rhsCst->isZero() && op.lhs().getType() == op.getType() &&
op.rhs().getType() == op.getType()) {
return rewriter.create<NotPrimOp>(op.getLoc(), op.lhs())
.getResult();
}
// eq(x, 0) -> not(x) when x is 1 bit.
if (rhsCst.value().isZero() && op.lhs().getType() == op.getType() &&
op.rhs().getType() == op.getType()) {
rewriter.replaceOpWithNewOp<NotPrimOp>(op, op.lhs());
return success();
}
// eq(x, 0) -> not(orr(x)) when x is >1 bit
if (rhsCst->isZero() && width > 1) {
auto orrOp = rewriter.create<OrRPrimOp>(op.getLoc(), op.lhs());
return rewriter.create<NotPrimOp>(op.getLoc(), orrOp).getResult();
}
// eq(x, 0) -> not(orr(x)) when x is >1 bit
if (rhsCst.value().isZero() && width > 1) {
auto orrOp = rewriter.create<OrRPrimOp>(op.getLoc(), op.lhs());
rewriter.replaceOpWithNewOp<NotPrimOp>(op, orrOp);
return success();
}
// eq(x, ~0) -> andr(x) when x is >1 bit
if (rhsCst->isAllOnes() && width > 1 &&
op.lhs().getType() == op.rhs().getType()) {
return rewriter.create<AndRPrimOp>(op.getLoc(), op.lhs())
.getResult();
}
}
// eq(x, ~0) -> andr(x) when x is >1 bit
if (rhsCst.value().isAllOnes() && width > 1 &&
op.lhs().getType() == op.rhs().getType()) {
rewriter.replaceOpWithNewOp<AndRPrimOp>(op, op.lhs());
return success();
}
}
return failure();
return {};
});
}
OpFoldResult NEQPrimOp::fold(ArrayRef<Attribute> operands) {
@ -703,7 +789,7 @@ OpFoldResult NEQPrimOp::fold(ArrayRef<Attribute> operands) {
if (lhs() == rhs())
return getIntAttr(getType(), APInt(1, 0));
if (auto rhsCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto rhsCst = getConstant(operands[1])) {
/// neq(x, 0) -> x when x is 1 bit.
/// TODO: Support SInt<1> on the LHS etc.
if (rhsCst.getValue().isZero() && lhs().getType() == getType() &&
@ -717,31 +803,35 @@ OpFoldResult NEQPrimOp::fold(ArrayRef<Attribute> operands) {
}
LogicalResult NEQPrimOp::canonicalize(NEQPrimOp op, PatternRewriter &rewriter) {
if (auto rhsCst = dyn_cast_or_null<ConstantOp>(op.rhs().getDefiningOp())) {
auto width = op.lhs().getType().cast<IntType>().getBitWidthOrSentinel();
// neq(x, 1) -> not(x) when x is 1 bit
if (rhsCst.value().isAllOnes() && op.lhs().getType() == op.getType() &&
op.rhs().getType() == op.getType()) {
rewriter.replaceOpWithNewOp<NotPrimOp>(op, op.lhs());
return success();
}
return canonicalizePrimOp(
op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
if (auto rhsCst = getConstant(operands[1])) {
auto width =
op.lhs().getType().cast<IntType>().getBitWidthOrSentinel();
// neq(x, 0) -> orr(x) when x is >1 bit
if (rhsCst.value().isZero() && width > 1) {
rewriter.replaceOpWithNewOp<OrRPrimOp>(op, op.lhs());
return success();
}
// neq(x, 1) -> not(x) when x is 1 bit
if (rhsCst->isAllOnes() && op.lhs().getType() == op.getType() &&
op.rhs().getType() == op.getType()) {
return rewriter.create<NotPrimOp>(op.getLoc(), op.lhs())
.getResult();
}
// neq(x, ~0) -> not(andr(x))) when x is >1 bit
if (rhsCst.value().isAllOnes() && width > 1 &&
op.lhs().getType() == op.rhs().getType()) {
auto andrOp = rewriter.create<AndRPrimOp>(op.getLoc(), op.lhs());
rewriter.replaceOpWithNewOp<NotPrimOp>(op, andrOp);
return success();
}
}
// neq(x, 0) -> orr(x) when x is >1 bit
if (rhsCst->isZero() && width > 1) {
return rewriter.create<OrRPrimOp>(op.getLoc(), op.lhs())
.getResult();
}
return failure();
// neq(x, ~0) -> not(andr(x))) when x is >1 bit
if (rhsCst->isAllOnes() && width > 1 &&
op.lhs().getType() == op.rhs().getType()) {
auto andrOp = rewriter.create<AndRPrimOp>(op.getLoc(), op.lhs());
return rewriter.create<NotPrimOp>(op.getLoc(), andrOp).getResult();
}
}
return {};
});
}
//===----------------------------------------------------------------------===//
@ -753,19 +843,12 @@ OpFoldResult AsSIntPrimOp::fold(ArrayRef<Attribute> operands) {
if (input().getType() == getType())
return input();
if (!operands[0])
return {};
// Constant clocks and resets are bool attributes.
if (auto attr = operands[0].dyn_cast<BoolAttr>())
return getIntAttr(getType(), APInt(/*bitWidth*/ 1, attr.getValue()));
// Be careful to only fold the cast into the constant if the size is known.
// Otherwise width inference may produce differently-sized constants if the
// sign changes.
if (auto attr = operands[0].dyn_cast<IntegerAttr>())
if (getType().hasWidth())
return getIntAttr(getType(), attr.getValue());
if (getType().hasWidth())
if (auto cst = getConstant(operands[0]))
return getIntAttr(getType(), *cst);
return {};
}
@ -775,19 +858,12 @@ OpFoldResult AsUIntPrimOp::fold(ArrayRef<Attribute> operands) {
if (input().getType() == getType())
return input();
if (!operands[0])
return {};
// Constant clocks and resets are bool attributes.
if (auto attr = operands[0].dyn_cast<BoolAttr>())
return getIntAttr(getType(), APInt(/*bitWidth*/ 1, attr.getValue()));
// Be careful to only fold the cast into the constant if the size is known.
// Otherwise width inference may produce differently-sized constants if the
// sign changes.
if (auto attr = operands[0].dyn_cast<IntegerAttr>())
if (getType().hasWidth())
return getIntAttr(getType(), attr.getValue());
if (getType().hasWidth())
if (auto cst = getConstant(operands[0]))
return getIntAttr(getType(), *cst);
return {};
}
@ -798,8 +874,8 @@ OpFoldResult AsAsyncResetPrimOp::fold(ArrayRef<Attribute> operands) {
return input();
// Constant fold.
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return BoolAttr::get(getContext(), attr.getValue().getBoolValue());
if (auto cst = getConstant(operands[0]))
return BoolAttr::get(getContext(), cst->getBoolValue());
return {};
}
@ -810,8 +886,8 @@ OpFoldResult AsClockPrimOp::fold(ArrayRef<Attribute> operands) {
return input();
// Constant fold.
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return BoolAttr::get(getContext(), attr.getValue().getBoolValue());
if (auto cst = getConstant(operands[0]))
return BoolAttr::get(getContext(), cst->getBoolValue());
return {};
}
@ -835,9 +911,8 @@ OpFoldResult NegPrimOp::fold(ArrayRef<Attribute> operands) {
// FIRRTL negate always adds a bit.
// -x ---> 0-sext(x) or 0-zext(x)
auto cst = getExtendedConstant(getOperand(), operands[0],
getType().getWidthOrSentinel());
if (cst.hasValue())
if (auto cst = getExtendedConstant(getOperand(), operands[0],
getType().getWidthOrSentinel()))
return getIntAttr(getType(), APInt((*cst).getBitWidth(), 0) - *cst);
return {};
@ -847,8 +922,9 @@ OpFoldResult NotPrimOp::fold(ArrayRef<Attribute> operands) {
if (!hasKnownWidthIntTypes(*this))
return {};
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return getIntAttr(getType(), ~attr.getValue());
if (auto cst = getExtendedConstant(getOperand(), operands[0],
getType().getWidthOrSentinel()))
return getIntAttr(getType(), ~*cst);
return {};
}
@ -858,8 +934,8 @@ OpFoldResult AndRPrimOp::fold(ArrayRef<Attribute> operands) {
return {};
// x == -1
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return getIntAttr(getType(), APInt(1, attr.getValue().isAllOnes()));
if (auto cst = getConstant(operands[0]))
return getIntAttr(getType(), APInt(1, cst->isAllOnes()));
// one bit is identity. Only applies to UInt since we can't make a cast
// here.
@ -874,8 +950,8 @@ OpFoldResult OrRPrimOp::fold(ArrayRef<Attribute> operands) {
return {};
// x != 0
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return getIntAttr(getType(), APInt(1, !attr.getValue().isZero()));
if (auto cst = getConstant(operands[0]))
return getIntAttr(getType(), APInt(1, !cst->isZero()));
// one bit is identity. Only applies to UInt since we can't make a cast
// here.
@ -890,9 +966,8 @@ OpFoldResult XorRPrimOp::fold(ArrayRef<Attribute> operands) {
return {};
// popcount(x) & 1
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return getIntAttr(getType(),
APInt(1, attr.getValue().countPopulation() & 1));
if (auto cst = getConstant(operands[0]))
return getIntAttr(getType(), APInt(1, cst->countPopulation() & 1));
// one bit is identity. Only applies to UInt since we can't make a cast here.
if (isUInt1(input().getType()))
@ -910,12 +985,11 @@ OpFoldResult CatPrimOp::fold(ArrayRef<Attribute> operands) {
return {};
// Constant fold cat.
if (auto lhs = operands[0].dyn_cast_or_null<IntegerAttr>())
if (auto rhs = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (auto lhs = getConstant(operands[0]))
if (auto rhs = getConstant(operands[1])) {
auto destWidth = getType().getWidthOrSentinel();
APInt tmp1 = lhs.getValue().zextOrSelf(destWidth)
<< rhs.getValue().getBitWidth();
APInt tmp2 = rhs.getValue().zextOrSelf(destWidth);
APInt tmp1 = lhs->zextOrSelf(destWidth) << rhs->getBitWidth();
APInt tmp2 = rhs->zextOrSelf(destWidth);
return getIntAttr(getType(), tmp1 | tmp2);
}
@ -927,18 +1001,19 @@ LogicalResult DShlPrimOp::canonicalize(DShlPrimOp op,
if (!hasKnownWidthIntTypes(op))
return failure();
// dshl(x, cst) -> shl(x, cst). The result size is generally much wider than
// what is needed for the constant.
if (auto rhsCst = dyn_cast_or_null<ConstantOp>(op.rhs().getDefiningOp())) {
// Shift amounts are always unsigned, but shift only takes a 32-bit amount.
uint64_t shiftAmt = rhsCst.value().getLimitedValue(1ULL << 31);
auto result =
rewriter.createOrFold<ShlPrimOp>(op.getLoc(), op.lhs(), shiftAmt);
rewriter.replaceOpWithNewOp<PadPrimOp>(
op, result, op.getType().cast<IntType>().getWidthOrSentinel());
return success();
}
return failure();
return canonicalizePrimOp(
op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
// dshl(x, cst) -> shl(x, cst). The result size is generally much wider
// than what is needed for the constant.
if (auto rhsCst = getConstant(operands[1])) {
// Shift amounts are always unsigned, but shift only takes a 32-bit
// amount.
uint64_t shiftAmt = rhsCst->getLimitedValue(1ULL << 31);
return rewriter.createOrFold<ShlPrimOp>(op.getLoc(), op.lhs(),
shiftAmt);
}
return {};
});
}
LogicalResult DShrPrimOp::canonicalize(DShrPrimOp op,
@ -946,18 +1021,19 @@ LogicalResult DShrPrimOp::canonicalize(DShrPrimOp op,
if (!hasKnownWidthIntTypes(op))
return failure();
// dshr(x, cst) -> shr(x, cst). The result size is generally much wider than
// what is needed for the constant.
if (auto rhsCst = dyn_cast_or_null<ConstantOp>(op.rhs().getDefiningOp())) {
// Shift amounts are always unsigned, but shift only takes a 32-bit amount.
uint64_t shiftAmt = rhsCst.value().getLimitedValue(1ULL << 31);
auto result =
rewriter.createOrFold<ShrPrimOp>(op.getLoc(), op.lhs(), shiftAmt);
rewriter.replaceOpWithNewOp<PadPrimOp>(
op, result, op.getType().cast<IntType>().getWidthOrSentinel());
return success();
}
return failure();
return canonicalizePrimOp(
op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
// dshr(x, cst) -> shr(x, cst). The result size is generally much wider
// than what is needed for the constant.
if (auto rhsCst = getConstant(operands[1])) {
// Shift amounts are always unsigned, but shift only takes a 32-bit
// amount.
uint64_t shiftAmt = rhsCst->getLimitedValue(1ULL << 31);
return rewriter.createOrFold<ShrPrimOp>(op.getLoc(), op.lhs(),
shiftAmt);
}
return {};
});
}
LogicalResult CatPrimOp::canonicalize(CatPrimOp op, PatternRewriter &rewriter) {
@ -999,9 +1075,9 @@ OpFoldResult BitsPrimOp::fold(ArrayRef<Attribute> operands) {
// Constant fold.
if (hasKnownWidthIntTypes(*this))
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return getIntAttr(
getType(), attr.getValue().lshr(lo()).truncOrSelf(hi() - lo() + 1));
if (auto cst = getConstant(operands[0]))
return getIntAttr(getType(),
cst->lshr(lo()).truncOrSelf(hi() - lo() + 1));
return {};
}
@ -1061,23 +1137,23 @@ OpFoldResult MuxPrimOp::fold(ArrayRef<Attribute> operands) {
return {};
// mux(0/1, x, y) -> x or y
if (auto cond = operands[0].dyn_cast_or_null<IntegerAttr>()) {
if (cond.getValue().isZero() && low().getType() == getType())
if (auto cond = getConstant(operands[0])) {
if (cond->isZero() && low().getType() == getType())
return low();
if (!cond.getValue().isZero() && high().getType() == getType())
if (!cond->isZero() && high().getType() == getType())
return high();
}
// mux(cond, x, cst)
if (auto lowCst = operands[2].dyn_cast_or_null<IntegerAttr>()) {
if (auto lowCst = getConstant(operands[2])) {
// mux(cond, c1, c2)
if (auto highCst = operands[1].dyn_cast_or_null<IntegerAttr>()) {
if (highCst.getType() == lowCst.getType() &&
highCst.getValue() == lowCst.getValue())
return highCst;
if (auto highCst = getConstant(operands[1])) {
// mux(cond, cst, cst) -> cst
if (highCst->getBitWidth() == lowCst->getBitWidth() &&
*highCst == *lowCst)
return getIntAttr(getType(), *highCst);
// mux(cond, 1, 0) -> cond
if (highCst.getValue().isOne() && lowCst.getValue().isZero() &&
getType() == sel().getType())
if (highCst->isOne() && lowCst->isZero() && getType() == sel().getType())
return sel();
// TODO: x ? ~0 : 0 -> sext(x)
@ -1138,24 +1214,19 @@ OpFoldResult PadPrimOp::fold(ArrayRef<Attribute> operands) {
return {};
// Constant fold.
if (auto cst = operands[0].dyn_cast_or_null<IntegerAttr>()) {
if (auto cst = getConstant(operands[0])) {
auto destWidth = getType().getWidthOrSentinel();
if (destWidth == -1)
return {};
if (inputType.isSigned() && cst.getValue().getBitWidth())
return getIntAttr(getType(), cst.getValue().sext(destWidth));
return getIntAttr(getType(), cst.getValue().zext(destWidth));
if (inputType.isSigned() && cst->getBitWidth())
return getIntAttr(getType(), cst->sext(destWidth));
return getIntAttr(getType(), cst->zext(destWidth));
}
return {};
}
void PadPrimOp::getCanonicalizationPatterns(RewritePatternSet &results,
MLIRContext *context) {
results.add<patterns::PadInvalid>(context);
}
OpFoldResult ShlPrimOp::fold(ArrayRef<Attribute> operands) {
auto input = this->input();
auto inputType = input.getType().cast<IntType>();
@ -1166,13 +1237,12 @@ OpFoldResult ShlPrimOp::fold(ArrayRef<Attribute> operands) {
return input;
// Constant fold.
if (auto cst = operands[0].dyn_cast_or_null<IntegerAttr>()) {
if (auto cst = getConstant(operands[0])) {
auto inputWidth = inputType.getWidthOrSentinel();
if (inputWidth != -1) {
auto resultWidth = inputWidth + shiftAmount;
shiftAmount = std::min(shiftAmount, resultWidth);
return getIntAttr(getType(),
cst.getValue().zext(resultWidth).shl(shiftAmount));
return getIntAttr(getType(), cst->zext(resultWidth).shl(shiftAmount));
}
}
return {};
@ -1199,12 +1269,12 @@ OpFoldResult ShrPrimOp::fold(ArrayRef<Attribute> operands) {
return getIntAttr(getType(), APInt(1, 0));
// Constant fold.
if (auto cst = operands[0].dyn_cast_or_null<IntegerAttr>()) {
if (auto cst = getConstant(operands[0])) {
APInt value;
if (inputType.isSigned())
value = cst.getValue().ashr(std::min(shiftAmount, inputWidth - 1));
value = cst->ashr(std::min(shiftAmount, inputWidth - 1));
else
value = cst.getValue().lshr(std::min(shiftAmount, inputWidth));
value = cst->lshr(std::min(shiftAmount, inputWidth));
auto resultWidth = std::max(inputWidth - shiftAmount, 1);
return getIntAttr(getType(), value.truncOrSelf(resultWidth));
}
@ -1249,11 +1319,11 @@ LogicalResult HeadPrimOp::canonicalize(HeadPrimOp op,
OpFoldResult HeadPrimOp::fold(ArrayRef<Attribute> operands) {
if (hasKnownWidthIntTypes(*this))
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>()) {
if (auto cst = getConstant(operands[0])) {
int shiftAmount =
input().getType().cast<IntType>().getWidthOrSentinel() - amount();
return getIntAttr(
getType(), attr.getValue().lshr(shiftAmount).truncOrSelf(amount()));
return getIntAttr(getType(),
cst->lshr(shiftAmount).truncOrSelf(amount()));
}
return {};
@ -1261,9 +1331,9 @@ OpFoldResult HeadPrimOp::fold(ArrayRef<Attribute> operands) {
OpFoldResult TailPrimOp::fold(ArrayRef<Attribute> operands) {
if (hasKnownWidthIntTypes(*this))
if (auto attr = operands[0].dyn_cast_or_null<IntegerAttr>())
return getIntAttr(getType(), attr.getValue().truncOrSelf(
getType().getWidthOrSentinel()));
if (auto cst = getConstant(operands[0]))
return getIntAttr(getType(),
cst->truncOrSelf(getType().getWidthOrSentinel()));
return {};
}
@ -1282,18 +1352,18 @@ LogicalResult TailPrimOp::canonicalize(TailPrimOp op,
LogicalResult SubaccessOp::canonicalize(SubaccessOp op,
PatternRewriter &rewriter) {
if (auto index = op.index().getDefiningOp()) {
if (auto constIndex = dyn_cast<ConstantOp>(index)) {
// The SubindexOp require the index value to be unsigned 32-bits
// integer.
auto value = constIndex.value().getExtValue();
auto valueAttr = rewriter.getI32IntegerAttr(value);
rewriter.replaceOpWithNewOp<SubindexOp>(op, op.result().getType(),
op.input(), valueAttr);
return success();
}
}
return failure();
return canonicalizePrimOp(
op, rewriter, [&](ArrayRef<Attribute> operands) -> OpFoldResult {
if (auto constIndex = getConstant(operands[1])) {
// The SubindexOp require the index value to be unsigned 32-bits
// integer.
auto value = constIndex->getExtValue();
auto valueAttr = rewriter.getI32IntegerAttr(value);
return rewriter.createOrFold<SubindexOp>(
op.getLoc(), op.result().getType(), op.input(), valueAttr);
}
return {};
});
}
//===----------------------------------------------------------------------===//

View File

@ -181,10 +181,8 @@ firrtl.circuit "padZeroReg" {
%n = firrtl.node %c171_ui8 : !firrtl.uint<8>
%1 = firrtl.cat %n, %r : (!firrtl.uint<8>, !firrtl.uint<8>) -> !firrtl.uint<16>
firrtl.connect %z, %1 : !firrtl.uint<16>, !firrtl.uint<16>
// CHECK: %[[C7:.+]] = firrtl.constant 171 : !firrtl.uint<8>
// CHECK: %[[invalid:.+]] = firrtl.invalidvalue : !firrtl.uint<8>
// CHECK: %[[C7_0:.+]] = firrtl.cat %c171_ui8, %invalid_ui8
// CHECK-NEXT: firrtl.connect %z, %[[C7_0]] : !firrtl.uint<16>, !firrtl.uint<16>
// CHECK: %[[TMP:.+]] = firrtl.constant 43776 : !firrtl.uint<16>
// CHECK-NEXT: firrtl.connect %z, %[[TMP]] : !firrtl.uint<16>, !firrtl.uint<16>
}
}

View File

@ -1,844 +0,0 @@
; RUN: firtool -split-input-file -verilog %s | FileCheck %s
; XFAIL: *
; This test checks register removal behavior for situations where the register
; is invalidated _through a primitive operation_. This is intended to tease out
; gnarly bugs where, due to a combination of canonicalization, folding, and
; constant propagation, CIRCT does not remove registers which the Scala FIRRTL
; Compiler (SFC) does. The CHECK/CHECK-NOT statements in this test indicate the
; SFC behavior.
;
; This test contains FAILING cases which should be fixed. For passing cases,
; see invalid-reg-pass.fir.
;
; The FIRRTL circuits in this file were generated using:
; https://github.com/seldridge/firrtl-torture/blob/main/Invalid.scala
circuit add :
module add :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<5>
output out_1 : UInt<5>
output out_2 : UInt<5>
output out_3 : UInt<5>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = add(in_1, in_0)
node _T_1 = tail(_T, 1)
r_0 <= _T_1
out_0 <= r_0
node _T_2 = add(in_1, invalid)
node _T_3 = tail(_T_2, 1)
r_1 <= _T_3
out_1 <= r_1
node _T_4 = add(invalid, in_0)
node _T_5 = tail(_T_4, 1)
r_2 <= _T_5
out_2 <= r_2
node _T_6 = add(invalid, invalid)
node _T_7 = tail(_T_6, 1)
r_3 <= _T_7
out_3 <= r_3
; CHECK-LABEL: module add
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit andr :
module andr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = andr(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = andr(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module andr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit asSInt :
module asSInt :
input clock : Clock
input reset : UInt<1>
input in : UInt<2>
output out_0 : SInt<2>
output out_1 : SInt<2>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : SInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : SInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = asSInt(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = asSInt(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module asSInt
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit asUInt :
module asUInt :
input clock : Clock
input reset : UInt<1>
input in : SInt<2>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : SInt<2>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = asUInt(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = asUInt(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module asUInt
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit bits :
module bits :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = bits(in, 3, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = bits(invalid, 3, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module bits
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit cat :
module cat :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<2>
input in_1 : UInt<2>
output out_0 : UInt<4>
output out_1 : UInt<4>
output out_2 : UInt<4>
output out_3 : UInt<4>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = cat(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = cat(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = cat(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = cat(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module cat
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit div :
module div :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<4>
output out_1 : UInt<4>
output out_2 : UInt<4>
output out_3 : UInt<4>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = div(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = div(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = div(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = div(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module div
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit dshl :
module dshl :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<2>
input in_1 : UInt<2>
output out_0 : UInt<5>
output out_1 : UInt<5>
output out_2 : UInt<5>
output out_3 : UInt<5>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = dshl(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = dshl(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = dshl(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = dshl(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module dshl
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit dshr :
module dshr :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<2>
input in_1 : UInt<2>
output out_0 : UInt<2>
output out_1 : UInt<2>
output out_2 : UInt<2>
output out_3 : UInt<2>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = gt(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = gt(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = gt(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = gt(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module dshr
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit geq :
module geq :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = geq(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = geq(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = geq(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = geq(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module geq
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit gt :
module gt :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = gt(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = gt(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = gt(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = gt(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module gt
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit head :
module head :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = head(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = head(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module head
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit leq :
module leq :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = leq(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = leq(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = leq(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = leq(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module leq
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit lt :
module lt :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = lt(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = lt(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = lt(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = lt(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module lt
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit mul :
module mul :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<8>
output out_1 : UInt<8>
output out_2 : UInt<8>
output out_3 : UInt<8>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = mul(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = mul(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = mul(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = mul(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module mul
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK: r_3
; // -----
circuit not :
module not :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<4>
output out_1 : UInt<4>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = not(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = not(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module not
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit orr :
module orr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = orr(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = orr(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module orr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit rem :
module rem :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<4>
output out_1 : UInt<4>
output out_2 : UInt<4>
output out_3 : UInt<4>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = rem(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = rem(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = rem(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = rem(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module rem
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit shl :
module shl :
input clock : Clock
input reset : UInt<1>
input in : UInt<2>
output out_0 : UInt<4>
output out_1 : UInt<4>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = shl(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = shl(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module shl
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit shr :
module shr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = shr(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = shr(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module shr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit sub :
module sub :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<5>
output out_1 : UInt<5>
output out_2 : UInt<5>
output out_3 : UInt<5>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = sub(in_1, in_0)
node _T_1 = tail(_T, 1)
r_0 <= _T_1
out_0 <= r_0
node _T_2 = sub(in_1, invalid)
node _T_3 = tail(_T_2, 1)
r_1 <= _T_3
out_1 <= r_1
node _T_4 = sub(invalid, in_0)
node _T_5 = tail(_T_4, 1)
r_2 <= _T_5
out_2 <= r_2
node _T_6 = sub(invalid, invalid)
node _T_7 = tail(_T_6, 1)
r_3 <= _T_7
out_3 <= r_3
; CHECK-LABEL: module sub
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit tail :
module tail :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = tail(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = tail(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module tail
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit xorr :
module xorr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = xorr(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = xorr(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module xorr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3

View File

@ -13,6 +13,52 @@
; The FIRRTL circuits in this file were generated using:
; https://github.com/seldridge/firrtl-torture/blob/main/Invalid.scala
circuit add :
module add :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<5>
output out_1 : UInt<5>
output out_2 : UInt<5>
output out_3 : UInt<5>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = add(in_1, in_0)
node _T_1 = tail(_T, 1)
r_0 <= _T_1
out_0 <= r_0
node _T_2 = add(in_1, invalid)
node _T_3 = tail(_T_2, 1)
r_1 <= _T_3
out_1 <= r_1
node _T_4 = add(invalid, in_0)
node _T_5 = tail(_T_4, 1)
r_2 <= _T_5
out_2 <= r_2
node _T_6 = add(invalid, invalid)
node _T_7 = tail(_T_6, 1)
r_3 <= _T_7
out_3 <= r_3
; CHECK-LABEL: module add
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit and :
module and :
input clock : Clock
@ -78,7 +124,7 @@ circuit asAsyncReset :
; CHECK-LABEL: module asAsyncReset
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_1 <-- fixed; upstream to Scala FIRRTL impl?
; // -----
@ -105,7 +151,7 @@ circuit asClock :
; CHECK-LABEL: module asClock
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_1 <-- fixed; upstream to Scala FIRRTL impl?
; CHECK-NOT: r_2
; CHECK-NOT: r_3
@ -134,7 +180,7 @@ circuit cvt :
; CHECK-LABEL: module cvt
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_1 <-- fixed; upstream to Scala FIRRTL impl?
; CHECK-NOT: r_2
; CHECK-NOT: r_3
@ -366,3 +412,788 @@ circuit xor :
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit andr :
module andr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = andr(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = andr(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module andr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit asSInt :
module asSInt :
input clock : Clock
input reset : UInt<1>
input in : UInt<2>
output out_0 : SInt<2>
output out_1 : SInt<2>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : SInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : SInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = asSInt(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = asSInt(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module asSInt
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit asUInt :
module asUInt :
input clock : Clock
input reset : UInt<1>
input in : SInt<2>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : SInt<2>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = asUInt(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = asUInt(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module asUInt
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit bits :
module bits :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = bits(in, 3, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = bits(invalid, 3, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module bits
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit cat :
module cat :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<2>
input in_1 : UInt<2>
output out_0 : UInt<4>
output out_1 : UInt<4>
output out_2 : UInt<4>
output out_3 : UInt<4>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = cat(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = cat(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = cat(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = cat(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module cat
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit dshl :
module dshl :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<2>
input in_1 : UInt<2>
output out_0 : UInt<5>
output out_1 : UInt<5>
output out_2 : UInt<5>
output out_3 : UInt<5>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = dshl(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = dshl(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = dshl(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = dshl(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module dshl
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit dshr :
module dshr :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<2>
input in_1 : UInt<2>
output out_0 : UInt<2>
output out_1 : UInt<2>
output out_2 : UInt<2>
output out_3 : UInt<2>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = dshr(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = dshr(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = dshr(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = dshr(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module dshr
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit head :
module head :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = head(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = head(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module head
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit lt :
module lt :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = lt(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = lt(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = lt(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = lt(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module lt
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit gt :
module gt :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = gt(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = gt(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = gt(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = gt(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module gt
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit leq :
module leq :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = leq(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = leq(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = leq(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = leq(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module leq
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit geq :
module geq :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
output out_2 : UInt<1>
output out_3 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = geq(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = geq(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = geq(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = geq(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module geq
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit mul :
module mul :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<8>
output out_1 : UInt<8>
output out_2 : UInt<8>
output out_3 : UInt<8>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<8>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = mul(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = mul(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = mul(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = mul(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module mul
; CHECK: r_0
; CHECK-NOT: r_1 <-- fixed; upstream to Scala FIRRTL impl?
; CHECK-NOT: r_2 <-- fixed; upstream to Scala FIRRTL impl?
; CHECK-NOT: r_3 <-- fixed; upstream to Scala FIRRTL impl?
; // -----
circuit not :
module not :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<4>
output out_1 : UInt<4>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = not(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = not(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module not
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit orr :
module orr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = orr(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = orr(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module orr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit shl :
module shl :
input clock : Clock
input reset : UInt<1>
input in : UInt<2>
output out_0 : UInt<4>
output out_1 : UInt<4>
wire invalid : UInt<2>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = shl(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = shl(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module shl
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit shr :
module shr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = shr(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = shr(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module shr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit sub :
module sub :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<5>
output out_1 : UInt<5>
output out_2 : UInt<5>
output out_3 : UInt<5>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<5>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = sub(in_1, in_0)
node _T_1 = tail(_T, 1)
r_0 <= _T_1
out_0 <= r_0
node _T_2 = sub(in_1, invalid)
node _T_3 = tail(_T_2, 1)
r_1 <= _T_3
out_1 <= r_1
node _T_4 = sub(invalid, in_0)
node _T_5 = tail(_T_4, 1)
r_2 <= _T_5
out_2 <= r_2
node _T_6 = sub(invalid, invalid)
node _T_7 = tail(_T_6, 1)
r_3 <= _T_7
out_3 <= r_3
; CHECK-LABEL: module sub
; CHECK: r_0
; CHECK: r_1
; CHECK: r_2
; CHECK-NOT: r_3
; // -----
circuit tail :
module tail :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<2>
output out_1 : UInt<2>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<2>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = tail(in, 2)
r_0 <= _T
out_0 <= r_0
node _T_1 = tail(invalid, 2)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module tail
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3
; // -----
circuit div :
module div :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<4>
output out_1 : UInt<4>
output out_2 : UInt<4>
output out_3 : UInt<4>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = div(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = div(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = div(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = div(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module div
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_2 <-- fixed; upstream to Scala FIRRTL impl?
; CHECK-NOT: r_3
; // -----
circuit rem :
module rem :
input clock : Clock
input reset : UInt<1>
input in_0 : UInt<4>
input in_1 : UInt<4>
output out_0 : UInt<4>
output out_1 : UInt<4>
output out_2 : UInt<4>
output out_3 : UInt<4>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_1)
reg r_2 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_2)
reg r_3 : UInt<4>, clock with :
reset => (UInt<1>("h0"), r_3)
node _T = rem(in_1, in_0)
r_0 <= _T
out_0 <= r_0
node _T_1 = rem(in_1, invalid)
r_1 <= _T_1
out_1 <= r_1
node _T_2 = rem(invalid, in_0)
r_2 <= _T_2
out_2 <= r_2
node _T_3 = rem(invalid, invalid)
r_3 <= _T_3
out_3 <= r_3
; CHECK-LABEL: module rem
; CHECK: r_0
; CHECK: r_1
; CHECK-NOT: r_2 <-- fixed; upstream to Scala FIRRTL impl?
; CHECK-NOT: r_3
; // -----
circuit xorr :
module xorr :
input clock : Clock
input reset : UInt<1>
input in : UInt<4>
output out_0 : UInt<1>
output out_1 : UInt<1>
wire invalid : UInt<4>
invalid is invalid
reg r_0 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_0)
reg r_1 : UInt<1>, clock with :
reset => (UInt<1>("h0"), r_1)
node _T = xorr(in)
r_0 <= _T
out_0 <= r_0
node _T_1 = xorr(invalid)
r_1 <= _T_1
out_1 <= r_1
; CHECK-LABEL: module xorr
; CHECK: r_0
; CHECK-NOT: r_1
; CHECK-NOT: r_2
; CHECK-NOT: r_3

View File

@ -10,6 +10,10 @@ firrtl.module @Casts(in %ui1 : !firrtl.uint<1>, in %si1 : !firrtl.sint<1>,
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
%c1_si1 = firrtl.constant 1 : !firrtl.sint<1>
%invalid_ui1 = firrtl.invalidvalue : !firrtl.uint<1>
%invalid_si1 = firrtl.invalidvalue : !firrtl.sint<1>
%invalid_clock = firrtl.invalidvalue : !firrtl.clock
%invalid_asyncreset = firrtl.invalidvalue : !firrtl.asyncreset
// No effect
// CHECK: firrtl.connect %out_ui1, %ui1 : !firrtl.uint<1>, !firrtl.uint<1>
@ -38,6 +42,20 @@ firrtl.module @Casts(in %ui1 : !firrtl.uint<1>, in %si1 : !firrtl.sint<1>,
// CHECK: firrtl.connect %out_asyncreset, %c1_asyncreset : !firrtl.asyncreset, !firrtl.asyncreset
%7 = firrtl.asAsyncReset %c1_ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset
firrtl.connect %out_asyncreset, %7 : !firrtl.asyncreset, !firrtl.asyncreset
// Invalid values
// CHECK: firrtl.connect %out_ui1, %c0_ui1 : !firrtl.uint<1>, !firrtl.uint<1>
%8 = firrtl.asUInt %invalid_si1 : (!firrtl.sint<1>) -> !firrtl.uint<1>
firrtl.connect %out_ui1, %8 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %out_si1, %c0_si1 : !firrtl.sint<1>, !firrtl.sint<1>
%9 = firrtl.asSInt %invalid_ui1 : (!firrtl.uint<1>) -> !firrtl.sint<1>
firrtl.connect %out_si1, %9 : !firrtl.sint<1>, !firrtl.sint<1>
// CHECK: firrtl.connect %out_clock, %c0_clock : !firrtl.clock, !firrtl.clock
%10 = firrtl.asClock %invalid_ui1 : (!firrtl.uint<1>) -> !firrtl.clock
firrtl.connect %out_clock, %10 : !firrtl.clock, !firrtl.clock
// CHECK: firrtl.connect %out_asyncreset, %c0_asyncreset : !firrtl.asyncreset, !firrtl.asyncreset
%11 = firrtl.asAsyncReset %invalid_ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset
firrtl.connect %out_asyncreset, %11 : !firrtl.asyncreset, !firrtl.asyncreset
}
// CHECK-LABEL: firrtl.module @Div
@ -282,7 +300,19 @@ firrtl.module @EQ(in %in1: !firrtl.uint<1>,
%4 = firrtl.eq %in4, %c0_ui1 : (!firrtl.uint<4>, !firrtl.uint<1>) -> !firrtl.uint<1>
firrtl.connect %out, %4 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: [[ORR:%.+]] = firrtl.orr %in4
// CHECK-NEXT: firrtl.not [[ORR]]
// CHECK-NEXT: firrtl.connect
%invalid_ui1 = firrtl.invalidvalue : !firrtl.uint<1>
%5 = firrtl.eq %in1, %invalid_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<1>
firrtl.connect %out, %5 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK-NEXT: firrtl.not %in1
// CHECK-NEXT: firrtl.connect
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%6 = firrtl.eq %in4, %invalid_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<1>
firrtl.connect %out, %6 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: [[ORR:%.+]] = firrtl.orr %in4
// CHECK-NEXT: firrtl.not [[ORR]]
// CHECK-NEXT: firrtl.connect
@ -308,9 +338,15 @@ firrtl.module @NEQ(in %in1: !firrtl.uint<1>,
// CHECK: firrtl.orr %in4
// CHECK-NEXT: firrtl.connect
%c15_ui4 = firrtl.constant 15 : !firrtl.uint<4>
%3 = firrtl.neq %in4, %c15_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<1>
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%3 = firrtl.neq %in4, %invalid_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<1>
firrtl.connect %out, %3 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.orr %in4
// CHECK-NEXT: firrtl.connect
%c15_ui4 = firrtl.constant 15 : !firrtl.uint<4>
%4 = firrtl.neq %in4, %c15_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<1>
firrtl.connect %out, %4 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: [[ANDR:%.+]] = firrtl.andr %in4
// CHECK-NEXT: firrtl.not [[ANDR]]
// CHECK-NEXT: firrtl.connect
@ -319,7 +355,8 @@ firrtl.module @NEQ(in %in1: !firrtl.uint<1>,
// CHECK-LABEL: firrtl.module @Cat
firrtl.module @Cat(in %in4: !firrtl.uint<4>,
out %out4: !firrtl.uint<4>,
out %outcst: !firrtl.uint<8>) {
out %outcst: !firrtl.uint<8>,
out %outcst2: !firrtl.uint<8>) {
// CHECK: firrtl.connect %out4, %in4
%0 = firrtl.bits %in4 3 to 2 : (!firrtl.uint<4>) -> !firrtl.uint<2>
@ -332,7 +369,12 @@ firrtl.module @Cat(in %in4: !firrtl.uint<4>,
%c3_ui4 = firrtl.constant 3 : !firrtl.uint<4>
%3 = firrtl.cat %c15_ui4, %c3_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<8>
firrtl.connect %outcst, %3 : !firrtl.uint<8>, !firrtl.uint<8>
}
// CHECK: firrtl.connect %outcst2, %c0_ui8
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%4 = firrtl.cat %invalid_ui4, %invalid_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<8>
firrtl.connect %outcst2, %4 : !firrtl.uint<8>, !firrtl.uint<8>
}
// CHECK-LABEL: firrtl.module @Bits
firrtl.module @Bits(in %in1: !firrtl.uint<1>,
@ -363,6 +405,11 @@ firrtl.module @Bits(in %in1: !firrtl.uint<1>,
// CHECK: firrtl.connect %out1, %in1
%5 = firrtl.bits %in1 0 to 0 : (!firrtl.uint<1>) -> !firrtl.uint<1>
firrtl.connect %out1, %5 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %out2, %c0_ui2
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%6 = firrtl.bits %invalid_ui4 2 to 1 : (!firrtl.uint<4>) -> !firrtl.uint<2>
firrtl.connect %out2, %6 : !firrtl.uint<2>, !firrtl.uint<2>
}
// CHECK-LABEL: firrtl.module @Head
@ -383,6 +430,11 @@ firrtl.module @Head(in %in4u: !firrtl.uint<4>,
%c10_ui4 = firrtl.constant 10 : !firrtl.uint<4>
%2 = firrtl.head %c10_ui4, 3 : (!firrtl.uint<4>) -> !firrtl.uint<3>
firrtl.connect %out3u, %2 : !firrtl.uint<3>, !firrtl.uint<3>
// CHECK: firrtl.connect %out3u, %c0_ui3
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%3 = firrtl.head %invalid_ui4, 3 : (!firrtl.uint<4>) -> !firrtl.uint<3>
firrtl.connect %out3u, %3 : !firrtl.uint<3>, !firrtl.uint<3>
}
// CHECK-LABEL: firrtl.module @Mux
@ -417,6 +469,11 @@ firrtl.module @Mux(in %in: !firrtl.uint<4>,
// CHECK: firrtl.connect %out, %invalid_ui4
%7 = firrtl.mux (%cond, %invalid_ui4, %invalid_ui4) : (!firrtl.uint<1>, !firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<4>
firrtl.connect %out, %7 : !firrtl.uint<4>, !firrtl.uint<4>
// CHECK: firrtl.connect %out, %c7_ui4
%invalid_ui1 = firrtl.invalidvalue : !firrtl.uint<1>
%8 = firrtl.mux (%invalid_ui1, %in, %c7_ui4) : (!firrtl.uint<1>, !firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<4>
firrtl.connect %out, %8 : !firrtl.uint<4>, !firrtl.uint<4>
}
// CHECK-LABEL: firrtl.module @Pad
@ -451,6 +508,11 @@ firrtl.module @Shl(in %in1u: !firrtl.uint<1>,
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
%1 = firrtl.shl %c1_ui1, 3 : (!firrtl.uint<1>) -> !firrtl.uint<4>
firrtl.connect %outu, %1 : !firrtl.uint<4>, !firrtl.uint<4>
// CHECK: firrtl.connect %outu, %c0_ui4
%invalid_ui1 = firrtl.invalidvalue : !firrtl.uint<1>
%2 = firrtl.shl %invalid_ui1, 3 : (!firrtl.uint<1>) -> !firrtl.uint<4>
firrtl.connect %outu, %2 : !firrtl.uint<4>, !firrtl.uint<4>
}
// CHECK-LABEL: firrtl.module @Shr
@ -512,6 +574,11 @@ firrtl.module @Shr(in %in1u: !firrtl.uint<1>,
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
%9 = firrtl.dshr %in0u, %c1_ui1 : (!firrtl.uint<0>, !firrtl.uint<1>) -> !firrtl.uint<0>
firrtl.connect %out1u, %9 : !firrtl.uint<1>, !firrtl.uint<0>
// CHECK: firrtl.connect %out1u, %c0_ui1
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%10 = firrtl.shr %invalid_ui4, 3 : (!firrtl.uint<4>) -> !firrtl.uint<1>
firrtl.connect %out1u, %10 : !firrtl.uint<1>, !firrtl.uint<1>
}
// CHECK-LABEL: firrtl.module @Tail
@ -532,31 +599,95 @@ firrtl.module @Tail(in %in4u: !firrtl.uint<4>,
%c10_ui4 = firrtl.constant 10 : !firrtl.uint<4>
%2 = firrtl.tail %c10_ui4, 1 : (!firrtl.uint<4>) -> !firrtl.uint<3>
firrtl.connect %out3u, %2 : !firrtl.uint<3>, !firrtl.uint<3>
// CHECK: firrtl.connect %out3u, %c0_ui3
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%3 = firrtl.tail %invalid_ui4, 1 : (!firrtl.uint<4>) -> !firrtl.uint<3>
firrtl.connect %out3u, %3 : !firrtl.uint<3>, !firrtl.uint<3>
}
// CHECK-LABEL: firrtl.module @Andr
firrtl.circuit "Andr" {
firrtl.module @Andr(out %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>,
out %c: !firrtl.uint<1>, out %d: !firrtl.uint<1>) {
%c2_ui2 = firrtl.constant 2 : !firrtl.uint<2>
%c3_ui2 = firrtl.constant 3 : !firrtl.uint<2>
%cn2_si2 = firrtl.constant -2 : !firrtl.sint<2>
%cn1_si2 = firrtl.constant -1 : !firrtl.sint<2>
%0 = firrtl.andr %c2_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%1 = firrtl.andr %c3_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%2 = firrtl.andr %cn2_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
%3 = firrtl.andr %cn1_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
// CHECK: %[[ONE:.+]] = firrtl.constant 1 : !firrtl.uint<1>
// CHECK: %[[ZERO:.+]] = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: firrtl.connect %a, %[[ZERO]]
firrtl.connect %a, %0 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %b, %[[ONE]]
firrtl.connect %b, %1 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %c, %[[ZERO]]
firrtl.connect %c, %2 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %d, %[[ONE]]
firrtl.connect %d, %3 : !firrtl.uint<1>, !firrtl.uint<1>
}
firrtl.module @Andr(out %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>,
out %c: !firrtl.uint<1>, out %d: !firrtl.uint<1>,
out %e: !firrtl.uint<1>) {
%invalid_ui2 = firrtl.invalidvalue : !firrtl.uint<2>
%c2_ui2 = firrtl.constant 2 : !firrtl.uint<2>
%c3_ui2 = firrtl.constant 3 : !firrtl.uint<2>
%cn2_si2 = firrtl.constant -2 : !firrtl.sint<2>
%cn1_si2 = firrtl.constant -1 : !firrtl.sint<2>
%0 = firrtl.andr %c2_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%1 = firrtl.andr %c3_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%2 = firrtl.andr %cn2_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
%3 = firrtl.andr %cn1_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
%4 = firrtl.andr %invalid_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
// CHECK: %[[ZERO:.+]] = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %[[ONE:.+]] = firrtl.constant 1 : !firrtl.uint<1>
// CHECK: firrtl.connect %a, %[[ZERO]]
firrtl.connect %a, %0 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %b, %[[ONE]]
firrtl.connect %b, %1 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %c, %[[ZERO]]
firrtl.connect %c, %2 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %d, %[[ONE]]
firrtl.connect %d, %3 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %e, %[[ZERO]]
firrtl.connect %e, %4 : !firrtl.uint<1>, !firrtl.uint<1>
}
// CHECK-LABEL: firrtl.module @Orr
firrtl.module @Orr(out %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>,
out %c: !firrtl.uint<1>, out %d: !firrtl.uint<1>,
out %e: !firrtl.uint<1>) {
%invalid_ui2 = firrtl.invalidvalue : !firrtl.uint<2>
%c0_ui2 = firrtl.constant 0 : !firrtl.uint<2>
%c2_ui2 = firrtl.constant 2 : !firrtl.uint<2>
%cn0_si2 = firrtl.constant 0 : !firrtl.sint<2>
%cn2_si2 = firrtl.constant -2 : !firrtl.sint<2>
%0 = firrtl.orr %c0_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%1 = firrtl.orr %c2_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%2 = firrtl.orr %cn0_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
%3 = firrtl.orr %cn2_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
%4 = firrtl.orr %invalid_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
// CHECK: %[[ZERO:.+]] = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %[[ONE:.+]] = firrtl.constant 1 : !firrtl.uint<1>
// CHECK: firrtl.connect %a, %[[ZERO]]
firrtl.connect %a, %0 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %b, %[[ONE]]
firrtl.connect %b, %1 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %c, %[[ZERO]]
firrtl.connect %c, %2 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %d, %[[ONE]]
firrtl.connect %d, %3 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %e, %[[ZERO]]
firrtl.connect %e, %4 : !firrtl.uint<1>, !firrtl.uint<1>
}
// CHECK-LABEL: firrtl.module @Xorr
firrtl.module @Xorr(out %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>,
out %c: !firrtl.uint<1>, out %d: !firrtl.uint<1>,
out %e: !firrtl.uint<1>) {
%invalid_ui2 = firrtl.invalidvalue : !firrtl.uint<2>
%c3_ui2 = firrtl.constant 3 : !firrtl.uint<2>
%c2_ui2 = firrtl.constant 2 : !firrtl.uint<2>
%cn1_si2 = firrtl.constant -1 : !firrtl.sint<2>
%cn2_si2 = firrtl.constant -2 : !firrtl.sint<2>
%0 = firrtl.xorr %c3_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%1 = firrtl.xorr %c2_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
%2 = firrtl.xorr %cn1_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
%3 = firrtl.xorr %cn2_si2 : (!firrtl.sint<2>) -> !firrtl.uint<1>
%4 = firrtl.xorr %invalid_ui2 : (!firrtl.uint<2>) -> !firrtl.uint<1>
// CHECK: %[[ZERO:.+]] = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %[[ONE:.+]] = firrtl.constant 1 : !firrtl.uint<1>
// CHECK: firrtl.connect %a, %[[ZERO]]
firrtl.connect %a, %0 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %b, %[[ONE]]
firrtl.connect %b, %1 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %c, %[[ZERO]]
firrtl.connect %c, %2 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %d, %[[ONE]]
firrtl.connect %d, %3 : !firrtl.uint<1>, !firrtl.uint<1>
// CHECK: firrtl.connect %e, %[[ZERO]]
firrtl.connect %e, %4 : !firrtl.uint<1>, !firrtl.uint<1>
}
// CHECK-LABEL: firrtl.module @Reduce
@ -581,6 +712,12 @@ firrtl.module @subaccess(out %result: !firrtl.uint<8>, in %vec0: !firrtl.vector<
%c11_ui8 = firrtl.constant 11 : !firrtl.uint<8>
%0 = firrtl.subaccess %vec0[%c11_ui8] : !firrtl.vector<uint<8>, 16>, !firrtl.uint<8>
firrtl.connect %result, %0 :!firrtl.uint<8>, !firrtl.uint<8>
// CHECK: [[TMP:%.+]] = firrtl.subindex %vec0[0]
// CHECK-NEXT: firrtl.connect %result, [[TMP]]
%invalid_ui8 = firrtl.invalidvalue : !firrtl.uint<8>
%1 = firrtl.subaccess %vec0[%invalid_ui8] : !firrtl.vector<uint<8>, 16>, !firrtl.uint<8>
firrtl.connect %result, %1 :!firrtl.uint<8>, !firrtl.uint<8>
}
// CHECK-LABEL: firrtl.module @issue326
@ -1553,6 +1690,20 @@ firrtl.module @add_cst_prop4(out %out_b: !firrtl.uint<5>) {
firrtl.connect %out_b, %add2 : !firrtl.uint<5>, !firrtl.uint<5>
}
// CHECK-LABEL: @add_cst_prop5
// CHECK: %[[pad:.+]] = firrtl.pad %tmp_a, 5
// CHECK-NEXT: firrtl.connect %out_b, %[[pad]]
// CHECK-NEXT: %[[pad:.+]] = firrtl.pad %tmp_a, 5
// CHECK_NEXT: firrtl.connect %out_b, %[[pad]]
firrtl.module @add_cst_prop5(out %out_b: !firrtl.uint<5>) {
%tmp_a = firrtl.wire : !firrtl.uint<4>
%c0_ui4 = firrtl.constant 0 : !firrtl.uint<4>
%add = firrtl.add %tmp_a, %c0_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<5>
firrtl.connect %out_b, %add : !firrtl.uint<5>, !firrtl.uint<5>
%add2 = firrtl.add %c0_ui4, %tmp_a : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<5>
firrtl.connect %out_b, %add2 : !firrtl.uint<5>, !firrtl.uint<5>
}
// CHECK-LABEL: @sub_cst_prop1
// CHECK-NEXT: %c1_ui9 = firrtl.constant 1 : !firrtl.uint<9>
// CHECK-NEXT: firrtl.connect %out_b, %c1_ui9 : !firrtl.uint<9>, !firrtl.uint<9>
@ -1582,11 +1733,16 @@ firrtl.module @sub_cst_prop2(out %out_b: !firrtl.sint<9>) {
// CHECK-LABEL: @sub_cst_prop3
// CHECK: %[[pad:.+]] = firrtl.pad %tmp_a, 5
// CHECK-NEXT: firrtl.connect %out_b, %[[pad]]
// CHECK: %[[neg:.+]] = firrtl.neg %tmp_a
// CHECK: %[[cast:.+]] = firrtl.asUInt %[[neg]]
// CHECK-NEXT: firrtl.connect %out_b, %[[cast]]
firrtl.module @sub_cst_prop3(out %out_b: !firrtl.uint<5>) {
%tmp_a = firrtl.wire : !firrtl.uint<4>
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%sub = firrtl.sub %tmp_a, %invalid_ui4 : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<5>
firrtl.connect %out_b, %sub : !firrtl.uint<5>, !firrtl.uint<5>
%sub2 = firrtl.sub %invalid_ui4, %tmp_a : (!firrtl.uint<4>, !firrtl.uint<4>) -> !firrtl.uint<5>
firrtl.connect %out_b, %sub2 : !firrtl.uint<5>, !firrtl.uint<5>
}
// CHECK-LABEL: @mul_cst_prop1
@ -1909,6 +2065,17 @@ firrtl.module @dshifts_to_ishifts(in %a_in: !firrtl.sint<58>,
%c438_ui10 = firrtl.constant 438 : !firrtl.uint<10>
%2 = firrtl.dshr %c_in, %c438_ui10 : (!firrtl.sint<58>, !firrtl.uint<10>) -> !firrtl.sint<58>
firrtl.connect %c_out, %2 : !firrtl.sint<58>, !firrtl.sint<58>
// CHECK: firrtl.connect %a_out, %a_in : !firrtl.sint<58>, !firrtl.sint<58>
%invalid_ui10 = firrtl.invalidvalue : !firrtl.uint<10>
%3 = firrtl.dshr %a_in, %invalid_ui10 : (!firrtl.sint<58>, !firrtl.uint<10>) -> !firrtl.sint<58>
firrtl.connect %a_out, %3 : !firrtl.sint<58>, !firrtl.sint<58>
// CHECK: [[TMP:%.+]] = firrtl.pad %b_in, 23 : (!firrtl.uint<8>) -> !firrtl.uint<23>
// CHECK: firrtl.connect %b_out, [[TMP]] : !firrtl.uint<23>, !firrtl.uint<23>
%invalid_ui4 = firrtl.invalidvalue : !firrtl.uint<4>
%4 = firrtl.dshl %b_in, %invalid_ui4 : (!firrtl.uint<8>, !firrtl.uint<4>) -> !firrtl.uint<23>
firrtl.connect %b_out, %4 : !firrtl.uint<23>, !firrtl.uint<23>
}
// CHECK-LABEL: firrtl.module @constReg
@ -2081,4 +2248,15 @@ firrtl.module @ZeroWidthCat(out %a: !firrtl.uint<1>) {
// CHECK-NEXT: firrtl.connect %a, %[[one]]
}
// Issue mentioned in PR #2251
// CHECK-LABEL: @Issue2251
firrtl.module @Issue2251(out %o: !firrtl.sint<15>) {
// pad used to always return an unsigned constant
%invalid_si1 = firrtl.invalidvalue : !firrtl.sint<1>
%0 = firrtl.pad %invalid_si1, 15 : (!firrtl.sint<1>) -> !firrtl.sint<15>
firrtl.connect %o, %0 : !firrtl.sint<15>, !firrtl.sint<15>
// CHECK: %[[zero:.+]] = firrtl.constant 0 : !firrtl.sint<15>
// CHECK-NEXT: firrtl.connect %o, %[[zero]]
}
}