diff --git a/pyproject.toml b/pyproject.toml index d5f5b4fb9c..99552dd600 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ irdl-to-pyrdl = "xdsl.tools.irdl_to_pyrdl:main" xdsl-run = "xdsl.tools.xdsl_run:main" xdsl-gui = "xdsl.interactive.app:main" xdsl-stubgen = "xdsl.utils.dialect_stub:make_all_stubs" +xdsl-tblgen = "xdsl.tools.tblgen_to_py:main" [tool.setuptools] platforms = ["Linux", "Mac OS-X", "Unix"] diff --git a/tests/tblgen_to_py/test.json b/tests/tblgen_to_py/test.json new file mode 100644 index 0000000000..94be79a562 --- /dev/null +++ b/tests/tblgen_to_py/test.json @@ -0,0 +1,1015 @@ +{ + "!instanceof": { + "AttrDef": [ + "Test_TestAttr" + ], + "Dialect": [ + "Test_Dialect" + ], + "Op": [ + "Test_AndOp", + "Test_AnyOp", + "Test_AttributesOp", + "Test_ConfinedOp", + "Test_Integers", + "Test_OrOp", + "Test_SummaryOp", + "Test_TypesOp", + "Test_VariadicityOp" + ], + "TypeDef": [ + "Test_SingletonAType", + "Test_SingletonBType", + "Test_SingletonCType" + ] + }, + "AnyI8": { + "!name": "AnyI8", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "AnyI" + ], + "bitwidth": 8, + "summary": "8-bit integer" + }, + "AnyInteger": { + "!name": "AnyInteger", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type" + ], + "summary": "integer" + }, + "AnyType": { + "!name": "AnyType", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type" + ], + "summary": "any type" + }, + "F32": { + "!name": "F32", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType", + "F" + ], + "bitwidth": 32, + "summary": "32-bit float" + }, + "F8E4M3FN": { + "!name": "F8E4M3FN", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType" + ], + "summary": "f8E4M3FN type" + }, + "I16": { + "!name": "I16", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType", + "I" + ], + "bitwidth": 16, + "summary": "16-bit signless integer" + }, + "I16Attr": { + "!name": "I16Attr", + "!superclasses": [ + "Constraint", + "AttrConstraint", + "Attr", + "TypedAttrBase", + "SignlessIntegerAttrBase", + "TypedSignlessIntegerAttrBase" + ], + "baseAttr": null, + "summary": "16-bit signless integer attribute", + "valueType": { + "def": "I16", + "kind": "def", + "printable": "I16" + } + }, + "I32": { + "!name": "I32", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType", + "I" + ], + "bitwidth": 32, + "summary": "32-bit signless integer" + }, + "Index": { + "!name": "Index", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType" + ], + "summary": "index" + }, + "NoneType": { + "!name": "NoneType", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType" + ], + "summary": "none type" + }, + "SI64": { + "!name": "SI64", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType", + "SI" + ], + "bitwidth": 64, + "summary": "64-bit signed integer" + }, + "Test_AndOp": { + "!name": "Test_AndOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "anonymous_330", + "kind": "def", + "printable": "anonymous_330" + }, + "in" + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins anonymous_330:$in)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "and", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "Test_AnyOp": { + "!name": "Test_AnyOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "AnyType", + "kind": "def", + "printable": "AnyType" + }, + "in" + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins AnyType:$in)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "any", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "Test_AttributesOp": { + "!name": "Test_AttributesOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "I16Attr", + "kind": "def", + "printable": "I16Attr" + }, + "int_attr" + ], + [ + { + "def": "Test_TestAttr", + "kind": "def", + "printable": "Test_TestAttr" + }, + "in" + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins I16Attr:$int_attr, Test_TestAttr:$test_attr)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "attributes", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "Test_ConfinedOp": { + "!name": "Test_ConfinedOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "anonymous_333", + "kind": "def", + "printable": "anonymous_333" + }, + "tensor" + ], + [ + { + "def": "anonymous_336", + "kind": "def", + "printable": "anonymous_336" + }, + "vector" + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins anonymous_333:$tensor, anonymous_336:$vector)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "confined", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "Test_Dialect": { + "!name": "Test_Dialect", + "!superclasses": [ + "Dialect" + ], + "name": "test", + "summary": null + }, + "Test_Integers": { + "!name": "Test_Integers", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "AnyI8", + "kind": "def", + "printable": "AnyI8" + }, + "any_int" + ], + [ + { + "def": "AnyInteger", + "kind": "def", + "printable": "AnyInteger" + }, + "any_integer" + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins AnyI8:$any_int, AnyInteger:$any_integer)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "integers", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "Test_OrOp": { + "!name": "Test_OrOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "anonymous_338", + "kind": "def", + "printable": "anonymous_338" + }, + "in" + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins anonymous_338:$in)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "or", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "Test_SingletonAType": { + "!name": "Test_SingletonAType", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "DialectType", + "AttrOrTypeDef", + "TypeDef", + "Test_Type" + ], + "assemblyFormat": null, + "dialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "summary": "", + "typeName": "test.singleton_a" + }, + "Test_SingletonBType": { + "!name": "Test_SingletonBType", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "DialectType", + "AttrOrTypeDef", + "TypeDef", + "Test_Type" + ], + "assemblyFormat": null, + "dialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "summary": "", + "typeName": "test.singleton_b" + }, + "Test_SingletonCType": { + "!name": "Test_SingletonCType", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "DialectType", + "AttrOrTypeDef", + "TypeDef", + "Test_Type" + ], + "assemblyFormat": null, + "dialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "summary": "", + "typeName": "test.singleton_c" + }, + "Test_SummaryOp": { + "!name": "Test_SummaryOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "summary", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "Op with a summary." + }, + "Test_TestAttr": { + "!name": "Test_TestAttr", + "!superclasses": [ + "Constraint", + "AttrConstraint", + "Attr", + "DialectAttr", + "AttrOrTypeDef", + "AttrDef", + "Test_Attr" + ], + "assemblyFormat": null, + "attrName": "test.test", + "baseAttr": null, + "dialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "summary": "", + "valueType": null + }, + "Test_TypesOp": { + "!name": "Test_TypesOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "I32", + "kind": "def", + "printable": "I32" + }, + "a" + ], + [ + { + "def": "SI64", + "kind": "def", + "printable": "SI64" + }, + "b" + ], + [ + { + "def": "UI8", + "kind": "def", + "printable": "UI8" + }, + "c" + ], + [ + { + "def": "Index", + "kind": "def", + "printable": "Index" + }, + "d" + ], + [ + { + "def": "F32", + "kind": "def", + "printable": "F32" + }, + "e" + ], + [ + { + "def": "NoneType", + "kind": "def", + "printable": "NoneType" + }, + "f" + ], + [ + { + "def": "anonymous_340", + "kind": "def", + "printable": "anonymous_340" + }, + null + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins I32:$a, SI64:$b, UI8:$c, Index:$d, F32:$e, NoneType:$f, anonymous_340)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "types", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "Test_VariadicityOp": { + "!name": "Test_VariadicityOp", + "!superclasses": [ + "Op", + "Test_Op" + ], + "arguments": { + "args": [ + [ + { + "def": "anonymous_343", + "kind": "def", + "printable": "anonymous_343" + }, + "variadic" + ], + [ + { + "def": "Test_SingletonCType", + "kind": "def", + "printable": "Test_SingletonCType" + }, + "required" + ] + ], + "kind": "dag", + "operator": { + "def": "ins", + "kind": "def", + "printable": "ins" + }, + "printable": "(ins anonymous_343:$variadic, anonymous_344:$optional, Test_SingletonCType:$required)" + }, + "assemblyFormat": null, + "opDialect": { + "def": "Test_Dialect", + "kind": "def", + "printable": "Test_Dialect" + }, + "opName": "variadicity", + "regions": { + "args": [], + "kind": "dag", + "operator": { + "def": "region", + "kind": "def", + "printable": "region" + }, + "printable": "(region)" + }, + "results": { + "args": [], + "kind": "dag", + "operator": { + "def": "outs", + "kind": "def", + "printable": "outs" + }, + "printable": "(outs)" + }, + "successors": { + "args": [], + "kind": "dag", + "operator": { + "def": "successor", + "kind": "def", + "printable": "successor" + }, + "printable": "(successor)" + }, + "summary": "" + }, + "UI8": { + "!name": "UI8", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "BuildableType", + "UI" + ], + "bitwidth": 8, + "summary": "8-bit unsigned integer" + }, + "anonymous_330": { + "!name": "anonymous_330", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "AllOfType" + ], + "allowedTypes": [ + { + "def": "Test_SingletonAType", + "kind": "def", + "printable": "Test_SingletonAType" + }, + { + "def": "AnyType", + "kind": "def", + "printable": "AnyType" + } + ], + "summary": " and any type" + }, + "anonymous_333": { + "!name": "anonymous_333", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "ConfinedType" + ], + "baseType": { + "def": "AnyType", + "kind": "def", + "printable": "AnyType" + }, + "summary": "" + }, + "anonymous_336": { + "!name": "anonymous_336", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "ConfinedType" + ], + "baseType": { + "def": "AnyType", + "kind": "def", + "printable": "AnyType" + }, + "summary": "" + }, + "anonymous_338": { + "!name": "anonymous_338", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "AnyTypeOf" + ], + "allowedTypes": [ + { + "def": "Test_SingletonAType", + "kind": "def", + "printable": "Test_SingletonAType" + }, + { + "def": "Test_SingletonBType", + "kind": "def", + "printable": "Test_SingletonBType" + }, + { + "def": "Test_SingletonCType", + "kind": "def", + "printable": "Test_SingletonCType" + } + ], + "summary": " or or " + }, + "anonymous_340": { + "!name": "anonymous_340", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Type", + "ConfinedType", + "SameBuildabilityAs", + "Complex" + ], + "baseType": { + "def": "AnyComplex", + "kind": "def", + "printable": "AnyComplex" + }, + "elementType": { + "def": "F8E4M3FN", + "kind": "def", + "printable": "F8E4M3FN" + }, + "summary": "complex type with f8E4M3FN type elements" + }, + "anonymous_343": { + "!name": "anonymous_343", + "!superclasses": [ + "Constraint", + "TypeConstraint", + "Variadic" + ], + "baseType": { + "def": "Test_SingletonAType", + "kind": "def", + "printable": "Test_SingletonAType" + }, + "summary": "variadic of " + } +} diff --git a/tests/tblgen_to_py/test.py b/tests/tblgen_to_py/test.py new file mode 100644 index 0000000000..bd40ad8a73 --- /dev/null +++ b/tests/tblgen_to_py/test.py @@ -0,0 +1,162 @@ +""" +This file is automatically generated by xDSL and not meant to be modified. + +It was generated from tests/tblgen_to_py/test.json +""" + +# ruff: noqa: F403, F405 + +from xdsl.dialects.builtin import * +from xdsl.ir import * +from xdsl.irdl import * + + +@irdl_attr_definition +class Test_SingletonAType(ParametrizedAttribute, TypeAttribute): + name = "test.singleton_a" + + +@irdl_attr_definition +class Test_SingletonBType(ParametrizedAttribute, TypeAttribute): + name = "test.singleton_b" + + +@irdl_attr_definition +class Test_SingletonCType(ParametrizedAttribute, TypeAttribute): + name = "test.singleton_c" + + +@irdl_attr_definition +class Test_TestAttr(ParametrizedAttribute): + name = "test.test" + + +@irdl_op_definition +class Test_AndOp(IRDLOperation): + name = "test.and" + + in_ = operand_def(AllOf((BaseAttr(Test_SingletonAType), AnyAttr()))) + + +@irdl_op_definition +class Test_AnyOp(IRDLOperation): + name = "test.any" + + in_ = operand_def(AnyAttr()) + + +@irdl_op_definition +class Test_AttributesOp(IRDLOperation): + name = "test.attributes" + + int_attr = prop_def( + ParamAttrConstraint( + IntegerAttr, + ( + AnyAttr(), + EqAttrConstraint(IntegerType(16)), + ), + ) + ) + + in_ = prop_def(BaseAttr(Test_TestAttr), prop_name="in") + + +@irdl_op_definition +class Test_ConfinedOp(IRDLOperation): + name = "test.confined" + + tensor = operand_def(AnyAttr()) + + vector = operand_def(AnyAttr()) + + +@irdl_op_definition +class Test_Integers(IRDLOperation): + name = "test.integers" + + any_int = operand_def( + ParamAttrConstraint( + IntegerType, + (EqAttrConstraint(IntAttr(8)), AnyAttr()), + ) + ) + + any_integer = operand_def(BaseAttr(IntegerType)) + + +@irdl_op_definition +class Test_OrOp(IRDLOperation): + name = "test.or" + + in_ = operand_def( + AnyOf( + ( + BaseAttr(Test_SingletonAType), + BaseAttr(Test_SingletonBType), + BaseAttr(Test_SingletonCType), + ) + ) + ) + + +@irdl_op_definition +class Test_SummaryOp(IRDLOperation): + """Op with a summary.""" + + name = "test.summary" + + +@irdl_op_definition +class Test_TypesOp(IRDLOperation): + name = "test.types" + + a = operand_def(EqAttrConstraint(IntegerType(32))) + + b = operand_def(EqAttrConstraint(IntegerType(64, Signedness.SIGNED))) + + c = operand_def(EqAttrConstraint(IntegerType(8, Signedness.UNSIGNED))) + + d = operand_def(EqAttrConstraint(IndexType())) + + e = operand_def(EqAttrConstraint(Float32Type())) + + f = operand_def(EqAttrConstraint(NoneType())) + + v1 = operand_def( + ParamAttrConstraint( + ComplexType, + (AnyAttr(),), + ) + ) + + +@irdl_op_definition +class Test_VariadicityOp(IRDLOperation): + name = "test.variadicity" + + variadic = var_operand_def(BaseAttr(Test_SingletonAType)) + + required = operand_def(BaseAttr(Test_SingletonCType)) + + +Test_Dialect = Dialect( + "test", + [ + Test_AndOp, + Test_AnyOp, + Test_AttributesOp, + Test_ConfinedOp, + Test_Integers, + Test_OrOp, + Test_SummaryOp, + Test_TypesOp, + Test_VariadicityOp, + ], + [ + Test_SingletonAType, + Test_SingletonBType, + Test_SingletonCType, + Test_TestAttr, + ], +) diff --git a/tests/tblgen_to_py/test_tblgen.py b/tests/tblgen_to_py/test_tblgen.py new file mode 100644 index 0000000000..56f9d0eafc --- /dev/null +++ b/tests/tblgen_to_py/test_tblgen.py @@ -0,0 +1,15 @@ +from io import StringIO + +from xdsl.tools.tblgen_to_py import tblgen_to_py + + +def test_run_tblgen_to_py(): + with StringIO() as output: + tblgen_to_py("tests/tblgen_to_py/test.json", output) + out_str = output.getvalue() + + with open("tests/tblgen_to_py/test.py") as f: + expected = f.read() + + assert len(out_str.strip()) == len(expected.strip()) + assert out_str.strip() == expected.strip() diff --git a/xdsl/tools/tblgen_to_py.py b/xdsl/tools/tblgen_to_py.py new file mode 100644 index 0000000000..554fac9060 --- /dev/null +++ b/xdsl/tools/tblgen_to_py.py @@ -0,0 +1,643 @@ +""" +This file implements the tool `xdsl-tblgen`. + +The primary function of `xdsl-tblgen` is to convert dialects defined in mlir +tablegen to python code which defines an xdsl dialect. To import tablegen into +python, it must first be converted to json using the tool `llvm-tblgen` with the +`--dump-json flag`. The generated json should then be input to `xdsl-tblgen` via +stdin or an input file. + +Example: +Suppose ${LLVM} is the filepath for the llvm repository with `llvm-tblgen` +built. A test dialect can then be generated by: +```bash +${LLVM}/bin/llvm-tblgen -D test ${LLVM}/mlir/test/tblgen-to-irdl/TestDialect.td -I ${LLVM}/mlir/include --dump-json | xdsl-tblgen +``` +In the above, "test" is the name of the dialect to generate, the first file is +the location of the tablegen file, and the folder after `-I` contains common +tablegen definition which the dialect depends on. +""" + +import argparse +import json +import subprocess +import textwrap +from dataclasses import dataclass, field +from enum import Enum +from io import StringIO +from keyword import iskeyword +from sys import stdin +from typing import IO, Any + + +@dataclass +class TblgenRecord: + js: Any + + def __getitem__(self, i: str) -> Any: + return self.js[i] + + @property + def name(self) -> str: + return self["!name"] + + @property + def summary(self) -> str: + return self["summary"] + + def print_summary(self): + return "" if self.summary == "" else f'"""{self.summary}"""' + + @property + def superclasses(self) -> set[str]: + return set(self["!superclasses"]) + + @property + def dialect(self) -> str: + return self["dialect"]["def"] + + +class TblgenOp(TblgenRecord): + @property + def dialect(self) -> str: + return self["opDialect"]["def"] + + @property + def op_name(self) -> str: + return self["opName"] + + @property + def assembly_format(self) -> str | None: + if "assemblyFormat" in self.js: + assembly = self["assemblyFormat"] + if isinstance(assembly, str): + return assembly + return None + + def _dag_to_py(self, dag: str) -> tuple[tuple[str, str], ...]: + args = self[dag]["args"] + return tuple((a["def"], n) for a, n in args) + + @property + def arguments(self) -> tuple[tuple[str, str], ...]: + return self._dag_to_py("arguments") + + @property + def results(self) -> tuple[tuple[str, str], ...]: + return self._dag_to_py("results") + + @property + def regions(self) -> tuple[tuple[str, str], ...]: + return self._dag_to_py("regions") + + @property + def successors(self) -> tuple[tuple[str, str], ...]: + return self._dag_to_py("successors") + + +class TblgenType(TblgenRecord): + @property + def type_name(self) -> str: + return self["typeName"] + + +class TblgenAttr(TblgenRecord): + @property + def attr_name(self) -> str: + return self["attrName"] + + +@dataclass +class TblgenLoader: + """ + Class for converting a json generated by `llvm-tblgen --dump-json` to python + code which builds an xDSL dialect. Must be initialised with json represented + as a python object. The generated code is stored as strings in the + `attributes` and `operations` dictionaries. + """ + + js: Any + + attributes: dict[str, str] = field(default_factory=dict) + operations: dict[str, str] = field(default_factory=dict) + used_records: set[str] = field(default_factory=set) + anon_counter: int = field(default_factory=int) + + def _get_op(self, name: str) -> TblgenOp: + self.used_records.add(name) + return TblgenOp(self.js[name]) + + def _get_type(self, name: str) -> TblgenType: + self.used_records.add(name) + return TblgenType(self.js[name]) + + def _get_attr(self, name: str) -> TblgenAttr: + self.used_records.add(name) + return TblgenAttr(self.js[name]) + + def _get_record(self, name: str) -> TblgenRecord: + self.used_records.add(name) + return TblgenRecord(self.js[name]) + + def get_dialect_name(self, dialect: str) -> str: + self.used_records.add(dialect) + return self.js[dialect]["name"] + + def generate_dialect(self, tblgen_dialect: str): + """ + Generate a dialect from the json object, generating all its contained + operations, types, and attributes and generating python code which + is stored in this class' fields. + """ + dialect_name = self.get_dialect_name(tblgen_dialect) + + # Get types + all_types = self.js["!instanceof"]["TypeDef"] + for t in all_types: + ty = self._get_type(t) + if ty.dialect == tblgen_dialect: + self.generate_type(ty) + + # Get attributes + all_attrs = self.js["!instanceof"]["AttrDef"] + for a in all_attrs: + attr = self._get_attr(a) + if attr.dialect == tblgen_dialect: + self.generate_attr(attr) + + # Get ops + all_ops = self.js["!instanceof"]["Op"] + for o in all_ops: + op = self._get_op(o) + if op.dialect == tblgen_dialect: + self.generate_op(op, dialect_name) + + def generate_type(self, tblgen_type: TblgenType): + """ + Generate a type from the json object, storing python code for it in + `self.attributes`. + """ + + string = textwrap.dedent(f""" + @irdl_attr_definition + class {tblgen_type.name}(ParametrizedAttribute, TypeAttribute): + {tblgen_type.print_summary()} + name = "{tblgen_type.type_name}" + """) + + self.attributes[tblgen_type.name] = string + + def generate_attr(self, tblgen_attr: TblgenAttr): + """ + Generate an attribute from the json object, storing python code for it in + `self.attributes`. + """ + + string = textwrap.dedent(f""" + @irdl_attr_definition + class {tblgen_attr.name}(ParametrizedAttribute): + {tblgen_attr.print_summary()} + name = "{tblgen_attr.attr_name}" + """) + + self.attributes[tblgen_attr.name] = string + + class _ArgType(Enum): + SINGLE = 0 + VARIADIC = 1 + OPTIONAL = 2 + PROP = 3 + OPTIONAL_PROP = 4 + + def _resolve_type_constraint(self, rec: TblgenRecord | str) -> str: + if isinstance(rec, str): + rec = self._get_record(rec) + if rec.name in self.attributes: + return f"BaseAttr({rec.name})" + + # match specific types + match rec.name: + case "NoneType": + return "EqAttrConstraint(NoneType())" + case "AnyInteger": + return "BaseAttr(IntegerType)" + case "AnySignlessInteger": + return textwrap.dedent(""" + ParamAttrConstraint( + IntegerType, + (AnyAttr(), EqAttrConstraint(SignednessAttr(Signedness.SIGNLESS))), + )""") + case "AnySignedInteger": + return textwrap.dedent(""" + ParamAttrConstraint( + IntegerType, + (AnyAttr(), EqAttrConstraint(SignednessAttr(Signedness.SIGNED))), + ) + """) + case "AnyUnsignedInteger": + return textwrap.dedent(""" + ParamAttrConstraint( + IntegerType, + (AnyAttr(), EqAttrConstraint(SignednessAttr(Signedness.UNSIGNED))), + ) + """) + case "Index": + return "EqAttrConstraint(IndexType())" + case "F16": + return "EqAttrConstraint(Float16Type())" + case "F32": + return "EqAttrConstraint(Float32Type())" + case "F64": + return "EqAttrConstraint(Float64Type())" + case "F80": + return "EqAttrConstraint(Float80Type())" + case "F128": + return "EqAttrConstraint(Float128Type())" + case "BF16": + return "EqAttrConstraint(BFloat16Type())" + case "AnyFloat": + return "AnyFloatConstr" + case "AnyComplex": + return "BaseAttr(ComplexType)" + case _: + pass + if "AnyTypeOf" in rec.superclasses: + return textwrap.dedent(f""" + AnyOf( + ( + {",".join(self._resolve_type_constraint(x["def"]) for x in rec["allowedTypes"])} + ) + ) + """) + + if "AllOfType" in rec.superclasses: + return textwrap.dedent(f""" + AllOf( + ( + {",".join(self._resolve_type_constraint(x["def"]) for x in rec["allowedTypes"])} + ) + ) + """) + + if "AnyI" in rec.superclasses: + return textwrap.dedent(f""" + ParamAttrConstraint( + IntegerType, + (EqAttrConstraint(IntAttr({rec["bitwidth"]})), AnyAttr()), + ) + """) + + if "I" in rec.superclasses: + return f"EqAttrConstraint(IntegerType({rec['bitwidth']}))" + if "SI" in rec.superclasses: + return ( + f"EqAttrConstraint(IntegerType({rec['bitwidth']}, Signedness.SIGNED))" + ) + if "UI" in rec.superclasses: + return ( + f"EqAttrConstraint(IntegerType({rec['bitwidth']}, Signedness.UNSIGNED))" + ) + if "Complex" in rec.superclasses: + return textwrap.dedent(f""" + ParamAttrConstraint( + ComplexType, + ({self._resolve_type_constraint(rec["elementType"]["def"])},), + ) + """) + + return "AnyAttr()" + + def _resolve_prop_constraint(self, rec: TblgenRecord | str) -> str: + if isinstance(rec, str): + rec = self._get_record(rec) + + if rec.name in self.attributes: + return f"BaseAttr({rec.name})" + + match rec.name: + case "BoolAttr": + return "BaseAttr(BoolAttr)" + case "IndexAttr": + return textwrap.dedent(""" + ParamAttrConstraint( + IntegerAttr, (AnyAttr(), EqAttrConstraint(IndexType())) + ) + """) + + case "APIntAttr": + return textwrap.dedent(""" + ParamAttrConstraint( + IntegerAttr, (AnyAttr(), AnyAttr()) + ) + """) # TODO can't represent APInt properly + + case "StrAttr": + return "BaseAttr(StringAttr)" + case "SymbolNameAttr": + return "BaseAttr(SymbolNameAttr)" + case "UnitAttr": + return "EqAttrConstraint(UnitAttr())" + case _: + pass + + if "AnyAttrOf" in rec.superclasses: + return textwrap.dedent(f""" + AnyOf( + {",".join(self._resolve_prop_constraint(x["def"]) for x in rec["allowedAttributes"])} + ) + ) + """) + + if ( + "AnyIntegerAttrBase" in rec.superclasses + or "SignlessIntegerAttrBase" in rec.superclasses + or "SignedIntegerAttrBase" in rec.superclasses + or "UnsignedIntegerAttrBase" in rec.superclasses + ): + return textwrap.dedent(f""" + ParamAttrConstraint( + IntegerAttr, + ( + AnyAttr(), + {self._resolve_type_constraint(rec["valueType"]["def"])}, + ), + ) + """) + + if "FloatAttrBase" in rec.superclasses: + return textwrap.dedent(f""" + ParamAttrConstraint( + FloatAttr, + ( + AnyAttr(), + {self._resolve_type_constraint(rec["valueType"]["def"])}, + ), + ) + """) + + return "AnyAttr()" + + def _resolve_name(self, name: Any) -> str: + if isinstance(name, str): + if iskeyword(name): + return f"{name}_" + return name + + self.anon_counter += 1 + return f"v{self.anon_counter}" + + def _resolve_constraint(self, rec: TblgenRecord | str) -> tuple[_ArgType, str]: + if isinstance(rec, str): + rec = self._get_record(rec) + + superclasses = rec.superclasses + if "Variadic" in superclasses: + return ( + self._ArgType.VARIADIC, + self._resolve_type_constraint(rec["baseType"]["def"]), + ) + elif "Optional" in superclasses: + return ( + self._ArgType.OPTIONAL, + self._resolve_type_constraint(rec["baseType"]["def"]), + ) + elif "Type" in superclasses: + return (self._ArgType.SINGLE, self._resolve_type_constraint(rec)) + elif "OptionalAttr" in superclasses: + return ( + self._ArgType.OPTIONAL_PROP, + self._resolve_prop_constraint(rec["baseAttr"]), + ) + else: + return (self._ArgType.PROP, self._resolve_prop_constraint(rec)) + + def generate_op(self, tblgen_op: TblgenOp, dialect_name: str): + """ + Generate an operation from the json object, storing python code for it in + `self.operations`. + """ + + fields = {"name": f'"{dialect_name}.{tblgen_op.op_name}"'} + + assembly = tblgen_op.assembly_format + if assembly is not None and "custom" not in assembly: + fields["assembly_format"] = assembly + + for [arg, orig_name] in tblgen_op.arguments: + name = self._resolve_name(orig_name) + (variadicity, constraint) = self._resolve_constraint(arg) + match variadicity: + case self._ArgType.SINGLE: + fields[name] = f"operand_def({constraint})" + case self._ArgType.OPTIONAL: + fields[name] = f"opt_operand_def({constraint})" + case self._ArgType.VARIADIC: + fields[name] = f"var_operand_def({constraint})" + case self._ArgType.PROP: + name_str = ( + f', prop_name = "{orig_name}"' if iskeyword(orig_name) else "" + ) + fields[name] = f"prop_def({constraint}{name_str})" + case self._ArgType.OPTIONAL_PROP: + name_str = ( + f', prop_name = "{orig_name}"' if iskeyword(orig_name) else "" + ) + fields[name] = f"opt_prop_def({constraint}{name_str})" + + for [res, name] in tblgen_op.results: + name = self._resolve_name(name) + (variadicity, constraint) = self._resolve_constraint(res) + match variadicity: + case self._ArgType.SINGLE: + fields[name] = f"result_def({constraint})" + case self._ArgType.OPTIONAL: + fields[name] = f"opt_result_def({constraint})" + case self._ArgType.VARIADIC: + fields[name] = f"var_result_def({constraint})" + case _: + continue + + for [region, name] in tblgen_op.regions: + name = self._resolve_name(name) + region = self._get_record(region) + variadic = "VariadicRegion" in region.superclasses + single_block = ( + "SizedRegion" in region.superclasses + and region.summary == "region with 1 blocks" + ) + match (variadic, single_block): + case (False, False): + fields[name] = "region_def()" + case (False, True): + fields[name] = 'region_def("single_block")' + case (True, False): + fields[name] = "var_region_def()" + case (True, True): + fields[name] = 'var_region_def("single_block")' + case _: + pass # Make pyright happy + + for [succ, name] in tblgen_op.successors: + name = self._resolve_name(name) + succ = self._get_record(succ) + if "VariadicRegion" in succ.superclasses: + fields[name] = "var_successor_def()" + else: + fields[name] = "successor_def()" + + field_string = textwrap.indent( + "\n\n".join(f"{x} = {d}" for x, d in fields.items()), " " + ) + string = f""" +@irdl_op_definition +class {tblgen_op.name}(IRDLOperation): + {tblgen_op.print_summary()} + +{field_string} +""" + + self.operations[tblgen_op.name] = string + + +def cull_json(output_file: IO[str] | None, loader: TblgenLoader): + js = loader.js + required_fields = { + "!name", + "!superclasses", + "assemblyFormat", + "summary", + "dialect", + "opDialect", + "typeName", + "attrName", + "opName", + "arguments", + "results", + "regions", + "successors", + "allowedTypes", + "bitwidth", + "elementType", + "valueType", + "baseType", + "baseAttr", + "def", + "name", + } + + def cull_field(js_in: dict[str, Any]) -> dict[str, Any]: + return {key: js_in[key] for key in js_in if key in required_fields} + + culled: dict[str, Any] = {key: cull_field(js[key]) for key in loader.used_records} + culled["!instanceof"] = { + key: js["!instanceof"][key] for key in ("TypeDef", "AttrDef", "Op", "Dialect") + } + + print(json.dumps(culled), file=output_file) + + +def tblgen_to_dialect( + input_file: str | None, + tblgen_dialect: str, + output_file: IO[str] | None, + loader: TblgenLoader, +): + dialect = loader.get_dialect_name(tblgen_dialect) + with StringIO() as out_str: + in_file = "stdin" if input_file is None else input_file + print( + textwrap.dedent(f"""\ + \""" + This file is automatically generated by xDSL and not meant to be modified. + + It was generated from {in_file} + \""" + + # ruff: noqa: F403, F405 + + from xdsl.dialects.builtin import * + from xdsl.ir import * + from xdsl.irdl import * + """), + file=out_str, + ) + + for attr in loader.attributes.values(): + print(attr, file=out_str) + + for op in loader.operations.values(): + print(op, file=out_str) + + print( + textwrap.dedent(f"""\ + {tblgen_dialect} = Dialect( + "{dialect}", + [{" ".join(f"{key}," for key in loader.operations.keys())}], + [{" ".join(f"{key}," for key in loader.attributes.keys())}], + )"""), + file=out_str, + ) + + content = out_str.getvalue() + + # Format output + output = subprocess.run( + [ + "ruff", + "format", + "--stdin-filename", + f"{dialect}.py", + ], + input=content, + capture_output=True, + text=True, + ) + + print(output.stdout, file=output_file, end="") + + +def tblgen_to_py( + input_file: str | None, output: IO[str] | None = None, *, cull: bool = False +): + if input_file is None: + in_file = stdin + else: + in_file = open(input_file) + + with in_file as file: + js = json.load(file) + loader = TblgenLoader(js) + dialects = js["!instanceof"]["Dialect"] + [dialect] = dialects + loader.generate_dialect(dialect) + + if cull: + cull_json(output, loader) + else: + tblgen_to_dialect(input_file, dialect, output, loader) + + +def main(): + # Parse CLI arguments + arg_parser = argparse.ArgumentParser( + description="Convert tblgen json to a Python definition of a xDSL dialect." + ) + arg_parser.add_argument( + "-o", "--output-file", required=False, type=str, help="path to output file" + ) + arg_parser.add_argument( + "-i", "--input_file", required=False, type=str, help="path to input file" + ) + arg_parser.add_argument( + "-c", + "--cull", + action="store_true", + help="Output a culled json with only necessary fields", + ) + args = arg_parser.parse_args() + + if args.output_file is not None: + with open(args.output_file, "w") as output: + tblgen_to_py(args.input_file, output, cull=args.cull) + else: + tblgen_to_py(args.input_file, cull=args.cull)