Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DebugInfo][HGLDD] Consuming Tywaves annotations containing extra source language information from Chisel and improve HGLDD format #7246

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f0df89e
Consume Tywaves annotations (retrieve source language type and constr…
rameloni May 20, 2024
5ebd5b0
Translate annotations to debug dialect operations
rameloni May 20, 2024
5e05d2c
Fix error in test tywaves-annotations.mlir
rameloni May 20, 2024
9e36c01
[Debug] Add support for source language type information to Debug Inf…
rameloni May 20, 2024
61b0ae8
Emit source language type information in HGLDD for variables and subf…
rameloni May 29, 2024
3082905
Add source language debug information for a module and emit it into h…
rameloni May 29, 2024
05cd31f
Update emit-hgldd.mlir test
rameloni May 29, 2024
f840171
Add test dbg.subfield test case in test/Dialect/Debug/basic.mlir
rameloni May 30, 2024
a1f795e
Add test dbg.moduleinfo test case in test/Dialect/Debug/basic.mlir
rameloni May 30, 2024
870406c
Update class name used for the tywaves annotation
rameloni Jul 9, 2024
29e388b
[DebugInfo] create debug operation for a ChiselEnum definition
rameloni Jul 9, 2024
35a6367
[DebugInfo] add support for enumVecAnnotation
rameloni Jul 10, 2024
dd90c7c
[DebugInfo] fix case of multi vec enum annotation for the same variable
rameloni Jul 10, 2024
a5d5938
[DebugInfo] update HGLDD to include enum definitions
rameloni Jul 10, 2024
db8bcea
Bug fix: emit enum_def_ref also for top variable vecs
rameloni Jul 10, 2024
d68fa19
Fix HGLDD error when a constructor param is of boolean type
rameloni Jul 11, 2024
858a4ab
[Debug info] Bug fix: assign enum definition in the same region
rameloni Aug 19, 2024
92f8d1a
Fix enum created twice for top variable. Add mlir tests for enum
rameloni Aug 21, 2024
f5bd7fd
Bug fix: chiselEnum field access resolved for memports
rameloni Oct 20, 2024
f254b85
Bug fix: firtool crashes with SRAMInterface of empty ports
rameloni Oct 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions include/circt/Analysis/DebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,24 @@

namespace circt {

using DIEnumDefId = uint16_t;
using DIEnumValMap = SmallDenseMap<int64_t, StringAttr>;
using DIEnumDefMap = SmallDenseMap<DIEnumDefId, DIEnumValMap>;

struct DIInstance;
struct DIVariable;

namespace detail {
struct DebugInfoBuilder;
} // namespace detail

struct DISourceLang {
/// The name of the type.
StringAttr typeName;
/// The constructor parameters of the type.
ArrayAttr params;
};

struct DIModule {
/// The operation that generated this level of hierarchy.
Operation *op = nullptr;
Expand All @@ -36,6 +47,15 @@ struct DIModule {
bool isExtern = false;
/// If this is an inline scope created by a `dbg.scope` operation.
bool isInline = false;

/// The source language type of this module.
DISourceLang sourceLangType;

/// The enum definitions of this module.
/// An optional map between the raw value of a bit to a string representation
/// `(e.g. 0 -> "A", 1 -> "B", 2 -> "Hello")`. This applies for example for
/// enums.
DIEnumDefMap enumDefinitions;
};

struct DIInstance {
Expand All @@ -54,6 +74,12 @@ struct DIVariable {
LocationAttr loc;
/// The SSA value representing the value of this variable.
Value value = nullptr;

/// The source language type of this module.
DISourceLang sourceLangType;

/// An optional reference to the enum definition
std::optional<DIEnumDefId> enumDefRef = std::nullopt;
};

/// Debug information attached to an operation and the operations nested within.
Expand Down
89 changes: 86 additions & 3 deletions include/circt/Dialect/Debug/DebugOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def ScopeOp : DebugOp<"scope"> {
}


def VariableOp : DebugOp<"variable"> {
def VariableOp : DebugOp<"variable", [AttrSizedOperandSegments]> {
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`
Expand All @@ -63,21 +63,57 @@ def VariableOp : DebugOp<"variable"> {
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.
present at the end of compilation.
In addition, the `dbg.variable` operation may contain extra information about
the variable, such as its source language type and eventual constructor
parameters. This is allows to reconstruct more precisely the source language.

See the rationale for examples and details. See the `dbg.scope` operation
for additional details on how to use the `scope` operand.
}];
let arguments = (ins
StrAttr:$name,
AnyType:$value,
OptionalAttr<StrAttr>:$typeName,
OptionalAttr<ArrayAttr>:$params,
Optional<EnumDefType>:$enumDef,
Optional<ScopeType>:$scope
);
// let results = (outs VariableType:$result);
let assemblyFormat = [{
$name `,` $value (`scope` $scope^)? attr-dict `:` type($value)
$name `,` $value (`scope` $scope^)? attr-dict (`enumDef` $enumDef^)? `:` type($value)
}];
}

def SubFieldOp : DebugOp<"subfield"> {
let summary = "A named value to be captured in debug info which is a subfield of an aggregate";
let description = [{
Marks a subfield of aggregate to be tracked in DI under the given name.
It is similar to `dbg.variable`, both store a value and contain source language name, type,
and constructor parameters, but `dbg.subfield` returns also a value. Unlike a `dbg.variable`,
it is contained in other debug operations like `dbg.struct` or `dbg.array` (here the usage of
the returned value). It is only used to represent a subfield of an aggregate and it cannot be
used to represent a variable directly declared in a module.

The addition of support for source language type and constructor parameters for top variables
and subfields (also nested) required to build this additional operation. The `dbg.variable`
explicitly represents the "top" variable instances in a module. For this reason, it wasn't used to mark
and it cannot mark subfields of aggregates.

The `dbg.subfield` doesn't have a `scope` operand, because it is a descendant of a `dbg.variable`.
}];
let arguments = (ins
StrAttr:$name,
AnyType:$value,
OptionalAttr<StrAttr>:$typeName,
OptionalAttr<ArrayAttr>:$params,
Optional<EnumDefType>:$enumDef
);
let results = (outs SubFieldType:$result);
let assemblyFormat = [{
$name `,` $value attr-dict (`enumDef` $enumDef^)? `:` type($value)
}];
}

def StructOp : DebugOp<"struct", [
Pure,
Expand Down Expand Up @@ -116,4 +152,51 @@ def ArrayOp : DebugOp<"array", [Pure, SameTypeOperands]> {
let hasCustomAssemblyFormat = 1;
}


def ModuleInfoOp : DebugOp<"moduleinfo"> {
let summary = "Define extra debug information for a module";
let description = [{
Creates debug information for a module. If present, this operations provides
extra information about the module type in the source language, such as its
type name and constructor parameters.
}];

let arguments = (ins
StrAttr:$typeName,
OptionalAttr<ArrayAttr>:$params
);

let assemblyFormat = [{ attr-dict }];
}

def EnumDefOp : DebugOp<"enumdef"> {
let summary = "Define the value variants of an enumeration";
let description = [{
Creates a definition of an enumeration type.
It is useful to reconstruct the named variants of an enum from a raw value.
Variants can be internally represented as a map (Int -> String).
It might be possible that the user select the order and single raw values
of the variants. Therefore, it is not possible to use an array to map the
named variants with the integer value.

This operation is declared once per enum present in a scope.

The result of this operation is used as operand in `dbg.variable` and
`dbg.subfield` operations if they originate from an enum type.
}];

let arguments = (ins
StrAttr:$enumTypeName,
I16Attr:$id,
DictionaryAttr:$variantsMap,
Optional<ScopeType>:$scope
);

let results = (outs EnumDefType:$result);

let assemblyFormat = [{
$enumTypeName `,` `id` $id `,` $variantsMap (`scope` $scope^)? attr-dict
}];
}

#endif // CIRCT_DIALECT_DEBUG_DEBUGOPS_TD
12 changes: 12 additions & 0 deletions include/circt/Dialect/Debug/DebugTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,16 @@ def ArrayType : DebugTypeDef<"Array"> {
let description = "The result of a `dbg.array` operation.";
}

def SubFieldType : DebugTypeDef<"SubField"> {
let mnemonic = "subfield";
let summary = "debug variable subfield of an aggregate";
let description = "The result of a `dbg.subfield` operation.";
}

def EnumDefType : DebugTypeDef<"EnumDef"> {
let mnemonic = "enumdef";
let summary = "debug enum definition";
let description = "The result of a `dbg.enumdef` operation.";
}

#endif // CIRCT_DIALECT_DEBUG_DEBUGTYPES_TD
3 changes: 3 additions & 0 deletions include/circt/Dialect/FIRRTL/AnnotationDetails.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ constexpr const char *wiringSourceAnnoClass =
// Attribute annotations.
constexpr const char *attributeAnnoClass = "firrtl.AttributeAnnotation";

// Tywaves Annotation: contains info about source level chisel extra information
constexpr const char *tywavesAnnoClass = "chisel3.tywavesinternal.TywavesAnnotation";

} // namespace firrtl
} // namespace circt

Expand Down
3 changes: 2 additions & 1 deletion include/circt/Dialect/FIRRTL/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ std::unique_ptr<mlir::Pass>
createLowerFIRRTLAnnotationsPass(bool ignoreUnhandledAnnotations = false,
bool ignoreClasslessAnnotations = false,
bool noRefTypePorts = false,
bool allowAddingPortsOnPublic = false);
bool allowAddingPortsOnPublic = false,
bool shouldEnableDebugInfo = false);

std::unique_ptr<mlir::Pass> createLowerOpenAggsPass();

Expand Down
2 changes: 2 additions & 0 deletions include/circt/Dialect/FIRRTL/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def LowerFIRRTLAnnotations : Pass<"firrtl-lower-annotations", "firrtl::CircuitOp
"Create normal ports, not ref type ports.">,
Option<"allowAddingPortsOnPublic", "allow-adding-ports-on-public-modules", "bool", "false",
"Allow public modules to gain additional ports as a result of wiring.">,
Option<"shouldEnableDebugInfo", "should-enable-debug-info", "bool", "true",
"Enable to consume debug annotations.">
];
let dependentDialects = ["hw::HWDialect"];
let statistics = [
Expand Down
37 changes: 36 additions & 1 deletion lib/Analysis/DebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ void DebugInfoBuilder::visitRoot(Operation *op) {
<< "Collect DI for module " << moduleOp.getNameAttr() << "\n");
auto &module = getOrCreateModule(moduleOp.getNameAttr());
module.op = op;
visitModule(moduleOp, module);

// TODO: add source language type info to the module (similarly to
// variables)

visitModule(moduleOp, module); // Visit the module
return WalkResult::skip();
}

Expand Down Expand Up @@ -103,6 +107,25 @@ void DebugInfoBuilder::visitModule(hw::HWModuleOp moduleOp, DIModule &module) {
node->op = scopeOp;
scopes.insert({scopeOp, node});
}

// Add the source language information to the module
if (auto sourceLangType = dyn_cast<debug::ModuleInfoOp>(op)) {
module.sourceLangType.typeName = sourceLangType.getTypeNameAttr();
module.sourceLangType.params = sourceLangType.getParamsAttr();
}

// Add enum definitions to the module if any
if (auto e = dyn_cast<debug::EnumDefOp>(op)) {
SmallDenseMap<int64_t, StringAttr> enumMap;

// Collect the enum map
for (auto na : e.getVariantsMapAttr()) {
auto key = na.getValue().cast<IntegerAttr>().getInt();
auto value = na.getName().cast<StringAttr>();
enumMap.insert({key, value});
}
module.enumDefinitions.insert({e.getId(), enumMap});
}
});

// Helper function to resolve a `scope` operand on a variable to the
Expand Down Expand Up @@ -139,7 +162,19 @@ void DebugInfoBuilder::visitModule(hw::HWModuleOp moduleOp, DIModule &module) {
var->name = varOp.getNameAttr();
var->loc = varOp.getLoc();
var->value = varOp.getValue();

// Attach to the variable the type information from the source language
// type information.
var->sourceLangType.typeName = varOp.getTypeNameAttr();
var->sourceLangType.params = varOp.getParamsAttr();

getScope(varOp.getScope()).variables.push_back(var);

// Add enum definition to the variable if any
if (auto e = varOp.getEnumDef())
if (auto enumDef = e.getDefiningOp<debug::EnumDefOp>()) {
var->enumDefRef = enumDef.getId();
}
return;
}

Expand Down
Loading