From c9709741652d32477047359729d0ea1c7a3275ba Mon Sep 17 00:00:00 2001 From: Fabian Schuiki Date: Wed, 13 Mar 2024 16:57:53 -0700 Subject: [PATCH] [ImportVerilog] Add basic expressions (#6788) Extend the `ImportVerilog` conversion to support most of the basic expressions that commonly appear in SystemVerilog input files. Also add the correpsonding expression ops to the Moore dialect, and finally get rid of the old MIR expressions file which is now obsolete. Thanks @hailongSun2000 and @albertethon for doing a lot of the leg work to get expression support in! Co-authored-by: Hailong Sun Co-authored-by: ShiZuoye Co-authored-by: Martin Erhart --- include/circt/Dialect/Moore/MIRExpressions.td | 92 ---- include/circt/Dialect/Moore/MooreOps.td | 449 +++++++++++++++++- include/circt/Dialect/Moore/MooreTypes.h | 11 + include/circt/Dialect/Moore/MooreTypes.td | 17 + lib/Conversion/ImportVerilog/Expressions.cpp | 289 +++++++++++ lib/Conversion/ImportVerilog/Structure.cpp | 3 + lib/Conversion/MooreToCore/MooreToCore.cpp | 33 +- lib/Dialect/Moore/MooreOps.cpp | 110 +++++ lib/Dialect/Moore/MooreTypes.cpp | 16 +- test/Conversion/ImportVerilog/basic.sv | 193 ++++++++ test/Conversion/ImportVerilog/errors.sv | 10 + test/Conversion/MooreToCore/basic.mlir | 68 +-- test/Dialect/Moore/basic.mlir | 114 ++++- test/Dialect/Moore/canonicalizers.mlir | 11 + test/Dialect/Moore/errors.mlir | 15 + 15 files changed, 1261 insertions(+), 170 deletions(-) delete mode 100644 include/circt/Dialect/Moore/MIRExpressions.td create mode 100644 test/Dialect/Moore/canonicalizers.mlir 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