mirror of https://github.com/llvm/circt.git
191 lines
6.0 KiB
C++
191 lines
6.0 KiB
C++
//===- DebugInfo.cpp - Debug info analysis --------------------------------===//
|
|
//
|
|
// 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/Analysis/DebugInfo.h"
|
|
#include "circt/Dialect/Debug/DebugOps.h"
|
|
#include "circt/Dialect/HW/HWOps.h"
|
|
#include "mlir/IR/BuiltinOps.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#define DEBUG_TYPE "di"
|
|
|
|
using namespace mlir;
|
|
using namespace circt;
|
|
|
|
namespace circt {
|
|
namespace detail {
|
|
|
|
/// Helper to populate a `DebugInfo` with nodes.
|
|
struct DebugInfoBuilder {
|
|
DebugInfoBuilder(DebugInfo &di) : di(di) {}
|
|
DebugInfo &di;
|
|
|
|
void visitRoot(Operation *op);
|
|
void visitModule(hw::HWModuleOp moduleOp, DIModule &module);
|
|
|
|
DIModule *createModule() {
|
|
return new (di.moduleAllocator.Allocate()) DIModule;
|
|
}
|
|
|
|
DIInstance *createInstance() {
|
|
return new (di.instanceAllocator.Allocate()) DIInstance;
|
|
}
|
|
|
|
DIVariable *createVariable() {
|
|
return new (di.variableAllocator.Allocate()) DIVariable;
|
|
}
|
|
|
|
DIModule &getOrCreateModule(StringAttr moduleName) {
|
|
auto &slot = di.moduleNodes[moduleName];
|
|
if (!slot) {
|
|
slot = createModule();
|
|
slot->name = moduleName;
|
|
}
|
|
return *slot;
|
|
}
|
|
};
|
|
|
|
void DebugInfoBuilder::visitRoot(Operation *op) {
|
|
op->walk<WalkOrder::PreOrder>([&](Operation *op) {
|
|
if (auto moduleOp = dyn_cast<hw::HWModuleOp>(op)) {
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
<< "Collect DI for module " << moduleOp.getNameAttr() << "\n");
|
|
auto &module = getOrCreateModule(moduleOp.getNameAttr());
|
|
module.op = op;
|
|
visitModule(moduleOp, module);
|
|
return WalkResult::skip();
|
|
}
|
|
|
|
if (auto moduleOp = dyn_cast<hw::HWModuleExternOp>(op)) {
|
|
LLVM_DEBUG(llvm::dbgs() << "Collect DI for extern module "
|
|
<< moduleOp.getNameAttr() << "\n");
|
|
auto &module = getOrCreateModule(moduleOp.getNameAttr());
|
|
module.op = op;
|
|
module.isExtern = true;
|
|
|
|
// Add variables for each of the ports.
|
|
for (auto &port : moduleOp.getPortList()) {
|
|
auto *var = createVariable();
|
|
var->name = port.name;
|
|
var->loc = port.loc;
|
|
module.variables.push_back(var);
|
|
}
|
|
|
|
return WalkResult::skip();
|
|
}
|
|
|
|
return WalkResult::advance();
|
|
});
|
|
}
|
|
|
|
void DebugInfoBuilder::visitModule(hw::HWModuleOp moduleOp, DIModule &module) {
|
|
// Try to gather debug info from debug ops in the module. If we find any,
|
|
// return. Otherwise collect ports, instances, and variables as a
|
|
// fallback.
|
|
|
|
// Check what kind of DI is present in the module. Also create additional
|
|
// `DIModule` hierarchy levels for each explicit scope op in the module.
|
|
SmallDenseMap<debug::ScopeOp, DIModule *> scopes;
|
|
bool hasVariables = false;
|
|
bool hasInstances = false;
|
|
moduleOp.walk([&](Operation *op) {
|
|
if (isa<debug::VariableOp>(op))
|
|
hasVariables = true;
|
|
if (auto scopeOp = dyn_cast<debug::ScopeOp>(op)) {
|
|
auto *node = createModule();
|
|
node->isInline = true;
|
|
node->name = scopeOp.getModuleNameAttr();
|
|
node->op = scopeOp;
|
|
scopes.insert({scopeOp, node});
|
|
}
|
|
});
|
|
|
|
// Helper function to resolve a `scope` operand on a variable to the
|
|
// `DIModule` into which the variable should be collected. If the `scope` is
|
|
// not set, or it isn't a valid `dbg.scope` op, returns the `module` argument
|
|
// of this function.
|
|
auto getScope = [&](Value scopeValue) -> DIModule & {
|
|
if (scopeValue)
|
|
if (auto scopeOp = scopeValue.getDefiningOp<debug::ScopeOp>())
|
|
return *scopes.lookup(scopeOp);
|
|
return module;
|
|
};
|
|
|
|
// If the module has no DI for variables, add variables for each of the ports
|
|
// as a fallback.
|
|
if (!hasVariables) {
|
|
auto inputValues = moduleOp.getBody().getArguments();
|
|
auto outputValues = moduleOp.getBodyBlock()->getTerminator()->getOperands();
|
|
for (auto &port : moduleOp.getPortList()) {
|
|
auto value = port.isOutput() ? outputValues[port.argNum]
|
|
: inputValues[port.argNum];
|
|
auto *var = createVariable();
|
|
var->name = port.name;
|
|
var->loc = port.loc;
|
|
var->value = value;
|
|
module.variables.push_back(var);
|
|
}
|
|
}
|
|
|
|
// Fill in any missing DI as a fallback.
|
|
moduleOp->walk([&](Operation *op) {
|
|
if (auto varOp = dyn_cast<debug::VariableOp>(op)) {
|
|
auto *var = createVariable();
|
|
var->name = varOp.getNameAttr();
|
|
var->loc = varOp.getLoc();
|
|
var->value = varOp.getValue();
|
|
getScope(varOp.getScope()).variables.push_back(var);
|
|
return;
|
|
}
|
|
|
|
if (auto scopeOp = dyn_cast<debug::ScopeOp>(op)) {
|
|
auto *instance = createInstance();
|
|
instance->name = scopeOp.getInstanceNameAttr();
|
|
instance->op = scopeOp;
|
|
instance->module = scopes.lookup(scopeOp);
|
|
getScope(scopeOp.getScope()).instances.push_back(instance);
|
|
}
|
|
|
|
// Fallback if the module has no DI for its instances.
|
|
if (!hasInstances) {
|
|
if (auto instOp = dyn_cast<hw::InstanceOp>(op)) {
|
|
auto &childModule =
|
|
getOrCreateModule(instOp.getModuleNameAttr().getAttr());
|
|
auto *instance = createInstance();
|
|
instance->name = instOp.getInstanceNameAttr();
|
|
instance->op = instOp;
|
|
instance->module = &childModule;
|
|
module.instances.push_back(instance);
|
|
|
|
// TODO: What do we do with the port assignments? These should be
|
|
// tracked somewhere.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Fallback if the module has no DI for its variables.
|
|
if (!hasVariables) {
|
|
if (auto wireOp = dyn_cast<hw::WireOp>(op)) {
|
|
auto *var = createVariable();
|
|
var->name = wireOp.getNameAttr();
|
|
var->loc = wireOp.getLoc();
|
|
var->value = wireOp;
|
|
module.variables.push_back(var);
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace circt
|
|
|
|
DebugInfo::DebugInfo(Operation *op) : operation(op) {
|
|
detail::DebugInfoBuilder(*this).visitRoot(op);
|
|
}
|