CIRCT  19.0.0git
DebugInfo.cpp
Go to the documentation of this file.
1 //===- DebugInfo.cpp - Debug info analysis --------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
11 #include "circt/Dialect/HW/HWOps.h"
12 #include "mlir/IR/BuiltinOps.h"
13 #include "llvm/Support/Debug.h"
14 
15 #define DEBUG_TYPE "di"
16 
17 using namespace mlir;
18 using namespace circt;
19 
20 namespace circt {
21 namespace detail {
22 
23 /// Helper to populate a `DebugInfo` with nodes.
25  DebugInfoBuilder(DebugInfo &di) : di(di) {}
27 
28  void visitRoot(Operation *op);
29  void visitModule(hw::HWModuleOp moduleOp, DIModule &module);
30 
32  return new (di.moduleAllocator.Allocate()) DIModule;
33  }
34 
36  return new (di.instanceAllocator.Allocate()) DIInstance;
37  }
38 
40  return new (di.variableAllocator.Allocate()) DIVariable;
41  }
42 
43  DIModule &getOrCreateModule(StringAttr moduleName) {
44  auto &slot = di.moduleNodes[moduleName];
45  if (!slot) {
46  slot = createModule();
47  slot->name = moduleName;
48  }
49  return *slot;
50  }
51 };
52 
53 void DebugInfoBuilder::visitRoot(Operation *op) {
54  op->walk<WalkOrder::PreOrder>([&](Operation *op) {
55  if (auto moduleOp = dyn_cast<hw::HWModuleOp>(op)) {
56  LLVM_DEBUG(llvm::dbgs()
57  << "Collect DI for module " << moduleOp.getNameAttr() << "\n");
58  auto &module = getOrCreateModule(moduleOp.getNameAttr());
59  module.op = op;
60  visitModule(moduleOp, module);
61  return WalkResult::skip();
62  }
63 
64  if (auto moduleOp = dyn_cast<hw::HWModuleExternOp>(op)) {
65  LLVM_DEBUG(llvm::dbgs() << "Collect DI for extern module "
66  << moduleOp.getNameAttr() << "\n");
67  auto &module = getOrCreateModule(moduleOp.getNameAttr());
68  module.op = op;
69  module.isExtern = true;
70 
71  // Add variables for each of the ports.
72  for (auto &port : moduleOp.getPortList()) {
73  auto *var = createVariable();
74  var->name = port.name;
75  var->loc = port.loc;
76  module.variables.push_back(var);
77  }
78 
79  return WalkResult::skip();
80  }
81 
82  return WalkResult::advance();
83  });
84 }
85 
86 void DebugInfoBuilder::visitModule(hw::HWModuleOp moduleOp, DIModule &module) {
87  // Try to gather debug info from debug ops in the module. If we find any,
88  // return. Otherwise collect ports, instances, and variables as a
89  // fallback.
90 
91  // Check what kind of DI is present in the module. Also create additional
92  // `DIModule` hierarchy levels for each explicit scope op in the module.
94  bool hasVariables = false;
95  bool hasInstances = false;
96  moduleOp.walk([&](Operation *op) {
97  if (isa<debug::VariableOp>(op))
98  hasVariables = true;
99  if (auto scopeOp = dyn_cast<debug::ScopeOp>(op)) {
100  auto *node = createModule();
101  node->isInline = true;
102  node->name = scopeOp.getModuleNameAttr();
103  node->op = scopeOp;
104  scopes.insert({scopeOp, node});
105  }
106  });
107 
108  // Helper function to resolve a `scope` operand on a variable to the
109  // `DIModule` into which the variable should be collected. If the `scope` is
110  // not set, or it isn't a valid `dbg.scope` op, returns the `module` argument
111  // of this function.
112  auto getScope = [&](Value scopeValue) -> DIModule & {
113  if (scopeValue)
114  if (auto scopeOp = scopeValue.getDefiningOp<debug::ScopeOp>())
115  return *scopes.lookup(scopeOp);
116  return module;
117  };
118 
119  // If the module has no DI for variables, add variables for each of the ports
120  // as a fallback.
121  if (!hasVariables) {
122  auto inputValues = moduleOp.getBody().getArguments();
123  auto outputValues = moduleOp.getBodyBlock()->getTerminator()->getOperands();
124  for (auto &port : moduleOp.getPortList()) {
125  auto value = port.isOutput() ? outputValues[port.argNum]
126  : inputValues[port.argNum];
127  auto *var = createVariable();
128  var->name = port.name;
129  var->loc = port.loc;
130  var->value = value;
131  module.variables.push_back(var);
132  }
133  }
134 
135  // Fill in any missing DI as a fallback.
136  moduleOp->walk([&](Operation *op) {
137  if (auto varOp = dyn_cast<debug::VariableOp>(op)) {
138  auto *var = createVariable();
139  var->name = varOp.getNameAttr();
140  var->loc = varOp.getLoc();
141  var->value = varOp.getValue();
142  getScope(varOp.getScope()).variables.push_back(var);
143  return;
144  }
145 
146  if (auto scopeOp = dyn_cast<debug::ScopeOp>(op)) {
147  auto *instance = createInstance();
148  instance->name = scopeOp.getInstanceNameAttr();
149  instance->op = scopeOp;
150  instance->module = scopes.lookup(scopeOp);
151  getScope(scopeOp.getScope()).instances.push_back(instance);
152  }
153 
154  // Fallback if the module has no DI for its instances.
155  if (!hasInstances) {
156  if (auto instOp = dyn_cast<hw::InstanceOp>(op)) {
157  auto &childModule =
158  getOrCreateModule(instOp.getModuleNameAttr().getAttr());
159  auto *instance = createInstance();
160  instance->name = instOp.getInstanceNameAttr();
161  instance->op = instOp;
162  instance->module = &childModule;
163  module.instances.push_back(instance);
164 
165  // TODO: What do we do with the port assignments? These should be
166  // tracked somewhere.
167  return;
168  }
169  }
170 
171  // Fallback if the module has no DI for its variables.
172  if (!hasVariables) {
173  if (auto wireOp = dyn_cast<hw::WireOp>(op)) {
174  auto *var = createVariable();
175  var->name = wireOp.getNameAttr();
176  var->loc = wireOp.getLoc();
177  var->value = wireOp;
178  module.variables.push_back(var);
179  return;
180  }
181  }
182  });
183 }
184 
185 } // namespace detail
186 } // namespace circt
187 
188 DebugInfo::DebugInfo(Operation *op) : operation(op) {
190 }
calyx::InstanceOp createInstance(Location loc, OpBuilder &builder, ComponentOp component, SmallVectorImpl< Type > &resultTypes, StringRef instanceName, StringRef componentName)
A helper function to create calyx.instance operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
SmallVector< DIInstance *, 0 > instances
Levels of hierarchy nested under this module.
Definition: DebugInfo.h:32
SmallVector< DIVariable *, 0 > variables
Variables declared within this module.
Definition: DebugInfo.h:34
Debug information attached to an operation and the operations nested within.
Definition: DebugInfo.h:63
llvm::SpecificBumpPtrAllocator< DIInstance > instanceAllocator
Definition: DebugInfo.h:75
llvm::SpecificBumpPtrAllocator< DIModule > moduleAllocator
Definition: DebugInfo.h:74
llvm::SpecificBumpPtrAllocator< DIVariable > variableAllocator
Definition: DebugInfo.h:76
llvm::MapVector< StringAttr, DIModule * > moduleNodes
A mapping from module name to module debug info.
Definition: DebugInfo.h:70
Helper to populate a DebugInfo with nodes.
Definition: DebugInfo.cpp:24
void visitRoot(Operation *op)
Definition: DebugInfo.cpp:53
DIModule & getOrCreateModule(StringAttr moduleName)
Definition: DebugInfo.cpp:43