mirror of https://github.com/llvm/circt.git
[Debug] Add debug dialect (#6308)
Add the `dbg` dialect, alongside a rationale document, basic types, and the three essential `dbg.variable`, `dbg.struct`, and `dbg.array` operations. The goal is for this dialect to be a home for all the machinery needed to track debug information in the IR. After discussions with a few people in the LLVM/MLIR space, I chose to create dedicated operations to track debug info in the IR, as opposed to an attribute/metadata based approach. The details are outlined in the rationale document. Subsequent PRs add simple debug info tracking to FIRRTL and firtool, and extend debug info emission to consider these operations.
This commit is contained in:
parent
2f8381cd1b
commit
a9a07b70d2
|
@ -0,0 +1,165 @@
|
|||
# Debug Dialect
|
||||
|
||||
This dialect provides operations and types to interleave debug information (DI)
|
||||
with other parts of the IR.
|
||||
|
||||
[TOC]
|
||||
|
||||
|
||||
## Rationale
|
||||
|
||||
The main goal of the debug dialect is to provide a mechanism to track the
|
||||
correspondence between values, types, and hierarchy of a source language and the
|
||||
IR being compiled and transformed. This allows simulators, synthesizers, and
|
||||
other debugging tools to reconstruct a source language view into the processed
|
||||
hardware and allow for easier debugging by humans.
|
||||
|
||||
Debug information in CIRCT follows these principles:
|
||||
|
||||
- **It is best effort:** DI is meant as a tool to aid humans in their debugging
|
||||
effort, not a contractual obligation to retain all source language semantics
|
||||
through the compilation pipeline. We preserve information as well as possible
|
||||
and reasonable, but accept the fact that certain optimizations may cause
|
||||
information to be discarded.
|
||||
|
||||
- **It affects the output:** Enabling the tracking of DI is expected to block
|
||||
certain optimizations. We undertake an effort to minimize the impact of DI on
|
||||
the output quality, size, simulation speed, or synthesis results, but accept
|
||||
the fact that preserving visibility and observability of source language
|
||||
constructs may prevent certain optimizations from running.
|
||||
|
||||
|
||||
### Representations
|
||||
|
||||
There are two mechanisms in MLIR that lend themselves to conveying debug
|
||||
information:
|
||||
|
||||
- **Attributes** attached to existing operations. This is similar to LLVM's
|
||||
approach of tracking DI in the operation's metadata. Translated to MLIR, an
|
||||
operation's location would be an obvious choice to do this tracking, since
|
||||
locations are well-preserved by passes and difficult to accidentally drop.
|
||||
MLIR currently does not support custom location attributes, which would
|
||||
require DI attributes to be attached to a `FusedLoc` as metadata.
|
||||
|
||||
- **Operations** interleaved with the rest of the IR. This makes DI a
|
||||
first-class citizen, but also causes debug information to potentially intefere
|
||||
with optimizations. For example, debug dialect ops introduce additional uses
|
||||
of values that might have otherwise been deleted by DCE. However, there may be
|
||||
alternative ways to dealing with such situations. For example, Verilog
|
||||
emission may simply ignore operations that are only used by debug ops,
|
||||
therefore achieving the same effect as DCE would have.
|
||||
|
||||
The debug dialect uses _operations_ to represent debug info. This decision was
|
||||
based on discussions with various people in the LLVM and MLIR community, where
|
||||
DI was commonly quoted as one of LLVM's weak points, with its living in metadata
|
||||
space making it more of a second-class citizen rather than a first-class
|
||||
concern. Since we want to represent source language types and constructs as
|
||||
accurately as possible, and we want to track if values are type-lowered,
|
||||
constant-folded, outlined, or adjusted in some other way, using operations seems
|
||||
like a natural choice. MLIR ops already have all the machinery needed to refer
|
||||
to values in the IR, and many passes will already do the right thing with them.
|
||||
|
||||
|
||||
## Representing Source Language Constructs
|
||||
|
||||
The `dbg.variable` op is the key mechanism to establish a mapping between
|
||||
high-level source language values and low-level values in the IR that are
|
||||
transformed by the compiler. Consider the following source language pseudocode:
|
||||
|
||||
```plain
|
||||
struct Req {
|
||||
data: i42,
|
||||
valid: i1,
|
||||
ready: &i1,
|
||||
}
|
||||
struct Resp {
|
||||
result: i42,
|
||||
done: i1,
|
||||
}
|
||||
module Foo {
|
||||
parameter Depth: uint;
|
||||
const Width: uint = 2**Depth;
|
||||
input req: Req;
|
||||
output resps: Resp[2];
|
||||
let x = req;
|
||||
}
|
||||
```
|
||||
|
||||
A frontend for this language could generate the following debug variables as
|
||||
part of the body of module `Foo`, in order to track the structs, arrays,
|
||||
parameters, constants, and local bindings present in the source language:
|
||||
|
||||
```mlir
|
||||
hw.module @Foo_Width12(
|
||||
in %req_data: i42,
|
||||
in %req_valid: i1,
|
||||
out req_ready: i1,
|
||||
out resps0_result: i42,
|
||||
out resps0_done: i1,
|
||||
out resps1_result: i42,
|
||||
out resps1_done: i1
|
||||
) {
|
||||
// %req_ready = ...
|
||||
// %resps0_result = ...
|
||||
// %resps0_done = ...
|
||||
// %resps1_result = ...
|
||||
// %resps1_done = ...
|
||||
|
||||
// parameter Depth
|
||||
%c12_i32 = hw.constant 12 : i32
|
||||
dbg.variable "Depth", %c12_i32 : i32
|
||||
|
||||
// const Width
|
||||
%c4096_i32 = hw.constant 4096 : i32
|
||||
dbg.variable "Width", %c4096_i32 : i32
|
||||
|
||||
// input req: Req
|
||||
%0 = dbg.struct {"data": %req_data, "valid": %req_valid, "ready": %req_ready} : i42, i1, i1
|
||||
dbg.variable "req", %0 : !dbg.struct
|
||||
|
||||
// output resps: Resp[2]
|
||||
%1 = dbg.struct {"result": %resps0_result, "done": %resps0_done} : i42, i1
|
||||
%2 = dbg.struct {"result": %resps1_result, "done": %resps1_done} : i42, i1
|
||||
%3 = dbg.array [%1, %2] : !dbg.struct, !dbg.struct
|
||||
dbg.variable "resps", %3 : !dbg.array
|
||||
|
||||
// let x = req
|
||||
dbg.variable "x", %0 : !dbg.struct
|
||||
|
||||
hw.output %req_ready, %resps0_result, %resps0_done, %resps1_result, %resps1_done : i1, i42, i1, i42, i1
|
||||
}
|
||||
```
|
||||
|
||||
Despite the fact that the `Req` and `Resp` structs, and `Resp[2]` array were
|
||||
unrolled and lowered into separate scalar values in the IR, and the `ready: &i1`
|
||||
input of `Req` having been turned into a `ready: i1` output, the `dbg.variable`
|
||||
op accurately tracks how the original source language values can be
|
||||
reconstructed. Note also how monomorphization has turned the `Depth` parameter
|
||||
and `Width` into constants in the IR, but the corresponding `dbg.variable` ops
|
||||
still expose the constant values under the name `Depth` and `Width` in the debug
|
||||
info.
|
||||
|
||||
|
||||
## Types
|
||||
|
||||
|
||||
### Overview
|
||||
|
||||
The debug dialect does not precisely track the type of struct and array
|
||||
aggregate values. Aggregates simply return the type `!dbg.struct` and
|
||||
`!dbg.array`, respectively.
|
||||
|
||||
Extracting and emitting the debug information of a piece of IR involves looking
|
||||
through debug ops to find actually emitted values that can be used to
|
||||
reconstruct the source language values. Therefore the actual structure of the
|
||||
debug ops is important, but their return type is not instrumental. The
|
||||
distinction between struct and array types is an arbitrary choice that can be
|
||||
changed easily, either by collapsing them into one aggregate type, or by more
|
||||
precisely listing field/element types and array dimensions if the need arises.
|
||||
|
||||
[include "Dialects/DebugTypes.md"]
|
||||
|
||||
|
||||
## Operations
|
||||
|
||||
[include "Dialects/DebugOps.md"]
|
|
@ -10,6 +10,7 @@ add_subdirectory(Arc)
|
|||
add_subdirectory(Calyx)
|
||||
add_subdirectory(Comb)
|
||||
add_subdirectory(DC)
|
||||
add_subdirectory(Debug)
|
||||
add_subdirectory(ESI)
|
||||
add_subdirectory(FIRRTL)
|
||||
add_subdirectory(FSM)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
add_circt_dialect(Debug dbg)
|
||||
add_circt_doc(DebugOps Dialects/DebugOps -gen-op-doc)
|
||||
add_circt_doc(DebugTypes Dialects/DebugTypes -gen-typedef-doc -dialect dbg)
|
||||
|
||||
add_dependencies(circt-headers MLIRDebugIncGen)
|
|
@ -0,0 +1,16 @@
|
|||
//===- Debug.td - Debug dialect ----------------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_DIALECT_DEBUG_DEBUG_TD
|
||||
#define CIRCT_DIALECT_DEBUG_DEBUG_TD
|
||||
|
||||
include "circt/Dialect/Debug/DebugDialect.td"
|
||||
include "circt/Dialect/Debug/DebugOps.td"
|
||||
include "circt/Dialect/Debug/DebugTypes.td"
|
||||
|
||||
#endif // CIRCT_DIALECT_DEBUG_DEBUG_TD
|
|
@ -0,0 +1,17 @@
|
|||
//===- DebugDialect.h - Debug dialect definition ----------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_DIALECT_DEBUG_DEBUGDIALECT_H
|
||||
#define CIRCT_DIALECT_DEBUG_DEBUGDIALECT_H
|
||||
|
||||
#include "mlir/IR/Dialect.h"
|
||||
|
||||
// Dialect definition generated from `DebugDialect.td`
|
||||
#include "circt/Dialect/Debug/DebugDialect.h.inc"
|
||||
|
||||
#endif // CIRCT_DIALECT_DEBUG_DEBUGDIALECT_H
|
|
@ -0,0 +1,28 @@
|
|||
//===- DebugDialect.td - Debug 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_DIALECT_DEBUG_DEBUGDIALECT_TD
|
||||
#define CIRCT_DIALECT_DEBUG_DEBUGDIALECT_TD
|
||||
|
||||
include "mlir/IR/DialectBase.td"
|
||||
include "mlir/IR/OpBase.td"
|
||||
|
||||
def DebugDialect : Dialect {
|
||||
let name = "dbg";
|
||||
let summary = "Facilities to represent debug information";
|
||||
let cppNamespace = "circt::debug";
|
||||
|
||||
let useDefaultTypePrinterParser = 1;
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
void registerOps();
|
||||
void registerTypes();
|
||||
}];
|
||||
}
|
||||
|
||||
#endif // CIRCT_DIALECT_DEBUG_DEBUGDIALECT_TD
|
|
@ -0,0 +1,24 @@
|
|||
//===- DebugOps.h - Debug 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_DIALECT_DEBUG_DEBUGOPS_H
|
||||
#define CIRCT_DIALECT_DEBUG_DEBUGOPS_H
|
||||
|
||||
#include "mlir/Bytecode/BytecodeOpInterface.h"
|
||||
#include "mlir/IR/OpImplementation.h"
|
||||
#include "mlir/Interfaces/InferTypeOpInterface.h"
|
||||
#include "mlir/Interfaces/SideEffectInterfaces.h"
|
||||
|
||||
#include "circt/Dialect/Debug/DebugDialect.h"
|
||||
#include "circt/Dialect/Debug/DebugTypes.h"
|
||||
|
||||
// Operation definitions generated from `Debug.td`
|
||||
#define GET_OP_CLASSES
|
||||
#include "circt/Dialect/Debug/Debug.h.inc"
|
||||
|
||||
#endif // CIRCT_DIALECT_DEBUG_DEBUGOPS_H
|
|
@ -0,0 +1,81 @@
|
|||
//===- DebugOps.td - Debug dialect operations --------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_DIALECT_DEBUG_DEBUGOPS_TD
|
||||
#define CIRCT_DIALECT_DEBUG_DEBUGOPS_TD
|
||||
|
||||
include "circt/Dialect/Debug/DebugDialect.td"
|
||||
include "circt/Dialect/Debug/DebugTypes.td"
|
||||
include "mlir/Interfaces/InferTypeOpInterface.td"
|
||||
include "mlir/Interfaces/SideEffectInterfaces.td"
|
||||
include "mlir/IR/OpBase.td"
|
||||
|
||||
class DebugOp<string mnemonic, list<Trait> traits = []> :
|
||||
Op<DebugDialect, mnemonic, traits>;
|
||||
|
||||
|
||||
def VariableOp : DebugOp<"variable"> {
|
||||
let summary = "A named value to be captured in debug info";
|
||||
let description = [{
|
||||
Marks a value to be tracked in DI under the given name. The `dbg.variable`
|
||||
operation is useful to represent named values in a source language. For
|
||||
example, ports, constants, parameters, variables, nodes, or name aliases can
|
||||
all be represented as a variable. In combination with `dbg.array` and
|
||||
`dbg.struct`, complex aggregate source language values can be described and
|
||||
reconstituted from individual IR values. The `dbg.variable` operation acts
|
||||
as a tracker that follows the evolution of its assigned value throughout the
|
||||
compiler's pass pipelines. The debug info analysis uses this op to populate
|
||||
a module's scope with named source language values, and to establish how
|
||||
these source language values can be reconstituted from the actual IR values
|
||||
present at the end of compilation.
|
||||
|
||||
See the rationale for examples and details.
|
||||
}];
|
||||
let arguments = (ins StrAttr:$name, AnyType:$value);
|
||||
let assemblyFormat = [{ $name `,` $value attr-dict `:` type($value) }];
|
||||
}
|
||||
|
||||
|
||||
def StructOp : DebugOp<"struct", [
|
||||
Pure,
|
||||
PredOpTrait<"number of fields and names match",
|
||||
CPred<"$fields.size() == $names.size()">>
|
||||
]> {
|
||||
let summary = "Aggregate values into a struct";
|
||||
let description = [{
|
||||
Creates a struct aggregate from a list of names and values. The `dbg.struct`
|
||||
operation allows for struct-like source language values to be captured in
|
||||
the debug info. This includes structs, unions, bidirectional bundles,
|
||||
interfaces, classes, and other similar structures.
|
||||
|
||||
See the rationale for examples and details.
|
||||
}];
|
||||
let arguments = (ins Variadic<AnyType>:$fields, StrArrayAttr:$names);
|
||||
let results = (outs StructType:$result);
|
||||
let hasCustomAssemblyFormat = 1;
|
||||
}
|
||||
|
||||
|
||||
def ArrayOp : DebugOp<"array", [Pure, SameTypeOperands]> {
|
||||
let summary = "Aggregate values into an array";
|
||||
let description = [{
|
||||
Creates an array aggregate from a list of values. The first operand is
|
||||
placed at array index 0. The last operand is placed at the highest array
|
||||
index. The `dbg.array` operation allows for array-like source language
|
||||
values to be captured in the debug info. This includes arrays, or in the
|
||||
case of SystemVerilog, packed and unpacked arrays, lists, sequences, queues,
|
||||
FIFOs, channels, and vectors.
|
||||
|
||||
See the rationale for examples and details.
|
||||
}];
|
||||
let arguments = (ins Variadic<AnyType>:$elements);
|
||||
let results = (outs ArrayType:$result);
|
||||
let hasCustomAssemblyFormat = 1;
|
||||
}
|
||||
|
||||
#endif // CIRCT_DIALECT_DEBUG_DEBUGOPS_TD
|
|
@ -0,0 +1,18 @@
|
|||
//===- DebugTypes.h - Debug dialect types -----------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_DIALECT_DEBUG_DEBUGTYPES_H
|
||||
#define CIRCT_DIALECT_DEBUG_DEBUGTYPES_H
|
||||
|
||||
#include "mlir/IR/BuiltinTypes.h"
|
||||
#include "mlir/IR/Types.h"
|
||||
|
||||
#define GET_TYPEDEF_CLASSES
|
||||
#include "circt/Dialect/Debug/DebugTypes.h.inc"
|
||||
|
||||
#endif // CIRCT_DIALECT_DEBUG_DEBUGTYPES_H
|
|
@ -0,0 +1,29 @@
|
|||
//===- DebugTypes.td - Debug dialect types -----------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_DIALECT_DEBUG_DEBUGTYPES_TD
|
||||
#define CIRCT_DIALECT_DEBUG_DEBUGTYPES_TD
|
||||
|
||||
include "circt/Dialect/Debug/DebugDialect.td"
|
||||
include "mlir/IR/AttrTypeBase.td"
|
||||
|
||||
class DebugTypeDef<string name> : TypeDef<DebugDialect, name> { }
|
||||
|
||||
def StructType : DebugTypeDef<"Struct"> {
|
||||
let mnemonic = "struct";
|
||||
let summary = "debug struct aggregate";
|
||||
let description = "The result of a `dbg.struct` operation.";
|
||||
}
|
||||
|
||||
def ArrayType : DebugTypeDef<"Array"> {
|
||||
let mnemonic = "array";
|
||||
let summary = "debug array aggregate";
|
||||
let description = "The result of a `dbg.array` operation.";
|
||||
}
|
||||
|
||||
#endif // CIRCT_DIALECT_DEBUG_DEBUGTYPES_TD
|
|
@ -18,6 +18,7 @@
|
|||
#include "circt/Dialect/Calyx/CalyxDialect.h"
|
||||
#include "circt/Dialect/Comb/CombDialect.h"
|
||||
#include "circt/Dialect/DC/DCDialect.h"
|
||||
#include "circt/Dialect/Debug/DebugDialect.h"
|
||||
#include "circt/Dialect/ESI/ESIDialect.h"
|
||||
#include "circt/Dialect/FIRRTL/CHIRRTLDialect.h"
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLDialect.h"
|
||||
|
@ -52,6 +53,7 @@ inline void registerAllDialects(mlir::DialectRegistry ®istry) {
|
|||
chirrtl::CHIRRTLDialect,
|
||||
comb::CombDialect,
|
||||
dc::DCDialect,
|
||||
debug::DebugDialect,
|
||||
esi::ESIDialect,
|
||||
firrtl::FIRRTLDialect,
|
||||
fsm::FSMDialect,
|
||||
|
|
|
@ -13,6 +13,7 @@ add_subdirectory(Arc)
|
|||
add_subdirectory(Calyx)
|
||||
add_subdirectory(Comb)
|
||||
add_subdirectory(DC)
|
||||
add_subdirectory(Debug)
|
||||
add_subdirectory(ESI)
|
||||
add_subdirectory(FIRRTL)
|
||||
add_subdirectory(FSM)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
add_circt_dialect_library(CIRCTDebug
|
||||
DebugDialect.cpp
|
||||
DebugOps.cpp
|
||||
DebugTypes.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${CIRCT_MAIN_INCLUDE_DIR}/circt/Dialect/Debug
|
||||
|
||||
DEPENDS
|
||||
MLIRDebugIncGen
|
||||
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
MLIRIR
|
||||
MLIRInferTypeOpInterface
|
||||
MLIRSideEffectInterfaces
|
||||
)
|
|
@ -0,0 +1,21 @@
|
|||
//===- DebugDialect.cpp - Debug dialect implementation --------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/Debug/DebugDialect.h"
|
||||
#include "circt/Dialect/Debug/DebugOps.h"
|
||||
|
||||
using namespace circt;
|
||||
using namespace debug;
|
||||
|
||||
void DebugDialect::initialize() {
|
||||
registerOps();
|
||||
registerTypes();
|
||||
}
|
||||
|
||||
// Dialect implementation generated from `DebugDialect.td`
|
||||
#include "circt/Dialect/Debug/DebugDialect.cpp.inc"
|
|
@ -0,0 +1,127 @@
|
|||
//===- DebugOps.cpp - Debug dialect 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/Debug/DebugOps.h"
|
||||
#include "mlir/IR/OpImplementation.h"
|
||||
|
||||
using namespace circt;
|
||||
using namespace debug;
|
||||
using namespace mlir;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// StructOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
ParseResult StructOp::parse(OpAsmParser &parser, OperationState &result) {
|
||||
// Parse the struct fields.
|
||||
SmallVector<Attribute> names;
|
||||
SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
|
||||
std::string nameBuffer;
|
||||
auto parseField = [&]() {
|
||||
nameBuffer.clear();
|
||||
if (parser.parseString(&nameBuffer) || parser.parseColon() ||
|
||||
parser.parseOperand(operands.emplace_back()))
|
||||
return failure();
|
||||
names.push_back(StringAttr::get(parser.getContext(), nameBuffer));
|
||||
return success();
|
||||
};
|
||||
if (parser.parseCommaSeparatedList(AsmParser::Delimiter::Braces, parseField))
|
||||
return failure();
|
||||
|
||||
// Parse the attribute dictionary.
|
||||
if (parser.parseOptionalAttrDict(result.attributes))
|
||||
return failure();
|
||||
|
||||
// Parse the field types, if there are any fields.
|
||||
SmallVector<Type> types;
|
||||
if (!operands.empty()) {
|
||||
if (parser.parseColon())
|
||||
return failure();
|
||||
auto typesLoc = parser.getCurrentLocation();
|
||||
if (parser.parseTypeList(types))
|
||||
return failure();
|
||||
if (types.size() != operands.size())
|
||||
return parser.emitError(typesLoc,
|
||||
"number of fields and types must match");
|
||||
}
|
||||
|
||||
// Resolve the operands.
|
||||
for (auto [operand, type] : llvm::zip(operands, types))
|
||||
if (parser.resolveOperand(operand, type, result.operands))
|
||||
return failure();
|
||||
|
||||
// Finalize the op.
|
||||
result.addAttribute("names", ArrayAttr::get(parser.getContext(), names));
|
||||
result.addTypes(StructType::get(parser.getContext()));
|
||||
return success();
|
||||
}
|
||||
|
||||
void StructOp::print(OpAsmPrinter &printer) {
|
||||
printer << " {";
|
||||
llvm::interleaveComma(llvm::zip(getFields(), getNames()), printer.getStream(),
|
||||
[&](auto pair) {
|
||||
auto [field, name] = pair;
|
||||
printer.printAttribute(name);
|
||||
printer << ": ";
|
||||
printer.printOperand(field);
|
||||
});
|
||||
printer << '}';
|
||||
printer.printOptionalAttrDict(getOperation()->getAttrs(), {"names"});
|
||||
if (!getFields().empty()) {
|
||||
printer << " : ";
|
||||
printer << getFields().getTypes();
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ArrayOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
ParseResult ArrayOp::parse(OpAsmParser &parser, OperationState &result) {
|
||||
// Parse the elements, attributes and types.
|
||||
SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
|
||||
if (parser.parseOperandList(operands, AsmParser::Delimiter::Square) ||
|
||||
parser.parseOptionalAttrDict(result.attributes))
|
||||
return failure();
|
||||
|
||||
// Resolve the operands.
|
||||
if (!operands.empty()) {
|
||||
Type type;
|
||||
if (parser.parseColon() || parser.parseType(type))
|
||||
return failure();
|
||||
for (auto operand : operands)
|
||||
if (parser.resolveOperand(operand, type, result.operands))
|
||||
return failure();
|
||||
}
|
||||
|
||||
// Finalize the op.
|
||||
result.addTypes(ArrayType::get(parser.getContext()));
|
||||
return success();
|
||||
}
|
||||
|
||||
void ArrayOp::print(OpAsmPrinter &printer) {
|
||||
printer << " [";
|
||||
printer << getElements();
|
||||
printer << ']';
|
||||
printer.printOptionalAttrDict(getOperation()->getAttrs());
|
||||
if (!getElements().empty()) {
|
||||
printer << " : ";
|
||||
printer << getElements()[0].getType();
|
||||
}
|
||||
}
|
||||
|
||||
// Operation implementations generated from `Debug.td`
|
||||
#define GET_OP_CLASSES
|
||||
#include "circt/Dialect/Debug/Debug.cpp.inc"
|
||||
|
||||
void DebugDialect::registerOps() {
|
||||
addOperations<
|
||||
#define GET_OP_LIST
|
||||
#include "circt/Dialect/Debug/Debug.cpp.inc"
|
||||
>();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
//===- DebugTypes.cpp - Debug dialect types -------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/Debug/DebugTypes.h"
|
||||
#include "circt/Dialect/Debug/DebugDialect.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/DialectImplementation.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
|
||||
using namespace circt;
|
||||
using namespace debug;
|
||||
using namespace mlir;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Table Gen
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define GET_TYPEDEF_CLASSES
|
||||
#include "circt/Dialect/Debug/DebugTypes.cpp.inc"
|
||||
|
||||
void DebugDialect::registerTypes() {
|
||||
addTypes<
|
||||
#define GET_TYPEDEF_LIST
|
||||
#include "circt/Dialect/Debug/DebugTypes.cpp.inc"
|
||||
>();
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// RUN: circt-opt %s --verify-diagnostics | circt-opt | FileCheck %s
|
||||
|
||||
// CHECK-LABEL: func.func @Foo
|
||||
func.func @Foo(%arg0: i32, %arg1: index, %arg2: f64) {
|
||||
// CHECK-NEXT: dbg.variable "foo", %arg0 : i32
|
||||
// CHECK-NEXT: dbg.variable "bar", %arg1 : index
|
||||
// CHECK-NEXT: dbg.variable "baz", %arg2 : f64
|
||||
dbg.variable "foo", %arg0 : i32
|
||||
dbg.variable "bar", %arg1 : index
|
||||
dbg.variable "baz", %arg2 : f64
|
||||
|
||||
// CHECK-NEXT: [[TMP:%.+]] = dbg.struct {"foo": %arg0, "bar": %arg1, "baz": %arg2} : i32, index, f64
|
||||
// CHECK-NEXT: dbg.variable "megafoo", [[TMP]] : !dbg.struct
|
||||
%0 = dbg.struct {"foo": %arg0, "bar": %arg1, "baz": %arg2} : i32, index, f64
|
||||
dbg.variable "megafoo", %0 : !dbg.struct
|
||||
|
||||
// CHECK-NEXT: [[TMP:%.+]] = dbg.array [%arg1, %arg1] : index
|
||||
// CHECK-NEXT: dbg.variable "megabar", [[TMP]] : !dbg.array
|
||||
%1 = dbg.array [%arg1, %arg1] : index
|
||||
dbg.variable "megabar", %1 : !dbg.array
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ target_link_libraries(circt-opt
|
|||
CIRCTDC
|
||||
CIRCTDCToHW
|
||||
CIRCTDCTransforms
|
||||
CIRCTDebug
|
||||
CIRCTESI
|
||||
CIRCTExportChiselInterface
|
||||
CIRCTExportVerilog
|
||||
|
|
Loading…
Reference in New Issue