Skip to content

Commit

Permalink
[OM] Rework ClassLike to use return style
Browse files Browse the repository at this point in the history
This changes the OM dialect Class/ExternClass to use a return style
field name/type specifier syntax and, for non-extern class, a terminator
op in the style of hw.output.

The ClassLike code requires updating their parser/printer for the new
syntax.

This change introduces two new inherent attributes for ClassLike:
* fieldNames: array of string attribute corresponding to field names in
  definition order
* fieldTypes: dictionary mapping field names to types

The ClassLike code now provides an API to get the type of a field based
on its name, abstracting the underlying attribute storage.

The firrtl LowerClasses logic requires changes to use this API when
constructing OM classes, as well as in the dialect conversion logic to
handle the convering the stored types.

The OM evaluator similarly is updated for the new field API.

The OM linker is updated to skip constructing the field name to type map
and instead using the getFieldType API provided by the classOp.

The OM object field verifier also no longer needs to construct the field
name to type maps and instead can just use the new API.

Quite a few tests required updating for the new field syntax.
  • Loading branch information
leonardt committed Sep 10, 2024
1 parent 6638aaf commit c8887e0
Show file tree
Hide file tree
Showing 25 changed files with 790 additions and 595 deletions.
1 change: 1 addition & 0 deletions include/circt/Dialect/OM/OMOpInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define CIRCT_DIALECT_OM_OMOPINTERFACES_H

#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/OpImplementation.h"
#include "llvm/ADT/APSInt.h"

#include "circt/Dialect/OM/OMOpInterfaces.h.inc"
Expand Down
27 changes: 10 additions & 17 deletions include/circt/Dialect/OM/OMOpInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,21 @@ def ClassLike : OpInterface<"ClassLike"> {
"mlir::StringAttr", "getSymNameAttrName", (ins)>,
InterfaceMethod<"Get the class-like formal parameter names attribute",
"mlir::ArrayAttr", "getFormalParamNames", (ins)>,
InterfaceMethod<"Get the class-like formal parameter names attribute name",
InterfaceMethod<"Get the class-like dictionary mapping field names to type",
"mlir::StringAttr", "getFormalParamNamesAttrName", (ins)>,
InterfaceMethod<"Get the class-like formal parameter names attribute name",
"mlir::DictionaryAttr", "getFieldTypes", (ins)>,
InterfaceMethod<"Get the class-like body region",
"mlir::Region &", "getBody", (ins)>,
InterfaceMethod<"Get the class-like body block",
"mlir::Block *", "getBodyBlock", (ins),
/*methodBody=*/[{ return $_op.getBodyBlock(); }]>
];
}

def ClassFieldLike : OpInterface<"ClassFieldLike"> {
let cppNamespace = "circt::om";

let description = [{
Common functionality for class-like field operations.
}];

let methods = [
InterfaceMethod<"Get the class-like field's type",
"mlir::Type", "getType", (ins)>,
InterfaceMethod<"Get the class-like field's name attribute",
"mlir::StringAttr", "getNameAttr", (ins)>
/*methodBody=*/[{ return $_op.getBodyBlock(); }]>,
InterfaceMethod<"Get the class-like field type",
"std::optional<mlir::Type>", "getFieldType", (ins "mlir::StringAttr":$field)>,
InterfaceMethod<"Get the class-like field names",
"mlir::ArrayAttr", "getFieldNames", (ins)>,
InterfaceMethod<"Replace field types dictionary attr",
"void", "replaceFieldTypes", (ins "mlir::AttrTypeReplacer":$replacer)>,
];
}

Expand Down
1 change: 1 addition & 0 deletions include/circt/Dialect/OM/OMOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/InferTypeOpInterface.h"

#define GET_OP_CLASSES
Expand Down
69 changes: 36 additions & 33 deletions include/circt/Dialect/OM/OMOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef CIRCT_DIALECT_OM_OMOPS_TD
#define CIRCT_DIALECT_OM_OMOPS_TD

include "mlir/Interfaces/ControlFlowInterfaces.td"
include "circt/Dialect/OM/OMDialect.td"
include "circt/Dialect/OM/OMEnums.td"
include "circt/Dialect/OM/OMOpInterfaces.td"
Expand All @@ -33,40 +34,56 @@ class OMOp<string mnemonic, list<Trait> traits = []> :

