[Moore] Drop named_constant op in favor of dbg.variable (#7624)

Remove the `NamedConstantOp` and replace its uses with `VariableOp` from
the debug dialect.

The op was originally added to track the value of constant parameters,
localparams, and specparams in the IR. In ImportVerilog, such parameters
would generate a corresponding `named_constant` op and all references to
the parameter by name would be replaced with the `named_constant`'s
result.

This doesn't really work well for parameters defined outside a module,
such as in packages or at the root of the Verilog source file. (Modules
are isolated from above, preventing the use of `named_constant`s from
outside the module.) Therefore expressions would generally fall back to
materializing constants directly where they were used.

Since the named constant ops are only there to track a constant value in
the IR for the user's debugging convenience, using the debug dialect
directly feels a lot more appropriate.
This commit is contained in:
Fabian Schuiki 2024-09-25 09:46:00 -07:00 committed by GitHub
parent 0ecbd6f3b8
commit 687f7fb38f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 163 additions and 210 deletions

View File

@ -459,7 +459,7 @@ def DetectEventOp : MooreOp<"detect_event", [
}
//===----------------------------------------------------------------------===//
// Expressions
// Constants
//===----------------------------------------------------------------------===//
def ConstantOp : MooreOp<"constant", [Pure, ConstantLike]> {
@ -476,37 +476,6 @@ def ConstantOp : MooreOp<"constant", [Pure, ConstantLike]> {
];
}
def Parameter : I32EnumAttrCase<"Parameter", 0, "parameter">;
def LocalParameter : I32EnumAttrCase<"LocalParameter", 1, "localparam">;
def SpecParameter : I32EnumAttrCase<"SpecParameter", 2, "specparam">;
def NamedConstAttr : I32EnumAttr<"NamedConst", "elaboration-time constants",
[Parameter, LocalParameter, SpecParameter]>{
let cppNamespace = "circt::moore";
}
def NamedConstantOp : MooreOp<"named_constant", [
SameOperandsAndResultType,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>
]>{
let summary = "An elaboration time constant expression";
let description = [{
Constants are named data objects that never change. SystemVerilog provides
three elaboration-time constants: parameter, localparam, and specparam.
See IEEE 1800-2017 § 6.20 "Constants".
}];
let arguments = (ins StrAttr:$name,
NamedConstAttr:$kind,
IntType:$value);
let results = (outs IntType:$result);
let assemblyFormat = [{
$kind
``custom<ImplicitSSAName>($name)
$value `:` type($result) attr-dict
}];
}
def StringConstantOp : MooreOp<"string_constant", [Pure]> {
let summary = "Produce a constant string value";
let description = [{
@ -522,6 +491,10 @@ def StringConstantOp : MooreOp<"string_constant", [Pure]> {
let assemblyFormat = "$value attr-dict `:` type($result)";
}
//===----------------------------------------------------------------------===//
// Expressions
//===----------------------------------------------------------------------===//
def ConversionOp : MooreOp<"conversion", [Pure]> {
let summary = "A type conversion";
let description = [{

View File

@ -40,6 +40,7 @@ add_circt_translation_library(CIRCTImportVerilog
slang_slang
LINK_LIBS PUBLIC
CIRCTDebug
CIRCTHW
CIRCTMoore
MLIRFuncDialect

View File

@ -264,8 +264,9 @@ LogicalResult ImportDriver::importVerilog(ModuleOp module) {
return success();
// Traverse the parsed Verilog AST and map it to the equivalent CIRCT ops.
mlirContext->loadDialect<moore::MooreDialect, hw::HWDialect,
cf::ControlFlowDialect, func::FuncDialect>();
mlirContext
->loadDialect<moore::MooreDialect, hw::HWDialect, cf::ControlFlowDialect,
func::FuncDialect, debug::DebugDialect>();
auto conversionTimer = ts.nest("Verilog to dialect mapping");
Context context(options, *compilation, module, driver.sourceManager,
bufferFilePaths);

View File

@ -11,6 +11,7 @@
#define CONVERSION_IMPORTVERILOG_IMPORTVERILOGINTERNALS_H
#include "circt/Conversion/ImportVerilog.h"
#include "circt/Dialect/Debug/DebugOps.h"
#include "circt/Dialect/HW/HWOps.h"
#include "circt/Dialect/Moore/MooreOps.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"

View File

@ -12,6 +12,21 @@
using namespace circt;
using namespace ImportVerilog;
//===----------------------------------------------------------------------===//
// Utilities
//===----------------------------------------------------------------------===//
static void guessNamespacePrefix(const slang::ast::Symbol &symbol,
SmallString<64> &prefix) {
if (symbol.kind != slang::ast::SymbolKind::Package)
return;
guessNamespacePrefix(symbol.getParentScope()->asSymbol(), prefix);
if (!symbol.name.empty()) {
prefix += symbol.name;
prefix += "::";
}
}
//===----------------------------------------------------------------------===//
// Base Visitor
//===----------------------------------------------------------------------===//
@ -20,6 +35,13 @@ namespace {
/// Base visitor which ignores AST nodes that are handled by Slang's name
/// resolution and type checking.
struct BaseVisitor {
Context &context;
Location loc;
OpBuilder &builder;
BaseVisitor(Context &context, Location loc)
: context(context), loc(loc), builder(context.builder) {}
// Skip semicolons.
LogicalResult visit(const slang::ast::EmptyMemberSymbol &) {
return success();
@ -41,6 +63,46 @@ struct BaseVisitor {
LogicalResult visit(const slang::ast::WildcardImportSymbol &) {
return success();
}
// Skip type parameters. The Slang AST is already monomorphized.
LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
return success();
}
// Handle parameters.
LogicalResult visit(const slang::ast::ParameterSymbol &param) {
visitParameter(param);
return success();
}
LogicalResult visit(const slang::ast::SpecparamSymbol &param) {
visitParameter(param);
return success();
}
template <class Node>
void visitParameter(const Node &param) {
// If debug info is enabled, try to materialize the parameter's constant
// value on a best-effort basis and create a `dbg.variable` to track the
// value.
if (!context.options.debugInfo)
return;
auto value =
context.materializeConstant(param.getValue(), param.getType(), loc);
if (!value)
return;
if (builder.getInsertionBlock()->getParentOp() == context.intoModuleOp)
context.orderedRootOps.insert({param.location, value.getDefiningOp()});
// Prefix the parameter name with the surrounding namespace to create
// somewhat sane names in the IR.
SmallString<64> paramName;
guessNamespacePrefix(param.getParentScope()->asSymbol(), paramName);
paramName += param.name;
builder.create<debug::VariableOp>(loc, builder.getStringAttr(paramName),
value, Value{});
}
};
} // namespace
@ -50,15 +112,9 @@ struct BaseVisitor {
namespace {
struct RootVisitor : public BaseVisitor {
using BaseVisitor::BaseVisitor;
using BaseVisitor::visit;
Context &context;
Location loc;
OpBuilder &builder;
RootVisitor(Context &context, Location loc)
: context(context), loc(loc), builder(context.builder) {}
// Handle packages.
LogicalResult visit(const slang::ast::PackageSymbol &package) {
return context.convertPackage(package);
@ -85,22 +141,9 @@ struct RootVisitor : public BaseVisitor {
namespace {
struct PackageVisitor : public BaseVisitor {
using BaseVisitor::BaseVisitor;
using BaseVisitor::visit;
Context &context;
Location loc;
OpBuilder &builder;
PackageVisitor(Context &context, Location loc)
: context(context), loc(loc), builder(context.builder) {}
// Ignore parameters. These are materialized on-the-fly as `ConstantOp`s.
LogicalResult visit(const slang::ast::ParameterSymbol &) { return success(); }
LogicalResult visit(const slang::ast::SpecparamSymbol &) { return success(); }
LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
return success();
}
// Handle functions and tasks.
LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
return context.convertFunction(subroutine);
@ -177,15 +220,9 @@ static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind) {
namespace {
struct ModuleVisitor : public BaseVisitor {
using BaseVisitor::BaseVisitor;
using BaseVisitor::visit;
Context &context;
Location loc;
OpBuilder &builder;
ModuleVisitor(Context &context, Location loc)
: context(context), loc(loc), builder(context.builder) {}
// Skip ports which are already handled by the module itself.
LogicalResult visit(const slang::ast::PortSymbol &) { return success(); }
LogicalResult visit(const slang::ast::MultiPortSymbol &) { return success(); }
@ -453,45 +490,6 @@ struct ModuleVisitor : public BaseVisitor {
return success();
}
// Handle parameters.
LogicalResult visit(const slang::ast::ParameterSymbol &paramNode) {
auto type = cast<moore::IntType>(context.convertType(paramNode.getType()));
if (!type)
return failure();
auto valueInt = paramNode.getValue().integer().as<uint64_t>().value();
Value value = builder.create<moore::ConstantOp>(loc, type, valueInt);
auto namedConstantOp = builder.create<moore::NamedConstantOp>(
loc, type, builder.getStringAttr(paramNode.name),
paramNode.isLocalParam()
? moore::NamedConstAttr::get(context.getContext(),
moore::NamedConst::LocalParameter)
: moore::NamedConstAttr::get(context.getContext(),
moore::NamedConst::Parameter),
value);
context.valueSymbols.insert(&paramNode, namedConstantOp);
return success();
}
// Handle specparam.
LogicalResult visit(const slang::ast::SpecparamSymbol &spNode) {
auto type = cast<moore::IntType>(context.convertType(spNode.getType()));
if (!type)
return failure();
auto valueInt = spNode.getValue().integer().as<uint64_t>().value();
Value value = builder.create<moore::ConstantOp>(loc, type, valueInt);
auto namedConstantOp = builder.create<moore::NamedConstantOp>(
loc, type, builder.getStringAttr(spNode.name),
moore::NamedConstAttr::get(context.getContext(),
moore::NamedConst::SpecParameter),
value);
context.valueSymbols.insert(&spNode, namedConstantOp);
return success();
}
// Handle generate block.
LogicalResult visit(const slang::ast::GenerateBlockSymbol &genNode) {
if (!genNode.isUninstantiated) {
@ -794,17 +792,6 @@ Context::convertPackage(const slang::ast::PackageSymbol &package) {
return success();
}
static void guessNamespacePrefix(const slang::ast::Symbol &symbol,
SmallString<64> &prefix) {
if (symbol.kind == slang::ast::SymbolKind::Root)
return;
guessNamespacePrefix(symbol.getParentScope()->asSymbol(), prefix);
if (!symbol.name.empty()) {
prefix += symbol.name;
prefix += "::";
}
}
/// Convert a function and its arguments to a function declaration in the IR.
/// This does not convert the function body.
FunctionLowering *

View File

@ -515,35 +515,6 @@ struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
}
};
struct NamedConstantOpConv : public OpConversionPattern<NamedConstantOp> {
using OpConversionPattern::OpConversionPattern;
LogicalResult
matchAndRewrite(NamedConstantOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type resultType = typeConverter->convertType(op.getResult().getType());
SmallString<32> symStr;
switch (op.getKind()) {
case NamedConst::Parameter:
symStr = "parameter";
break;
case NamedConst::LocalParameter:
symStr = "localparameter";
break;
case NamedConst::SpecParameter:
symStr = "specparameter";
break;
}
auto symAttr =
rewriter.getStringAttr(symStr + Twine("_") + adaptor.getName());
rewriter.replaceOpWithNewOp<hw::WireOp>(op, resultType, adaptor.getValue(),
op.getNameAttr(),
hw::InnerSymAttr::get(symAttr));
return success();
}
};
struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
using OpConversionPattern::OpConversionPattern;
LogicalResult
@ -1375,7 +1346,7 @@ static void populateOpConversion(RewritePatternSet &patterns,
// Patterns of miscellaneous operations.
ConstantOpConv, ConcatOpConversion, ReplicateOpConversion,
ExtractOpConversion, DynExtractOpConversion, DynExtractRefOpConversion,
ConversionOpConversion, ReadOpConversion, NamedConstantOpConv,
ConversionOpConversion, ReadOpConversion,
StructExtractOpConversion, StructExtractRefOpConversion,
ExtractRefOpConversion, StructCreateOpConversion, ConditionalOpConversion,
YieldOpConversion, OutputOpConversion,

View File

@ -633,14 +633,6 @@ OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
return getValueAttr();
}
//===----------------------------------------------------------------------===//
// NamedConstantOp
//===----------------------------------------------------------------------===//
void NamedConstantOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
setNameFn(getResult(), getName());
}
//===----------------------------------------------------------------------===//
// ConcatOp
//===----------------------------------------------------------------------===//

View File

@ -1,4 +1,4 @@
// RUN: circt-translate --import-verilog %s | FileCheck %s
// RUN: circt-verilog --parse-only -g %s | FileCheck %s
// RUN: circt-verilog --ir-moore %s
// REQUIRES: slang
@ -132,30 +132,6 @@ module Basic;
bit [0:0] b1;
bit b2 = b1;
// CHECK: [[TMP:%.+]] = moore.constant 1 : l32
// CHECK: %p1 = moore.named_constant parameter [[TMP]] : l32
parameter p1 = 1;
// CHECK: [[TMP:%.+]] = moore.constant 1 : l32
// CHECK: %p2 = moore.named_constant parameter [[TMP]] : l32
parameter p2 = p1;
// CHECK: [[TMP:%.+]] = moore.constant 2 : l32
// CHECK: %lp1 = moore.named_constant localparam [[TMP]] : l32
localparam lp1 = 2;
// CHECK: [[TMP:%.+]] = moore.constant 2 : l32
// CHECK: %lp2 = moore.named_constant localparam [[TMP]] : l32
localparam lp2 = lp1;
// CHECK: [[TMP:%.+]] = moore.constant 3 : l32
// CHECK: %sp1 = moore.named_constant specparam [[TMP]] : l32
specparam sp1 = 3;
// CHECK: [[TMP:%.+]] = moore.constant 3 : l32
// CHECK: %sp2 = moore.named_constant specparam [[TMP]] : l32
specparam sp2 = sp1;
// CHECK: moore.procedure initial {
// CHECK: }
initial;
@ -236,11 +212,62 @@ endmodule
// CHECK-LABEL: func.func private @dummyB(
// CHECK-LABEL: func.func private @dummyC(
// CHECK-LABEL: func.func private @dummyD(
// CHECK-LABEL: func.func private @dummyE(
// CHECK-LABEL: func.func private @dummyF(
function void dummyA(); endfunction
function void dummyB(); endfunction
function void dummyC(); endfunction
function void dummyD(int a); endfunction
function bit dummyE(bit a); return a; endfunction
function void dummyF(integer a); endfunction
// CHECK-LABEL: moore.module @Parameters(
module Parameters;
// CHECK: [[TMP:%.+]] = moore.constant 1 : l32
// CHECK: dbg.variable "p1", [[TMP]] : !moore.l32
parameter p1 = 1;
// CHECK: [[TMP:%.+]] = moore.constant 1 : l32
// CHECK: dbg.variable "p2", [[TMP]] : !moore.l32
parameter p2 = p1;
// CHECK: [[TMP:%.+]] = moore.constant 2 : l32
// CHECK: dbg.variable "lp1", [[TMP]] : !moore.l32
localparam lp1 = 2;
// CHECK: [[TMP:%.+]] = moore.constant 2 : l32
// CHECK: dbg.variable "lp2", [[TMP]] : !moore.l32
localparam lp2 = lp1;
// CHECK: [[TMP:%.+]] = moore.constant 3 : l32
// CHECK: dbg.variable "sp1", [[TMP]] : !moore.l32
specparam sp1 = 3;
// CHECK: [[TMP:%.+]] = moore.constant 3 : l32
// CHECK: dbg.variable "sp2", [[TMP]] : !moore.l32
specparam sp2 = sp1;
initial begin
// CHECK: [[TMP:%.+]] = moore.constant 1 : l32
// CHECK: func.call @dummyF([[TMP]])
// CHECK: [[TMP:%.+]] = moore.constant 1 : l32
// CHECK: func.call @dummyF([[TMP]])
// CHECK: [[TMP:%.+]] = moore.constant 2 : l32
// CHECK: func.call @dummyF([[TMP]])
// CHECK: [[TMP:%.+]] = moore.constant 2 : l32
// CHECK: func.call @dummyF([[TMP]])
// CHECK: [[TMP:%.+]] = moore.constant 3 : l32
// CHECK: func.call @dummyF([[TMP]])
// CHECK: [[TMP:%.+]] = moore.constant 3 : l32
// CHECK: func.call @dummyF([[TMP]])
dummyF(p1);
dummyF(p2);
dummyF(lp1);
dummyF(lp2);
dummyF(sp1);
dummyF(sp2);
end
endmodule
// CHECK-LABEL: func.func private @ConditionalStatements(
// CHECK-SAME: %arg0: !moore.i1
@ -1529,27 +1556,28 @@ endmodule
// CHECK-LABEL: moore.module @GenerateConstructs()
module GenerateConstructs;
genvar i;
parameter p=2;
// CHECK: [[TMP:%.+]] = moore.constant 2
// CHECK: dbg.variable "p", [[TMP]]
parameter p = 2;
generate
// CHECK: [[TMP1:%.+]] = moore.constant 0 : l32
// CHECK: %i = moore.named_constant localparam [[TMP1]] : l32
// CHECK: [[TMP2:%.+]] = moore.conversion %i : !moore.l32 -> !moore.i32
// CHECK: %g1 = moore.variable [[TMP2]] : <i32>
// CHECK: [[TMP3:%.+]] = moore.constant 1 : l32
// CHECK: %i_0 = moore.named_constant localparam name "i" [[TMP3]] : l32
// CHECK: [[TMP4:%.+]] = moore.conversion %i_0 : !moore.l32 -> !moore.i32
// CHECK: %g1_1 = moore.variable name "g1" [[TMP4]] : <i32>
for(i=0; i<2; i=i+1) begin
int g1 = i;
// CHECK: [[TMP:%.+]] = moore.constant 0
// CHECK: dbg.variable "i", [[TMP]]
// CHECK: [[TMP:%.+]] = moore.constant 0
// CHECK: %g1 = moore.variable [[TMP]]
// CHECK: [[TMP:%.+]] = moore.constant 1
// CHECK: dbg.variable "i", [[TMP]]
// CHECK: [[TMP:%.+]] = moore.constant 1
// CHECK: moore.variable name "g1" [[TMP]]
for (i = 0; i < 2; i = i + 1) begin
integer g1 = i;
end
// CHECK: [[TMP:%.+]] = moore.constant 2 : i32
// CHECK: %g2 = moore.variable [[TMP]] : <i32>
if(p == 2) begin
if (p == 2) begin
int g2 = 2;
end
else begin
end else begin
int g2 = 3;
end
@ -1558,10 +1586,10 @@ module GenerateConstructs;
case (p)
2: begin
int g3 = 2;
end
end
default: begin
int g3 = 3;
end
end
endcase
endgenerate
endmodule
@ -1979,3 +2007,21 @@ module ImmediateAssertiWithActionBlock;
// CHEK: }
assert (x) a = 1; else a = 0;
endmodule
// CHECK: [[TMP:%.+]] = moore.constant 42 : i32
// CHECK: dbg.variable "rootParam1", [[TMP]] : !moore.i32
parameter int rootParam1 = 42;
// CHECK: [[TMP:%.+]] = moore.constant 9001 : i32
// CHECK: dbg.variable "rootParam2", [[TMP]] : !moore.i32
localparam int rootParam2 = 9001;
package ParamPackage;
// CHECK: [[TMP:%.+]] = moore.constant 42 : i32
// CHECK: dbg.variable "ParamPackage::param1", [[TMP]] : !moore.i32
parameter int param1 = 42;
// CHECK: [[TMP:%.+]] = moore.constant 9001 : i32
// CHECK: dbg.variable "ParamPackage::param2", [[TMP]] : !moore.i32
localparam int param2 = 9001;
endpackage

View File

@ -362,25 +362,6 @@ moore.module private @SubModule_0(in %a : !moore.l1, in %b : !moore.l1, out c :
moore.output %0 : !moore.l1
}
// CHECK-LABEL: hw.module @ParamTest() {
moore.module @ParamTest(){
// CHECK-NEXT: [[Pa:%.+]] = hw.constant 1 : i32
// CHECK-NEXT: %p1 = hw.wire [[Pa]] sym @parameter_p1 : i32
%0 = moore.constant 1 : l32
%p1 = moore.named_constant parameter %0 : l32
// CHECK-NEXT: [[LPa:%.+]] = hw.constant 2 : i32
// CHECK-NEXT: %lp1 = hw.wire [[LPa]] sym @localparameter_lp1 : i32
%1 = moore.constant 2 : l32
%lp1 = moore.named_constant localparam %1 : l32
// CHECK-NEXT: [[SPa:%.+]] = hw.constant 3 : i32
// CHECK-NEXT: %sp1 = hw.wire [[SPa]] sym @specparameter_sp1 : i32
%2 = moore.constant 3 : l32
%sp1 = moore.named_constant specparam %2 : l32
}
moore.module @Variable() {
// CHECK: [[TMP0:%.+]] = hw.constant 0 : i32
// CHECK: %a = llhd.sig [[TMP0]] : i32