diff --git a/include/circt/Analysis/DebugAnalysis.h b/include/circt/Analysis/DebugAnalysis.h new file mode 100644 index 0000000000..9d6fc044b4 --- /dev/null +++ b/include/circt/Analysis/DebugAnalysis.h @@ -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 debugOps; + DenseSet debugValues; + DenseSet debugOperands; +}; + +} // namespace circt + +#endif // CIRCT_ANALYSIS_DEBUGANALYSIS_H diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 43714e06db..832ee132c0 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -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 ) diff --git a/lib/Analysis/DebugAnalysis.cpp b/lib/Analysis/DebugAnalysis.cpp new file mode 100644 index 0000000000..0725ea1a13 --- /dev/null +++ b/lib/Analysis/DebugAnalysis.cpp @@ -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 worklist; + + DenseSet debugOps; + DenseSet debugValues; + DenseSet 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(op->getDialect())) { + addDebugOp(op); + return; + } + for (auto ®ion : op->getRegions()) + for (auto &block : region) + for (auto arg : block.getArguments()) + if (isa(arg.getType().getDialect())) + addDebugValue(arg); + for (auto result : op->getResults()) + if (isa(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(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); +} diff --git a/lib/Analysis/TestPasses.cpp b/lib/Analysis/TestPasses.cpp index 7c1867829c..9aa447d423 100644 --- a/lib/Analysis/TestPasses.cpp +++ b/lib/Analysis/TestPasses.cpp @@ -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> { + 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(); + 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 { return std::make_unique(); }); - mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> { + registerPass([]() -> std::unique_ptr { return std::make_unique(); }); - mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> { + registerPass([]() -> std::unique_ptr { + return std::make_unique(); + }); + registerPass([]() -> std::unique_ptr { return std::make_unique(); }); } diff --git a/lib/Target/DebugInfo/CMakeLists.txt b/lib/Target/DebugInfo/CMakeLists.txt index a50b2eda37..ed99311fe9 100644 --- a/lib/Target/DebugInfo/CMakeLists.txt +++ b/lib/Target/DebugInfo/CMakeLists.txt @@ -9,8 +9,9 @@ add_circt_translation_library(CIRCTTargetDebugInfo LINK_LIBS PUBLIC CIRCTComb CIRCTDebug - CIRCTDebugInfoAnalysis + CIRCTDebugAnalysis CIRCTHW + CIRCTOM CIRCTSeq CIRCTSupport CIRCTSV diff --git a/lib/Target/DebugInfo/TranslateRegistration.cpp b/lib/Target/DebugInfo/TranslateRegistration.cpp index ece88ed33b..ea054e32ed 100644 --- a/lib/Target/DebugInfo/TranslateRegistration.cpp +++ b/lib/Target/DebugInfo/TranslateRegistration.cpp @@ -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(); registry.insert(); registry.insert(); + registry.insert(); } void registerDumpTranslation() { diff --git a/test/Analysis/debug-analysis.mlir b/test/Analysis/debug-analysis.mlir new file mode 100644 index 0000000000..03e793c800 --- /dev/null +++ b/test/Analysis/debug-analysis.mlir @@ -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 +}