class OMClassLike<string mnemonic, list<Trait> traits = []> :
OMOp<mnemonic, traits # [
SingleBlock, NoTerminator, Symbol, RegionKindInterface, IsolatedFromAbove,
Symbol, RegionKindInterface, IsolatedFromAbove,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmBlockArgumentNames"]>,
DeclareOpInterfaceMethods<ClassLike>]> {

let arguments = (ins
SymbolNameAttr:$sym_name,
StrArrayAttr:$formalParamNames
StrArrayAttr:$formalParamNames,
ArrayAttr:$fieldNames,
DictionaryAttr:$fieldTypes
);

let regions = (region
SizedRegion<1>:$body
);

let builders = [
OpBuilder<(ins "::mlir::Twine":$name)>,
OpBuilder<(ins "::mlir::Twine":$name,
"::mlir::ArrayRef<::mlir::StringRef>":$formalParamNames)>,
"::mlir::ArrayRef<::mlir::StringRef>":$formalParamNames,
"::mlir::DictionaryAttr":$fieldTypes)>,
OpBuilder<(ins "::mlir::Twine":$name,
"::mlir::ArrayRef<::mlir::StringRef>":$formalParamNames), [{
build(odsBuilder, odsState, odsBuilder.getStringAttr(name),
odsBuilder.getStrArrayAttr(formalParamNames));
}]>,
OpBuilder<(ins "::mlir::Twine":$name), [{
build(odsBuilder, odsState, odsBuilder.getStringAttr(name),
odsBuilder.getStrArrayAttr({}));
}]>,
OpBuilder<(ins "::mlir::Twine":$name, "::mlir::DictionaryAttr":$fieldTypes), [{
build(odsBuilder, odsState, name, {}, fieldTypes);
}]>,
OpBuilder<(ins "::mlir::StringAttr":$name,
"::mlir::ArrayAttr":$formalParamNames), [{
build(odsBuilder, odsState, name, formalParamNames,
odsBuilder.getArrayAttr({}), odsBuilder.getDictionaryAttr({}));

}]>
];

let hasCustomAssemblyFormat = 1;

let hasVerifier = 1;
}

class OMClassFieldLike<string mnemonic, list<Trait> traits = []> :
OMOp<mnemonic, traits # [
DeclareOpInterfaceMethods<ClassFieldLike>]> {
}

//===----------------------------------------------------------------------===//
// Class definitions
//===----------------------------------------------------------------------===//

