[HW] Add simple expressions to the parameter support.

This includes export verilog and legalizenames support.

This is just a minimal first step.  It has several issues that we
need to improve over time:

1) Support variadic operators.
2) Support better .mlir syntax
3) Precedence aware printing in generated verilog.
This commit is contained in:
Chris Lattner 2021-09-22 09:34:56 -07:00
parent d8ee280f8e
commit a0afaa44f4
9 changed files with 106 additions and 3 deletions

View File

@ -12,6 +12,13 @@
#include "mlir/IR/Attributes.h"
#include "mlir/IR/BuiltinAttributes.h"
namespace circt {
namespace hw {
class PBOAttr;
enum class PBO : uint32_t;
} // namespace hw
} // namespace circt
#define GET_ATTRDEF_CLASSES
#include "circt/Dialect/HW/HWAttributes.h.inc"

View File

@ -85,6 +85,22 @@ def ParamVerbatimAttr : AttrDef<HWDialect, "ParamVerbatim"> {
let mnemonic = "param.verbatim";
}
let cppNamespace = "circt::hw" in {
def PBO_Add : I32EnumAttrCase<"Add", 0, "add">;
def PBO_Mul : I32EnumAttrCase<"Mul", 1, "mul">;
def PBOAttr : I32EnumAttr<"PBO", "Parameter Binary Operation Code",
[PBO_Add, PBO_Mul]>;
}
def ParamBinaryAttr : AttrDef<HWDialect, "ParamBinary"> {
let summary = "Binary operation combining two parameter expressions";
let parameters = (ins "PBO":$opcode,
"::mlir::Attribute":$lhs, "::mlir::Attribute":$rhs,
AttributeSelfTypeParameter<"">:$type);
let mnemonic = "param.binary";
}
let cppNamespace = "circt::hw" in {
def WUW_Undefined : I32EnumAttrCase<"Undefined", 0>;
def WUW_PortOrder : I32EnumAttrCase<"PortOrder", 1>;

View File

@ -96,3 +96,34 @@ Attribute ParamVerbatimAttr::parse(MLIRContext *context, DialectAsmParser &p,
void ParamVerbatimAttr::print(DialectAsmPrinter &p) const {
p << "param.verbatim<" << getValue() << ">";
}
//===----------------------------------------------------------------------===//
// ParamBinaryAttr
//===----------------------------------------------------------------------===//
Attribute ParamBinaryAttr::parse(MLIRContext *context, DialectAsmParser &p,
Type type) {
Attribute lhs, rhs;
StringRef opcodeStr;
auto loc = p.getCurrentLocation();
if (p.parseLess() || p.parseKeyword(&opcodeStr) ||
p.parseAttribute(lhs, type) || p.parseComma() ||
p.parseAttribute(rhs, type) || p.parseGreater())
return Attribute();
Optional<PBO> opcode = symbolizePBO(opcodeStr);
if (!opcode.hasValue()) {
p.emitError(loc, "unknown binary operator name");
return {};
}
return ParamBinaryAttr::get(context, *opcode, lhs, rhs, type);
}
void ParamBinaryAttr::print(DialectAsmPrinter &p) const {
p << "param.binary<" << stringifyPBO(getOpcode()) << " ";
p.printAttributeWithoutType(getLhs());
p << ", ";
p.printAttributeWithoutType(getRhs());
p << ">";
}

View File

@ -52,6 +52,16 @@ LogicalResult hw::checkParameterInContext(Attribute value, Operation *module,
value.isa<StringAttr>() || value.isa<ParamVerbatimAttr>())
return success();
// Check both arms of an expression.
if (auto binop = value.dyn_cast<ParamBinaryAttr>()) {
if (failed(checkParameterInContext(binop.getLhs(), module, usingOp,
disallowParamRefs)) ||
failed(checkParameterInContext(binop.getRhs(), 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<ParamDeclRefAttr>()) {

View File

@ -220,6 +220,19 @@ remapRenamedParameters(Attribute value, HWModuleOp module,
value.isa<StringAttr>() || value.isa<ParamVerbatimAttr>())
return value;
// Remap leaves of expressions if needed.
if (auto binOp = value.dyn_cast<ParamBinaryAttr>()) {
auto newLHS =
remapRenamedParameters(binOp.getLhs(), module, renamedParameterInfo);
auto newRHS =
remapRenamedParameters(binOp.getRhs(), module, renamedParameterInfo);
// Don't rebuild an attribute if nothing changed.
if (newLHS == binOp.getLhs() && newRHS == binOp.getRhs())
return value;
return ParamBinaryAttr::get(value.getContext(), binOp.getOpcode(), newLHS,
newRHS, value.getType());
}
// TODO: Handle nested expressions when we support them.
// Otherwise this must be a parameter reference.

View File

@ -83,6 +83,19 @@ static void printParamValue(Attribute value, Operation *op, StringRef paramName,
os << verbatimParam.getValue().getValue();
} else if (auto parameterRef = value.dyn_cast<ParamDeclRefAttr>()) {
os << parameterRef.getName().getValue();
} else if (auto paramBinOp = value.dyn_cast<ParamBinaryAttr>()) {
printParamValue(paramBinOp.getLhs(), op, paramName, os);
// FIXME: Handle precedence, support variadic versions of these.
switch (paramBinOp.getOpcode()) {
case PBO::Add:
os << " + ";
break;
case PBO::Mul:
os << " * ";
break;
}
printParamValue(paramBinOp.getRhs(), op, paramName, os);
} else {
os << "<<UNKNOWN MLIRATTR: " << value << ">>";
emitError(op->getLoc(), "unknown parameter value '")

View File

@ -82,7 +82,12 @@ hw.module.extern @NoArg<param: i42>()
// CHECK-LABEL: hw.module @UseParameters<p1: i42>() {
hw.module @UseParameters<p1: i42>() {
// CHECK: hw.instance "verbatimparam" @NoArg<param: i42 = #hw.param.verbatim<"\22FOO\22">>() -> ()
// CHECK: hw.instance "verbatimparam" @NoArg<param: i42 =
// CHECK-SAME: #hw.param.verbatim<"\22FOO\22">>() -> ()
hw.instance "verbatimparam" @NoArg<param: i42 = #hw.param.verbatim<"\"FOO\"">>() -> ()
// CHECK: hw.instance "verbatimparam" @NoArg<param: i42 =
// CHECK-SAME: #hw.param.binary<add #hw.param.verbatim<"xxx">, 17>>() -> ()
hw.instance "verbatimparam" @NoArg<param: i42 = #hw.param.binary<add #hw.param.verbatim<"xxx">, 17>>() -> ()
hw.output
}

View File

@ -122,6 +122,9 @@ hw.module @parameters<p1: i42 = 17, wire: i1>(%p1: i8) {
// CHECK: hw.instance "inst" @module_with_bool<bparam: i1 = #hw.param.decl.ref<"wire_1">>
hw.instance "inst" @module_with_bool<bparam: i1 = #hw.param.decl.ref<"wire">>() -> ()
// CHECK: hw.instance "inst2" @module_with_bool<bparam: i1 = #hw.param.binary<add #hw.param.verbatim<"wire">, true>>()
hw.instance "inst2" @module_with_bool<bparam: i1 = #hw.param.binary<add #hw.param.verbatim<"wire">, true>>() -> ()
}
// CHECK-LABEL: hw.module @use_parameters

View File

@ -861,7 +861,7 @@ hw.module @UseParameterized(%a: i8) -> (ww: i8, xx: i8, yy: i8, zz: i8) {
}
// CHECK-LABEL: module UseParameterValue
hw.module @UseParameterValue<xx: i42>(%arg0: i8) -> (out: i8) {
hw.module @UseParameterValue<xx: i42>(%arg0: i8) -> (out1: i8, out2: i8) {
// CHECK-NEXT: #(parameter [41:0] xx) (
// CHECK: parameters2 #(
@ -869,6 +869,11 @@ hw.module @UseParameterValue<xx: i42>(%arg0: i8) -> (out: i8) {
// CHECK-NEXT: ) inst1 (
%a = hw.instance "inst1" @parameters2<p1: i42 = #hw.param.decl.ref<"xx">, p2: i1 = 0>(arg0: %arg0: i8) -> (out: i8)
hw.output %a : i8
// CHECK: parameters2 #(
// CHECK-NEXT: .p1(xx + 42'd17)
// CHECK-NEXT: ) inst2 (
%b = hw.instance "inst2" @parameters2<p1: i42 = #hw.param.binary<add #hw.param.verbatim<"xx">, 17>, p2: i1 = 0>(arg0: %arg0: i8) -> (out: i8)
hw.output %a, %b : i8, i8
}