From 84a7c8b1b0d50a3d178d0aca1ae9ead928e7ce51 Mon Sep 17 00:00:00 2001 From: Nandor Licker Date: Sat, 24 Feb 2024 10:09:00 +0200 Subject: [PATCH] [Emit] Organize output files using the `emit` dialect (#6727) --- docs/Dialects/Emit/RationaleEmit.md | 22 +++++ docs/Dialects/Emit/_index.md | 3 + include/circt/Dialect/CMakeLists.txt | 1 + include/circt/Dialect/Emit/CMakeLists.txt | 14 +++ include/circt/Dialect/Emit/Emit.td | 24 +++++ include/circt/Dialect/Emit/EmitDialect.h | 22 +++++ include/circt/Dialect/Emit/EmitDialect.td | 28 ++++++ include/circt/Dialect/Emit/EmitOps.h | 28 ++++++ include/circt/Dialect/Emit/EmitOps.td | 112 ++++++++++++++++++++++ include/circt/InitAllDialects.h | 2 + lib/Dialect/CMakeLists.txt | 1 + lib/Dialect/Emit/CMakeLists.txt | 30 ++++++ lib/Dialect/Emit/EmitDialect.cpp | 34 +++++++ lib/Dialect/Emit/EmitOps.cpp | 74 ++++++++++++++ test/Dialect/Emit/emit-errors.mlir | 11 +++ test/Dialect/Emit/round-trip.mlir | 13 +++ tools/circt-opt/CMakeLists.txt | 1 + 17 files changed, 420 insertions(+) create mode 100644 docs/Dialects/Emit/RationaleEmit.md create mode 100644 docs/Dialects/Emit/_index.md create mode 100644 include/circt/Dialect/Emit/CMakeLists.txt create mode 100644 include/circt/Dialect/Emit/Emit.td create mode 100644 include/circt/Dialect/Emit/EmitDialect.h create mode 100644 include/circt/Dialect/Emit/EmitDialect.td create mode 100644 include/circt/Dialect/Emit/EmitOps.h create mode 100644 include/circt/Dialect/Emit/EmitOps.td create mode 100644 lib/Dialect/Emit/CMakeLists.txt create mode 100644 lib/Dialect/Emit/EmitDialect.cpp create mode 100644 lib/Dialect/Emit/EmitOps.cpp create mode 100644 test/Dialect/Emit/emit-errors.mlir create mode 100644 test/Dialect/Emit/round-trip.mlir diff --git a/docs/Dialects/Emit/RationaleEmit.md b/docs/Dialects/Emit/RationaleEmit.md new file mode 100644 index 000000000000..9af512cf4d63 --- /dev/null +++ b/docs/Dialects/Emit/RationaleEmit.md @@ -0,0 +1,22 @@ +# Emission (Emit) Dialect Rationale + +This document describes various design points of the `emit` dialect, why it is +the way it is, and current status. This follows in the spirit of other [MLIR +Rationale docs](https://mlir.llvm.org/docs/Rationale/). + +## Introduction + +The `emit` dialects controls the structure and formatting of the files emitted +from CIRCT. It captures information about both SystemVerilog output files and +generic collateral files. The ops are translated to output files in +`ExportVerilog`. Presently, the dialect is intertwined with SystemVerilog - +it can reference items in a design through symbols to emit references to +them through SystemVerilog names in the output. + +## Operations + +The dialect is centred around the `emit.file` operation which groups a list +of statements in its body, responsible for producing the contents of the file. +The `emit.file_list` operation pins down a list of references to emitted files +and outputs a file list file enumerating the paths to them. +Together, these operations represent the SV and collateral output of CIRCT. diff --git a/docs/Dialects/Emit/_index.md b/docs/Dialects/Emit/_index.md new file mode 100644 index 000000000000..196ef16afd19 --- /dev/null +++ b/docs/Dialects/Emit/_index.md @@ -0,0 +1,3 @@ +# 'emit' Dialect + +[include "Dialects/Emit.md"] diff --git a/include/circt/Dialect/CMakeLists.txt b/include/circt/Dialect/CMakeLists.txt index c6cef8ef3b29..4f212153993a 100644 --- a/include/circt/Dialect/CMakeLists.txt +++ b/include/circt/Dialect/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(Calyx) add_subdirectory(Comb) add_subdirectory(DC) add_subdirectory(Debug) +add_subdirectory(Emit) add_subdirectory(ESI) add_subdirectory(FIRRTL) add_subdirectory(FSM) diff --git a/include/circt/Dialect/Emit/CMakeLists.txt b/include/circt/Dialect/Emit/CMakeLists.txt new file mode 100644 index 000000000000..874084453376 --- /dev/null +++ b/include/circt/Dialect/Emit/CMakeLists.txt @@ -0,0 +1,14 @@ +##===- CMakeLists.txt - Emit dialect build definitions --------*- cmake -*-===// +## +## 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 +## +##===----------------------------------------------------------------------===// +## +## +##===----------------------------------------------------------------------===// + +add_circt_dialect(Emit emit) +add_circt_dialect_doc(Emit emit) +add_dependencies(circt-headers MLIREmitIncGen) diff --git a/include/circt/Dialect/Emit/Emit.td b/include/circt/Dialect/Emit/Emit.td new file mode 100644 index 000000000000..031eaedc2fbb --- /dev/null +++ b/include/circt/Dialect/Emit/Emit.td @@ -0,0 +1,24 @@ +//===- Emit.td - Emit dialect definition -------------------*- 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 is the top level file for the `emit` dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_EMIT_EMIT_TD +#define CIRCT_DIALECT_EMIT_EMIT_TD + +include "mlir/IR/AttrTypeBase.td" +include "mlir/IR/OpBase.td" +include "mlir/IR/OpAsmInterface.td" +include "mlir/IR/SymbolInterfaces.td" + +include "circt/Dialect/Emit/EmitDialect.td" +include "circt/Dialect/Emit/EmitOps.td" + +#endif // CIRCT_DIALECT_EMIT_EMIT_TD diff --git a/include/circt/Dialect/Emit/EmitDialect.h b/include/circt/Dialect/Emit/EmitDialect.h new file mode 100644 index 000000000000..44796799b819 --- /dev/null +++ b/include/circt/Dialect/Emit/EmitDialect.h @@ -0,0 +1,22 @@ +//===- EmitDialect.h - Emit dialect declaration -----------------*- C++ -*-===// +// +// 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 file defines an `emit` MLIR dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_EMIT_EMITDIALECT_H +#define CIRCT_DIALECT_EMIT_EMITDIALECT_H + +#include "circt/Dialect/HW/HWAttributes.h" +#include "circt/Support/LLVM.h" +#include "mlir/IR/Dialect.h" + +#include "circt/Dialect/Emit/EmitDialect.h.inc" + +#endif // CIRCT_DIALECT_EMIT_EMITDIALECT_H diff --git a/include/circt/Dialect/Emit/EmitDialect.td b/include/circt/Dialect/Emit/EmitDialect.td new file mode 100644 index 000000000000..5a17bbbd8f3b --- /dev/null +++ b/include/circt/Dialect/Emit/EmitDialect.td @@ -0,0 +1,28 @@ +//===- EmitDialect.td - Emit dialect definition ------------*- 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 contains the EmitDialect definition to be included in other files. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_EMIT_EMITDIALECT +#define CIRCT_DIALECT_EMIT_EMITDIALECT + +def EmitDialect : Dialect { + let name = "emit"; + let cppNamespace = "::circt::emit"; + + let summary = "Types and operations for the `emit` dialect"; + let description = [{ + The `emit` dialect is intended to model the structure of the emitted RTL. + + It organizes the files, file lists, directories and collateral. + }]; +} + +#endif // CIRCT_DIALECT_EMIT_EMITDIALECT diff --git a/include/circt/Dialect/Emit/EmitOps.h b/include/circt/Dialect/Emit/EmitOps.h new file mode 100644 index 000000000000..2a0812822674 --- /dev/null +++ b/include/circt/Dialect/Emit/EmitOps.h @@ -0,0 +1,28 @@ +//===- EmitOps.h - Declare Emit dialect operations --------------*- C++ -*-===// +// +// 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 file declares the operation classes for the Emit dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_EMIT_EMITOPS_H +#define CIRCT_DIALECT_EMIT_EMITOPS_H + +#include "mlir/Bytecode/BytecodeOpInterface.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/SymbolTable.h" + +#include "circt/Dialect/Emit/EmitDialect.h" +#include "circt/Dialect/Seq/SeqDialect.h" +#include "circt/Dialect/Seq/SeqTypes.h" +#include "circt/Support/BuilderUtils.h" + +#define GET_OP_CLASSES +#include "circt/Dialect/Emit/Emit.h.inc" + +#endif // CIRCT_DIALECT_EMIT_EMITOPS_H diff --git a/include/circt/Dialect/Emit/EmitOps.td b/include/circt/Dialect/Emit/EmitOps.td new file mode 100644 index 000000000000..b7eaa3eeed21 --- /dev/null +++ b/include/circt/Dialect/Emit/EmitOps.td @@ -0,0 +1,112 @@ +//===- EmitOps.td - `emit` dialect 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 MLIR ops for `emit`. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_EMIT_EMITOPS_TD +#define CIRCT_DIALECT_EMIT_EMITOPS_TD + +include "circt/Dialect/Emit/EmitDialect.td" +include "mlir/IR/OpAsmInterface.td" + +class EmitOp traits = []> : + Op; + +def FileOp : EmitOp<"file", [ + Symbol, + SingleBlock, + NoTerminator, + NoRegionArguments, + IsolatedFromAbove +]> { + let summary = "Represents the contents of an emitted file"; + + let description = [{ + This operation groups a set of nested operations to be emitted to a file. + + Other operations (such as file lists)can reference a file to access its + filename through an optional symbol. + }]; + + let regions = (region SizedRegion<1>:$body); + let arguments = (ins + StrAttr:$file_name, + OptionalAttr:$sym_name + ); + let results = (outs); + + let assemblyFormat = "$file_name (`sym` $sym_name^)? $body attr-dict"; + + let skipDefaultBuilders = 1; + let builders = [ + // Creates a file with a callback. While the callback is executed, the + // insertion point of the builder is moved inside the body of the file op. + OpBuilder<(ins "StringRef":$fileName, "StringRef":$symName, + CArg<"llvm::function_ref">:$bodyCtor)>, + OpBuilder<(ins "StringRef":$fileName, + CArg<"llvm::function_ref">:$bodyCtor)>, + ]; + + let extraClassDeclaration = [{ + // SymbolOpInterface + static bool isOptionalSymbol() { return true; } + + // Utilities + Block *getBodyBlock() { return &getBodyRegion().front(); } + }]; +} + +def VerbatimOp : EmitOp<"verbatim", [HasParent<"circt::emit::FileOp">]> { + let summary = "Verbatim opaque text emitted inline."; + let description = [{ + This operation produces opaque text inline in the file. + + `emit.verbatim` allows symbol reference substitutions with {{0}} syntax. + }]; + + let arguments = (ins + StrAttr:$text + ); + + let assemblyFormat = [{ + $text attr-dict + }]; +} + + +def FileListOp : EmitOp<"file_list", [ + Symbol, + DeclareOpInterfaceMethods +]> { + let summary = "Represents a file list"; + + let description = [{ + This operation emits a file list referencing a set of files. + + File lists can be references from other ops (including other file lists) + through an optional symbol. + }]; + + let arguments = (ins + StrAttr:$file_name, + FlatSymbolRefArrayAttr:$files, + OptionalAttr:$sym_name + ); + let results = (outs); + + let assemblyFormat = "$file_name `,` $files (`sym` $sym_name^)? attr-dict"; + + let extraClassDeclaration = [{ + // SymbolOpInterface + static bool isOptionalSymbol() { return true; } + }]; +} + +#endif // CIRCT_DIALECT_EMIT_EMITOPS_TD diff --git a/include/circt/InitAllDialects.h b/include/circt/InitAllDialects.h index f60b3c2e883f..399ab495c905 100644 --- a/include/circt/InitAllDialects.h +++ b/include/circt/InitAllDialects.h @@ -20,6 +20,7 @@ #include "circt/Dialect/DC/DCDialect.h" #include "circt/Dialect/Debug/DebugDialect.h" #include "circt/Dialect/ESI/ESIDialect.h" +#include "circt/Dialect/Emit/EmitDialect.h" #include "circt/Dialect/FIRRTL/CHIRRTLDialect.h" #include "circt/Dialect/FIRRTL/FIRRTLDialect.h" #include "circt/Dialect/FSM/FSMOps.h" @@ -55,6 +56,7 @@ inline void registerAllDialects(mlir::DialectRegistry ®istry) { comb::CombDialect, dc::DCDialect, debug::DebugDialect, + emit::EmitDialect, esi::ESIDialect, firrtl::FIRRTLDialect, fsm::FSMDialect, diff --git a/lib/Dialect/CMakeLists.txt b/lib/Dialect/CMakeLists.txt index 3e28ee559f6f..09c8a71a7d52 100644 --- a/lib/Dialect/CMakeLists.txt +++ b/lib/Dialect/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(Calyx) add_subdirectory(Comb) add_subdirectory(DC) add_subdirectory(Debug) +add_subdirectory(Emit) add_subdirectory(ESI) add_subdirectory(FIRRTL) add_subdirectory(FSM) diff --git a/lib/Dialect/Emit/CMakeLists.txt b/lib/Dialect/Emit/CMakeLists.txt new file mode 100644 index 000000000000..37089dc80f39 --- /dev/null +++ b/lib/Dialect/Emit/CMakeLists.txt @@ -0,0 +1,30 @@ +##===- CMakeLists.txt - build definitions for Emit ------------*- cmake -*-===// +## +## 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 +## +##===----------------------------------------------------------------------===// +## +## +##===----------------------------------------------------------------------===// + +add_circt_dialect_library(CIRCTEmit + EmitDialect.cpp + EmitOps.cpp + + ADDITIONAL_HEADER_DIRS + ${CIRCT_MAIN_INCLUDE_DIR}/circt/Dialect/Emit + + DEPENDS + MLIREmitIncGen + + LINK_COMPONENTS + Support + + LINK_LIBS PUBLIC + CIRCTHW + MLIRIR + MLIRPass + MLIRTransforms +) diff --git a/lib/Dialect/Emit/EmitDialect.cpp b/lib/Dialect/Emit/EmitDialect.cpp new file mode 100644 index 000000000000..8b5f98063437 --- /dev/null +++ b/lib/Dialect/Emit/EmitDialect.cpp @@ -0,0 +1,34 @@ +//===- EmitDialect.cpp - Implement the Emit dialect -----------------------===// +// +// 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 file implements the Emit dialect. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/Emit/EmitDialect.h" +#include "circt/Dialect/Emit/EmitOps.h" +#include "circt/Dialect/HW/HWOps.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/DialectImplementation.h" + +using namespace circt; +using namespace emit; + +//===----------------------------------------------------------------------===// +// Dialect specification. +//===----------------------------------------------------------------------===// + +void EmitDialect::initialize() { + addOperations< +#define GET_OP_LIST +#include "circt/Dialect/Emit/Emit.cpp.inc" + >(); +} + +#include "circt/Dialect/Emit/EmitDialect.cpp.inc" diff --git a/lib/Dialect/Emit/EmitOps.cpp b/lib/Dialect/Emit/EmitOps.cpp new file mode 100644 index 000000000000..f899ccadd3c0 --- /dev/null +++ b/lib/Dialect/Emit/EmitOps.cpp @@ -0,0 +1,74 @@ +//===- EmitOps.cpp - Implement the Emit operations ------------------------===// +// +// 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 file implements `sim` dialect ops. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/Emit/EmitOps.h" + +using namespace mlir; +using namespace circt; +using namespace emit; + +//===----------------------------------------------------------------------===// +// FileOp +//===----------------------------------------------------------------------===// + +void FileOp::build(OpBuilder &builder, OperationState &result, + StringRef fileName, StringRef symName, + llvm::function_ref bodyCtor) { + MLIRContext *context = builder.getContext(); + OpBuilder::InsertionGuard guard(builder); + + auto &props = result.getOrAddProperties(); + props.sym_name = StringAttr::get(context, symName); + props.file_name = StringAttr::get(context, fileName); + + builder.createBlock(result.addRegion()); + if (bodyCtor) + bodyCtor(); +} + +void FileOp::build(OpBuilder &builder, OperationState &result, + StringRef fileName, llvm::function_ref bodyCtor) { + MLIRContext *context = builder.getContext(); + OpBuilder::InsertionGuard guard(builder); + + auto &props = result.getOrAddProperties(); + props.file_name = StringAttr::get(context, fileName); + + builder.createBlock(result.addRegion()); + if (bodyCtor) + bodyCtor(); +} + +//===----------------------------------------------------------------------===// +// FileListOp +//===----------------------------------------------------------------------===// + +LogicalResult FileListOp::verifySymbolUses(SymbolTableCollection &symbolTable) { + for (auto sym : getFiles()) { + Operation *op = symbolTable.lookupNearestSymbolFrom( + getOperation(), sym.cast()); + if (!op) + return emitError("invalid symbol reference: ") << sym; + + if (!isa(op)) + return emitError("referenced operation is not a file: ") << sym; + } + return success(); +} + +//===----------------------------------------------------------------------===// +// TableGen generated logic. +//===----------------------------------------------------------------------===// + +// Provide the autogenerated implementation guts for the Op classes. +#define GET_OP_CLASSES +#include "circt/Dialect/Emit/Emit.cpp.inc" diff --git a/test/Dialect/Emit/emit-errors.mlir b/test/Dialect/Emit/emit-errors.mlir new file mode 100644 index 000000000000..b28ce870de90 --- /dev/null +++ b/test/Dialect/Emit/emit-errors.mlir @@ -0,0 +1,11 @@ +// RUN: circt-opt %s -verify-diagnostics --split-input-file + +sv.macro.decl @SomeMacro + +// expected-error @below {{referenced operation is not a file: @SomeMacro}} +emit.file_list "filelist.f", [@SomeMacro] + +// ----- + +// expected-error @below {{invalid symbol reference: @InvalidRef}} +emit.file_list "filelist.f", [@InvalidRef] diff --git a/test/Dialect/Emit/round-trip.mlir b/test/Dialect/Emit/round-trip.mlir new file mode 100644 index 000000000000..ae83d5caefa7 --- /dev/null +++ b/test/Dialect/Emit/round-trip.mlir @@ -0,0 +1,13 @@ +// RUN: circt-opt %s | circt-opt | FileCheck %s + +sv.macro.decl @SomeMacro + +// CHECK: emit.file "some-file.sv" sym @SomeFile.sv +emit.file "some-file.sv" sym @SomeFile.sv { + // CHECK: emit.verbatim "SimpleVerbatim" + emit.verbatim "SimpleVerbatim" + +} + +// CHECK: emit.file_list "filelist.f", [@SomeFile.sv] sym @SomeFileList +emit.file_list "filelist.f", [@SomeFile.sv] sym @SomeFileList diff --git a/tools/circt-opt/CMakeLists.txt b/tools/circt-opt/CMakeLists.txt index 8bafa4dd28f3..c71f0890aec3 100644 --- a/tools/circt-opt/CMakeLists.txt +++ b/tools/circt-opt/CMakeLists.txt @@ -28,6 +28,7 @@ target_link_libraries(circt-opt CIRCTDCToHW CIRCTDCTransforms CIRCTDebug + CIRCTEmit CIRCTESI CIRCTExportChiselInterface CIRCTExportVerilog