mirror of https://github.com/llvm/circt.git
[Debug] Add debug-only value/op analysis (#6335)
Add the `DebugAnalysis` which marks values and operations that only feed into debug info. This will later allow ExportVerilog to skip expressions and statements that are exclusively used by debug ops and are irrelevant for synthesis and simulation. The analysis is fairly straightforward at the moment. In the future, we may want to be more clever about what we mark as debug-only. For example, certain debug info output formats may allow us to emit entire expressions and state machines, such that those can be stripped from the Verilog output. In that case, we'd want to look through expressions, wires, and registers when marking ops as debug-only. But some other formats may only allow us to point at named signals in the Verilog, such that any expression or register has to remain in Verilog, and possibly be visible under a name for the debug info to make use of it. This commit also bundles the `DebugInfo` analysis, which extracts the source language hierarchy and variable layout from the IR, together with this new `DebugAnalysis`, which marks ops as debug-only.
This commit is contained in:
parent
34c320f474
commit
a50b0bb1cc
|
@ -0,0 +1,34 @@
|
|||
//===- DebugAnalysis.h ----------------------------------------------------===//
|
||||
//
|
||||
// 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_ANALYSIS_DEBUGANALYSIS_H
|
||||
#define CIRCT_ANALYSIS_DEBUGANALYSIS_H
|
||||
|
||||
#include "circt/Support/LLVM.h"
|
||||
#include "mlir/IR/Value.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
|
||||
namespace mlir {
|
||||
class Operation;
|
||||
class OpOperand;
|
||||
} // namespace mlir
|
||||
|
||||
namespace circt {
|
||||
|
||||
/// Identify operations and values that are only used for debug info.
|
||||
struct DebugAnalysis {
|
||||
DebugAnalysis(Operation *op);
|
||||
|
||||
DenseSet<Operation *> debugOps;
|
||||
DenseSet<Value> debugValues;
|
||||
DenseSet<OpOperand *> debugOperands;
|
||||
};
|
||||
|
||||
} // namespace circt
|
||||
|
||||
#endif // CIRCT_ANALYSIS_DEBUGANALYSIS_H
|
|
@ -1,9 +1,21 @@
|
|||
set(LLVM_OPTIONAL_SOURCES
|
||||
DebugAnalysis.cpp
|
||||
DebugInfo.cpp
|
||||
DependenceAnalysis.cpp
|
||||
SchedulingAnalysis.cpp
|
||||
TestPasses.cpp
|
||||
)
|
||||
)
|
||||
|
||||
add_circt_library(CIRCTDebugAnalysis
|
||||
DebugAnalysis.cpp
|
||||
DebugInfo.cpp
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
CIRCTComb
|
||||
CIRCTDebug
|
||||
CIRCTHW
|
||||
MLIRIR
|
||||
)
|
||||
|
||||
add_circt_library(CIRCTDependenceAnalysis
|
||||
DependenceAnalysis.cpp
|
||||
|
@ -12,7 +24,7 @@ add_circt_library(CIRCTDependenceAnalysis
|
|||
MLIRIR
|
||||
MLIRAffineUtils
|
||||
MLIRTransformUtils
|
||||
)
|
||||
)
|
||||
|
||||
add_circt_library(CIRCTSchedulingAnalysis
|
||||
SchedulingAnalysis.cpp
|
||||
|
@ -22,23 +34,15 @@ add_circt_library(CIRCTSchedulingAnalysis
|
|||
MLIRIR
|
||||
CIRCTDependenceAnalysis
|
||||
CIRCTScheduling
|
||||
)
|
||||
)
|
||||
|
||||
add_circt_library(CIRCTAnalysisTestPasses
|
||||
TestPasses.cpp
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
CIRCTDebugAnalysis
|
||||
CIRCTDependenceAnalysis
|
||||
CIRCTSchedulingAnalysis
|
||||
CIRCTHW
|
||||
MLIRPass
|
||||
)
|
||||
|
||||
add_circt_library(CIRCTDebugInfoAnalysis
|
||||
DebugInfo.cpp
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
CIRCTDebug
|
||||
CIRCTHW
|
||||
MLIRIR
|
||||
)
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
//===- DebugAnalysis.cpp --------------------------------------------------===//
|
||||
//
|
||||
// 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/DebugAnalysis.h"
|
||||
#include "circt/Dialect/Comb/CombOps.h"
|
||||
#include "circt/Dialect/Debug/DebugOps.h"
|
||||
#include "circt/Dialect/HW/HWOps.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace circt;
|
||||
using namespace debug;
|
||||
using namespace mlir;
|
||||
|
||||
namespace {
|
||||
struct DebugAnalysisBuilder {
|
||||
DebugAnalysisBuilder(Operation *rootOp) : rootOp(rootOp) {}
|
||||
void run();
|
||||
void addDebugOp(Operation *op);
|
||||
void addDebugValue(Value value);
|
||||
void addDebugOperand(OpOperand *operand);
|
||||
void maybeDebugOp(Operation *op);
|
||||
|
||||
Operation *rootOp;
|
||||
SetVector<Operation *> worklist;
|
||||
|
||||
DenseSet<Operation *> debugOps;
|
||||
DenseSet<Value> debugValues;
|
||||
DenseSet<OpOperand *> debugOperands;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void DebugAnalysisBuilder::run() {
|
||||
// Find all debug ops nested under the root op and mark them as debug-only
|
||||
// to kickstart the analysis.
|
||||
rootOp->walk([&](Operation *op) {
|
||||
if (isa<debug::DebugDialect>(op->getDialect())) {
|
||||
addDebugOp(op);
|
||||
return;
|
||||
}
|
||||
for (auto ®ion : op->getRegions())
|
||||
for (auto &block : region)
|
||||
for (auto arg : block.getArguments())
|
||||
if (isa<debug::DebugDialect>(arg.getType().getDialect()))
|
||||
addDebugValue(arg);
|
||||
for (auto result : op->getResults())
|
||||
if (isa<debug::DebugDialect>(result.getType().getDialect()))
|
||||
addDebugValue(result);
|
||||
});
|
||||
|
||||
// Visit operations and check if all their operands or all their uses are
|
||||
// marked as debug-only. If they are, mark the op itself as debug-only.
|
||||
while (!worklist.empty()) {
|
||||
auto *op = worklist.pop_back_val();
|
||||
if (debugOps.contains(op))
|
||||
continue;
|
||||
|
||||
// Do not propagate through stateful elements. This should probably be
|
||||
// configurable, since certain forms of debug info extraction would be able
|
||||
// to pull entire state machines out of the design. For now this just
|
||||
// represents the common denominator across all debug infos.
|
||||
if (!isa<hw::HWDialect, comb::CombDialect>(op->getDialect()))
|
||||
continue;
|
||||
if (op->hasAttr("name"))
|
||||
continue;
|
||||
|
||||
if (op->getNumResults() > 0) {
|
||||
auto allUsesDebug = llvm::all_of(op->getUses(), [&](auto &use) {
|
||||
return debugOperands.contains(&use);
|
||||
});
|
||||
if (allUsesDebug) {
|
||||
addDebugOp(op);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (op->getNumOperands() > 0) {
|
||||
auto allOperandsDebug =
|
||||
llvm::all_of(op->getOperands(), [&](auto operand) {
|
||||
return debugValues.contains(operand);
|
||||
});
|
||||
if (allOperandsDebug) {
|
||||
addDebugOp(op);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugAnalysisBuilder::addDebugOp(Operation *op) {
|
||||
if (debugOps.insert(op).second) {
|
||||
for (auto &operand : op->getOpOperands())
|
||||
addDebugOperand(&operand);
|
||||
for (auto result : op->getResults())
|
||||
addDebugValue(result);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugAnalysisBuilder::addDebugValue(Value value) {
|
||||
if (debugValues.insert(value).second) {
|
||||
for (auto *user : value.getUsers())
|
||||
maybeDebugOp(user);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugAnalysisBuilder::addDebugOperand(OpOperand *operand) {
|
||||
if (debugOperands.insert(operand).second)
|
||||
maybeDebugOp(operand->get().getDefiningOp());
|
||||
}
|
||||
|
||||
void DebugAnalysisBuilder::maybeDebugOp(Operation *op) {
|
||||
if (!op || debugOps.contains(op))
|
||||
return;
|
||||
worklist.insert(op);
|
||||
}
|
||||
|
||||
DebugAnalysis::DebugAnalysis(Operation *op) {
|
||||
DebugAnalysisBuilder builder(op);
|
||||
builder.run();
|
||||
debugOps = std::move(builder.debugOps);
|
||||
debugValues = std::move(builder.debugValues);
|
||||
debugOperands = std::move(builder.debugOperands);
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Analysis/DebugAnalysis.h"
|
||||
#include "circt/Analysis/DependenceAnalysis.h"
|
||||
#include "circt/Analysis/SchedulingAnalysis.h"
|
||||
#include "circt/Dialect/HW/HWInstanceGraph.h"
|
||||
|
@ -18,15 +19,42 @@
|
|||
#include "mlir/Dialect/Affine/IR/AffineOps.h"
|
||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/IR/Value.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace mlir::affine;
|
||||
using namespace circt;
|
||||
using namespace circt::analysis;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DependenceAnalysis passes.
|
||||
// DebugAnalysis
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct TestDebugAnalysisPass
|
||||
: public PassWrapper<TestDebugAnalysisPass, OperationPass<mlir::ModuleOp>> {
|
||||
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestDebugAnalysisPass)
|
||||
|
||||
void runOnOperation() override;
|
||||
StringRef getArgument() const override { return "test-debug-analysis"; }
|
||||
StringRef getDescription() const override {
|
||||
return "Perform debug analysis and emit results as attributes";
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void TestDebugAnalysisPass::runOnOperation() {
|
||||
auto *context = &getContext();
|
||||
auto &analysis = getAnalysis<DebugAnalysis>();
|
||||
for (auto *op : analysis.debugOps) {
|
||||
op->setAttr("debug.only", UnitAttr::get(context));
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DependenceAnalysis
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
@ -77,7 +105,7 @@ void TestDependenceAnalysisPass::runOnOperation() {
|
|||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DependenceAnalysis passes.
|
||||
// SchedulingAnalysis
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
@ -114,7 +142,7 @@ void TestSchedulingAnalysisPass::runOnOperation() {
|
|||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InferTopModule passes.
|
||||
// InstanceGraph
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
@ -154,13 +182,16 @@ void InferTopModulePass::runOnOperation() {
|
|||
namespace circt {
|
||||
namespace test {
|
||||
void registerAnalysisTestPasses() {
|
||||
mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
registerPass([]() -> std::unique_ptr<Pass> {
|
||||
return std::make_unique<TestDependenceAnalysisPass>();
|
||||
});
|
||||
mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
registerPass([]() -> std::unique_ptr<Pass> {
|
||||
return std::make_unique<TestSchedulingAnalysisPass>();
|
||||
});
|
||||
mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
registerPass([]() -> std::unique_ptr<Pass> {
|
||||
return std::make_unique<TestDebugAnalysisPass>();
|
||||
});
|
||||
registerPass([]() -> std::unique_ptr<Pass> {
|
||||
return std::make_unique<InferTopModulePass>();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ add_circt_translation_library(CIRCTTargetDebugInfo
|
|||
LINK_LIBS PUBLIC
|
||||
CIRCTComb
|
||||
CIRCTDebug
|
||||
CIRCTDebugInfoAnalysis
|
||||
CIRCTDebugAnalysis
|
||||
CIRCTHW
|
||||
CIRCTOM
|
||||
CIRCTSeq
|
||||
CIRCTSupport
|
||||
CIRCTSV
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "circt/Dialect/Comb/CombDialect.h"
|
||||
#include "circt/Dialect/Debug/DebugDialect.h"
|
||||
#include "circt/Dialect/HW/HWDialect.h"
|
||||
#include "circt/Dialect/OM/OMDialect.h"
|
||||
#include "circt/Dialect/SV/SVDialect.h"
|
||||
#include "circt/Dialect/Seq/SeqDialect.h"
|
||||
#include "circt/Target/DebugInfo.h"
|
||||
|
@ -26,6 +27,7 @@ static void registerDialects(DialectRegistry ®istry) {
|
|||
registry.insert<hw::HWDialect>();
|
||||
registry.insert<seq::SeqDialect>();
|
||||
registry.insert<sv::SVDialect>();
|
||||
registry.insert<om::OMDialect>();
|
||||
}
|
||||
|
||||
void registerDumpTranslation() {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// RUN: circt-opt %s --test-debug-analysis | FileCheck %s
|
||||
|
||||
// CHECK-LABEL: @Foo(
|
||||
hw.module @Foo(out z: i42) {
|
||||
// CHECK: hw.constant 0 : i42 {debug.only}
|
||||
// CHECK: dbg.variable "a", {{%.+}} {debug.only}
|
||||
%c0_i42 = hw.constant 0 : i42
|
||||
dbg.variable "a", %c0_i42 : i42
|
||||
|
||||
// CHECK: hw.constant 1 : i42
|
||||
// CHECK-NOT: debug.only
|
||||
// CHECK: dbg.variable "b", {{%.+}} {debug.only}
|
||||
%c1_i42 = hw.constant 1 : i42
|
||||
dbg.variable "b", %c1_i42 : i42
|
||||
|
||||
hw.output %c1_i42 : i42
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @Empty(
|
||||
// CHECK-NOT: debug.only
|
||||
hw.module @Empty() {}
|
||||
|
||||
// CHECK-LABEL: @DebugOnlyBody(
|
||||
hw.module @DebugOnlyBody(in %a: i1, in %b: i1) {
|
||||
// CHECK: comb.and {{.+}} {debug.only}
|
||||
// CHECK: dbg.struct {{.+}} {debug.only}
|
||||
// CHECK: dbg.variable "a", {{%.+}} {debug.only}
|
||||
// CHECK: dbg.variable "b", {{%.+}} {debug.only}
|
||||
// CHECK: dbg.variable "c", {{%.+}} {debug.only}
|
||||
%0 = comb.and %a, %b : i1
|
||||
%1 = dbg.struct {"x": %0} : i1
|
||||
dbg.variable "a", %a : i1
|
||||
dbg.variable "b", %b : i1
|
||||
dbg.variable "c", %1 : !dbg.struct
|
||||
}
|
Loading…
Reference in New Issue