diff --git a/include/circt/Dialect/Moore/MIRExpressions.td b/include/circt/Dialect/Moore/MIRExpressions.td deleted file mode 100644 index 3f0333dc9081..000000000000 --- a/include/circt/Dialect/Moore/MIRExpressions.td +++ /dev/null @@ -1,92 +0,0 @@ -//===- MIRExpressions.td - Moore MIR expression ops --------*- tablegen -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This describes the ops for Moore MIR expressions. -// -//===----------------------------------------------------------------------===// - -def ConstantOp : MIROp<"constant", [Pure]> { - let summary = "A constant value"; - - let arguments = (ins I32Attr:$value); - let results = (outs MooreIntType:$result); - let assemblyFormat = "$value attr-dict `:` qualified(type($result))"; -} - -def ConcatOp : MIROp<"concat", [ - Pure, DeclareOpInterfaceMethods -]> { - let summary = "A concatenation of expressions"; - let description = [{ - This operation represents the SystemVerilog concatenation expression - `{x, y, z}`. See IEEE 1800-2017 §11.4.12 "Concatenation operators". - - All operands must be simple bit vector types. - - The concatenation result is a simple bit vector type. The result is unsigned - regardless of the sign of the operands (see concatenation-specific rules in - IEEE 1800-2017 §11.8.1 "Rules for expression types"). The size of the result - is the sum of the sizes of all operands. If any of the operands is - four-valued, the result is four-valued; otherwise it is two-valued. - }]; - let arguments = (ins Variadic:$values); - let results = (outs SimpleBitVectorType:$result); - let assemblyFormat = [{ - $values attr-dict `:` functional-type($values, $result) - }]; -} - -//===----------------------------------------------------------------------===// -// Shift operations -//===----------------------------------------------------------------------===// - -class ShiftOp : MIROp -]> { - let arguments = (ins SimpleBitVectorType:$value, - SimpleBitVectorType:$amount, - UnitAttr:$arithmetic); - let results = (outs SimpleBitVectorType:$result); - let assemblyFormat = [{ - ( `arithmetic` $arithmetic^ )? $value `,` $amount attr-dict - `:` type($value) `,` type($amount) - }]; -} - -def ShlOp : ShiftOp<"shl"> { - let summary = "A logical or arithmetic left-shift expression"; - let description = [{ - This operation represents the SystemVerilog logical and arithmetic - left-shift expressions `<<` and `<<<`. - See IEEE 1800-2017 §11.4.10 "Shift operators". - - The value to be shifted and the amount must be simple bit vector types. - The shift result is of the same type as the input value. - - The logical and arithmetic shift both insert zeros in place of the shifted - bits. - }]; -} - -def ShrOp : ShiftOp<"shr"> { - let summary = "A logical or arithmetic right-shift expression"; - let description = [{ - This operation represents the SystemVerilog logical and arithmetic - right-shift expressions `>>` and `>>>`. - See IEEE 1800-2017 §11.4.10 "Shift operators". - - The value to be shifted and the amount must be simple bit vector types. - The shift result is of the same type as the input value. - - The logical shift always inserts zeros in place of the shifted bits. - The arithmetic shift inserts zeros if the result type is unsigned or the - MSB (sign bit) if the result type is signed. - }]; -} diff --git a/include/circt/Dialect/Moore/MooreOps.td b/include/circt/Dialect/Moore/MooreOps.td index c4d5e10f5f2c..b8c35721d673 100644 --- a/include/circt/Dialect/Moore/MooreOps.td +++ b/include/circt/Dialect/Moore/MooreOps.td @@ -22,12 +22,6 @@ include "mlir/Interfaces/SideEffectInterfaces.td" class MooreOp traits = []> : Op; -// Base class for the MIR operations in this dialect. -class MIROp traits = []> : - MooreOp<"mir." # mnemonic, traits>; - -include "circt/Dialect/Moore/MIRExpressions.td" - //===----------------------------------------------------------------------===// // Structure //===----------------------------------------------------------------------===// @@ -204,4 +198,447 @@ def NonBlockingAssignOp : AssignOpBase<"nonblocking_assign"> { }]; } +//===----------------------------------------------------------------------===// +// Expressions +//===----------------------------------------------------------------------===// + +def ConstantOp : MooreOp<"constant", [Pure]> { + let summary = "A constant integer value"; + let arguments = (ins APIntAttr:$value); + let results = (outs SimpleBitVectorType:$result); + let hasCustomAssemblyFormat = 1; + let hasVerifier = 1; + let builders = [ + OpBuilder<(ins "Type":$type, "const APInt &":$value)>, + OpBuilder<(ins "Type":$type, "int64_t":$value)>, + ]; +} + +def ConversionOp : MooreOp<"conversion", [Pure]> { + let summary = "A type conversion"; + let description = [{ + An explicit or implicit type conversion. These are either generated + automatically in order to make assignments compatible: + + ``` + int a; + shortint b; + a = b; // generates an implicit cast from shortint to int + ``` + + Or explicitly by the user through a type, sign, or const cast expression: + + ``` + byte'(a) + unsigned'(a) + signed'(a) + 42'(a) + ``` + + See IEEE 1800-2017 § 6.24 "Casting". + }]; + let arguments = (ins AnyType:$input); + let results = (outs AnyType:$result); + let assemblyFormat = [{ + $input attr-dict `:` type($input) `->` type($result) + }]; + let hasFolder = 1; +} + +def NegOp : MooreOp<"neg", [Pure, SameOperandsAndResultType]> { + let summary = "Arithmetic negation"; + let description = [{ + Negate a value to its two's complement form. If any bit in the input is Z or + X, all bits in the result are X. + + See IEEE 1800-2017 § 11.4.3 "Arithmetic operators". + }]; + let arguments = (ins SimpleBitVectorType:$input); + let results = (outs SimpleBitVectorType:$result); + let assemblyFormat = [{ + $input attr-dict `:` type($input) + }]; +} + +def NotOp : MooreOp<"not", [Pure, SameOperandsAndResultType]> { + let summary = "Bitwise unary negation"; + let description = [{ + Applies the boolean NOT operation to each bit in the input. Corresponds to + the `~` operator, as well as the negation in the `~&`, `~|`, `^~`, and `~^` + reduction operators. + + See IEEE 1800-2017 § 11.4.8 "Bitwise operators". + + | Input | Result | + |-------|--------| + | 0 | 1 | + | 1 | 0 | + | X | X | + | Z | X | + }]; + let arguments = (ins SimpleBitVectorType:$input); + let results = (outs SimpleBitVectorType:$result); + let assemblyFormat = [{ + $input attr-dict `:` type($input) + }]; +} + +class ReduceOpBase : MooreOp() + .getSimpleBitVector() + .toSingleBit() + .getType($_self.getContext()) + }]> +]> { + let arguments = (ins SimpleBitVectorType:$input); + let results = (outs AnySingleBitType:$result); + let assemblyFormat = [{ + $input attr-dict `:` type($input) `->` type($result) + }]; + let summary = !subst("$op", operatorName, "Reduction $op operator"); + let description = !subst("$op", operatorName, [{ + Reduces all bits in the input to a single result bit by iteratively applying + the boolean $op operator. If the input has only a single bit, that bit is + returned. + + See IEEE 1800-2017 § 11.4.9 "Reduction operators". See the corresponding + `and`, `or`, and `xor` operations for the truth table. + }]); +} + +def ReduceAndOp : ReduceOpBase<"reduce_and", "AND">; +def ReduceOrOp : ReduceOpBase<"reduce_or", "OR">; +def ReduceXorOp : ReduceOpBase<"reduce_xor", "XOR">; + +def BoolCastOp : MooreOp<"bool_cast", [ + Pure, + TypesMatchWith<"result is single bit matching input domain", + "input", "result", [{ + IntType::get($_self.getContext(), + IntType::getAtomForDomain($_self.cast().getDomain())) + }]> +]> { + let summary = "Cast a value to a single bit boolean"; + let description = [{ + Convert a nonzero or true value into 1, a zero or false value into 0, and + any value containing Z or X bits into a X. This conversion is useful in + combination with the logical and, or, implication, equivalence, and negation + operators. + + See IEEE 1800-2017 § 11.4.7 "Logical operators". + }]; + let arguments = (ins UnpackedType:$input); + let results = (outs AnySingleBitType:$result); + let assemblyFormat = [{ + $input attr-dict `:` type($input) `->` type($result) + }]; + let hasFolder = 1; +} + +class BinaryOpBase traits = []> : + MooreOp { + let arguments = (ins SimpleBitVectorType:$lhs, SimpleBitVectorType:$rhs); + let results = (outs SimpleBitVectorType:$result); + let assemblyFormat = [{ + $lhs `,` $rhs attr-dict `:` type($result) + }]; +} + +def AddOp : BinaryOpBase<"add", [Commutative]> { + let summary = "Addition"; + let description = [{ + Add the operands. If any bit in the two operands is Z or X, all bits in the + result are X. + + See IEEE 1800-2017 § 11.4.3 "Arithmetic operators". + }]; +} + +def SubOp : BinaryOpBase<"sub"> { + let summary = "Subtraction"; + let description = [{ + Subtract the right-hand side from the left-hand side operand. If any bit in + the two operands is Z or X, all bits in the result are X. + + See IEEE 1800-2017 § 11.4.3 "Arithmetic operators". + }]; +} + +def MulOp : BinaryOpBase<"mul", [Commutative]> { + let summary = "Multiplication"; + let description = [{ + Multiply the operands. If any bit in the two operands is Z or X, all bits in + the result are X. + + See IEEE 1800-2017 § 11.4.3 "Arithmetic operators". + }]; +} + +def DivOp : BinaryOpBase<"div"> { + let summary = "Division"; + let description = [{ + Divide the left-hand side by the right-hand side operand. Any fractional + part is truncated toward zero. If the right-hand side is zero, all bits of + the result are X. If any bit in the two operands is Z or X, all bits in the + result are X. + + See IEEE 1800-2017 § 11.4.3 "Arithmetic operators". + }]; +} + +def ModOp : BinaryOpBase<"mod"> { + let summary = "Remainder"; + let description = [{ + Compute the remainder of the left-hand side divided by the right-hand side + operand. If the right-hand side is zero, all bits of the result are X. The + sign of the result is the sign of the left-hand side. If any bit in the two + operands is Z or X, all bits in the result are X. + + See IEEE 1800-2017 § 11.4.3 "Arithmetic operators". + + Consider the following examples: + + | LHS | RHS | Result | + |-----|-----|--------| + | 11 | 3 | 2 | + | -11 | 3 | -2 | + | 11 | -3 | 2 | + | -11 | -3 | -2 | + }]; +} + +def AndOp : BinaryOpBase<"and", [Commutative]> { + let summary = "Bitwise AND operation"; + let description = [{ + Applies the boolean AND operation to each pair of corresponding bits in the + left- and right-hand side operand. Corresponds to the `&` operator. + + See IEEE 1800-2017 § 11.4.8 "Bitwise operators". + + | | 0 | 1 | X | Z | + |---|---|---|---|---| + | 0 | 0 | 0 | 0 | 0 | + | 1 | 0 | 1 | X | X | + | X | 0 | X | X | X | + | Z | 0 | X | X | X | + }]; +} + +def OrOp : BinaryOpBase<"or", [Commutative]> { + let summary = "Bitwise OR operation"; + let description = [{ + Applies the boolean OR operation to each pair of corresponding bits in the + left- and right-hand side operand. Corresponds to the `|` operator. + + See IEEE 1800-2017 § 11.4.8 "Bitwise operators". + + | | 0 | 1 | X | Z | + |---|---|---|---|---| + | 0 | 0 | 1 | X | X | + | 1 | 1 | 1 | 1 | 1 | + | X | X | 1 | X | X | + | Z | X | 1 | X | X | + }]; +} + +def XorOp : BinaryOpBase<"xor", [Commutative]> { + let summary = "Bitwise XOR operation"; + let description = [{ + Applies the boolean XOR operation to each pair of corresponding bits in the + left- and right-hand side operand. Corresponds to the `^` operator. + + See IEEE 1800-2017 § 11.4.8 "Bitwise operators". + + | | 0 | 1 | X | Z | + |---|---|---|---|---| + | 0 | 0 | 1 | X | X | + | 1 | 1 | 0 | X | X | + | X | X | X | X | X | + | Z | X | X | X | X | + }]; +} + +class ShiftOpBase : MooreOp +]> { + let description = [{ + Shifts the `value` to the left or right by `amount` number of bits. The + result has the same type as the input value. The amount is always treated as + an unsigned number and has no effect on the signedness of the result. X or + Z bits in the input value are simply shifted left or right the same way 0 or + 1 bits are. If the amount contains X or Z bits, all result bits are X. + + `shl` shifts bits to the left, filling in 0 for the vacated least + significant bits. `shr` and `ashr` shift bits to the right; `shr` fills in + 0 for the vacated most significant bits, and `ashr` copies the input's sign + bit into the vacated most significant bits. Note that in contrast to the SV + spec, the `ashr` _always_ fills in the sign bit regardless of the signedness + of the input. + + `shl` corresponds to the `<<` and `<<<` operators. `shr` corresponds to the + `>>` operator, and the `>>>` operator applied to an unsigned value. `ashr` + corresponds to the `>>>` operator applied to a signed value. + + See IEEE 1800-2017 § 11.4.10 "Shift operators". + }]; + let arguments = (ins SimpleBitVectorType:$value, SimpleBitVectorType:$amount); + let results = (outs SimpleBitVectorType:$result); + let assemblyFormat = [{ + $value `,` $amount attr-dict `:` type($value) `,` type($amount) + }]; +} + +def ShlOp : ShiftOpBase<"shl"> { let summary = "Logical left shift"; } +def ShrOp : ShiftOpBase<"shr"> { let summary = "Logical right shift"; } +def AShrOp : ShiftOpBase<"ashr"> { let summary = "Arithmetic right shift"; } + +class LogicalEqOpBase : MooreOp().getDomain())) + }]> +]> { + let description = [{ + Compares the bits in the left- and right-hand side operand and returns a + single bit 0, 1, or X result. If all corresponding bits in the left- and + right-hand side are equal, and all are 0 or 1 (not X or Z), the two operands + are considered equal (`eq` returns 1, `ne` returns 0). If any bits are not + equal, but all are 0 or 1, the two operands are considered not equal (`eq` + returns 0, `ne` returns 1). If any bit in the two operands is Z or X, + returns X. `eq` corresponds to the `==` operator and `ne` to the `!=` + operator. + + See IEEE 1800-2017 § 11.4.5 "Equality operators". + }]; + let arguments = (ins SimpleBitVectorType:$lhs, SimpleBitVectorType:$rhs); + let results = (outs AnySingleBitType:$result); + let assemblyFormat = [{ + $lhs `,` $rhs attr-dict `:` type($lhs) `->` type($result) + }]; +} + +def EqOp : LogicalEqOpBase<"eq"> { let summary = "Logical equality"; } +def NeOp : LogicalEqOpBase<"ne"> { let summary = "Logical inequality"; } + +class CaseEqOpBase : MooreOp { + let description = [{ + Compares the bits in the left- and right-hand side operand and returns a + single bit 0 or 1 result. If all corresponding bits in the left- and + right-hand side are equal (both 0, 1, X, or Z), the two operands are + considered equal (`case_eq` returns 1, `case_ne` returns 0). If any bits are + not equal, the two operands are considered not equal (`case_eq` returns 0, + `case_ne` returns 1). `case_eq` corresponds to the `===` operator and + `case_ne` to the `!==` operator. + + See IEEE 1800-2017 § 11.4.5 "Equality operators". + }]; + let arguments = (ins SimpleBitVectorType:$lhs, SimpleBitVectorType:$rhs); + let results = (outs BitType:$result); + let assemblyFormat = [{ + $lhs `,` $rhs attr-dict `:` type($lhs) + }]; +} + +def CaseEqOp : CaseEqOpBase<"case_eq"> { let summary = "Case equality"; } +def CaseNeOp : CaseEqOpBase<"case_ne"> { let summary = "Case inequality"; } + +class WildcardEqOpBase : MooreOp().getDomain())) + }]> +]> { + let description = [{ + Compares the bits in the left- and right-hand side operand and returns a + single bit 0, 1, or X result. If any bit in the left-hand side is Z or X, + returns X. Performs the same comparison as the `eq` and `ne` operations, but + all right-hand side bits that are X or Z are skipped. Therefore, X and Z in + the right-hand side act as wildcards or "don't care" values. `wildcard_eq` + corresponds to the `==?` operator and `wildcard_ne` to the `!=?` operator. + + See IEEE 1800-2017 § 11.4.6 "Wildcard equality operators". + }]; + let arguments = (ins SimpleBitVectorType:$lhs, SimpleBitVectorType:$rhs); + let results = (outs AnySingleBitType:$result); + let assemblyFormat = [{ + $lhs `,` $rhs attr-dict `:` type($lhs) `->` type($result) + }]; +} + +def WildcardEqOp : WildcardEqOpBase<"wildcard_eq"> { + let summary = "Wildcard equality"; +} +def WildcardNeOp : WildcardEqOpBase<"wildcard_ne"> { + let summary = "Wildcard inequality"; +} + +class RelationalOpBase : MooreOp().getDomain())) + }]> +]> { + let description = [{ + Compares the left- and right-hand side operand and returns a single bit 0, + 1, or X result. If any bit in the two operands is Z or X, returns X. + Otherwise, if all bits are 0 or 1, `lt`, `le`, `gt`, and `ge` return whether + the left-hand side is less than, less than or equal to, greater than, or + greater than or equal to the right-hand side, respectively. `lt` corresponds + to the `<` operator, `le` to `<=`, `gt` to `>`, and `ge` to `>=`. + + See IEEE 1800-2017 § 11.4.4 "Relational operators". + }]; + let arguments = (ins SimpleBitVectorType:$lhs, SimpleBitVectorType:$rhs); + let results = (outs AnySingleBitType:$result); + let assemblyFormat = [{ + $lhs `,` $rhs attr-dict `:` type($lhs) `->` type($result) + }]; +} + +def LtOp : RelationalOpBase<"lt"> { let summary = "Less than"; } +def LeOp : RelationalOpBase<"le"> { let summary = "Less than or equal"; } +def GtOp : RelationalOpBase<"gt"> { let summary = "Greater than"; } +def GeOp : RelationalOpBase<"ge"> { let summary = "Greater than or equal"; } + +def ConcatOp : MooreOp<"concat", [ + Pure, DeclareOpInterfaceMethods +]> { + let summary = "A concatenation of expressions"; + let description = [{ + This operation represents the SystemVerilog concatenation expression + `{x, y, z}`. See IEEE 1800-2017 §11.4.12 "Concatenation operators". + + All operands must be simple bit vector types. + + The concatenation result is a simple bit vector type. The result is unsigned + regardless of the sign of the operands (see concatenation-specific rules in + IEEE 1800-2017 §11.8.1 "Rules for expression types"). The size of the result + is the sum of the sizes of all operands. If any of the operands is + four-valued, the result is four-valued; otherwise it is two-valued. + }]; + let arguments = (ins Variadic:$values); + let results = (outs SimpleBitVectorType:$result); + let assemblyFormat = [{ + $values attr-dict `:` functional-type($values, $result) + }]; +} + #endif // CIRCT_DIALECT_MOORE_MOOREOPS diff --git a/include/circt/Dialect/Moore/MooreTypes.h b/include/circt/Dialect/Moore/MooreTypes.h index 95c1bdbfa56b..7a4987911cd9 100644 --- a/include/circt/Dialect/Moore/MooreTypes.h +++ b/include/circt/Dialect/Moore/MooreTypes.h @@ -157,6 +157,15 @@ struct SimpleBitVectorType { /// Get the range of the type. Range getRange() const { return Range(size, RangeDir::Down, 0); } + /// Get a single bit version of this type by setting its size to 1. + SimpleBitVectorType toSingleBit() const { + auto type = *this; + type.size = 1; + type.explicitSize = false; + type.usedAtom = false; + return type; + } + /// Check whether this type is equivalent to another. bool isEquivalent(const SimpleBitVectorType &other) const { return domain == other.domain && sign == other.sign && size == other.size; @@ -544,6 +553,8 @@ class IntType static Domain getDomain(Kind kind); /// Get the size of one of the integer types. static unsigned getBitSize(Kind kind); + /// Get the integer type that corresponds to a single bit of the given domain. + static Kind getAtomForDomain(Domain domain); /// Get the integer type that corresponds to a domain and bit size. For /// example, returns `int` for `(TwoValued, 32)`. static std::optional getKindFromDomainAndSize(Domain domain, diff --git a/include/circt/Dialect/Moore/MooreTypes.td b/include/circt/Dialect/Moore/MooreTypes.td index 779eb8efd891..94adc0926fe0 100644 --- a/include/circt/Dialect/Moore/MooreTypes.td +++ b/include/circt/Dialect/Moore/MooreTypes.td @@ -33,6 +33,23 @@ def SimpleBitVectorType : MooreType().isSimpleBitVector() }]>, "simple bit vector type", "moore::UnpackedType">; +/// A single bit type (`bit`, `logic`, `reg`). +def AnySingleBitType : MooreType() && + $_self.cast().getBitSize() == 1 + }]>, "single bit type", "moore::IntType">; + +/// A `bit` type. +def BitType : MooreType() && + $_self.cast().getBitSize() == 1 && + $_self.cast().getDomain() == moore::Domain::TwoValued + }]>, "`bit` type", "moore::IntType"> { + let builderCall = [{ + $_builder.getType(IntType::Kind::Bit) + }]; +} + //===----------------------------------------------------------------------===// // Integer atom types //===----------------------------------------------------------------------===// diff --git a/lib/Conversion/ImportVerilog/Expressions.cpp b/lib/Conversion/ImportVerilog/Expressions.cpp index 5c91d22ffa85..6712097840e1 100644 --- a/lib/Conversion/ImportVerilog/Expressions.cpp +++ b/lib/Conversion/ImportVerilog/Expressions.cpp @@ -22,6 +22,37 @@ struct ExprVisitor { ExprVisitor(Context &context, Location loc) : context(context), loc(loc), builder(context.builder) {} + /// Helper function to convert a value to its simple bit vector + /// representation, if it has one. Otherwise returns null. + Value convertToSimpleBitVector(Value value) { + if (!value) + return {}; + if (auto type = dyn_cast_or_null(value.getType())) { + if (type.isSimpleBitVector()) + return value; + if (auto sbvt = type.castToSimpleBitVectorOrNull()) + return builder.create( + loc, sbvt.getType(builder.getContext()), value); + } + mlir::emitError(loc, "expression of type ") + << value.getType() << " cannot be cast to a simple bit vector"; + return {}; + } + + /// Helper function to convert a value to its "truthy" boolean value. + Value convertToBool(Value value) { + if (!value) + return {}; + if (auto type = dyn_cast_or_null(value.getType())) + if (type.getBitSize() == 1) + return value; + if (auto type = dyn_cast_or_null(value.getType())) + return builder.create(loc, value); + mlir::emitError(loc, "expression of type ") + << value.getType() << " cannot be cast to a boolean"; + return {}; + } + // Handle named values, such as references to declared variables. Value visit(const slang::ast::NamedValueExpression &expr) { if (auto value = context.valueSymbols.lookup(&expr.symbol)) @@ -32,6 +63,17 @@ struct ExprVisitor { return {}; } + // Handle type conversions (explicit and implicit). + Value visit(const slang::ast::ConversionExpression &expr) { + auto type = context.convertType(*expr.type); + if (!type) + return {}; + auto operand = context.convertExpression(expr.operand()); + if (!operand) + return {}; + return builder.create(loc, type, operand); + } + // Handle blocking and non-blocking assignments. Value visit(const slang::ast::AssignmentExpression &expr) { auto lhs = context.convertExpression(expr.left()); @@ -39,6 +81,9 @@ struct ExprVisitor { if (!lhs || !rhs) return {}; + if (lhs.getType() != rhs.getType()) + rhs = builder.create(loc, lhs.getType(), rhs); + if (expr.timingControl) { auto loc = context.convertLocation(expr.timingControl->sourceRange); mlir::emitError(loc, "delayed assignments not supported"); @@ -52,6 +97,250 @@ struct ExprVisitor { return lhs; } + // Helper function to convert an argument to a simple bit vector type, pass it + // to a reduction op, and optionally invert the result. + template + Value createReduction(Value arg, bool invert) { + arg = convertToSimpleBitVector(arg); + if (!arg) + return {}; + Value result = builder.create(loc, arg); + if (invert) + result = builder.create(loc, result); + return result; + } + + // Handle unary operators. + Value visit(const slang::ast::UnaryExpression &expr) { + auto arg = context.convertExpression(expr.operand()); + if (!arg) + return {}; + + using slang::ast::UnaryOperator; + switch (expr.op) { + // `+a` is simply `a`, but converted to a simple bit vector type since + // this is technically an arithmetic operation. + case UnaryOperator::Plus: + return convertToSimpleBitVector(arg); + + case UnaryOperator::Minus: + arg = convertToSimpleBitVector(arg); + if (!arg) + return {}; + return builder.create(loc, arg); + + case UnaryOperator::BitwiseNot: + arg = convertToSimpleBitVector(arg); + if (!arg) + return {}; + return builder.create(loc, arg); + + case UnaryOperator::BitwiseAnd: + return createReduction(arg, false); + case UnaryOperator::BitwiseOr: + return createReduction(arg, false); + case UnaryOperator::BitwiseXor: + return createReduction(arg, false); + case UnaryOperator::BitwiseNand: + return createReduction(arg, true); + case UnaryOperator::BitwiseNor: + return createReduction(arg, true); + case UnaryOperator::BitwiseXnor: + return createReduction(arg, true); + + case UnaryOperator::LogicalNot: + arg = convertToBool(arg); + if (!arg) + return {}; + return builder.create(loc, arg); + + case UnaryOperator::Preincrement: + case UnaryOperator::Predecrement: + case UnaryOperator::Postincrement: + case UnaryOperator::Postdecrement: + break; + } + + mlir::emitError(loc, "unsupported unary operator"); + return {}; + } + + // Helper function to convert two arguments to a simple bit vector type and + // pass them into a binary op. + template + Value createBinary(Value lhs, Value rhs) { + lhs = convertToSimpleBitVector(lhs); + rhs = convertToSimpleBitVector(rhs); + if (!lhs || !rhs) + return {}; + return builder.create(loc, lhs, rhs); + } + + // Handle binary operators. + Value visit(const slang::ast::BinaryExpression &expr) { + auto lhs = context.convertExpression(expr.left()); + auto rhs = context.convertExpression(expr.right()); + if (!lhs || !rhs) + return {}; + + using slang::ast::BinaryOperator; + switch (expr.op) { + case BinaryOperator::Add: + return createBinary(lhs, rhs); + case BinaryOperator::Subtract: + return createBinary(lhs, rhs); + case BinaryOperator::Multiply: + return createBinary(lhs, rhs); + case BinaryOperator::Divide: + return createBinary(lhs, rhs); + case BinaryOperator::Mod: + return createBinary(lhs, rhs); + + case BinaryOperator::BinaryAnd: + return createBinary(lhs, rhs); + case BinaryOperator::BinaryOr: + return createBinary(lhs, rhs); + case BinaryOperator::BinaryXor: + return createBinary(lhs, rhs); + case BinaryOperator::BinaryXnor: { + auto result = createBinary(lhs, rhs); + if (!result) + return {}; + return builder.create(loc, result); + } + + case BinaryOperator::Equality: + return createBinary(lhs, rhs); + case BinaryOperator::Inequality: + return createBinary(lhs, rhs); + case BinaryOperator::CaseEquality: + return createBinary(lhs, rhs); + case BinaryOperator::CaseInequality: + return createBinary(lhs, rhs); + case BinaryOperator::WildcardEquality: + return createBinary(lhs, rhs); + case BinaryOperator::WildcardInequality: + return createBinary(lhs, rhs); + + case BinaryOperator::GreaterThanEqual: + return createBinary(lhs, rhs); + case BinaryOperator::GreaterThan: + return createBinary(lhs, rhs); + case BinaryOperator::LessThanEqual: + return createBinary(lhs, rhs); + case BinaryOperator::LessThan: + return createBinary(lhs, rhs); + + // See IEEE 1800-2017 § 11.4.7 "Logical operators". + case BinaryOperator::LogicalAnd: { + // TODO: This should short-circuit. Put the RHS code into an scf.if. + lhs = convertToBool(lhs); + rhs = convertToBool(rhs); + if (!lhs || !rhs) + return {}; + return builder.create(loc, lhs, rhs); + } + case BinaryOperator::LogicalOr: { + // TODO: This should short-circuit. Put the RHS code into an scf.if. + lhs = convertToBool(lhs); + rhs = convertToBool(rhs); + if (!lhs || !rhs) + return {}; + return builder.create(loc, lhs, rhs); + } + case BinaryOperator::LogicalImplication: { + // `(lhs -> rhs)` equivalent to `(!lhs || rhs)`. + lhs = convertToBool(lhs); + rhs = convertToBool(rhs); + if (!lhs || !rhs) + return {}; + auto notLHS = builder.create(loc, lhs); + return builder.create(loc, notLHS, rhs); + } + case BinaryOperator::LogicalEquivalence: { + // `(lhs <-> rhs)` equivalent to `(lhs && rhs) || (!lhs && !rhs)`. + lhs = convertToBool(lhs); + rhs = convertToBool(rhs); + if (!lhs || !rhs) + return {}; + auto notLHS = builder.create(loc, lhs); + auto notRHS = builder.create(loc, rhs); + auto both = builder.create(loc, lhs, rhs); + auto notBoth = builder.create(loc, notLHS, notRHS); + return builder.create(loc, both, notBoth); + } + + case BinaryOperator::LogicalShiftLeft: + return createBinary(lhs, rhs); + case BinaryOperator::LogicalShiftRight: + return createBinary(lhs, rhs); + case BinaryOperator::ArithmeticShiftLeft: + return createBinary(lhs, rhs); + case BinaryOperator::ArithmeticShiftRight: { + // The `>>>` operator is an arithmetic right shift if the LHS operand is + // signed, or a logical right shift if the operand is unsigned. + lhs = convertToSimpleBitVector(lhs); + rhs = convertToSimpleBitVector(rhs); + if (!lhs || !rhs) + return {}; + if (cast(lhs.getType()) + .getSimpleBitVector() + .isSigned()) + return builder.create(loc, lhs, rhs); + return builder.create(loc, lhs, rhs); + } + + case BinaryOperator::Power: + break; + } + + mlir::emitError(loc, "unsupported binary operator"); + return {}; + } + + // Materialize a Slang integer literal as a constant op. + Value convertSVInt(const slang::SVInt &value, Type type) { + if (value.hasUnknown()) { + mlir::emitError(loc, "literals with X or Z bits not supported"); + return {}; + } + if (value.getBitWidth() > 64) { + mlir::emitError(loc, "unsupported bit width: literal is ") + << value.getBitWidth() << " bits wide; only 64 supported"; + return {}; + } + auto truncValue = value.as().value(); + return builder.create(loc, type, truncValue); + } + + // Handle `'0`, `'1`, `'x`, and `'z` literals. + Value visit(const slang::ast::UnbasedUnsizedIntegerLiteral &expr) { + auto type = context.convertType(*expr.type); + if (!type) + return {}; + return convertSVInt(expr.getValue(), type); + } + + // Handle integer literals. + Value visit(const slang::ast::IntegerLiteral &expr) { + auto type = context.convertType(*expr.type); + if (!type) + return {}; + return convertSVInt(expr.getValue(), type); + } + + // Handle concatenations. + Value visit(const slang::ast::ConcatenationExpression &expr) { + SmallVector operands; + for (auto *operand : expr.operands()) { + auto value = context.convertExpression(*operand); + if (!value) + return {}; + operands.push_back(value); + } + return builder.create(loc, operands); + } + /// Emit an error for all other expressions. template Value visit(T &&node) { diff --git a/lib/Conversion/ImportVerilog/Structure.cpp b/lib/Conversion/ImportVerilog/Structure.cpp index b4a17d904076..f830253669d0 100644 --- a/lib/Conversion/ImportVerilog/Structure.cpp +++ b/lib/Conversion/ImportVerilog/Structure.cpp @@ -106,6 +106,9 @@ struct MemberVisitor { if (!lhs || !rhs) return failure(); + if (lhs.getType() != rhs.getType()) + rhs = builder.create(loc, lhs.getType(), rhs); + builder.create(loc, lhs, rhs); return success(); } diff --git a/lib/Conversion/MooreToCore/MooreToCore.cpp b/lib/Conversion/MooreToCore/MooreToCore.cpp index f3a916c8ea3e..6357897f322d 100644 --- a/lib/Conversion/MooreToCore/MooreToCore.cpp +++ b/lib/Conversion/MooreToCore/MooreToCore.cpp @@ -186,24 +186,30 @@ struct ShrOpConversion : public OpConversionPattern { matchAndRewrite(ShrOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override { Type resultType = typeConverter->convertType(op.getResult().getType()); - bool hasSignedResultType = op.getResult() - .getType() - .cast() - .castToSimpleBitVector() - .isSigned(); // Comb shift operations require the same bit-width for value and amount Value amount = adjustIntegerWidth(rewriter, adaptor.getAmount(), resultType.getIntOrFloatBitWidth(), op->getLoc()); + rewriter.replaceOpWithNewOp( + op, resultType, adaptor.getValue(), amount, false); + return success(); + } +}; - if (adaptor.getArithmetic() && hasSignedResultType) { - rewriter.replaceOpWithNewOp( - op, resultType, adaptor.getValue(), amount, false); - return success(); - } +struct AShrOpConversion : public OpConversionPattern { + using OpConversionPattern::OpConversionPattern; - rewriter.replaceOpWithNewOp( + LogicalResult + matchAndRewrite(AShrOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + Type resultType = typeConverter->convertType(op.getResult().getType()); + + // Comb shift operations require the same bit-width for value and amount + Value amount = + adjustIntegerWidth(rewriter, adaptor.getAmount(), + resultType.getIntOrFloatBitWidth(), op->getLoc()); + rewriter.replaceOpWithNewOp( op, resultType, adaptor.getValue(), amount, false); return success(); } @@ -215,9 +221,7 @@ struct ShrOpConversion : public OpConversionPattern { // Conversion Infrastructure //===----------------------------------------------------------------------===// -static bool isMooreType(Type type) { - return type.isa() || type.isa(); -} +static bool isMooreType(Type type) { return type.isa(); } static bool hasMooreType(TypeRange types) { return llvm::any_of(types, isMooreType); @@ -287,6 +291,7 @@ static void populateOpConversion(RewritePatternSet &patterns, CallOpConversion, ShlOpConversion, ShrOpConversion, + AShrOpConversion, UnrealizedConversionCastConversion >(typeConverter, context); // clang-format on diff --git a/lib/Dialect/Moore/MooreOps.cpp b/lib/Dialect/Moore/MooreOps.cpp index af1d0e99daf4..1027708c7d40 100644 --- a/lib/Dialect/Moore/MooreOps.cpp +++ b/lib/Dialect/Moore/MooreOps.cpp @@ -45,6 +45,94 @@ void VariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { setNameFn(getResult(), getName()); } +//===----------------------------------------------------------------------===// +// ConstantOp +//===----------------------------------------------------------------------===// + +void ConstantOp::print(OpAsmPrinter &p) { + p << " "; + p.printAttributeWithoutType(getValueAttr()); + p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"}); + p << " : "; + p.printType(getType()); +} + +ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) { + // Parse the constant value without bit width. + APInt value; + auto valueLoc = parser.getCurrentLocation(); + + if (parser.parseInteger(value) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColon()) + return failure(); + + // Parse the result type.. + UnpackedType type; + auto typeLoc = parser.getCurrentLocation(); + if (parser.parseType(type)) + return failure(); + + // Ensure that the result type is a simple bit vector type. + auto sbvt = type.getSimpleBitVectorOrNull(); + if (!sbvt) + return parser.emitError(typeLoc, "expected simple bit vector type"); + + // Extend or truncate the constant value to match the size of the type. + if (sbvt.size > value.getBitWidth()) { + // sext is always safe here, even for unsigned values, because the + // parseOptionalInteger method will return something with a zero in the + // top bits if it is a positive number. + value = value.sext(sbvt.size); + } else if (sbvt.size < value.getBitWidth()) { + // The parser can return an unnecessarily wide result with leading + // zeros. This isn't a problem, but truncating off bits is bad. + unsigned neededBits = + value.isNegative() ? value.getSignificantBits() : value.getActiveBits(); + if (sbvt.size < neededBits) + return parser.emitError(valueLoc, + "constant out of range for result type ") + << type; + value = value.trunc(sbvt.size); + } + + // Build the attribute and op. + auto attrType = IntegerType::get(parser.getContext(), sbvt.size); + auto attrValue = IntegerAttr::get(attrType, value); + + result.addAttribute("value", attrValue); + result.addTypes(type); + return success(); +} + +LogicalResult ConstantOp::verify() { + auto sbvt = getType().getSimpleBitVector(); + auto width = getValue().getBitWidth(); + if (width != sbvt.size) + return emitError("attribute width ") + << width << " does not match return type's width " << sbvt.size; + return success(); +} + +void ConstantOp::build(OpBuilder &builder, OperationState &result, Type type, + const APInt &value) { + auto sbvt = type.cast().getSimpleBitVector(); + assert(sbvt.size == value.getBitWidth() && + "APInt width must match simple bit vector's bit width"); + build(builder, result, type, + builder.getIntegerAttr(builder.getIntegerType(sbvt.size), value)); +} + +/// This builder allows construction of small signed integers like 0, 1, -1 +/// matching a specified MLIR type. This shouldn't be used for general constant +/// folding because it only works with values that can be expressed in an +/// `int64_t`. +void ConstantOp::build(OpBuilder &builder, OperationState &result, Type type, + int64_t value) { + auto sbvt = type.cast().getSimpleBitVector(); + build(builder, result, type, + APInt(sbvt.size, (uint64_t)value, /*isSigned=*/true)); +} + //===----------------------------------------------------------------------===// // ConcatOp //===----------------------------------------------------------------------===// @@ -66,6 +154,28 @@ LogicalResult ConcatOp::inferReturnTypes( return success(); } +//===----------------------------------------------------------------------===// +// ConversionOp +//===----------------------------------------------------------------------===// + +OpFoldResult ConversionOp::fold(FoldAdaptor adaptor) { + // Fold away no-op casts. + if (getInput().getType() == getResult().getType()) + return getInput(); + return {}; +} + +//===----------------------------------------------------------------------===// +// BoolCastOp +//===----------------------------------------------------------------------===// + +OpFoldResult BoolCastOp::fold(FoldAdaptor adaptor) { + // Fold away no-op casts. + if (getInput().getType() == getResult().getType()) + return getInput(); + return {}; +} + //===----------------------------------------------------------------------===// // TableGen generated logic. //===----------------------------------------------------------------------===// diff --git a/lib/Dialect/Moore/MooreTypes.cpp b/lib/Dialect/Moore/MooreTypes.cpp index a9bb7247c6c0..1d1124870d2a 100644 --- a/lib/Dialect/Moore/MooreTypes.cpp +++ b/lib/Dialect/Moore/MooreTypes.cpp @@ -446,13 +446,23 @@ unsigned IntType::getBitSize(Kind kind) { llvm_unreachable("all kinds should be handled"); } +IntType::Kind IntType::getAtomForDomain(Domain domain) { + switch (domain) { + case Domain::TwoValued: + return IntType::Bit; + case Domain::FourValued: + return IntType::Logic; + } + llvm_unreachable("all domains should be handled"); +} + std::optional IntType::getKindFromDomainAndSize(Domain domain, unsigned size) { + if (size == 1) + return getAtomForDomain(domain); switch (domain) { case Domain::TwoValued: switch (size) { - case 1: - return IntType::Bit; case 8: return IntType::Byte; case 16: @@ -466,8 +476,6 @@ std::optional IntType::getKindFromDomainAndSize(Domain domain, } case Domain::FourValued: switch (size) { - case 1: - return IntType::Logic; case 32: return IntType::Integer; default: diff --git a/test/Conversion/ImportVerilog/basic.sv b/test/Conversion/ImportVerilog/basic.sv index 00bdf6b72967..88e3796a26c8 100644 --- a/test/Conversion/ImportVerilog/basic.sv +++ b/test/Conversion/ImportVerilog/basic.sv @@ -96,3 +96,196 @@ module Statements; x <= y; end endmodule + +// CHECK-LABEL: moore.module @Expressions { +module Expressions; + // CHECK: %a = moore.variable : !moore.int + // CHECK: %b = moore.variable : !moore.int + // CHECK: %c = moore.variable : !moore.int + int a, b, c; + int unsigned u; + bit [1:0][3:0] v; + integer d, e, f; + bit x; + logic y; + + initial begin + // CHECK: moore.constant 0 : !moore.packed> + c = '0; + // CHECK: moore.constant -1 : !moore.packed> + c = '1; + // CHECK: moore.constant 42 : !moore.int + c = 42; + // CHECK: moore.constant 42 : !moore.packed> + c = 19'd42; + // CHECK: moore.constant 42 : !moore.packed, 18:0>> + c = 19'sd42; + // CHECK: moore.concat %a, %b, %c : (!moore.int, !moore.int, !moore.int) -> !moore.packed> + a = {a, b, c}; + // CHECK: moore.concat %d, %e : (!moore.integer, !moore.integer) -> !moore.packed> + d = {d, e}; + + //===------------------------------------------------------------------===// + // Unary operators + + // CHECK: moore.blocking_assign %c, %a : !moore.int + c = +a; + // CHECK: moore.neg %a : !moore.int + c = -a; + // CHECK: [[TMP1:%.+]] = moore.conversion %v : !moore.packed, 1:0>> -> !moore.packed> + // CHECK: [[TMP2:%.+]] = moore.neg [[TMP1]] : !moore.packed> + // CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.packed> -> !moore.int + c = -v; + // CHECK: moore.not %a : !moore.int + c = ~a; + // CHECK: moore.reduce_and %a : !moore.int -> !moore.bit + x = &a; + // CHECK: moore.reduce_and %d : !moore.integer -> !moore.logic + y = &d; + // CHECK: moore.reduce_or %a : !moore.int -> !moore.bit + x = |a; + // CHECK: moore.reduce_xor %a : !moore.int -> !moore.bit + x = ^a; + // CHECK: [[TMP:%.+]] = moore.reduce_and %a : !moore.int -> !moore.bit + // CHECK: moore.not [[TMP]] : !moore.bit + x = ~&a; + // CHECK: [[TMP:%.+]] = moore.reduce_or %a : !moore.int -> !moore.bit + // CHECK: moore.not [[TMP]] : !moore.bit + x = ~|a; + // CHECK: [[TMP:%.+]] = moore.reduce_xor %a : !moore.int -> !moore.bit + // CHECK: moore.not [[TMP]] : !moore.bit + x = ~^a; + // CHECK: [[TMP:%.+]] = moore.reduce_xor %a : !moore.int -> !moore.bit + // CHECK: moore.not [[TMP]] : !moore.bit + x = ^~a; + // CHECK: [[TMP:%.+]] = moore.bool_cast %a : !moore.int -> !moore.bit + // CHECK: moore.not [[TMP]] : !moore.bit + x = !a; + + //===------------------------------------------------------------------===// + // Binary operators + + // CHECK: moore.add %a, %b : !moore.int + c = a + b; + // CHECK: [[TMP1:%.+]] = moore.conversion %a : !moore.int -> !moore.packed> + // CHECK: [[TMP2:%.+]] = moore.conversion %v : !moore.packed, 1:0>> -> !moore.packed> + // CHECK: [[TMP3:%.+]] = moore.add [[TMP1]], [[TMP2]] : !moore.packed> + // CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.packed> -> !moore.int + c = a + v; + // CHECK: moore.sub %a, %b : !moore.int + c = a - b; + // CHECK: moore.mul %a, %b : !moore.int + c = a * b; + // CHECK: moore.div %d, %e : !moore.integer + f = d / e; + // CHECK: moore.mod %d, %e : !moore.integer + f = d % e; + + // CHECK: moore.and %a, %b : !moore.int + c = a & b; + // CHECK: moore.or %a, %b : !moore.int + c = a | b; + // CHECK: moore.xor %a, %b : !moore.int + c = a ^ b; + // CHECK: [[TMP:%.+]] = moore.xor %a, %b : !moore.int + // CHECK: moore.not [[TMP]] : !moore.int + c = a ~^ b; + // CHECK: [[TMP:%.+]] = moore.xor %a, %b : !moore.int + // CHECK: moore.not [[TMP]] : !moore.int + c = a ^~ b; + + // CHECK: moore.eq %a, %b : !moore.int -> !moore.bit + x = a == b; + // CHECK: moore.eq %d, %e : !moore.integer -> !moore.logic + y = d == e; + // CHECK: moore.ne %a, %b : !moore.int -> !moore.bit + x = a != b ; + // CHECK: moore.case_eq %a, %b : !moore.int + x = a === b; + // CHECK: moore.case_ne %a, %b : !moore.int + x = a !== b; + // CHECK: moore.wildcard_eq %a, %b : !moore.int -> !moore.bit + x = a ==? b; + // CHECK: [[TMP:%.+]] = moore.conversion %a : !moore.int -> !moore.integer + // CHECK: moore.wildcard_eq [[TMP]], %d : !moore.integer -> !moore.logic + y = a ==? d; + // CHECK: [[TMP:%.+]] = moore.conversion %b : !moore.int -> !moore.integer + // CHECK: moore.wildcard_eq %d, [[TMP]] : !moore.integer -> !moore.logic + y = d ==? b; + // CHECK: moore.wildcard_eq %d, %e : !moore.integer -> !moore.logic + y = d ==? e; + // CHECK: moore.wildcard_ne %a, %b : !moore.int -> !moore.bit + x = a !=? b; + + // CHECK: moore.ge %a, %b : !moore.int -> !moore.bit + c = a >= b; + // CHECK: moore.gt %a, %b : !moore.int -> !moore.bit + c = a > b; + // CHECK: moore.le %a, %b : !moore.int -> !moore.bit + c = a <= b; + // CHECK: moore.lt %a, %b : !moore.int -> !moore.bit + c = a < b; + + // CHECK: [[A:%.+]] = moore.bool_cast %a : !moore.int -> !moore.bit + // CHECK: [[B:%.+]] = moore.bool_cast %b : !moore.int -> !moore.bit + // CHECK: moore.and [[A]], [[B]] : !moore.bit + c = a && b; + // CHECK: [[A:%.+]] = moore.bool_cast %a : !moore.int -> !moore.bit + // CHECK: [[B:%.+]] = moore.bool_cast %b : !moore.int -> !moore.bit + // CHECK: moore.or [[A]], [[B]] : !moore.bit + c = a || b; + // CHECK: [[A:%.+]] = moore.bool_cast %a : !moore.int -> !moore.bit + // CHECK: [[B:%.+]] = moore.bool_cast %b : !moore.int -> !moore.bit + // CHECK: [[NOT_A:%.+]] = moore.not [[A]] : !moore.bit + // CHECK: moore.or [[NOT_A]], [[B]] : !moore.bit + c = a -> b; + // CHECK: [[A:%.+]] = moore.bool_cast %a : !moore.int -> !moore.bit + // CHECK: [[B:%.+]] = moore.bool_cast %b : !moore.int -> !moore.bit + // CHECK: [[NOT_A:%.+]] = moore.not [[A]] : !moore.bit + // CHECK: [[NOT_B:%.+]] = moore.not [[B]] : !moore.bit + // CHECK: [[BOTH:%.+]] = moore.and [[A]], [[B]] : !moore.bit + // CHECK: [[NOT_BOTH:%.+]] = moore.and [[NOT_A]], [[NOT_B]] : !moore.bit + // CHECK: moore.or [[BOTH]], [[NOT_BOTH]] : !moore.bit + c = a <-> b; + + // CHECK: moore.shl %a, %b : !moore.int, !moore.int + c = a << b; + // CHECK: moore.shr %a, %b : !moore.int, !moore.int + c = a >> b; + // CHECK: moore.shl %a, %b : !moore.int, !moore.int + c = a <<< b; + // CHECK: moore.ashr %a, %b : !moore.int, !moore.int + c = a >>> b; + // CHECK: moore.shr %u, %b : !moore.int, !moore.int + c = u >>> b; + end +endmodule + +// CHECK-LABEL: moore.module @Conversion { +module Conversion; + // Implicit conversion. + // CHECK: %a = moore.variable + // CHECK: [[TMP:%.+]] = moore.conversion %a : !moore.shortint -> !moore.int + // CHECK: %b = moore.variable [[TMP]] + shortint a; + int b = a; + + // Explicit conversion. + // CHECK: [[TMP1:%.+]] = moore.conversion %a : !moore.shortint -> !moore.byte + // CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.byte -> !moore.int + // CHECK: %c = moore.variable [[TMP2]] + int c = byte'(a); + + // Sign conversion. + // CHECK: [[TMP:%.+]] = moore.conversion %b : !moore.int -> !moore.packed, 31:0>> + // CHECK: %d1 = moore.variable [[TMP]] + // CHECK: [[TMP:%.+]] = moore.conversion %b : !moore.int -> !moore.packed> + // CHECK: %d2 = moore.variable [[TMP]] + bit signed [31:0] d1 = signed'(b); + bit [31:0] d2 = unsigned'(b); + + // Width conversion. + // CHECK: [[TMP:%.+]] = moore.conversion %b : !moore.int -> !moore.packed, 18:0>> + // CHECK: %e = moore.variable [[TMP]] + bit signed [18:0] e = 19'(b); +endmodule diff --git a/test/Conversion/ImportVerilog/errors.sv b/test/Conversion/ImportVerilog/errors.sv index eb078c27663e..d95ef2072f8e 100644 --- a/test/Conversion/ImportVerilog/errors.sv +++ b/test/Conversion/ImportVerilog/errors.sv @@ -76,3 +76,13 @@ module Foo; release a; end endmodule + +// ----- + +module Foo; + logic x; + // expected-error @below {{literals with X or Z bits not supported}} + initial x = 'x; + // expected-error @below {{literals with X or Z bits not supported}} + initial x = 'z; +endmodule diff --git a/test/Conversion/MooreToCore/basic.mlir b/test/Conversion/MooreToCore/basic.mlir index a797afe7d54b..f08e36f3200f 100644 --- a/test/Conversion/MooreToCore/basic.mlir +++ b/test/Conversion/MooreToCore/basic.mlir @@ -1,11 +1,5 @@ // RUN: circt-opt %s --convert-moore-to-core --verify-diagnostics | FileCheck %s -// CHECK-LABEL: llhd.entity @test1 -llhd.entity @test1() -> () { - // CHECK-NEXT: %c5_i32 = hw.constant 5 : i32 - %0 = moore.mir.constant 5 : !moore.int -} - // CHECK-LABEL: func @FuncArgsAndReturns // CHECK-SAME: (%arg0: i8, %arg1: i32, %arg2: i1) -> i8 func.func @FuncArgsAndReturns(%arg0: !moore.byte, %arg1: !moore.int, %arg2: !moore.bit) -> !moore.byte { @@ -55,34 +49,40 @@ func.func @UnrealizedConversionCast(%arg0: !moore.byte) -> !moore.shortint { func.func @Expressions(%arg0: !moore.bit, %arg1: !moore.logic, %arg2: !moore.packed>, %arg3: !moore.packed, 4:0>>) { // CHECK-NEXT: %0 = comb.concat %arg0, %arg0 : i1, i1 // CHECK-NEXT: %1 = comb.concat %arg1, %arg1 : i1, i1 - %0 = moore.mir.concat %arg0, %arg0 : (!moore.bit, !moore.bit) -> !moore.packed> - %1 = moore.mir.concat %arg1, %arg1 : (!moore.logic, !moore.logic) -> !moore.packed> - // CHECK-NEXT: %[[V0:.+]] = hw.constant 0 : i5 - // CHECK-NEXT: %[[V1:.+]] = comb.concat %[[V0]], %arg0 : i5, i1 - // CHECK-NEXT: comb.shl %arg2, %[[V1]] : i6 - // CHECK-NEXT: %[[V2:.+]] = comb.extract %arg2 from 5 : (i6) -> i1 - // CHECK-NEXT: %[[V3:.+]] = hw.constant false - // CHECK-NEXT: %[[V4:.+]] = comb.icmp eq %[[V2]], %[[V3]] : i1 - // CHECK-NEXT: %[[V5:.+]] = comb.extract %arg2 from 0 : (i6) -> i5 - // CHECK-NEXT: %[[V6:.+]] = hw.constant -1 : i5 - // CHECK-NEXT: %[[V7:.+]] = comb.mux %[[V4]], %[[V5]], %[[V6]] : i5 - // CHECK-NEXT: comb.shl %arg3, %[[V7]] : i5 - %2 = moore.mir.shl %arg2, %arg0 : !moore.packed>, !moore.bit - %3 = moore.mir.shl arithmetic %arg3, %arg2 : !moore.packed, 4:0>>, !moore.packed> - // CHECK-NEXT: %[[V8:.+]] = hw.constant 0 : i5 - // CHECK-NEXT: %[[V9:.+]] = comb.concat %[[V8]], %arg0 : i5, i1 - // CHECK-NEXT: comb.shru %arg2, %[[V9]] : i6 - // CHECK-NEXT: comb.shru %arg2, %arg2 : i6 - // CHECK-NEXT: %[[V10:.+]] = comb.extract %arg2 from 5 : (i6) -> i1 - // CHECK-NEXT: %[[V11:.+]] = hw.constant false - // CHECK-NEXT: %[[V12:.+]] = comb.icmp eq %[[V10]], %[[V11]] : i1 - // CHECK-NEXT: %[[V13:.+]] = comb.extract %arg2 from 0 : (i6) -> i5 - // CHECK-NEXT: %[[V14:.+]] = hw.constant -1 : i5 - // CHECK-NEXT: %[[V15:.+]] = comb.mux %[[V12]], %[[V13]], %[[V14]] : i5 - // CHECK-NEXT: comb.shrs %arg3, %[[V15]] : i5 - %4 = moore.mir.shr %arg2, %arg0 : !moore.packed>, !moore.bit - %5 = moore.mir.shr arithmetic %arg2, %arg2 : !moore.packed>, !moore.packed> - %6 = moore.mir.shr arithmetic %arg3, %arg2 : !moore.packed, 4:0>>, !moore.packed> + moore.concat %arg0, %arg0 : (!moore.bit, !moore.bit) -> !moore.packed> + moore.concat %arg1, %arg1 : (!moore.logic, !moore.logic) -> !moore.packed> + + // CHECK-NEXT: [[V0:%.+]] = hw.constant 0 : i5 + // CHECK-NEXT: [[V1:%.+]] = comb.concat [[V0]], %arg0 : i5, i1 + // CHECK-NEXT: comb.shl %arg2, [[V1]] : i6 + moore.shl %arg2, %arg0 : !moore.packed>, !moore.bit + + // CHECK-NEXT: [[V2:%.+]] = comb.extract %arg2 from 5 : (i6) -> i1 + // CHECK-NEXT: [[V3:%.+]] = hw.constant false + // CHECK-NEXT: [[V4:%.+]] = comb.icmp eq [[V2]], [[V3]] : i1 + // CHECK-NEXT: [[V5:%.+]] = comb.extract %arg2 from 0 : (i6) -> i5 + // CHECK-NEXT: [[V6:%.+]] = hw.constant -1 : i5 + // CHECK-NEXT: [[V7:%.+]] = comb.mux [[V4]], [[V5]], [[V6]] : i5 + // CHECK-NEXT: comb.shl %arg3, [[V7]] : i5 + moore.shl %arg3, %arg2 : !moore.packed, 4:0>>, !moore.packed> + + // CHECK-NEXT: [[V8:%.+]] = hw.constant 0 : i5 + // CHECK-NEXT: [[V9:%.+]] = comb.concat [[V8]], %arg0 : i5, i1 + // CHECK-NEXT: comb.shru %arg2, [[V9]] : i6 + moore.shr %arg2, %arg0 : !moore.packed>, !moore.bit + + // CHECK-NEXT: comb.shrs %arg2, %arg2 : i6 + moore.ashr %arg2, %arg2 : !moore.packed>, !moore.packed> + + // CHECK-NEXT: [[V10:%.+]] = comb.extract %arg2 from 5 : (i6) -> i1 + // CHECK-NEXT: [[V11:%.+]] = hw.constant false + // CHECK-NEXT: [[V12:%.+]] = comb.icmp eq [[V10]], [[V11]] : i1 + // CHECK-NEXT: [[V13:%.+]] = comb.extract %arg2 from 0 : (i6) -> i5 + // CHECK-NEXT: [[V14:%.+]] = hw.constant -1 : i5 + // CHECK-NEXT: [[V15:%.+]] = comb.mux [[V12]], [[V13]], [[V14]] : i5 + // CHECK-NEXT: comb.shrs %arg3, [[V15]] : i5 + moore.ashr %arg3, %arg2 : !moore.packed, 4:0>>, !moore.packed> + // CHECK-NEXT: return return } diff --git a/test/Dialect/Moore/basic.mlir b/test/Dialect/Moore/basic.mlir index d18e9eb4afd1..235078515914 100644 --- a/test/Dialect/Moore/basic.mlir +++ b/test/Dialect/Moore/basic.mlir @@ -38,28 +38,102 @@ moore.module @Foo { moore.module @Bar { } -// CHECK-LABEL: llhd.entity @test1 -llhd.entity @test1() -> () { - // CHECK-NEXT: [[CONST:%.*]] = moore.mir.constant 5 : !moore.int - %0 = moore.mir.constant 5 : !moore.int -} +// CHECK-LABEL: moore.module @Expressions +moore.module @Expressions { + %b1 = moore.variable : !moore.bit + %l1 = moore.variable : !moore.logic + %b5 = moore.variable : !moore.packed> + %int = moore.variable : !moore.int + %int2 = moore.variable : !moore.int + %integer = moore.variable : !moore.integer + %integer2 = moore.variable : !moore.integer + + // CHECK: moore.constant 0 : !moore.int + moore.constant 0 : !moore.int + // CHECK: moore.constant -2 : !moore.packed> + moore.constant 2 : !moore.packed> + // CHECK: moore.constant -2 : !moore.packed, 1:0>> + moore.constant -2 : !moore.packed, 1:0>> + + // CHECK: moore.conversion %b5 : !moore.packed> -> !moore.packed> + moore.conversion %b5 : !moore.packed> -> !moore.packed> + + // CHECK: moore.neg %int : !moore.int + moore.neg %int : !moore.int + // CHECK: moore.not %int : !moore.int + moore.not %int : !moore.int + + // CHECK: moore.reduce_and %int : !moore.int -> !moore.bit + moore.reduce_and %int : !moore.int -> !moore.bit + // CHECK: moore.reduce_or %int : !moore.int -> !moore.bit + moore.reduce_or %int : !moore.int -> !moore.bit + // CHECK: moore.reduce_xor %int : !moore.int -> !moore.bit + moore.reduce_xor %int : !moore.int -> !moore.bit + // CHECK: moore.reduce_xor %integer : !moore.integer -> !moore.logic + moore.reduce_xor %integer : !moore.integer -> !moore.logic + + // CHECK: moore.bool_cast %int : !moore.int -> !moore.bit + moore.bool_cast %int : !moore.int -> !moore.bit + // CHECK: moore.bool_cast %integer : !moore.integer -> !moore.logic + moore.bool_cast %integer : !moore.integer -> !moore.logic + + // CHECK: moore.add %int, %int2 : !moore.int + moore.add %int, %int2 : !moore.int + // CHECK: moore.sub %int, %int2 : !moore.int + moore.sub %int, %int2 : !moore.int + // CHECK: moore.mul %int, %int2 : !moore.int + moore.mul %int, %int2 : !moore.int + // CHECK: moore.div %int, %int2 : !moore.int + moore.div %int, %int2 : !moore.int + // CHECK: moore.mod %int, %int2 : !moore.int + moore.mod %int, %int2 : !moore.int + + // CHECK: moore.and %int, %int2 : !moore.int + moore.and %int, %int2 : !moore.int + // CHECK: moore.or %int, %int2 : !moore.int + moore.or %int, %int2 : !moore.int + // CHECK: moore.xor %int, %int2 : !moore.int + moore.xor %int, %int2 : !moore.int -// CHECK-LABEL: func @Expressions -func.func @Expressions(%a: !moore.bit, %b: !moore.logic, %c: !moore.packed>) { - // CHECK: %0 = moore.mir.concat - // CHECK: %1 = moore.mir.concat - %0 = moore.mir.concat %a, %a : (!moore.bit, !moore.bit) -> !moore.packed> - %1 = moore.mir.concat %b, %b : (!moore.logic, !moore.logic) -> !moore.packed> + // CHECK: moore.shl %l1, %b1 : !moore.logic, !moore.bit + moore.shl %l1, %b1 : !moore.logic, !moore.bit + // CHECK: moore.shr %l1, %b1 : !moore.logic, !moore.bit + moore.shr %l1, %b1 : !moore.logic, !moore.bit + // CHECK: moore.ashr %b5, %b1 : !moore.packed>, !moore.bit + moore.ashr %b5, %b1 : !moore.packed>, !moore.bit - // CHECK: %2 = moore.mir.shl % - // CHECK: %3 = moore.mir.shl arithmetic % - %2 = moore.mir.shl %b, %a : !moore.logic, !moore.bit - %3 = moore.mir.shl arithmetic %c, %a : !moore.packed>, !moore.bit + // CHECK: moore.eq %int, %int2 : !moore.int -> !moore.bit + moore.eq %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.ne %int, %int2 : !moore.int -> !moore.bit + moore.ne %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.ne %integer, %integer2 : !moore.integer -> !moore.logic + moore.ne %integer, %integer2 : !moore.integer -> !moore.logic + // CHECK: moore.case_eq %int, %int2 : !moore.int + moore.case_eq %int, %int2 : !moore.int + // CHECK: moore.case_ne %int, %int2 : !moore.int + moore.case_ne %int, %int2 : !moore.int + // CHECK: moore.wildcard_eq %int, %int2 : !moore.int -> !moore.bit + moore.wildcard_eq %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.wildcard_ne %int, %int2 : !moore.int -> !moore.bit + moore.wildcard_ne %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.wildcard_ne %integer, %integer2 : !moore.integer -> !moore.logic + moore.wildcard_ne %integer, %integer2 : !moore.integer -> !moore.logic - // CHECK: %4 = moore.mir.shr % - // CHECK: %5 = moore.mir.shr arithmetic % - %4 = moore.mir.shr %b, %a : !moore.logic, !moore.bit - %5 = moore.mir.shr arithmetic %c, %a : !moore.packed>, !moore.bit + // CHECK: moore.lt %int, %int2 : !moore.int -> !moore.bit + moore.lt %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.le %int, %int2 : !moore.int -> !moore.bit + moore.le %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.gt %int, %int2 : !moore.int -> !moore.bit + moore.gt %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.ge %int, %int2 : !moore.int -> !moore.bit + moore.ge %int, %int2 : !moore.int -> !moore.bit + // CHECK: moore.ge %integer, %integer2 : !moore.integer -> !moore.logic + moore.ge %integer, %integer2 : !moore.integer -> !moore.logic - return + // CHECK: moore.concat %b1 : (!moore.bit) -> !moore.packed> + moore.concat %b1 : (!moore.bit) -> !moore.packed> + // CHECK: moore.concat %b5, %b1 : (!moore.packed>, !moore.bit) -> !moore.packed> + moore.concat %b5, %b1 : (!moore.packed>, !moore.bit) -> !moore.packed> + // CHECK: moore.concat %l1, %l1, %l1 : (!moore.logic, !moore.logic, !moore.logic) -> !moore.packed> + moore.concat %l1, %l1, %l1 : (!moore.logic, !moore.logic, !moore.logic) -> !moore.packed> } diff --git a/test/Dialect/Moore/canonicalizers.mlir b/test/Dialect/Moore/canonicalizers.mlir new file mode 100644 index 000000000000..0179d20b675e --- /dev/null +++ b/test/Dialect/Moore/canonicalizers.mlir @@ -0,0 +1,11 @@ +// RUN: circt-opt --canonicalize %s | FileCheck %s + +// CHECK-LABEL: func.func @Casts +func.func @Casts(%arg0: !moore.bit) -> (!moore.bit, !moore.bit) { + // CHECK-NOT: moore.conversion + // CHECK-NOT: moore.bool_cast + %0 = moore.conversion %arg0 : !moore.bit -> !moore.bit + %1 = moore.bool_cast %arg0 : !moore.bit -> !moore.bit + // CHECK: return %arg0, %arg0 + return %0, %1 : !moore.bit, !moore.bit +} diff --git a/test/Dialect/Moore/errors.mlir b/test/Dialect/Moore/errors.mlir index 66c7e329469f..e78bde208592 100644 --- a/test/Dialect/Moore/errors.mlir +++ b/test/Dialect/Moore/errors.mlir @@ -8,3 +8,18 @@ moore.module @Bar { // expected-error @below {{symbol 'Foo' must reference a 'moore.module', but got a 'func.func' instead}} moore.instance "foo" @Foo } + +// ----- + +// expected-error @below {{constant out of range for result type '!moore.bit'}} +moore.constant 42 : !moore.bit + +// ----- + +// expected-error @below {{constant out of range for result type '!moore.bit'}} +moore.constant -2 : !moore.bit + +// ----- + +// expected-error @below {{attribute width 9 does not match return type's width 8}} +"moore.constant" () {value = 42 : i9} : () -> !moore.byte