mirror of https://github.com/llvm/circt.git
[MSFT] Introduce msft.module.extern op for PyCDE lowering. (#2106)
The op is identical to hw.module.extern, but it can be referred to by a msft.instance op. This adds a cleaner layering where PyCDE only generates msft.module/msft.module.extern/msft.instance ops. Before, it would generate msft.module/hw.module.extern, leading to a mix of msft.instance/hw.instance for LowerMSFTToHW to consume. This change introduces a clear contract for PyCDE's output: all module and instance ops are from the MSFT dialect. Some of the implementation is duplicated into MSFTOps.cpp from HWOps.cpp, but refactoring is left as a future exercise, captured in https://github.com/llvm/circt/issues/2107.
This commit is contained in:
parent
dc35179ce5
commit
66acfe31b7
|
@ -174,7 +174,7 @@ class _SpecializedModule:
|
|||
mlir.ir.TypeAttr.get(i.attr.type))
|
||||
for i in self.parameters
|
||||
]
|
||||
return hw.HWModuleExternOp(
|
||||
return msft.MSFTModuleExternOp(
|
||||
symbol,
|
||||
self.input_ports,
|
||||
self.output_ports,
|
||||
|
|
|
@ -114,14 +114,14 @@ poly.print()
|
|||
# CHECK: %example.y = msft.instance @example @PolyComputeForCoeff_62_42_6(%c23_i32) : (i32) -> i32
|
||||
# CHECK: %example2.y = msft.instance @example2 @PolyComputeForCoeff_62_42_6(%example.y) : (i32) -> i32
|
||||
# CHECK: %example2_1.y = msft.instance @example2_1 @PolyComputeForCoeff_1_2_3_4_5(%example.y) : (i32) -> i32
|
||||
# CHECK: %CoolPolynomialCompute.y = hw.instance "CoolPolynomialCompute" @supercooldevice(x: %{{.+}}: i32) -> (y: i32)
|
||||
# CHECK: hw.instance "M" @parameterized_extern<a: i64 = 8, b: i64 = 3>() -> ()
|
||||
# CHECK: %CoolPolynomialCompute.y = msft.instance @CoolPolynomialCompute @supercooldevice(%{{.+}}) : (i32) -> i32
|
||||
# CHECK: msft.instance @M @parameterized_extern() <a: i64 = 8, b: i64 = 3> : () -> ()
|
||||
# CHECK: msft.output %example.y : i32
|
||||
# CHECK: }
|
||||
# CHECK: msft.module @PolyComputeForCoeff_62_42_6 {coefficients = {coeff = [62, 42, 6]}} (%x: i32) -> (y: i32)
|
||||
# CHECK: msft.module @PolyComputeForCoeff_1_2_3_4_5 {coefficients = {coeff = [1, 2, 3, 4, 5]}} (%x: i32) -> (y: i32)
|
||||
# CHECK: hw.module.extern @supercooldevice(%x: i32) -> (y: i32) attributes {verilogName = "supercooldevice"}
|
||||
# CHECK: hw.module.extern @parameterized_extern<a: i64, b: i64>() attributes {verilogName = "parameterized_extern"}
|
||||
# CHECK: msft.module.extern @supercooldevice(%x: i32) -> (y: i32) attributes {verilogName = "supercooldevice"}
|
||||
# CHECK: msft.module.extern @parameterized_extern<a: i64, b: i64>() attributes {verilogName = "parameterized_extern"}
|
||||
|
||||
print("Generating rest...")
|
||||
poly.generate()
|
||||
|
@ -134,7 +134,7 @@ poly.print()
|
|||
# CHECK: %example.y = hw.instance "example" sym @example @PolyComputeForCoeff_62_42_6(x: %c23_i32: i32) -> (y: i32)
|
||||
# CHECK: %example2.y = hw.instance "example2" sym @example2 @PolyComputeForCoeff_62_42_6(x: %0: i32) -> (y: i32)
|
||||
# CHECK: %example2_1.y = hw.instance "example2_1" sym @example2_1 @PolyComputeForCoeff_1_2_3_4_5(x: %1: i32) -> (y: i32)
|
||||
# CHECK: %CoolPolynomialCompute.y = hw.instance "CoolPolynomialCompute" @supercooldevice(x: %c23_i32{{.*}}: i32) -> (y: i32)
|
||||
# CHECK: %CoolPolynomialCompute.y = hw.instance "CoolPolynomialCompute" sym @CoolPolynomialCompute @supercooldevice(x: %c23_i32{{.*}}: i32) -> (y: i32)
|
||||
# CHECK-LABEL: hw.module @PolyComputeForCoeff_62_42_6(%x: i32) -> (y: i32)
|
||||
# CHECK: hw.constant 62
|
||||
# CHECK: hw.constant 42
|
||||
|
|
|
@ -6,6 +6,12 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// "Forward-declare" these HW attributes rather than including or duplicating
|
||||
// them here. This lets us to refer to them in ODS, but delegates to HW in C++.
|
||||
// These are used to represent parameters to MSFTModuleExternOp.
|
||||
def ParamDeclAttr : Attr<CPred<"$_self.isa<hw::ParamDeclAttr>()">>;
|
||||
def ParamDeclArrayAttr : TypedArrayAttrBase<ParamDeclAttr, "parameter array">;
|
||||
|
||||
def PrimitiveType : I32EnumAttr<"PrimitiveType",
|
||||
"Type of device at physical location", [
|
||||
I32EnumAttrCase<"M20K", 1>,
|
||||
|
|
|
@ -20,7 +20,8 @@ def InstanceOp : MSFTOp<"instance", [
|
|||
|
||||
let arguments = (ins SymbolNameAttr:$sym_name,
|
||||
FlatSymbolRefAttr:$moduleName,
|
||||
Variadic<AnyType>:$inputs);
|
||||
Variadic<AnyType>:$inputs,
|
||||
OptionalAttr<ParamDeclArrayAttr>:$parameters);
|
||||
let results = (outs Variadic<AnyType>);
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
|
@ -44,7 +45,7 @@ def InstanceOp : MSFTOp<"instance", [
|
|||
|
||||
/// sym keyword for optional symbol simplifies parsing
|
||||
let assemblyFormat = [{
|
||||
$sym_name $moduleName `(` $inputs `)` attr-dict
|
||||
$sym_name $moduleName `(` $inputs `)` custom<ParameterList>($parameters) attr-dict
|
||||
`:` functional-type($inputs, results)
|
||||
}];
|
||||
}
|
||||
|
@ -125,6 +126,36 @@ def MSFTModuleOp : MSFTOp<"module",
|
|||
let parser = "return ::parse$cppClass(parser, result);";
|
||||
}
|
||||
|
||||
def MSFTModuleExternOp : MSFTOp<"module.extern",
|
||||
[Symbol, HasParent<"mlir::ModuleOp">]> {
|
||||
let summary = "MSFT external Module";
|
||||
let description = [{
|
||||
Identical to `hw.module.extern`, and trivially lowers to that. This op
|
||||
exists so that we can use `msft.instance` to refer to both `msft.module` and
|
||||
`msft.module.extern`, rather than mixing `hw.instance` with `msft.instance`.
|
||||
}];
|
||||
let arguments = (ins StrArrayAttr:$argNames, StrArrayAttr:$resultNames,
|
||||
ParamDeclArrayAttr:$parameters,
|
||||
OptionalAttr<StrAttr>:$verilogName);
|
||||
let regions = (region SizedRegion<0>:$body);
|
||||
|
||||
let skipDefaultBuilders = 1;
|
||||
let builders = [
|
||||
OpBuilder<(ins "StringAttr":$name, "const hw::ModulePortInfo &":$ports,
|
||||
CArg<"StringRef", "StringRef()">:$verilogName,
|
||||
CArg<"ArrayAttr", "{}">:$parameters,
|
||||
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>
|
||||
];
|
||||
|
||||
let printer = "return ::print$cppClass(p, *this);";
|
||||
let parser = "return ::parse$cppClass(parser, result);";
|
||||
let verifier = "return ::verify$cppClass(*this);";
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
/// Decode information about the input and output ports on this module.
|
||||
hw::ModulePortInfo getPorts();
|
||||
}];
|
||||
}
|
||||
|
||||
def OutputOp : MSFTOp<"output", [Terminator, HasParent<"MSFTModuleOp">,
|
||||
NoSideEffect, ReturnLike]> {
|
||||
|
|
|
@ -10,6 +10,35 @@ import circt.support as support
|
|||
from mlir.ir import *
|
||||
|
||||
|
||||
def create_parameters(parameters: dict[str, _ir.Attribute], module: ModuleLike):
|
||||
# Compute mapping from parameter name to index, and initialize array.
|
||||
mod_param_decls = module.parameters
|
||||
mod_param_decls_idxs = {
|
||||
decl.name: idx for (idx, decl) in enumerate(mod_param_decls)
|
||||
}
|
||||
inst_param_array = [None] * len(module.parameters)
|
||||
|
||||
# Fill in all the parameters specified.
|
||||
if isinstance(parameters, DictAttr):
|
||||
parameters = {i.name: i.attr for i in parameters}
|
||||
for (pname, pval) in parameters.items():
|
||||
if pname not in mod_param_decls_idxs:
|
||||
raise ValueError(
|
||||
f"Could not find parameter '{pname}' in module parameter decls")
|
||||
idx = mod_param_decls_idxs[pname]
|
||||
param_decl = mod_param_decls[idx]
|
||||
inst_param_array[idx] = hw.ParamDeclAttr.get(pname, param_decl.param_type,
|
||||
pval)
|
||||
|
||||
# Fill in the defaults from the module param decl.
|
||||
for (idx, pval) in enumerate(inst_param_array):
|
||||
if pval is not None:
|
||||
continue
|
||||
inst_param_array[idx] = mod_param_decls[idx]
|
||||
|
||||
return inst_param_array
|
||||
|
||||
|
||||
class InstanceBuilder(support.NamedValueOpView):
|
||||
"""Helper class to incrementally construct an instance of a module."""
|
||||
|
||||
|
@ -26,29 +55,7 @@ class InstanceBuilder(support.NamedValueOpView):
|
|||
self.module = module
|
||||
instance_name = StringAttr.get(name)
|
||||
module_name = FlatSymbolRefAttr.get(StringAttr(module.name).value)
|
||||
|
||||
mod_param_decls = module.parameters
|
||||
mod_param_decls_idxs = {
|
||||
decl.name: idx for (idx, decl) in enumerate(mod_param_decls)
|
||||
}
|
||||
inst_param_array = [None] * len(module.parameters)
|
||||
# Fill in all the parameters specified.
|
||||
if isinstance(parameters, DictAttr):
|
||||
parameters = {i.name: i.attr for i in parameters}
|
||||
for (pname, pval) in parameters.items():
|
||||
if pname not in mod_param_decls_idxs:
|
||||
raise ValueError(
|
||||
f"Could not find parameter '{pname}' in module parameter decls")
|
||||
idx = mod_param_decls_idxs[pname]
|
||||
param_decl = mod_param_decls[idx]
|
||||
inst_param_array[idx] = hw.ParamDeclAttr.get(pname, param_decl.param_type,
|
||||
pval)
|
||||
# Fill in the defaults from the module param decl.
|
||||
for (idx, pval) in enumerate(inst_param_array):
|
||||
if pval is not None:
|
||||
continue
|
||||
inst_param_array[idx] = mod_param_decls[idx]
|
||||
|
||||
inst_param_array = create_parameters(parameters, module)
|
||||
if sym_name:
|
||||
sym_name = StringAttr.get(sym_name)
|
||||
pre_args = [instance_name, module_name]
|
||||
|
@ -183,6 +190,12 @@ class ModuleLike:
|
|||
def is_external(self):
|
||||
return len(self.regions[0].blocks) == 0
|
||||
|
||||
@property
|
||||
def parameters(self) -> list[ParamDeclAttr]:
|
||||
return [
|
||||
hw.ParamDeclAttr(a) for a in ArrayAttr(self.attributes["parameters"])
|
||||
]
|
||||
|
||||
def create(self,
|
||||
name: str,
|
||||
parameters: Dict[str, object] = {},
|
||||
|
@ -305,21 +318,10 @@ class HWModuleOp(ModuleLike):
|
|||
self.body.blocks.append(*self.type.inputs)
|
||||
return self.body.blocks[0]
|
||||
|
||||
@property
|
||||
def parameters(self) -> list[ParamDeclAttr]:
|
||||
return [
|
||||
hw.ParamDeclAttr(a) for a in ArrayAttr(self.attributes["parameters"])
|
||||
]
|
||||
|
||||
|
||||
class HWModuleExternOp(ModuleLike):
|
||||
"""Specialization for the HW module op class."""
|
||||
|
||||
@property
|
||||
def parameters(self) -> list[ParamDeclAttr]:
|
||||
return [
|
||||
hw.ParamDeclAttr(a) for a in ArrayAttr(self.attributes["parameters"])
|
||||
]
|
||||
pass
|
||||
|
||||
|
||||
class ConstantOp:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
from circt.dialects import msft as _msft
|
||||
from circt.dialects import hw, msft as _msft
|
||||
import circt.dialects._hw_ops_ext as _hw_ext
|
||||
import circt.support as support
|
||||
|
||||
|
@ -18,6 +18,7 @@ class InstanceBuilder(support.NamedValueOpView):
|
|||
input_port_mapping,
|
||||
*,
|
||||
sym_name=None,
|
||||
parameters=None,
|
||||
loc=None,
|
||||
ip=None):
|
||||
self.module = module
|
||||
|
@ -26,14 +27,22 @@ class InstanceBuilder(support.NamedValueOpView):
|
|||
if sym_name:
|
||||
sym_name = _ir.StringAttr.get(sym_name)
|
||||
pre_args = [instance_name, module_name]
|
||||
if parameters is not None:
|
||||
parameters = _hw_ext.create_parameters(parameters, module)
|
||||
else:
|
||||
parameters = []
|
||||
post_args = [_ir.ArrayAttr.get(parameters)]
|
||||
results = module.type.results
|
||||
|
||||
super().__init__(_msft.InstanceOp,
|
||||
results,
|
||||
input_port_mapping,
|
||||
pre_args, [],
|
||||
loc=loc,
|
||||
ip=ip)
|
||||
super().__init__(
|
||||
_msft.InstanceOp,
|
||||
results,
|
||||
input_port_mapping,
|
||||
pre_args,
|
||||
post_args,
|
||||
loc=loc,
|
||||
ip=ip,
|
||||
)
|
||||
|
||||
def create_default_value(self, index, data_type, arg_name):
|
||||
type = self.module.type.inputs[index]
|
||||
|
@ -86,3 +95,28 @@ class MSFTModuleOp(_hw_ext.ModuleLike):
|
|||
@property
|
||||
def entry_block(self):
|
||||
return self.regions[0].blocks[0]
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
return [
|
||||
hw.ParamDeclAttr.get(p.name, _ir.TypeAttr.get(p.attr.type), p.attr)
|
||||
for p in _ir.DictAttr(self.attributes["parameters"])
|
||||
]
|
||||
|
||||
|
||||
class MSFTModuleExternOp(_hw_ext.ModuleLike):
|
||||
|
||||
def create(self,
|
||||
name: str,
|
||||
parameters=None,
|
||||
results=None,
|
||||
loc=None,
|
||||
ip=None,
|
||||
**kwargs):
|
||||
return InstanceBuilder(self,
|
||||
name,
|
||||
kwargs,
|
||||
sym_name=self.verilogName.value,
|
||||
parameters=parameters,
|
||||
loc=loc,
|
||||
ip=ip)
|
||||
|
|
|
@ -11,12 +11,15 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/MSFT/MSFTOps.h"
|
||||
#include "circt/Dialect/HW/HWAttributes.h"
|
||||
#include "circt/Dialect/HW/HWOps.h"
|
||||
#include "circt/Dialect/HW/ModuleImplementation.h"
|
||||
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/DialectImplementation.h"
|
||||
#include "mlir/IR/FunctionImplementation.h"
|
||||
#include "mlir/IR/FunctionSupport.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
|
||||
using namespace circt;
|
||||
|
@ -97,7 +100,8 @@ LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
|
|||
return emitError("Cannot find module definition '") << moduleName() << "'";
|
||||
|
||||
// It must be some sort of module.
|
||||
if (!hw::isAnyModule(module) && !isa<MSFTModuleOp>(module))
|
||||
if (!hw::isAnyModule(module) &&
|
||||
!isa<MSFTModuleOp, MSFTModuleExternOp>(module))
|
||||
return emitError("symbol reference '")
|
||||
<< moduleName() << "' isn't a module";
|
||||
return success();
|
||||
|
@ -227,5 +231,319 @@ static void printMSFTModuleOp(OpAsmPrinter &p, MSFTModuleOp mod) {
|
|||
/*printBlockTerminators=*/true);
|
||||
}
|
||||
|
||||
/// Parse an parameter list if present. Same format as HW dialect.
|
||||
/// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
|
||||
/// parameter-decl ::= identifier `:` type
|
||||
/// parameter-decl ::= identifier `:` type `=` attribute
|
||||
///
|
||||
static ParseResult parseParameterList(OpAsmParser &parser,
|
||||
SmallVector<Attribute> ¶meters) {
|
||||
|
||||
return parser.parseCommaSeparatedList(
|
||||
OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
|
||||
std::string name;
|
||||
Type type;
|
||||
Attribute value;
|
||||
|
||||
if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
|
||||
return failure();
|
||||
|
||||
// Parse the default value if present.
|
||||
if (succeeded(parser.parseOptionalEqual())) {
|
||||
if (parser.parseAttribute(value, type))
|
||||
return failure();
|
||||
}
|
||||
|
||||
auto &builder = parser.getBuilder();
|
||||
parameters.push_back(hw::ParamDeclAttr::get(
|
||||
builder.getContext(), builder.getStringAttr(name),
|
||||
TypeAttr::get(type), value));
|
||||
return success();
|
||||
});
|
||||
}
|
||||
|
||||
/// Shim to also use this for the InstanceOp custom parser.
|
||||
static ParseResult parseParameterList(OpAsmParser &parser,
|
||||
ArrayAttr ¶meters) {
|
||||
SmallVector<Attribute> parseParameters;
|
||||
if (failed(parseParameterList(parser, parseParameters)))
|
||||
return failure();
|
||||
|
||||
parameters = ArrayAttr::get(parser.getContext(), parseParameters);
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
/// Print a parameter list for a module or instance. Same format as HW dialect.
|
||||
static void printParameterList(OpAsmPrinter &p, Operation *op,
|
||||
ArrayAttr parameters) {
|
||||
if (parameters.empty())
|
||||
return;
|
||||
|
||||
p << '<';
|
||||
llvm::interleaveComma(parameters, p, [&](Attribute param) {
|
||||
auto paramAttr = param.cast<hw::ParamDeclAttr>();
|
||||
p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
|
||||
if (auto value = paramAttr.getValue()) {
|
||||
p << " = ";
|
||||
p.printAttributeWithoutType(value);
|
||||
}
|
||||
});
|
||||
p << '>';
|
||||
}
|
||||
|
||||
/// Check parameter specified by `value` to see if it is valid within the scope
|
||||
/// of the specified module `module`. If not, emit an error at the location of
|
||||
/// `usingOp` and return failure, otherwise return success. If `usingOp` is
|
||||
/// null, then no diagnostic is generated. Same format as HW dialect.
|
||||
///
|
||||
/// If `disallowParamRefs` is true, then parameter references are not allowed.
|
||||
static LogicalResult checkParameterInContext(Attribute value, Operation *module,
|
||||
Operation *usingOp,
|
||||
bool disallowParamRefs) {
|
||||
// Literals are always ok. Their types are already known to match
|
||||
// expectations.
|
||||
if (value.isa<IntegerAttr>() || value.isa<FloatAttr>() ||
|
||||
value.isa<StringAttr>() || value.isa<hw::ParamVerbatimAttr>())
|
||||
return success();
|
||||
|
||||
// Check both subexpressions of an expression.
|
||||
if (auto expr = value.dyn_cast<hw::ParamExprAttr>()) {
|
||||
for (auto op : expr.getOperands())
|
||||
if (failed(
|
||||
checkParameterInContext(op, module, usingOp, disallowParamRefs)))
|
||||
return failure();
|
||||
return success();
|
||||
}
|
||||
|
||||
// Parameter references need more analysis to make sure they are valid within
|
||||
// this module.
|
||||
if (auto parameterRef = value.dyn_cast<hw::ParamDeclRefAttr>()) {
|
||||
auto nameAttr = parameterRef.getName();
|
||||
|
||||
// Don't allow references to parameters from the default values of a
|
||||
// parameter list.
|
||||
if (disallowParamRefs) {
|
||||
if (usingOp)
|
||||
usingOp->emitOpError("parameter ")
|
||||
<< nameAttr << " cannot be used as a default value for a parameter";
|
||||
return failure();
|
||||
}
|
||||
|
||||
// Find the corresponding attribute in the module.
|
||||
for (auto param : module->getAttrOfType<ArrayAttr>("parameters")) {
|
||||
auto paramAttr = param.cast<hw::ParamDeclAttr>();
|
||||
if (paramAttr.getName() != nameAttr)
|
||||
continue;
|
||||
|
||||
// If the types match then the reference is ok.
|
||||
if (paramAttr.getType().getValue() == parameterRef.getType())
|
||||
return success();
|
||||
|
||||
if (usingOp) {
|
||||
auto diag = usingOp->emitOpError("parameter ")
|
||||
<< nameAttr << " used with type " << parameterRef.getType()
|
||||
<< "; should have type " << paramAttr.getType().getValue();
|
||||
diag.attachNote(module->getLoc()) << "module declared here";
|
||||
}
|
||||
return failure();
|
||||
}
|
||||
|
||||
if (usingOp) {
|
||||
auto diag = usingOp->emitOpError("use of unknown parameter ") << nameAttr;
|
||||
diag.attachNote(module->getLoc()) << "module declared here";
|
||||
}
|
||||
return failure();
|
||||
}
|
||||
|
||||
if (usingOp)
|
||||
usingOp->emitOpError("invalid parameter value ") << value;
|
||||
return failure();
|
||||
}
|
||||
|
||||
static ParseResult parseMSFTModuleExternOp(OpAsmParser &parser,
|
||||
OperationState &result) {
|
||||
using namespace mlir::function_like_impl;
|
||||
|
||||
auto loc = parser.getCurrentLocation();
|
||||
|
||||
SmallVector<OpAsmParser::OperandType, 4> entryArgs;
|
||||
SmallVector<NamedAttrList, 4> argAttrs;
|
||||
SmallVector<NamedAttrList, 4> resultAttrs;
|
||||
SmallVector<Type, 4> argTypes;
|
||||
SmallVector<Type, 4> resultTypes;
|
||||
SmallVector<Attribute> parameters;
|
||||
auto &builder = parser.getBuilder();
|
||||
|
||||
// Parse the name as a symbol.
|
||||
StringAttr nameAttr;
|
||||
if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
|
||||
result.attributes))
|
||||
return failure();
|
||||
|
||||
// Parse the function signature.
|
||||
bool isVariadic = false;
|
||||
SmallVector<Attribute> resultNames;
|
||||
if (parseParameterList(parser, parameters) ||
|
||||
hw::module_like_impl::parseModuleFunctionSignature(
|
||||
parser, entryArgs, argTypes, argAttrs, isVariadic, resultTypes,
|
||||
resultAttrs, resultNames) ||
|
||||
// If function attributes are present, parse them.
|
||||
parser.parseOptionalAttrDictWithKeyword(result.attributes))
|
||||
return failure();
|
||||
|
||||
// Record the argument and result types as an attribute. This is necessary
|
||||
// for external modules.
|
||||
auto type = builder.getFunctionType(argTypes, resultTypes);
|
||||
result.addAttribute(getTypeAttrName(), TypeAttr::get(type));
|
||||
|
||||
auto *context = result.getContext();
|
||||
|
||||
if (hasAttribute("resultNames", result.attributes) ||
|
||||
hasAttribute("parameters", result.attributes)) {
|
||||
parser.emitError(
|
||||
loc, "explicit `resultNames` / `parameters` attributes not allowed");
|
||||
return failure();
|
||||
}
|
||||
|
||||
// Use the argument and result names if not already specified.
|
||||
SmallVector<Attribute> argNames;
|
||||
if (!entryArgs.empty()) {
|
||||
for (auto &arg : entryArgs)
|
||||
argNames.push_back(
|
||||
hw::module_like_impl::getPortNameAttr(context, arg.name));
|
||||
} else if (!argTypes.empty()) {
|
||||
// The parser returns empty names in a special way.
|
||||
argNames.assign(argTypes.size(), StringAttr::get(context, ""));
|
||||
}
|
||||
|
||||
// An explicit `argNames` attribute overrides the MLIR names. This is how
|
||||
// we represent port names that aren't valid MLIR identifiers. Result and
|
||||
// parameter names are printed quoted when they aren't valid identifiers, so
|
||||
// they don't need this affordance.
|
||||
if (!hasAttribute("argNames", result.attributes))
|
||||
result.addAttribute("argNames", ArrayAttr::get(context, argNames));
|
||||
result.addAttribute("resultNames", ArrayAttr::get(context, resultNames));
|
||||
result.addAttribute("parameters", ArrayAttr::get(context, parameters));
|
||||
|
||||
assert(argAttrs.size() == argTypes.size());
|
||||
assert(resultAttrs.size() == resultTypes.size());
|
||||
|
||||
// Add the attributes to the function arguments.
|
||||
addArgAndResultAttrs(builder, result, argAttrs, resultAttrs);
|
||||
|
||||
// Extern modules carry an empty region to work with HWModuleImplementation.h.
|
||||
result.addRegion();
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
static void printMSFTModuleExternOp(OpAsmPrinter &p, MSFTModuleExternOp op) {
|
||||
using namespace mlir::function_like_impl;
|
||||
|
||||
auto typeAttr = op->getAttrOfType<TypeAttr>(getTypeAttrName());
|
||||
FunctionType fnType = typeAttr.getValue().cast<FunctionType>();
|
||||
auto argTypes = fnType.getInputs();
|
||||
auto resultTypes = fnType.getResults();
|
||||
|
||||
// Print the operation and the function name.
|
||||
p << ' ';
|
||||
p.printSymbolName(SymbolTable::getSymbolName(op).getValue());
|
||||
|
||||
// Print the parameter list if present.
|
||||
printParameterList(p, op, op->getAttrOfType<ArrayAttr>("parameters"));
|
||||
|
||||
bool needArgNamesAttr = false;
|
||||
hw::module_like_impl::printModuleSignature(
|
||||
p, op, argTypes, /*isVariadic=*/false, resultTypes, needArgNamesAttr);
|
||||
|
||||
SmallVector<StringRef, 3> omittedAttrs;
|
||||
if (!needArgNamesAttr)
|
||||
omittedAttrs.push_back("argNames");
|
||||
omittedAttrs.push_back("resultNames");
|
||||
omittedAttrs.push_back("parameters");
|
||||
|
||||
printFunctionAttributes(p, op, argTypes.size(), resultTypes.size(),
|
||||
omittedAttrs);
|
||||
}
|
||||
|
||||
static LogicalResult verifyMSFTModuleExternOp(MSFTModuleExternOp module) {
|
||||
using namespace mlir::function_like_impl;
|
||||
auto typeAttr = module->getAttrOfType<TypeAttr>(getTypeAttrName());
|
||||
auto moduleType = typeAttr.getValue().cast<FunctionType>();
|
||||
auto argNames = module->getAttrOfType<ArrayAttr>("argNames");
|
||||
auto resultNames = module->getAttrOfType<ArrayAttr>("resultNames");
|
||||
if (argNames.size() != moduleType.getNumInputs())
|
||||
return module->emitOpError("incorrect number of argument names");
|
||||
if (resultNames.size() != moduleType.getNumResults())
|
||||
return module->emitOpError("incorrect number of result names");
|
||||
|
||||
SmallPtrSet<Attribute, 4> paramNames;
|
||||
|
||||
// Check parameter default values are sensible.
|
||||
for (auto param : module->getAttrOfType<ArrayAttr>("parameters")) {
|
||||
auto paramAttr = param.cast<hw::ParamDeclAttr>();
|
||||
|
||||
// Check that we don't have any redundant parameter names. These are
|
||||
// resolved by string name: reuse of the same name would cause ambiguities.
|
||||
if (!paramNames.insert(paramAttr.getName()).second)
|
||||
return module->emitOpError("parameter ")
|
||||
<< paramAttr << " has the same name as a previous parameter";
|
||||
|
||||
// Default values are allowed to be missing, check them if present.
|
||||
auto value = paramAttr.getValue();
|
||||
if (!value)
|
||||
continue;
|
||||
|
||||
if (value.getType() != paramAttr.getType().getValue())
|
||||
return module->emitOpError("parameter ")
|
||||
<< paramAttr << " should have type "
|
||||
<< paramAttr.getType().getValue() << "; has type "
|
||||
<< value.getType();
|
||||
|
||||
// Verify that this is a valid parameter value, disallowing parameter
|
||||
// references. We could allow parameters to refer to each other in the
|
||||
// future with lexical ordering if there is a need.
|
||||
if (failed(checkParameterInContext(value, module, module,
|
||||
/*disallowParamRefs=*/true)))
|
||||
return failure();
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
hw::ModulePortInfo MSFTModuleExternOp::getPorts() {
|
||||
using namespace mlir::function_like_impl;
|
||||
|
||||
SmallVector<hw::PortInfo> inputs, outputs;
|
||||
|
||||
auto typeAttr = getOperation()->getAttrOfType<TypeAttr>(getTypeAttrName());
|
||||
auto moduleType = typeAttr.getValue().cast<FunctionType>();
|
||||
auto argTypes = moduleType.getInputs();
|
||||
auto resultTypes = moduleType.getResults();
|
||||
|
||||
auto argNames = getOperation()->getAttrOfType<ArrayAttr>("argNames");
|
||||
for (unsigned i = 0, e = argTypes.size(); i < e; ++i) {
|
||||
bool isInOut = false;
|
||||
auto type = argTypes[i];
|
||||
|
||||
if (auto inout = type.dyn_cast<hw::InOutType>()) {
|
||||
isInOut = true;
|
||||
type = inout.getElementType();
|
||||
}
|
||||
|
||||
auto direction =
|
||||
isInOut ? hw::PortDirection::INOUT : hw::PortDirection::INPUT;
|
||||
|
||||
inputs.push_back({argNames[i].cast<StringAttr>(), direction, type, i});
|
||||
}
|
||||
|
||||
auto resultNames = getOperation()->getAttrOfType<ArrayAttr>("resultNames");
|
||||
for (unsigned i = 0, e = resultTypes.size(); i < e; ++i)
|
||||
outputs.push_back({resultNames[i].cast<StringAttr>(),
|
||||
hw::PortDirection::OUTPUT, resultTypes[i], i});
|
||||
|
||||
return hw::ModulePortInfo(inputs, outputs);
|
||||
}
|
||||
|
||||
#define GET_OP_CLASSES
|
||||
#include "circt/Dialect/MSFT/MSFT.cpp.inc"
|
||||
|
|
|
@ -62,7 +62,7 @@ InstanceOpLowering::matchAndRewrite(InstanceOp msftInst, OpAdaptor adaptor,
|
|||
msftInst.getLoc(), referencedModule, msftInst.instanceNameAttr(),
|
||||
SmallVector<Value>(adaptor.getOperands().begin(),
|
||||
adaptor.getOperands().end()),
|
||||
/*parameters=*/ArrayAttr{}, msftInst.sym_nameAttr());
|
||||
msftInst.parameters().getValueOr(ArrayAttr()), msftInst.sym_nameAttr());
|
||||
rewriter.replaceOp(msftInst, hwInst.getResults());
|
||||
return success();
|
||||
}
|
||||
|
@ -99,6 +99,27 @@ ModuleOpLowering::matchAndRewrite(MSFTModuleOp mod, OpAdaptor adaptor,
|
|||
hwmod.getBody().end());
|
||||
return success();
|
||||
}
|
||||
namespace {
|
||||
|
||||
/// Lower MSFT's ModuleExternOp to HW's.
|
||||
struct ModuleExternOpLowering : public OpConversionPattern<MSFTModuleExternOp> {
|
||||
public:
|
||||
using OpConversionPattern::OpConversionPattern;
|
||||
|
||||
LogicalResult
|
||||
matchAndRewrite(MSFTModuleExternOp mod, OpAdaptor adaptor,
|
||||
ConversionPatternRewriter &rewriter) const final;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
LogicalResult ModuleExternOpLowering::matchAndRewrite(
|
||||
MSFTModuleExternOp mod, OpAdaptor adaptor,
|
||||
ConversionPatternRewriter &rewriter) const {
|
||||
rewriter.replaceOpWithNewOp<hw::HWModuleExternOp>(
|
||||
mod, mod.getNameAttr(), mod.getPorts(), mod.verilogName().getValueOr(""),
|
||||
mod.parameters());
|
||||
return success();
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Lower MSFT's OutputOp to HW's.
|
||||
|
@ -123,7 +144,7 @@ struct LowerToHWPass : public LowerToHWBase<LowerToHWPass> {
|
|||
|
||||
void LowerToHWPass::runOnOperation() {
|
||||
auto top = getOperation();
|
||||
auto ctxt = &getContext();
|
||||
auto *ctxt = &getContext();
|
||||
|
||||
// The `hw::InstanceOp` (which `msft::InstanceOp` lowers to) convenience
|
||||
// builder gets its argNames and resultNames from the `hw::HWModuleOp`. So we
|
||||
|
@ -132,12 +153,13 @@ void LowerToHWPass::runOnOperation() {
|
|||
// Convert everything except instance ops first.
|
||||
|
||||
ConversionTarget target(*ctxt);
|
||||
target.addIllegalOp<MSFTModuleOp, OutputOp>();
|
||||
target.addIllegalOp<MSFTModuleOp, MSFTModuleExternOp, OutputOp>();
|
||||
target.addLegalDialect<hw::HWDialect>();
|
||||
target.addLegalDialect<sv::SVDialect>();
|
||||
|
||||
RewritePatternSet patterns(ctxt);
|
||||
patterns.insert<ModuleOpLowering>(ctxt);
|
||||
patterns.insert<ModuleExternOpLowering>(ctxt);
|
||||
patterns.insert<OutputOpLowering>(ctxt);
|
||||
|
||||
if (failed(applyPartialConversion(top, target, std::move(patterns))))
|
||||
|
|
|
@ -7,6 +7,11 @@ hw.module @top () {
|
|||
msft.instance @foo @Foo() : () -> (i32)
|
||||
// CHECK: %foo.x = msft.instance @foo @Foo() : () -> i32
|
||||
// HWLOW: %foo.x = hw.instance "foo" sym @foo @Foo() -> (x: i32)
|
||||
|
||||
%true = hw.constant true
|
||||
%extern.out = msft.instance @extern @Extern(%true)<param: i1 = false> : (i1) -> i1
|
||||
// CHECK: %extern.out = msft.instance @extern @Extern(%true) <param: i1 = false> : (i1) -> i1
|
||||
// HWLOW: %extern.out = hw.instance "extern" sym @extern @Extern<param: i1 = false>(in: %true: i1) -> (out: i1)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: msft.module @B {WIDTH = 1 : i64} (%a: i4) -> (nameOfPortInSV: i4) {
|
||||
|
@ -26,3 +31,7 @@ msft.module @Foo { "WIDTH" = 1 } () -> (x: i32) {
|
|||
%c0 = hw.constant 0 : i32
|
||||
msft.output %c0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: msft.module.extern @Extern<param: i1>(%in: i1) -> (out: i1)
|
||||
// HWLOW-LABEL: hw.module.extern @Extern<param: i1>(%in: i1) -> (out: i1)
|
||||
msft.module.extern @Extern<param: i1> (%in: i1) -> (out: i1)
|
||||
|
|
Loading…
Reference in New Issue