def ClassOp : OMClassLike<"class"> {
def ClassOp : OMClassLike<"class", [
SingleBlockImplicitTerminator<"ClassFieldsOp">]> {
let extraClassDeclaration = [{
mlir::Block *getBodyBlock() { return &getBody().front(); }
// This builds a ClassOp, and populates it with the CLassFieldOps.
Expand All @@ -82,26 +99,24 @@ def ClassOp : OMClassLike<"class"> {
static mlir::RegionKind getRegionKind(unsigned index) {
return mlir::RegionKind::Graph;
}

circt::om::ClassFieldsOp getFieldsOp() {
return mlir::cast<ClassFieldsOp>(this->getBodyBlock()->getTerminator());
}
}];
}

def ClassFieldOp : OMClassFieldLike<"class.field",
[HasParent<"ClassOp">]> {
let arguments = (ins
SymbolNameAttr:$name,
AnyType:$value
);

let assemblyFormat = [{
$name `,` $value attr-dict `:` type($value)
}];
def ClassFieldsOp : OMOp<"class.fields", [Terminator, ReturnLike, Pure,
HasParent<"ClassOp">]> {
let arguments = (ins Variadic<AnyType>:$fields);
let assemblyFormat = "attr-dict ($fields^ `:` qualified(type($fields)))?";
}

//===----------------------------------------------------------------------===//
// External class definitions
//===----------------------------------------------------------------------===//

def ClassExternOp : OMClassLike<"class.extern"> {
def ClassExternOp : OMClassLike<"class.extern", [NoTerminator]> {
let extraClassDeclaration = [{
mlir::Block *getBodyBlock() { return &getBody().front(); }

Expand All @@ -112,18 +127,6 @@ def ClassExternOp : OMClassLike<"class.extern"> {
}];
}

def ClassExternFieldOp : OMClassFieldLike<"class.extern.field",
[HasParent<"ClassExternOp">]> {
let arguments = (ins
SymbolNameAttr:$name,
TypeAttr:$type
);

let assemblyFormat = [{
$name attr-dict `:` $type
}];
}

//===----------------------------------------------------------------------===//
// Object instantiations and fields
//===----------------------------------------------------------------------===//
Expand Down
85 changes: 36 additions & 49 deletions integration_test/Bindings/Python/dialects/om.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,108 +14,95 @@

module = Module.parse("""
module {
om.class @node() {
om.class @node() -> (field2: !om.string) {
%0 = om.constant #om.list<!om.string,["MyThing" : !om.string]> : !om.list<!om.string>
%1 = om.constant "Component.inst1.foo" : !om.string
om.class.field @field2, %1 : !om.string
om.class.fields %1 : !om.string
}
om.class @comp(
%inst1_propOut_bore: !om.class.type<@node>,
%inst2_propOut_bore: !om.class.type<@node>) {
om.class.field @field2, %inst1_propOut_bore : !om.class.type<@node>
om.class.field @field3, %inst2_propOut_bore : !om.class.type<@node>
%inst2_propOut_bore: !om.class.type<@node>) -> (field2: !om.class.type<@node>, field3: !om.class.type<@node>) {
om.class.fields %inst1_propOut_bore, %inst2_propOut_bore : !om.class.type<@node>, !om.class.type<@node>
}
om.class @Client() {
om.class @Client() -> (client_omnode_0_OMIROut: !om.class.type<@comp>, node0_OMIROut : !om.class.type<@node>, node1_OMIROut : !om.class.type<@node>) {
%0 = om.object @node() : () -> !om.class.type<@node>
%2 = om.object @comp(%0, %0) : (!om.class.type<@node>, !om.class.type<@node>) -> !om.class.type<@comp>
om.class.field @client_omnode_0_OMIROut, %2 : !om.class.type<@comp>
om.class.field @node0_OMIROut, %0 : !om.class.type<@node>
om.class.field @node1_OMIROut, %0 : !om.class.type<@node>
om.class.fields %2, %0, %0 : !om.class.type<@comp>, !om.class.type<@node>, !om.class.type<@node>
}
om.class @Test(%param: !om.integer) {
om.class @Test(%param: !om.integer) -> (field: !om.integer, child: !om.class.type<@Child>, reference: !om.ref, list: !om.list<!om.string>, tuple: tuple<!om.list<!om.string>, !om.integer>, nest: !om.class.type<@Nest>, map: !om.map<!om.string, !om.integer>, map_create: !om.map<!om.string, !om.integer>, true: i1, false: i1) {
%sym = om.constant #om.ref<<@Root::@x>> : !om.ref
om.class.field @field, %param : !om.integer
%c_14 = om.constant #om.integer<14> : !om.integer
%0 = om.object @Child(%c_14) : (!om.integer) -> !om.class.type<@Child>
om.class.field @child, %0 : !om.class.type<@Child>
om.class.field @reference, %sym : !om.ref
%list = om.constant #om.list<!om.string, ["X" : !om.string, "Y" : !om.string]> : !om.list<!om.string>
om.class.field @list, %list : !om.list<!om.string>
%tuple = om.tuple_create %list, %c_14: !om.list<!om.string>, !om.integer
om.class.field @tuple, %tuple : tuple<!om.list<!om.string>, !om.integer>
%c_15 = om.constant #om.integer<15> : !om.integer
%1 = om.object @Child(%c_15) : (!om.integer) -> !om.class.type<@Child>
%list_child = om.list_create %0, %1: !om.class.type<@Child>
%2 = om.object @Nest(%list_child) : (!om.list<!om.class.type<@Child>>) -> !om.class.type<@Nest>
om.class.field @nest, %2 : !om.class.type<@Nest>
%3 = om.constant #om.map<!om.integer, {a = #om.integer<42>, b = #om.integer<32>}> : !om.map<!om.string, !om.integer>
om.class.field @map, %3 : !om.map<!om.string, !om.integer>
%x = om.constant "X" : !om.string
%y = om.constant "Y" : !om.string
%entry1 = om.tuple_create %x, %c_14: !om.string, !om.integer
%entry2 = om.tuple_create %y, %c_15: !om.string, !om.integer
%map = om.map_create %entry1, %entry2: !om.string, !om.integer
om.class.field @map_create, %map : !om.map<!om.string, !om.integer>
%true = om.constant true
om.class.field @true, %true : i1
%false = om.constant false
om.class.field @false, %false : i1
om.class.fields %param, %0, %sym, %list, %tuple, %2, %3, %map, %true, %false : !om.integer, !om.class.type<@Child>, !om.ref, !om.list<!om.string>, tuple<!om.list<!om.string>, !om.integer>, !om.class.type<@Nest>, !om.map<!om.string, !om.integer>, !om.map<!om.string, !om.integer>, i1, i1
}
om.class @Child(%0: !om.integer) {
om.class.field @foo, %0 : !om.integer
om.class @Child(%0: !om.integer) -> (foo: !om.integer) {
om.class.fields %0 : !om.integer
}
om.class @Nest(%0: !om.list<!om.class.type<@Child>>) {
om.class.field @list_child, %0 : !om.list<!om.class.type<@Child>>
om.class @Nest(%0: !om.list<!om.class.type<@Child>>) -> (list_child: !om.list<!om.class.type<@Child>>) {
om.class.fields %0 : !om.list<!om.class.type<@Child>>
}
hw.module @Root(in %clock: i1) {
%0 = sv.wire sym @x : !hw.inout<i1>
}
om.class @Paths(%basepath: !om.frozenbasepath) {
om.class @Paths(%basepath: !om.frozenbasepath) -> (path: !om.frozenpath, deleted: !om.frozenpath) {
%0 = om.frozenbasepath_create %basepath "Foo/bar"
%1 = om.frozenpath_create reference %0 "Bar/baz:Baz>w"
om.class.field @path, %1 : !om.frozenpath
%3 = om.frozenpath_empty
om.class.field @deleted, %3 : !om.frozenpath
om.class.fields %1, %3 : !om.frozenpath, !om.frozenpath
}
om.class @Class1(%input: !om.integer) {
om.class @Class1(%input: !om.integer) -> (value: !om.integer, input: !om.integer) {
%0 = om.constant #om.integer<1 : si3> : !om.integer
om.class.field @value, %0 : !om.integer
om.class.field @input, %input : !om.integer
om.class.fields %0, %input : !om.integer, !om.integer
}
om.class @Class2() {
om.class @Class2() -> (value: !om.integer) {
%0 = om.constant #om.integer<2 : si3> : !om.integer
om.class.field @value, %0 : !om.integer
om.class.fields %0 : !om.integer
}
om.class @IntegerBinaryArithmeticObjectsDelayed() {
om.class @IntegerBinaryArithmeticObjectsDelayed() -> (result: !om.integer) {
%0 = om.object @Class1(%5) : (!om.integer) -> !om.class.type<@Class1>
%1 = om.object.field %0, [@value] : (!om.class.type<@Class1>) -> !om.integer
%2 = om.object @Class2() : () -> !om.class.type<@Class2>
%3 = om.object.field %2, [@value] : (!om.class.type<@Class2>) -> !om.integer
%5 = om.integer.add %1, %3 : !om.integer
om.class.field @result, %5 : !om.integer
om.class.fields %5 : !om.integer
}
}
""")
Expand Down Expand Up @@ -157,23 +144,23 @@
print(obj.field)

# location of the om.class.field @field
# CHECK: loc("-":27:7)
print(obj.get_field_loc("field"))
# CHECK: loc("-":22:20)
print("field:", obj.get_field_loc("field"))

# CHECK: 14
print(obj.child.foo)
# CHECK: loc("-":65:7)
# CHECK: loc("-":53:21)
print(obj.child.get_field_loc("foo"))
# CHECK: ('Root', 'x')
print(obj.reference)
(fst, snd) = obj.tuple
# CHECK: 14
print(snd)

# CHECK: loc("-":39:7)
print(obj.get_field_loc("tuple"))
# CHECK: loc("-":31:16)
print("tuple", obj.get_field_loc("tuple"))

# CHECK: loc("-":25:5)
# CHECK: loc("-":22:5)
print(obj.loc)

try:
Expand All @@ -185,13 +172,13 @@
for (name, field) in obj:
# location from om.class.field @child, %0 : !om.class.type<@Child>
# CHECK: name: child, field: <circt.dialects.om.Object object
# CHECK-SAME: loc: loc("-":30:12)
# CHECK-SAME: loc: loc("-":26:12)
# location from om.class.field @field, %param : !om.integer
# CHECK: name: field, field: 42
# CHECK-SAME: loc: loc("-":27:7)
# CHECK-SAME: loc: loc("-":22:20)
# location from om.class.field @reference, %sym : !om.ref
# CHECK: name: reference, field: ('Root', 'x')
# CHECK-SAME: loc: loc("-":33:7)
# CHECK-SAME: loc: loc("-":23:14)
loc = obj.get_field_loc(name)
print(f"name: {name}, field: {field}, loc: {loc}")

Expand Down Expand Up @@ -260,12 +247,12 @@
base_path_type = paths_class.regions[0].blocks[0].arguments[0].type
assert isinstance(base_path_type, om.BasePathType)

paths_fields = [
op for op in paths_class.regions[0].blocks[0]
if isinstance(op, om.ClassFieldOp)
]
for paths_field in paths_fields:
assert isinstance(paths_field.value.type, om.PathType)
paths_ops = paths_class.regions[0].blocks[0].operations
# NOTE: would be nice if this supported [-1] indexing syntax
class_fields_op = paths_ops[len(paths_ops) - 1]
assert len(class_fields_op.operands)
for arg in class_fields_op.operands:
assert isinstance(arg.type, om.PathType)

delayed = evaluator.instantiate("IntegerBinaryArithmeticObjectsDelayed")

Expand Down
Loading

0 comments on commit c8887e0

Please sign in to comment.