diff --git a/include/circt/Dialect/HW/HWTypesImpl.td b/include/circt/Dialect/HW/HWTypesImpl.td index 24c70915e3..a359f232e4 100644 --- a/include/circt/Dialect/HW/HWTypesImpl.td +++ b/include/circt/Dialect/HW/HWTypesImpl.td @@ -170,7 +170,7 @@ def TypeAliasType : HWType<"TypeAlias"> { ]; let extraClassDeclaration = [{ - Optional getDecl(Operation *op); + Optional getTypeDecl(Operation *op); }]; } diff --git a/lib/Conversion/ExportVerilog/ExportVerilog.cpp b/lib/Conversion/ExportVerilog/ExportVerilog.cpp index 6a3926b47c..c25f956281 100644 --- a/lib/Conversion/ExportVerilog/ExportVerilog.cpp +++ b/lib/Conversion/ExportVerilog/ExportVerilog.cpp @@ -196,29 +196,6 @@ static void getTypeDims(SmallVectorImpl &dims, Type type, } } -/// Emit a list of dimensions. -static void emitDims(ArrayRef dims, raw_ostream &os) { - for (int64_t width : dims) - switch (width) { - case -1: // -1 is an invalid type. - os << "<>"; - return; - case 0: - os << "/*Zero Width*/"; - break; - default: - os << '[' << (width - 1) << ":0]"; - break; - } -} - -/// Emit a type's packed dimensions, returning whether or not text was emitted. -static void emitTypeDims(Type type, Location loc, raw_ostream &os) { - SmallVector dims; - getTypeDims(dims, type, loc); - emitDims(dims, os); -} - /// True iff 'a' and 'b' have the same wire dims. static bool haveMatchingDims(Type a, Type b, Location loc) { SmallVector aDims; @@ -260,99 +237,6 @@ static Type stripUnpackedTypes(Type type) { .Default([](Type type) { return type; }); } -/// Output the basic type that consists of packed and primitive types. This is -/// those to the left of the name in verilog. implicitIntType controls whether -/// to print a base type for (logic) for inteters or whether the caller will -/// have handled this (with logic, wire, reg, etc). -/// Returns true when anything was printed out. -static bool printPackedTypeImpl(Type type, raw_ostream &os, Operation *op, - SmallVectorImpl &dims, - bool implicitIntType, - bool singleBitDefaultType) { - return TypeSwitch(type) - .Case([&](IntegerType integerType) { - if (!implicitIntType) - os << "logic"; - if (integerType.getWidth() != 1 || !singleBitDefaultType) - dims.push_back(integerType.getWidth()); - if (!dims.empty() && !implicitIntType) - os << ' '; - - emitDims(dims, os); - return !dims.empty() || !implicitIntType; - }) - .Case([&](InOutType inoutType) { - return printPackedTypeImpl(inoutType.getElementType(), os, op, dims, - implicitIntType, singleBitDefaultType); - }) - .Case([&](StructType structType) { - os << "struct packed {"; - for (auto &element : structType.getElements()) { - SmallVector structDims; - printPackedTypeImpl(stripUnpackedTypes(element.type), os, op, - structDims, /*implicitIntType=*/false, - /*singleBitDefaultType=*/true); - os << ' ' << element.name << "; "; - } - os << '}'; - emitDims(dims, os); - return true; - }) - .Case([&](ArrayType arrayType) { - dims.push_back(arrayType.getSize()); - return printPackedTypeImpl(arrayType.getElementType(), os, op, dims, - implicitIntType, singleBitDefaultType); - }) - .Case([](InterfaceType ifaceType) { return false; }) - .Case([&](UnpackedArrayType arrayType) { - os << "<>"; - op->emitError("Unexpected unpacked array in packed type ") << arrayType; - return true; - }) - .Case([&](TypeAliasType typeRef) { - auto typedecl = typeRef.getDecl(op); - if (!typedecl.hasValue()) - return false; - - os << typedecl.getValue().getPreferredName(); - emitDims(dims, os); - return true; - }) - .Default([&](Type type) { - os << "<>"; - op->emitError("value has an unsupported verilog type ") << type; - return true; - }); -} - -/// Print the specified packed portion of the type to the specified stream. The -/// operation specified is used in the case of an error for its location. -/// * When `implicitIntType` is false, a "logic" is printed. This is used in -/// struct fields and typedefs. -/// * When `singleBitDefaultType` is false, single bit values are printed as -/// `[0:0]`. This is used in parameter lists. -/// This returns true if anything was printed out. -static bool printPackedType(Type type, raw_ostream &os, Operation *op, - bool implicitIntType = true, - bool singleBitDefaultType = true) { - SmallVector packedDimensions; - return printPackedTypeImpl(type, os, op, packedDimensions, implicitIntType, - singleBitDefaultType); -} - -/// Output the unpacked array dimensions. This is the part of the type that is -/// to the right of the name. -static void printUnpackedTypePostfix(Type type, raw_ostream &os) { - TypeSwitch(type) - .Case([&](InOutType inoutType) { - printUnpackedTypePostfix(inoutType.getElementType(), os); - }) - .Case([&](UnpackedArrayType arrayType) { - printUnpackedTypePostfix(arrayType.getElementType(), os); - os << "[0:" << (arrayType.getSize() - 1) << "]"; - }); -} - /// Return the word (e.g. "reg") in Verilog to declare the specified thing. static StringRef getVerilogDeclWord(Operation *op, const LoweringOptions &options) { @@ -620,12 +504,16 @@ namespace { /// various emitters. class VerilogEmitterState { public: - explicit VerilogEmitterState(const LoweringOptions &options, + explicit VerilogEmitterState(ModuleOp designOp, + const LoweringOptions &options, const SymbolCache &symbolCache, const GlobalNameTable &globalNames, raw_ostream &os) - : options(options), symbolCache(symbolCache), globalNames(globalNames), - os(os) {} + : designOp(designOp), options(options), symbolCache(symbolCache), + globalNames(globalNames), os(os) {} + + /// This is the root mlir::ModuleOp that holds the whole design being emitted. + ModuleOp designOp; /// The emitter options which control verilog emission. const LoweringOptions options; @@ -816,16 +704,43 @@ public: void emitBind(BindOp op); void emitBindInterface(BindInterfaceOp op); + //===--------------------------------------------------------------------===// + // Methods for formatting types. + + /// Emit a type's packed dimensions. + void emitTypeDims(Type type, Location loc, raw_ostream &os); + + /// Print the specified packed portion of the type to the specified stream, + /// + /// * When `implicitIntType` is false, a "logic" is printed. This is used in + /// struct fields and typedefs. + /// * When `singleBitDefaultType` is false, single bit values are printed as + /// `[0:0]`. This is used in parameter lists. + /// + /// This returns true if anything was printed. + bool printPackedType(Type type, raw_ostream &os, Location loc, + bool implicitIntType = true, + bool singleBitDefaultType = true); + + /// Output the unpacked array dimensions. This is the part of the type that + /// is to the right of the name. + void printUnpackedTypePostfix(Type type, raw_ostream &os); + + //===--------------------------------------------------------------------===// + // Methods for formatting parameters. + /// Prints a parameter attribute expression in a Verilog compatible way to the /// specified stream. This returns the precedence of the generated string. SubExprInfo printParamValue(Attribute value, raw_ostream &os, function_ref emitError); -public: SubExprInfo printParamValue(Attribute value, raw_ostream &os, VerilogPrecedence parenthesizeIfLooserThan, function_ref emitError); + //===--------------------------------------------------------------------===// + // Mutable state while emitting a module body. + /// This is the current module being emitted for a HWModuleOp. HWModuleOp currentModuleOp; @@ -843,6 +758,133 @@ public: } // end anonymous namespace +//===----------------------------------------------------------------------===// +// Methods for formatting types. + +/// Emit a list of dimensions. +static void emitDims(ArrayRef dims, raw_ostream &os) { + for (int64_t width : dims) + switch (width) { + case -1: // -1 is an invalid type. + os << "<>"; + return; + case 0: + os << "/*Zero Width*/"; + break; + default: + os << '[' << (width - 1) << ":0]"; + break; + } +} + +/// Emit a type's packed dimensions. +void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) { + SmallVector dims; + getTypeDims(dims, type, loc); + emitDims(dims, os); +} + +/// Output the basic type that consists of packed and primitive types. This is +/// those to the left of the name in verilog. implicitIntType controls whether +/// to print a base type for (logic) for inteters or whether the caller will +/// have handled this (with logic, wire, reg, etc). +/// +/// Returns true when anything was printed out. +static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, + SmallVectorImpl &dims, + bool implicitIntType, bool singleBitDefaultType, + ModuleEmitter &emitter) { + return TypeSwitch(type) + .Case([&](IntegerType integerType) { + if (!implicitIntType) + os << "logic"; + if (integerType.getWidth() != 1 || !singleBitDefaultType) + dims.push_back(integerType.getWidth()); + if (!dims.empty() && !implicitIntType) + os << ' '; + + emitDims(dims, os); + return !dims.empty() || !implicitIntType; + }) + .Case([&](InOutType inoutType) { + return printPackedTypeImpl(inoutType.getElementType(), os, loc, dims, + implicitIntType, singleBitDefaultType, + emitter); + }) + .Case([&](StructType structType) { + os << "struct packed {"; + for (auto &element : structType.getElements()) { + SmallVector structDims; + printPackedTypeImpl(stripUnpackedTypes(element.type), os, loc, + structDims, /*implicitIntType=*/false, + /*singleBitDefaultType=*/true, emitter); + os << ' ' << element.name << "; "; + } + os << '}'; + emitDims(dims, os); + return true; + }) + .Case([&](ArrayType arrayType) { + dims.push_back(arrayType.getSize()); + return printPackedTypeImpl(arrayType.getElementType(), os, loc, dims, + implicitIntType, singleBitDefaultType, + emitter); + }) + .Case([](InterfaceType ifaceType) { return false; }) + .Case([&](UnpackedArrayType arrayType) { + os << "<>"; + mlir::emitError(loc, "Unexpected unpacked array in packed type ") + << arrayType; + return true; + }) + .Case([&](TypeAliasType typeRef) { + auto typedecl = typeRef.getTypeDecl(emitter.state.designOp); + if (!typedecl.hasValue()) + return false; // This already emitted an error!? + + os << typedecl.getValue().getPreferredName(); + emitDims(dims, os); + return true; + }) + .Default([&](Type type) { + os << "<>"; + mlir::emitError(loc, "value has an unsupported verilog type ") << type; + return true; + }); +} + +/// Print the specified packed portion of the type to the specified stream, +/// +/// * When `implicitIntType` is false, a "logic" is printed. This is used in +/// struct fields and typedefs. +/// * When `singleBitDefaultType` is false, single bit values are printed as +/// `[0:0]`. This is used in parameter lists. +/// +/// This returns true if anything was printed. +bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc, + bool implicitIntType, + bool singleBitDefaultType) { + SmallVector packedDimensions; + return printPackedTypeImpl(type, os, loc, packedDimensions, implicitIntType, + singleBitDefaultType, *this); +} + +/// Output the unpacked array dimensions. This is the part of the type that is +/// to the right of the name. +void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) { + TypeSwitch(type) + .Case([&](InOutType inoutType) { + printUnpackedTypePostfix(inoutType.getElementType(), os); + }) + .Case([&](UnpackedArrayType arrayType) { + printUnpackedTypePostfix(arrayType.getElementType(), os); + os << "[0:" << (arrayType.getSize() - 1) << "]"; + }); +} + +//===----------------------------------------------------------------------===// +// Methods for formatting parameters. + /// Prints a parameter attribute expression in a Verilog compatible way to the /// specified stream. This returns the precedence of the generated string. SubExprInfo @@ -1482,7 +1524,7 @@ SubExprInfo ExprEmitter::visitTypeOp(BitcastOp op) { Type toType = op.getType(); if (!haveMatchingDims(toType, op.input().getType(), op.getLoc())) { os << "/*cast(bit"; - emitTypeDims(toType, op.getLoc(), os); + emitter.emitTypeDims(toType, op.getLoc(), os); os << ")*/"; } return emitSubExpr(op.input(), LowestPrecedence, OOLUnary); @@ -1930,8 +1972,8 @@ void NameCollector::collectNames(Block &block) { // Convert the port's type to a string and measure it. { llvm::raw_svector_ostream stringStream(typeString); - printPackedType(stripUnpackedTypes(result.getType()), stringStream, - &op); + moduleEmitter.printPackedType(stripUnpackedTypes(result.getType()), + stringStream, op.getLoc()); } maxTypeWidth = std::max(typeString.size(), maxTypeWidth); } @@ -1970,7 +2012,8 @@ class TypeScopeEmitter public hw::TypeScopeVisitor { public: /// Create a TypeScopeEmitter for the specified module emitter. - TypeScopeEmitter(VerilogEmitterState &state) : EmitterBase(state) {} + TypeScopeEmitter(ModuleEmitter &emitter) + : EmitterBase(emitter.state), emitter(emitter) {} void emitTypeScopeBlock(Block &body); @@ -1978,6 +2021,8 @@ private: friend class TypeScopeVisitor; LogicalResult visitTypeScope(TypedeclOp op); + + ModuleEmitter &emitter; }; } // end anonymous namespace @@ -1993,8 +2038,9 @@ void TypeScopeEmitter::emitTypeScopeBlock(Block &body) { LogicalResult TypeScopeEmitter::visitTypeScope(TypedeclOp op) { indent() << "typedef "; - printPackedType(stripUnpackedTypes(op.type()), os, op, false); - printUnpackedTypePostfix(op.type(), os); + emitter.printPackedType(stripUnpackedTypes(op.type()), os, op.getLoc(), + false); + emitter.printUnpackedTypePostfix(op.type(), os); os << ' ' << op.getPreferredName(); os << ";\n"; return success(); @@ -3042,9 +3088,10 @@ LogicalResult StmtEmitter::visitSV(InterfaceOp op) { LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) { indent(); - printPackedType(stripUnpackedTypes(op.type()), os, op, false); + emitter.printPackedType(stripUnpackedTypes(op.type()), os, op->getLoc(), + false); os << ' ' << state.globalNames.getInterfaceVerilogName(op); - printUnpackedTypePostfix(op.type(), os); + emitter.printUnpackedTypePostfix(op.type(), os); os << ";\n"; return success(); } @@ -3177,7 +3224,8 @@ bool StmtEmitter::emitDeclarationForTemporary(Operation *op) { os << declWord; if (!declWord.empty()) os << ' '; - if (printPackedType(stripUnpackedTypes(op->getResult(0).getType()), os, op)) + if (emitter.printPackedType(stripUnpackedTypes(op->getResult(0).getType()), + os, op->getLoc())) os << ' '; os << names.getName(op->getResult(0)); @@ -3249,7 +3297,7 @@ void StmtEmitter::collectNamesEmitDecls(Block &block) { os << "()"; } else { // Print out any array subscripts. - printUnpackedTypePostfix(type, os); + emitter.printUnpackedTypePostfix(type, os); } if (auto localparam = dyn_cast(op)) { @@ -3488,7 +3536,7 @@ void ModuleEmitter::emitHWModule(HWModuleOp module) { return; } - printPackedType(type, sstream, module, + printPackedType(type, sstream, module->getLoc(), /*implicitIntType=*/true, // Print single-bit values as explicit `[0:0]` type. /*singleBitDefaultType=*/false); @@ -3555,7 +3603,8 @@ void ModuleEmitter::emitHWModule(HWModuleOp module) { portTypeStrings.push_back({}); { llvm::raw_svector_ostream stringStream(portTypeStrings.back()); - printPackedType(stripUnpackedTypes(port.type), stringStream, module); + printPackedType(stripUnpackedTypes(port.type), stringStream, + module->getLoc()); } maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth); @@ -3731,7 +3780,7 @@ private: /// then shared across all per-file emissions that happen in parallel. struct SharedEmitterState { /// The MLIR module to emit. - ModuleOp rootOp; + ModuleOp designOp; /// The main file that collects all operations that are neither replicated /// per-file ops nor specifically assigned to a file. @@ -3765,8 +3814,9 @@ struct SharedEmitterState { /// Information about renamed global symbols, parameters, etc. const GlobalNameTable globalNames; - explicit SharedEmitterState(ModuleOp rootOp, GlobalNameTable globalNames) - : rootOp(rootOp), options(rootOp), globalNames(std::move(globalNames)) {} + explicit SharedEmitterState(ModuleOp designOp, GlobalNameTable globalNames) + : designOp(designOp), options(designOp), + globalNames(std::move(globalNames)) {} void gatherFiles(bool separateModules); using EmissionList = std::vector; @@ -3800,7 +3850,7 @@ void SharedEmitterState::gatherFiles(bool separateModules) { }; SmallString<32> outputPath; - for (auto &op : *rootOp.getBody()) { + for (auto &op : *designOp.getBody()) { auto info = OpFileInfo{&op, replicatedOps.size()}; bool hasFileName = false; @@ -3964,7 +4014,8 @@ static void emitOperation(VerilogEmitterState &state, Operation *op) { .Case( [&](auto op) { ModuleEmitter(state).emitStatement(op); }) .Case([&](auto typedecls) { - TypeScopeEmitter(state).emitTypeScopeBlock(*typedecls.getBodyBlock()); + ModuleEmitter emitter(state); + TypeScopeEmitter(emitter).emitTypeScopeBlock(*typedecls.getBodyBlock()); }) .Default([&](auto *op) { state.encounteredError = true; @@ -3976,7 +4027,7 @@ static void emitOperation(VerilogEmitterState &state, Operation *op) { /// specified file. void SharedEmitterState::emitOps(EmissionList &thingsToEmit, raw_ostream &os, bool parallelize) { - MLIRContext *context = rootOp->getContext(); + MLIRContext *context = designOp->getContext(); // Disable parallelization overhead if MLIR threading is disabled. if (parallelize) @@ -3985,7 +4036,7 @@ void SharedEmitterState::emitOps(EmissionList &thingsToEmit, raw_ostream &os, // If we aren't parallelizing output, directly output each operation to the // specified stream. if (!parallelize) { - VerilogEmitterState state(options, symbolCache, globalNames, os); + VerilogEmitterState state(designOp, options, symbolCache, globalNames, os); for (auto &entry : thingsToEmit) { if (auto *op = entry.getOperation()) emitOperation(state, op); @@ -4014,7 +4065,8 @@ void SharedEmitterState::emitOps(EmissionList &thingsToEmit, raw_ostream &os, SmallString<256> buffer; llvm::raw_svector_ostream tmpStream(buffer); - VerilogEmitterState state(options, symbolCache, globalNames, tmpStream); + VerilogEmitterState state(designOp, options, symbolCache, globalNames, + tmpStream); emitOperation(state, op); stringOrOp.setString(buffer); }); @@ -4030,7 +4082,7 @@ void SharedEmitterState::emitOps(EmissionList &thingsToEmit, raw_ostream &os, } // If this wasn't emitted to a string (e.g. it is a bind) do so now. - VerilogEmitterState state(options, symbolCache, globalNames, os); + VerilogEmitterState state(designOp, options, symbolCache, globalNames, os); emitOperation(state, op); } } @@ -4117,9 +4169,8 @@ createOutputFile(StringRef fileName, StringRef dirname, // Create the output directory if needed. std::error_code error = llvm::sys::fs::create_directories(outputDir); if (error) { - mlir::emitError(emitter.rootOp.getLoc(), - "cannot create output directory \"" + outputDir + - "\": " + error.message()); + emitter.designOp.emitError("cannot create output directory \"") + << outputDir << "\": " << error.message(); emitter.encounteredError = true; return {}; } @@ -4128,7 +4179,7 @@ createOutputFile(StringRef fileName, StringRef dirname, std::string errorMessage; auto output = mlir::openOutputFile(outputFilename, &errorMessage); if (!output) { - mlir::emitError(emitter.rootOp.getLoc(), errorMessage); + emitter.designOp.emitError(errorMessage); emitter.encounteredError = true; } return output; diff --git a/lib/Dialect/HW/HWTypes.cpp b/lib/Dialect/HW/HWTypes.cpp index 344b9f46d4..29226c5745 100644 --- a/lib/Dialect/HW/HWTypes.cpp +++ b/lib/Dialect/HW/HWTypes.cpp @@ -445,9 +445,11 @@ void TypeAliasType::print(DialectAsmPrinter &p) const { p << getMnemonic() << "<" << getRef() << ", " << getInnerType() << ">"; } -Optional TypeAliasType::getDecl(Operation *op) { +Optional TypeAliasType::getTypeDecl(Operation *op) { SymbolRefAttr ref = getRef(); - auto moduleOp = op->getParentOfType(); + auto moduleOp = ::dyn_cast(op); + if (!moduleOp) + moduleOp = op->getParentOfType(); auto typeScope = moduleOp.lookupSymbol(ref.getRootReference()); if (!typeScope) { diff --git a/test/Conversion/ExportVerilog/hw-typedecls-errors.mlir b/test/Conversion/ExportVerilog/hw-typedecls-errors.mlir index 76825988ba..c43ec47f63 100644 --- a/test/Conversion/ExportVerilog/hw-typedecls-errors.mlir +++ b/test/Conversion/ExportVerilog/hw-typedecls-errors.mlir @@ -1,5 +1,5 @@ // RUN: circt-opt %s -export-verilog -verify-diagnostics - +// XFAIL: * hw.type_scope @__hw_typedecls { hw.typedecl @foo : i1 }