From abfc3879e0f0973b3c8bba8db4f8cd09d19d6434 Mon Sep 17 00:00:00 2001 From: Fabian Schuiki Date: Wed, 25 Oct 2023 10:13:15 -0700 Subject: [PATCH] Bump LLVM to 7ce613fc77af092dd6e9db71ce3747b75bc5616e --- include/circt/Dialect/FIRRTL/NLATable.h | 2 +- lib/Bindings/Python/CMakeLists.txt | 8 - lib/Bindings/Python/dialects/_comb_ops_ext.py | 178 ------ lib/Bindings/Python/dialects/_esi_ops_ext.py | 44 -- lib/Bindings/Python/dialects/_fsm_ops_ext.py | 167 ------ lib/Bindings/Python/dialects/_hw_ops_ext.py | 529 ----------------- .../Python/dialects/_hwarith_ops_ext.py | 76 --- lib/Bindings/Python/dialects/_msft_ops_ext.py | 79 --- lib/Bindings/Python/dialects/_seq_ops_ext.py | 133 ----- lib/Bindings/Python/dialects/_sv_ops_ext.py | 100 ---- lib/Bindings/Python/dialects/comb.py | 201 ++++++- lib/Bindings/Python/dialects/esi.py | 50 +- lib/Bindings/Python/dialects/fsm.py | 169 ++++++ lib/Bindings/Python/dialects/hw.py | 545 +++++++++++++++++- lib/Bindings/Python/dialects/hwarith.py | 85 +++ lib/Bindings/Python/dialects/msft.py | 64 +- lib/Bindings/Python/dialects/seq.py | 140 ++++- lib/Bindings/Python/dialects/sv.py | 105 +++- lib/Conversion/HWToLLVM/HWToLLVM.cpp | 15 +- lib/Dialect/FIRRTL/FIRRTLOpInterfaces.cpp | 2 +- lib/Dialect/FIRRTL/NLATable.cpp | 4 +- llvm | 2 +- .../HWToLLVM/convert_aggregates.mlir | 9 +- 23 files changed, 1362 insertions(+), 1345 deletions(-) delete mode 100644 lib/Bindings/Python/dialects/_comb_ops_ext.py delete mode 100644 lib/Bindings/Python/dialects/_esi_ops_ext.py delete mode 100644 lib/Bindings/Python/dialects/_fsm_ops_ext.py delete mode 100644 lib/Bindings/Python/dialects/_hw_ops_ext.py delete mode 100644 lib/Bindings/Python/dialects/_hwarith_ops_ext.py delete mode 100644 lib/Bindings/Python/dialects/_msft_ops_ext.py delete mode 100644 lib/Bindings/Python/dialects/_seq_ops_ext.py delete mode 100644 lib/Bindings/Python/dialects/_sv_ops_ext.py diff --git a/include/circt/Dialect/FIRRTL/NLATable.h b/include/circt/Dialect/FIRRTL/NLATable.h index 36deaf2e97a3..7fff7537a480 100644 --- a/include/circt/Dialect/FIRRTL/NLATable.h +++ b/include/circt/Dialect/FIRRTL/NLATable.h @@ -165,7 +165,7 @@ class NLATable { /// Remove the NLA from the Module. This updates the module name to NLA /// tracking. void removeNLAfromModule(hw::HierPathOp nla, StringAttr mod) { - llvm::erase_value(nodeMap[mod], nla); + llvm::erase(nodeMap[mod], nla); } /// Remove all the nlas in the set `nlas` from the module. This updates the diff --git a/lib/Bindings/Python/CMakeLists.txt b/lib/Bindings/Python/CMakeLists.txt index e43ebfea2dc4..5a4649c1cbaa 100644 --- a/lib/Bindings/Python/CMakeLists.txt +++ b/lib/Bindings/Python/CMakeLists.txt @@ -71,7 +71,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/CombOps.td SOURCES dialects/comb.py - dialects/_comb_ops_ext.py DIALECT_NAME comb) declare_mlir_dialect_python_bindings( @@ -80,7 +79,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/ESIOps.td SOURCES dialects/esi.py - dialects/_esi_ops_ext.py DIALECT_NAME esi) declare_mlir_dialect_python_bindings( @@ -89,7 +87,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/HWOps.td SOURCES dialects/hw.py - dialects/_hw_ops_ext.py DIALECT_NAME hw) declare_mlir_dialect_python_bindings( @@ -98,7 +95,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/MSFTOps.td SOURCES dialects/msft.py - dialects/_msft_ops_ext.py DIALECT_NAME msft) declare_mlir_dialect_python_bindings( @@ -115,7 +111,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/SeqOps.td SOURCES dialects/seq.py - dialects/_seq_ops_ext.py DIALECT_NAME seq) declare_mlir_dialect_python_bindings( @@ -124,7 +119,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/SVOps.td SOURCES dialects/sv.py - dialects/_sv_ops_ext.py DIALECT_NAME sv) declare_mlir_dialect_python_bindings( @@ -133,7 +127,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/FSMOps.td SOURCES dialects/fsm.py - dialects/_fsm_ops_ext.py DIALECT_NAME fsm) declare_mlir_dialect_python_bindings( @@ -142,7 +135,6 @@ declare_mlir_dialect_python_bindings( TD_FILE dialects/HWArithOps.td SOURCES dialects/hwarith.py - dialects/_hwarith_ops_ext.py DIALECT_NAME hwarith) declare_mlir_dialect_python_bindings( diff --git a/lib/Bindings/Python/dialects/_comb_ops_ext.py b/lib/Bindings/Python/dialects/_comb_ops_ext.py deleted file mode 100644 index 3020deaece18..000000000000 --- a/lib/Bindings/Python/dialects/_comb_ops_ext.py +++ /dev/null @@ -1,178 +0,0 @@ -from . import comb -from ..support import NamedValueOpView, get_value -from ..ir import IntegerAttr, IntegerType - - -# Builder base classes for non-variadic unary and binary ops. -class UnaryOpBuilder(NamedValueOpView): - - def operand_names(self): - return ["input"] - - def result_names(self): - return ["result"] - - -def UnaryOp(base): - - class _Class(base): - - @classmethod - def create(cls, input=None, result_type=None): - mapping = {"input": input} if input else {} - return UnaryOpBuilder(cls, result_type, mapping) - - return _Class - - -class ExtractOpBuilder(UnaryOpBuilder): - - def __init__(self, low_bit, data_type, input_port_mapping={}, **kwargs): - low_bit = IntegerAttr.get(IntegerType.get_signless(32), low_bit) - super().__init__(comb.ExtractOp, data_type, input_port_mapping, [], - [low_bit], **kwargs) - - -class BinaryOpBuilder(NamedValueOpView): - - def operand_names(self): - return ["lhs", "rhs"] - - def result_names(self): - return ["result"] - - -def BinaryOp(base): - - class _Class(base): - - @classmethod - def create(cls, lhs=None, rhs=None, result_type=None): - mapping = {} - if lhs: - mapping["lhs"] = lhs - if rhs: - mapping["rhs"] = rhs - return BinaryOpBuilder(cls, result_type, mapping) - - return _Class - - -# Base classes for the variadic ops. -def VariadicOp(base): - - class _Class(base): - - @classmethod - def create(cls, *args): - return cls([get_value(a) for a in args]) - - return _Class - - -# Base class for miscellaneous ops that can't be abstracted but should provide a -# create method for uniformity. -def CreatableOp(base): - - class _Class(base): - - @classmethod - def create(cls, *args, **kwargs): - return cls(*args, **kwargs) - - return _Class - - -# Sugar classes for the various non-variadic unary ops. -class ExtractOp: - - @staticmethod - def create(low_bit, result_type, input=None): - mapping = {"input": input} if input else {} - return ExtractOpBuilder(low_bit, - result_type, - mapping, - needs_result_type=True) - - -@UnaryOp -class ParityOp: - pass - - -# Sugar classes for the various non-variadic binary ops. -@BinaryOp -class DivSOp: - pass - - -@BinaryOp -class DivUOp: - pass - - -@BinaryOp -class ModSOp: - pass - - -@BinaryOp -class ModUOp: - pass - - -@BinaryOp -class ShlOp: - pass - - -@BinaryOp -class ShrSOp: - pass - - -@BinaryOp -class ShrUOp: - pass - - -@BinaryOp -class SubOp: - pass - - -# Sugar classes for the variadic ops. -@VariadicOp -class AddOp: - pass - - -@VariadicOp -class MulOp: - pass - - -@VariadicOp -class AndOp: - pass - - -@VariadicOp -class OrOp: - pass - - -@VariadicOp -class XorOp: - pass - - -@VariadicOp -class ConcatOp: - pass - - -# Sugar classes for miscellaneous ops. -@CreatableOp -class MuxOp: - pass diff --git a/lib/Bindings/Python/dialects/_esi_ops_ext.py b/lib/Bindings/Python/dialects/_esi_ops_ext.py deleted file mode 100644 index 4ab88eb43687..000000000000 --- a/lib/Bindings/Python/dialects/_esi_ops_ext.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -from typing import Dict, List, Optional, Sequence, Type - -from . import esi -from .. import support - -from .. import ir - - -class RequestToServerConnectionOp: - - @property - def clientNamePath(self) -> List[str]: - return [ - ir.StringAttr(x).value - for x in ir.ArrayAttr(self.attributes["clientNamePath"]) - ] - - -class RequestToClientConnectionOp: - - @property - def clientNamePath(self) -> List[str]: - return [ - ir.StringAttr(x).value - for x in ir.ArrayAttr(self.attributes["clientNamePath"]) - ] - - -class RandomAccessMemoryDeclOp: - - @property - def innerType(self): - return ir.TypeAttr(self.attributes["innerType"]) - - -class ESIPureModuleOp: - - def add_entry_block(self): - if len(self.body.blocks) > 0: - raise IndexError('The module already has an entry block') - self.body.blocks.append() - return self.body.blocks[0] diff --git a/lib/Bindings/Python/dialects/_fsm_ops_ext.py b/lib/Bindings/Python/dialects/_fsm_ops_ext.py deleted file mode 100644 index 1c8028928f31..000000000000 --- a/lib/Bindings/Python/dialects/_fsm_ops_ext.py +++ /dev/null @@ -1,167 +0,0 @@ -# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -# See https://llvm.org/LICENSE.txt for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -from . import fsm as fsm -from .. import support -from ..ir import * - - -def _get_or_add_single_block(region, args=[]): - if len(region.blocks) == 0: - region.blocks.append(*args) - return region.blocks[0] - - -class MachineOp: - - def __init__(self, - name, - initial_state, - input_ports, - output_ports, - *, - attributes={}, - loc=None, - ip=None): - attributes["sym_name"] = StringAttr.get(name) - attributes["initialState"] = StringAttr.get(initial_state) - - input_types = [] - output_types = [] - for (i, (_, port_type)) in enumerate(input_ports): - input_types.append(port_type) - - for (i, (_, port_type)) in enumerate(output_ports): - output_types.append(port_type) - - attributes["function_type"] = TypeAttr.get( - FunctionType.get(inputs=input_types, results=output_types)) - - OpView.__init__( - self, - self.build_generic(attributes=attributes, - results=[], - operands=[], - successors=None, - regions=1, - loc=loc, - ip=ip)) - - _get_or_add_single_block(self.body, self.type.inputs) - - @property - def type(self): - return FunctionType(TypeAttr(self.attributes["function_type"]).value) - - def instantiate(self, name: str, loc=None, ip=None, **kwargs): - """ FSM Instantiation function""" - in_names = support.attribute_to_var(self.attributes['in_names']) - inputs = [kwargs[port].value for port in in_names] - - # Clock and resets are not part of the input ports of the FSM, but - # it is at the point of `fsm.hw_instance` instantiation that they - # must be connected. - # Attach backedges to these, and associate these backedges to the operation. - # They can then be accessed at the point of instantiation and assigned. - clock = support.BackedgeBuilder().create( - IntegerType.get_signed(1), - StringAttr(self.attributes['clock_name']).value, self) - reset = support.BackedgeBuilder().create( - IntegerType.get_signed(1), - StringAttr(self.attributes['reset_name']).value, self) - - op = fsm.HWInstanceOp(outputs=self.type.results, - inputs=inputs, - sym_name=StringAttr.get(name), - machine=FlatSymbolRefAttr.get(self.sym_name.value), - clock=clock.result, - reset=reset.result if reset else None, - loc=loc, - ip=ip) - op.backedges = {} - - def set_OpOperand(name, backedge): - index = None - for i, operand in enumerate(op.operands): - if operand == backedge.result: - index = i - break - assert index is not None - op_operand = support.OpOperand(op, index, op.operands[index], op) - setattr(op, f'_{name}_backedge', op_operand) - op.backedges[i] = backedge - - set_OpOperand('clock', clock) - if reset: - set_OpOperand('reset', reset) - - return op - - -class TransitionOp: - - def __init__(self, next_state, *, loc=None, ip=None): - attributes = { - "nextState": FlatSymbolRefAttr.get(next_state), - } - super().__init__( - self.build_generic(attributes=attributes, - results=[], - operands=[], - successors=None, - regions=2, - loc=loc, - ip=ip)) - - @staticmethod - def create(to_state): - op = fsm.TransitionOp(to_state) - return op - - def set_guard(self, guard_fn): - """Executes a function to generate a guard for the transition. - The function is executed within the guard region of this operation.""" - guard_block = _get_or_add_single_block(self.guard) - with InsertionPoint(guard_block): - guard = guard_fn() - guard_type = support.type_to_pytype(guard.type) - if guard_type.width != 1: - raise ValueError('The guard must be a single bit') - fsm.ReturnOp(operand=guard) - - -class StateOp: - - def __init__(self, name, *, loc=None, ip=None): - attributes = {} - attributes["sym_name"] = StringAttr.get(name) - - OpView.__init__( - self, - self.build_generic(attributes=attributes, - results=[], - operands=[], - successors=None, - regions=2, - loc=loc, - ip=ip)) - - @staticmethod - def create(name): - return fsm.StateOp(name) - - @property - def output(self): - return _get_or_add_single_block(super().output) - - @property - def transitions(self): - return _get_or_add_single_block(super().transitions) - - -class OutputOp: - - @staticmethod - def create(*operands): - return fsm.OutputOp(operands) diff --git a/lib/Bindings/Python/dialects/_hw_ops_ext.py b/lib/Bindings/Python/dialects/_hw_ops_ext.py deleted file mode 100644 index 5be34854c815..000000000000 --- a/lib/Bindings/Python/dialects/_hw_ops_ext.py +++ /dev/null @@ -1,529 +0,0 @@ -from __future__ import annotations - -from typing import Dict, Type - -from . import hw -from .. import support -from ..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.""" - - def __init__(self, - module, - name, - input_port_mapping, - *, - results=None, - parameters={}, - sym_name=None, - loc=None, - ip=None): - self.module = module - instance_name = StringAttr.get(name) - module_name = FlatSymbolRefAttr.get(StringAttr(module.name).value) - inst_param_array = create_parameters(parameters, module) - if sym_name: - inner_sym = hw.InnerSymAttr.get(StringAttr.get(sym_name)) - else: - inner_sym = None - pre_args = [instance_name, module_name] - post_args = [ - ArrayAttr.get([StringAttr.get(x) for x in self.operand_names()]), - ArrayAttr.get([StringAttr.get(x) for x in self.result_names()]), - ArrayAttr.get(inst_param_array) - ] - if results is None: - results = module.type.output_types - - if not isinstance(module, hw.HWModuleExternOp): - input_name_type_lookup = { - name: support.type_to_pytype(ty) - for name, ty in zip(self.operand_names(), module.type.input_types) - } - for input_name, input_value in input_port_mapping.items(): - if input_name not in input_name_type_lookup: - continue # This error gets caught and raised later. - mod_input_type = input_name_type_lookup[input_name] - if support.type_to_pytype(input_value.type) != mod_input_type: - raise TypeError(f"Input '{input_name}' has type '{input_value.type}' " - f"but expected '{mod_input_type}'") - - super().__init__(hw.InstanceOp, - results, - input_port_mapping, - pre_args, - post_args, - needs_result_type=True, - inner_sym=inner_sym, - loc=loc, - ip=ip) - - def create_default_value(self, index, data_type, arg_name): - type = self.module.type.input_types[index] - return support.BackedgeBuilder.create(type, - arg_name, - self, - instance_of=self.module) - - def operand_names(self): - return self.module.type.input_names - - def result_names(self): - return self.module.type.output_names - - -class ModuleLike: - """Custom Python base class for module-like operations.""" - - def __init__( - self, - name, - input_ports=[], - output_ports=[], - *, - parameters=[], - attributes={}, - body_builder=None, - loc=None, - ip=None, - ): - """ - Create a module-like with the provided `name`, `input_ports`, and - `output_ports`. - - `name` is a string representing the module name. - - `input_ports` is a list of pairs of string names and mlir.ir types. - - `output_ports` is a list of pairs of string names and mlir.ir types. - - `body_builder` is an optional callback, when provided a new entry block - is created and the callback is invoked with the new op as argument within - an InsertionPoint context already set for the block. The callback is - expected to insert a terminator in the block. - """ - # Copy the mutable default arguments. 'Cause python. - input_ports = list(input_ports) - output_ports = list(output_ports) - parameters = list(parameters) - attributes = dict(attributes) - - operands = [] - results = [] - attributes["sym_name"] = StringAttr.get(str(name)) - - module_ports = [] - input_names = [] - port_locs = [] - unknownLoc = Location.unknown().attr - for (i, (port_name, port_type)) in enumerate(input_ports): - input_name = StringAttr.get(str(port_name)) - input_dir = hw.ModulePortDirection.INPUT - input_port = hw.ModulePort(input_name, port_type, input_dir) - module_ports.append(input_port) - input_names.append(input_name) - port_locs.append(unknownLoc) - - output_types = [] - output_names = [] - for (i, (port_name, port_type)) in enumerate(output_ports): - output_name = StringAttr.get(str(port_name)) - output_dir = hw.ModulePortDirection.OUTPUT - output_port = hw.ModulePort(output_name, port_type, output_dir) - module_ports.append(output_port) - output_names.append(output_name) - port_locs.append(unknownLoc) - attributes["port_locs"] = ArrayAttr.get(port_locs) - attributes["per_port_attrs"] = ArrayAttr.get([]) - - if len(parameters) > 0 or "parameters" not in attributes: - attributes["parameters"] = ArrayAttr.get(parameters) - - attributes["module_type"] = TypeAttr.get(hw.ModuleType.get(module_ports)) - - super().__init__( - self.build_generic(attributes=attributes, - results=results, - operands=operands, - loc=loc, - ip=ip)) - - if body_builder: - entry_block = self.add_entry_block() - - with InsertionPoint(entry_block): - with support.BackedgeBuilder(): - outputs = body_builder(self) - _create_output_op(name, output_ports, entry_block, outputs) - - @property - def type(self): - return hw.ModuleType(TypeAttr(self.attributes["module_type"]).value) - - @property - def name(self): - return self.attributes["sym_name"] - - @property - 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 instantiate(self, - name: str, - parameters: Dict[str, object] = {}, - results=None, - sym_name=None, - loc=None, - ip=None, - **kwargs): - return InstanceBuilder(self, - name, - kwargs, - parameters=parameters, - results=results, - sym_name=sym_name, - loc=loc, - ip=ip) - - -def _create_output_op(cls_name, output_ports, entry_block, bb_ret): - """Create the hw.OutputOp from the body_builder return.""" - - # Determine if the body already has an output op. - block_len = len(entry_block.operations) - if block_len > 0: - last_op = entry_block.operations[block_len - 1] - if isinstance(last_op, hw.OutputOp): - # If it does, the return from body_builder must be None. - if bb_ret is not None and bb_ret != last_op: - raise support.ConnectionError( - f"In {cls_name}, cannot return value from body_builder and " - "create hw.OutputOp") - return - - # If builder didn't create an output op and didn't return anything, this op - # mustn't have any outputs. - if bb_ret is None: - if len(output_ports) == 0: - hw.OutputOp([]) - return - raise support.ConnectionError( - f"In {cls_name}, must return module output values") - - # Now create the output op depending on the object type returned - outputs: list[Value] = list() - - # Only acceptable return is a dict of port, value mappings. - if not isinstance(bb_ret, dict): - raise support.ConnectionError( - f"In {cls_name}, can only return a dict of port, value mappings " - "from body_builder.") - - # A dict of `OutputPortName` -> ValueLike must be converted to a list in port - # order. - unconnected_ports = [] - for (name, port_type) in output_ports: - if name not in bb_ret: - unconnected_ports.append(name) - outputs.append(None) - else: - val = support.get_value(bb_ret[name]) - if val is None: - field_type = type(bb_ret[name]) - raise TypeError( - f"In {cls_name}, body_builder return doesn't support type " - f"'{field_type}'") - if val.type != port_type: - if isinstance(port_type, hw.TypeAliasType) and \ - port_type.inner_type == val.type: - val = hw.BitcastOp.create(port_type, val).result - else: - raise TypeError( - f"In {cls_name}, output port '{name}' type ({val.type}) doesn't " - f"match declared type ({port_type})") - outputs.append(val) - bb_ret.pop(name) - if len(unconnected_ports) > 0: - raise support.UnconnectedSignalError(cls_name, unconnected_ports) - if len(bb_ret) > 0: - raise support.ConnectionError( - f"Could not map the following to output ports in {cls_name}: " + - ",".join(bb_ret.keys())) - - hw.OutputOp(outputs) - - -class HWModuleOp(ModuleLike): - """Specialization for the HW module op class.""" - - def __init__( - self, - name, - input_ports=[], - output_ports=[], - *, - parameters=[], - attributes={}, - body_builder=None, - loc=None, - ip=None, - ): - if "comment" not in attributes: - attributes["comment"] = StringAttr.get("") - super().__init__(name, - input_ports, - output_ports, - parameters=parameters, - attributes=attributes, - body_builder=body_builder, - loc=loc, - ip=ip) - - @property - def body(self): - return self.regions[0] - - @property - def entry_block(self): - return self.regions[0].blocks[0] - - @property - def input_indices(self): - indices: dict[int, str] = {} - op_names = self.type.input_names - for idx, name in enumerate(op_names): - indices[name] = idx - return indices - - # Support attribute access to block arguments by name - def __getattr__(self, name): - if name in self.input_indices: - index = self.input_indices[name] - return self.entry_block.arguments[index] - raise AttributeError(f"unknown input port name {name}") - - def inputs(self) -> dict[str:Value]: - ret = {} - for (name, idx) in self.input_indices.items(): - ret[name] = self.entry_block.arguments[idx] - return ret - - def outputs(self) -> dict[str:Type]: - result_names = self.type.output_names - result_types = self.type.output_types - return dict(zip(result_names, result_types)) - - def add_entry_block(self): - if not self.is_external: - raise IndexError('The module already has an entry block') - self.body.blocks.append(*self.type.input_types) - return self.body.blocks[0] - - -class HWModuleExternOp(ModuleLike): - """Specialization for the HW module op class.""" - - def __init__( - self, - name, - input_ports=[], - output_ports=[], - *, - parameters=[], - attributes={}, - body_builder=None, - loc=None, - ip=None, - ): - if "comment" not in attributes: - attributes["comment"] = StringAttr.get("") - super().__init__(name, - input_ports, - output_ports, - parameters=parameters, - attributes=attributes, - body_builder=body_builder, - loc=loc, - ip=ip) - - -class ConstantOp: - - @staticmethod - def create(data_type, value): - return hw.ConstantOp(IntegerAttr.get(data_type, value)) - - -class BitcastOp: - - @staticmethod - def create(data_type, value): - value = support.get_value(value) - return hw.BitcastOp(data_type, value) - - -class ArrayGetOp: - - @staticmethod - def create(array_value, idx): - array_value = support.get_value(array_value) - array_type = support.get_self_or_inner(array_value.type) - if isinstance(idx, int): - idx_width = (array_type.size - 1).bit_length() - idx_val = ConstantOp.create(IntegerType.get_signless(idx_width), - idx).result - else: - idx_val = support.get_value(idx) - return hw.ArrayGetOp(array_value, idx_val) - - -class ArraySliceOp: - - @staticmethod - def create(array_value, low_index, ret_type): - array_value = support.get_value(array_value) - array_type = support.get_self_or_inner(array_value.type) - if isinstance(low_index, int): - idx_width = (array_type.size - 1).bit_length() - idx_width = max(1, idx_width) # hw.constant cannot produce i0. - idx_val = ConstantOp.create(IntegerType.get_signless(idx_width), - low_index).result - else: - idx_val = support.get_value(low_index) - return hw.ArraySliceOp(ret_type, array_value, idx_val) - - -class ArrayCreateOp: - - @staticmethod - def create(elements): - if not elements: - raise ValueError("Cannot 'create' an array of length zero") - vals = [] - type = None - for i, arg in enumerate(elements): - arg_val = support.get_value(arg) - vals.append(arg_val) - if type is None: - type = arg_val.type - elif type != arg_val.type: - raise TypeError( - f"Argument {i} has a different element type ({arg_val.type}) than the element type of the array ({type})" - ) - return hw.ArrayCreateOp(hw.ArrayType.get(type, len(vals)), vals) - - -class ArrayConcatOp: - - @staticmethod - def create(*sub_arrays): - vals = [] - types = [] - element_type = None - for i, array in enumerate(sub_arrays): - array_value = support.get_value(array) - array_type = support.type_to_pytype(array_value.type) - if array_value is None or not isinstance(array_type, hw.ArrayType): - raise TypeError(f"Cannot concatenate {array_value}") - if element_type is None: - element_type = array_type.element_type - elif element_type != array_type.element_type: - raise TypeError( - f"Argument {i} has a different element type ({element_type}) than the element type of the array ({array_type.element_type})" - ) - - vals.append(array_value) - types.append(array_type) - - size = sum(t.size for t in types) - combined_type = hw.ArrayType.get(element_type, size) - return hw.ArrayConcatOp(combined_type, vals) - - -class StructCreateOp: - - @staticmethod - def create(elements, result_type: Type = None): - elem_name_values = [ - (name, support.get_value(value)) for (name, value) in elements - ] - struct_fields = [(name, value.type) for (name, value) in elem_name_values] - struct_type = hw.StructType.get(struct_fields) - - if result_type is None: - result_type = struct_type - else: - result_type_inner = support.get_self_or_inner(result_type) - if result_type_inner != struct_type: - raise TypeError( - f"result type:\n\t{result_type_inner}\nmust match generated struct type:\n\t{struct_type}" - ) - - return hw.StructCreateOp(result_type, - [value for (_, value) in elem_name_values]) - - -class StructExtractOp: - - @staticmethod - def create(struct_value, field_name: str): - struct_value = support.get_value(struct_value) - struct_type = support.get_self_or_inner(struct_value.type) - field_type = struct_type.get_field(field_name) - return hw.StructExtractOp(field_type, struct_value, - StringAttr.get(field_name)) - - -class TypedeclOp: - - @staticmethod - def create(sym_name: str, type: Type, verilog_name: str = None): - return hw.TypedeclOp(StringAttr.get(sym_name), - TypeAttr.get(type), - verilogName=verilog_name) - - -class TypeScopeOp: - - @staticmethod - def create(sym_name: str): - op = hw.TypeScopeOp(StringAttr.get(sym_name)) - op.regions[0].blocks.append() - return op - - @property - def body(self): - return self.regions[0].blocks[0] diff --git a/lib/Bindings/Python/dialects/_hwarith_ops_ext.py b/lib/Bindings/Python/dialects/_hwarith_ops_ext.py deleted file mode 100644 index 503d3e6fe5da..000000000000 --- a/lib/Bindings/Python/dialects/_hwarith_ops_ext.py +++ /dev/null @@ -1,76 +0,0 @@ -from ..support import NamedValueOpView, get_value -from ..ir import IntegerAttr, IntegerType - - -class BinaryOpBuilder(NamedValueOpView): - - def operand_names(self): - return ["lhs", "rhs"] - - def result_names(self): - return ["result"] - - -def BinaryOp(base): - - class _Class(base): - - @classmethod - def create(cls, lhs=None, rhs=None, result_type=None): - return cls([get_value(lhs), get_value(rhs)]) - - return _Class - - -@BinaryOp -class DivOp: - pass - - -@BinaryOp -class SubOp: - pass - - -@BinaryOp -class AddOp: - pass - - -@BinaryOp -class MulOp: - pass - - -class CastOp: - - @classmethod - def create(cls, value, result_type): - return cls(result_type, value) - - -class ICmpOp: - # Predicate constants. - - # `==` and `!=` - PRED_EQ = 0b000 - PRED_NE = 0b001 - # `<` and `>=` - PRED_LT = 0b010 - PRED_GE = 0b011 - # `<=` and `>` - PRED_LE = 0b100 - PRED_GT = 0b101 - - @classmethod - def create(cls, pred, a, b): - if isinstance(pred, int): - pred = IntegerAttr.get(IntegerType.get_signless(64), pred) - return cls(pred, a, b) - - -class ConstantOp: - - @classmethod - def create(cls, data_type, value): - return cls(IntegerAttr.get(data_type, value)) diff --git a/lib/Bindings/Python/dialects/_msft_ops_ext.py b/lib/Bindings/Python/dialects/_msft_ops_ext.py deleted file mode 100644 index bf8e04305a9a..000000000000 --- a/lib/Bindings/Python/dialects/_msft_ops_ext.py +++ /dev/null @@ -1,79 +0,0 @@ -# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -# See https://llvm.org/LICENSE.txt for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -from typing import Dict, List, Type - -from . import hw, msft as _msft -from . import _hw_ops_ext as _hw_ext -from .. import support - -from .. import ir as _ir - - -class PhysicalRegionOp: - - def add_bounds(self, bounds): - existing_bounds = [b for b in _ir.ArrayAttr(self.attributes["bounds"])] - existing_bounds.append(bounds) - new_bounds = _ir.ArrayAttr.get(existing_bounds) - self.attributes["bounds"] = new_bounds - - -class InstanceOp: - - @property - def moduleName(self): - return _ir.FlatSymbolRefAttr(self.attributes["moduleName"]) - - -class EntityExternOp: - - @staticmethod - def create(symbol, metadata=""): - symbol_attr = support.var_to_attribute(symbol) - metadata_attr = support.var_to_attribute(metadata) - return _msft.EntityExternOp(symbol_attr, metadata_attr) - - -class InstanceHierarchyOp: - - @staticmethod - def create(root_mod, instance_name=None): - hier = _msft.InstanceHierarchyOp(root_mod, instName=instance_name) - hier.body.blocks.append() - return hier - - @property - def top_module_ref(self): - return self.attributes["topModuleRef"] - - -class DynamicInstanceOp: - - @staticmethod - def create(name_ref): - inst = _msft.DynamicInstanceOp(name_ref) - inst.body.blocks.append() - return inst - - @property - def instance_path(self): - path = [] - next = self - while isinstance(next, DynamicInstanceOp): - path.append(next.attributes["instanceRef"]) - next = next.operation.parent.opview - path.reverse() - return _ir.ArrayAttr.get(path) - - @property - def instanceRef(self): - return self.attributes["instanceRef"] - - -class PDPhysLocationOp: - - @property - def loc(self): - return _msft.PhysLocationAttr(self.attributes["loc"]) diff --git a/lib/Bindings/Python/dialects/_seq_ops_ext.py b/lib/Bindings/Python/dialects/_seq_ops_ext.py deleted file mode 100644 index 77f8a2e80e83..000000000000 --- a/lib/Bindings/Python/dialects/_seq_ops_ext.py +++ /dev/null @@ -1,133 +0,0 @@ -from ..support import BackedgeBuilder, NamedValueOpView -from ..ir import IntegerType, OpView, StringAttr -from . import hw - - -class CompRegLikeBuilder(NamedValueOpView): - - def result_names(self): - return ["data"] - - def create_initial_value(self, index, data_type, arg_name): - if arg_name == "input": - operand_type = data_type - else: - operand_type = IntegerType.get_signless(1) - return BackedgeBuilder.create(operand_type, arg_name, self) - - -class CompRegLike: - - def __init__(self, - data_type, - input, - clk, - clockEnable=None, - *, - reset=None, - reset_value=None, - power_on_value=None, - name=None, - sym_name=None, - loc=None, - ip=None): - operands = [input, clk] - results = [] - attributes = {} - results.append(data_type) - operand_segment_sizes = [1, 1] - if isinstance(self, CompRegOp): - if clockEnable is not None: - raise Exception("Clock enable not supported on compreg") - elif isinstance(self, CompRegClockEnabledOp): - if clockEnable is None: - raise Exception("Clock enable required on compreg.ce") - operands.append(clockEnable) - operand_segment_sizes.append(1) - else: - assert False, "Class not recognized" - if reset is not None and reset_value is not None: - operands.append(reset) - operands.append(reset_value) - operand_segment_sizes += [1, 1] - else: - operand_segment_sizes += [0, 0] - operands += [None, None] - - if power_on_value is not None: - operands.append(power_on_value) - operand_segment_sizes.append(1) - else: - operands.append(None) - operand_segment_sizes.append(0) - if name is None: - attributes["name"] = StringAttr.get("") - else: - attributes["name"] = StringAttr.get(name) - if sym_name is not None: - attributes["inner_sym"] = hw.InnerSymAttr.get(StringAttr.get(sym_name)) - - self._ODS_OPERAND_SEGMENTS = operand_segment_sizes - - OpView.__init__( - self, - self.build_generic( - attributes=attributes, - results=results, - operands=operands, - loc=loc, - ip=ip, - ), - ) - - -class CompRegBuilder(CompRegLikeBuilder): - - def operand_names(self): - return ["input", "clk"] - - -class CompRegOp(CompRegLike): - - @classmethod - def create(cls, - result_type, - reset=None, - reset_value=None, - name=None, - sym_name=None, - **kwargs): - return CompRegBuilder(cls, - result_type, - kwargs, - reset=reset, - reset_value=reset_value, - name=name, - sym_name=sym_name, - needs_result_type=True) - - -class CompRegClockEnabledBuilder(CompRegLikeBuilder): - - def operand_names(self): - return ["input", "clk", "clockEnable"] - - -class CompRegClockEnabledOp(CompRegLike): - - @classmethod - def create(cls, - result_type, - reset=None, - reset_value=None, - name=None, - sym_name=None, - **kwargs): - return CompRegClockEnabledBuilder(cls, - result_type, - kwargs, - reset=reset, - reset_value=reset_value, - name=name, - sym_name=sym_name, - needs_result_type=True) diff --git a/lib/Bindings/Python/dialects/_sv_ops_ext.py b/lib/Bindings/Python/dialects/_sv_ops_ext.py deleted file mode 100644 index eb0c9cbf7365..000000000000 --- a/lib/Bindings/Python/dialects/_sv_ops_ext.py +++ /dev/null @@ -1,100 +0,0 @@ -# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -# See https://llvm.org/LICENSE.txt for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -from ..ir import ArrayAttr, Attribute, FlatSymbolRefAttr, OpView, StringAttr -from . import sv, hw -from .. import support - - -class IfDefOp: - - def __init__(self, cond: Attribute, *, loc=None, ip=None): - operands = [] - results = [] - attributes = {"cond": cond} - regions = 2 - super().__init__( - self.build_generic(attributes=attributes, - results=results, - operands=operands, - successors=None, - regions=regions, - loc=loc, - ip=ip)) - self.regions[0].blocks.append() - self.regions[1].blocks.append() - - -class WireOp: - - def __init__(self, - data_type, - name, - *, - sym_name=None, - svAttributes=None, - loc=None, - ip=None): - attributes = {"name": StringAttr.get(name)} - if sym_name is not None: - attributes["inner_sym"] = hw.InnerSymAttr.get(StringAttr.get(sym_name)) - if svAttributes is not None: - attributes["svAttributes"] = ArrayAttr.get(svAttributes) - OpView.__init__( - self, - self.build_generic(attributes=attributes, - results=[data_type], - operands=[], - successors=None, - regions=0, - loc=loc, - ip=ip)) - - @staticmethod - def create(data_type, name=None, sym_name=None): - if not isinstance(data_type, hw.InOutType): - data_type = hw.InOutType.get(data_type) - return sv.WireOp(data_type, name, sym_name=sym_name) - - -class RegOp: - - def __init__(self, - data_type, - name, - *, - sym_name=None, - svAttributes=None, - loc=None, - ip=None): - attributes = {"name": StringAttr.get(name)} - if sym_name is not None: - attributes["inner_sym"] = hw.InnerSymAttr.get(StringAttr.get(sym_name)) - if svAttributes is not None: - attributes["svAttributes"] = ArrayAttr.get(svAttributes) - OpView.__init__( - self, - self.build_generic(attributes=attributes, - results=[data_type], - operands=[], - successors=None, - regions=0, - loc=loc, - ip=ip)) - - -class AssignOp: - - @staticmethod - def create(dest, src): - return sv.AssignOp(dest=dest, src=src) - - -class ReadInOutOp: - - @staticmethod - def create(value): - value = support.get_value(value) - type = support.get_self_or_inner(value.type).element_type - return sv.ReadInOutOp(value) diff --git a/lib/Bindings/Python/dialects/comb.py b/lib/Bindings/Python/dialects/comb.py index 5ef1e31a17be..a0df29f6d463 100644 --- a/lib/Bindings/Python/dialects/comb.py +++ b/lib/Bindings/Python/dialects/comb.py @@ -2,11 +2,12 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -from ._comb_ops_gen import * - -from ..support import NamedValueOpView - +from . import comb +from ..dialects._ods_common import _cext as _ods_cext from ..ir import IntegerAttr, IntegerType, OpView +from ..support import NamedValueOpView, get_value +from ._comb_ops_gen import * +from ._comb_ops_gen import _Dialect # Sugar classes for the various possible verions of ICmpOp. @@ -96,3 +97,195 @@ class GtUOp: @CompareOp(9) class GeUOp: pass + + +# Builder base classes for non-variadic unary and binary ops. +class UnaryOpBuilder(NamedValueOpView): + + def operand_names(self): + return ["input"] + + def result_names(self): + return ["result"] + + +def UnaryOp(base): + + class _Class(base): + + @classmethod + def create(cls, input=None, result_type=None): + mapping = {"input": input} if input else {} + return UnaryOpBuilder(cls, result_type, mapping) + + return _Class + + +class ExtractOpBuilder(UnaryOpBuilder): + + def __init__(self, low_bit, data_type, input_port_mapping={}, **kwargs): + low_bit = IntegerAttr.get(IntegerType.get_signless(32), low_bit) + super().__init__(comb.ExtractOp, data_type, input_port_mapping, [], + [low_bit], **kwargs) + + +class BinaryOpBuilder(NamedValueOpView): + + def operand_names(self): + return ["lhs", "rhs"] + + def result_names(self): + return ["result"] + + +def BinaryOp(base): + + class _Class(base): + + @classmethod + def create(cls, lhs=None, rhs=None, result_type=None): + mapping = {} + if lhs: + mapping["lhs"] = lhs + if rhs: + mapping["rhs"] = rhs + return BinaryOpBuilder(cls, result_type, mapping) + + return _Class + + +# Base classes for the variadic ops. +def VariadicOp(base): + + class _Class(base): + + @classmethod + def create(cls, *args): + return cls([get_value(a) for a in args]) + + return _Class + + +# Base class for miscellaneous ops that can't be abstracted but should provide a +# create method for uniformity. +def CreatableOp(base): + + class _Class(base): + + @classmethod + def create(cls, *args, **kwargs): + return cls(*args, **kwargs) + + return _Class + + +# Sugar classes for the various non-variadic unary ops. +@_ods_cext.register_operation(_Dialect, replace=True) +class ExtractOp(ExtractOp): + + @staticmethod + def create(low_bit, result_type, input=None): + mapping = {"input": input} if input else {} + return ExtractOpBuilder(low_bit, + result_type, + mapping, + needs_result_type=True) + + +@UnaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class ParityOp(ParityOp): + pass + + +# Sugar classes for the various non-variadic binary ops. +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class DivSOp(DivSOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class DivUOp(DivUOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class ModSOp(ModSOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class ModUOp(ModUOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class ShlOp(ShlOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class ShrSOp(ShrSOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class ShrUOp(ShrUOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class SubOp(SubOp): + pass + + +# Sugar classes for the variadic ops. +@VariadicOp +@_ods_cext.register_operation(_Dialect, replace=True) +class AddOp(AddOp): + pass + + +@VariadicOp +@_ods_cext.register_operation(_Dialect, replace=True) +class MulOp(MulOp): + pass + + +@VariadicOp +@_ods_cext.register_operation(_Dialect, replace=True) +class AndOp(AndOp): + pass + + +@VariadicOp +@_ods_cext.register_operation(_Dialect, replace=True) +class OrOp(OrOp): + pass + + +@VariadicOp +@_ods_cext.register_operation(_Dialect, replace=True) +class XorOp(XorOp): + pass + + +@VariadicOp +@_ods_cext.register_operation(_Dialect, replace=True) +class ConcatOp(ConcatOp): + pass + + +# Sugar classes for miscellaneous ops. +@CreatableOp +@_ods_cext.register_operation(_Dialect, replace=True) +class MuxOp(MuxOp): + pass diff --git a/lib/Bindings/Python/dialects/esi.py b/lib/Bindings/Python/dialects/esi.py index 8148ee5f3c75..255c5ae4e037 100644 --- a/lib/Bindings/Python/dialects/esi.py +++ b/lib/Bindings/Python/dialects/esi.py @@ -2,10 +2,58 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -from ._esi_ops_gen import * +from __future__ import annotations + +from . import esi +from .. import ir +from .. import support from .._mlir_libs._circt._esi import * +from ..dialects._ods_common import _cext as _ods_cext +from ._esi_ops_gen import * +from ._esi_ops_gen import _Dialect +from typing import Dict, List, Optional, Sequence, Type class ChannelSignaling: ValidReady = 0 FIFO0 = 1 + + +@_ods_cext.register_operation(_Dialect, replace=True) +class RequestToServerConnectionOp(RequestToServerConnectionOp): + + @property + def clientNamePath(self) -> List[str]: + return [ + ir.StringAttr(x).value + for x in ir.ArrayAttr(self.attributes["clientNamePath"]) + ] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class RequestToClientConnectionOp(RequestToClientConnectionOp): + + @property + def clientNamePath(self) -> List[str]: + return [ + ir.StringAttr(x).value + for x in ir.ArrayAttr(self.attributes["clientNamePath"]) + ] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class RandomAccessMemoryDeclOp(RandomAccessMemoryDeclOp): + + @property + def innerType(self): + return ir.TypeAttr(self.attributes["innerType"]) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ESIPureModuleOp(ESIPureModuleOp): + + def add_entry_block(self): + if len(self.body.blocks) > 0: + raise IndexError('The module already has an entry block') + self.body.blocks.append() + return self.body.blocks[0] diff --git a/lib/Bindings/Python/dialects/fsm.py b/lib/Bindings/Python/dialects/fsm.py index 72fd4571a457..a7db46360a25 100644 --- a/lib/Bindings/Python/dialects/fsm.py +++ b/lib/Bindings/Python/dialects/fsm.py @@ -2,4 +2,173 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +from . import fsm as fsm +from .. import support +from ..dialects._ods_common import _cext as _ods_cext +from ..ir import * from ._fsm_ops_gen import * +from ._fsm_ops_gen import _Dialect + + +def _get_or_add_single_block(region, args=[]): + if len(region.blocks) == 0: + region.blocks.append(*args) + return region.blocks[0] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class MachineOp(MachineOp): + + def __init__(self, + name, + initial_state, + input_ports, + output_ports, + *, + attributes={}, + loc=None, + ip=None): + attributes["sym_name"] = StringAttr.get(name) + attributes["initialState"] = StringAttr.get(initial_state) + + input_types = [] + output_types = [] + for (i, (_, port_type)) in enumerate(input_ports): + input_types.append(port_type) + + for (i, (_, port_type)) in enumerate(output_ports): + output_types.append(port_type) + + attributes["function_type"] = TypeAttr.get( + FunctionType.get(inputs=input_types, results=output_types)) + + OpView.__init__( + self, + self.build_generic(attributes=attributes, + results=[], + operands=[], + successors=None, + regions=1, + loc=loc, + ip=ip)) + + _get_or_add_single_block(self.body, self.type.inputs) + + @property + def type(self): + return FunctionType(TypeAttr(self.attributes["function_type"]).value) + + def instantiate(self, name: str, loc=None, ip=None, **kwargs): + """ FSM Instantiation function""" + in_names = support.attribute_to_var(self.attributes['in_names']) + inputs = [kwargs[port].value for port in in_names] + + # Clock and resets are not part of the input ports of the FSM, but + # it is at the point of `fsm.hw_instance` instantiation that they + # must be connected. + # Attach backedges to these, and associate these backedges to the operation. + # They can then be accessed at the point of instantiation and assigned. + clock = support.BackedgeBuilder().create( + IntegerType.get_signed(1), + StringAttr(self.attributes['clock_name']).value, self) + reset = support.BackedgeBuilder().create( + IntegerType.get_signed(1), + StringAttr(self.attributes['reset_name']).value, self) + + op = fsm.HWInstanceOp(outputs=self.type.results, + inputs=inputs, + sym_name=StringAttr.get(name), + machine=FlatSymbolRefAttr.get(self.sym_name.value), + clock=clock.result, + reset=reset.result if reset else None, + loc=loc, + ip=ip) + op.backedges = {} + + def set_OpOperand(name, backedge): + index = None + for i, operand in enumerate(op.operands): + if operand == backedge.result: + index = i + break + assert index is not None + op_operand = support.OpOperand(op, index, op.operands[index], op) + setattr(op, f'_{name}_backedge', op_operand) + op.backedges[i] = backedge + + set_OpOperand('clock', clock) + if reset: + set_OpOperand('reset', reset) + + return op + + +@_ods_cext.register_operation(_Dialect, replace=True) +class TransitionOp(TransitionOp): + + def __init__(self, next_state, *, loc=None, ip=None): + attributes = { + "nextState": FlatSymbolRefAttr.get(next_state), + } + super().__init__( + self.build_generic(attributes=attributes, + results=[], + operands=[], + successors=None, + regions=2, + loc=loc, + ip=ip)) + + @staticmethod + def create(to_state): + op = fsm.TransitionOp(to_state) + return op + + def set_guard(self, guard_fn): + """Executes a function to generate a guard for the transition. + The function is executed within the guard region of this operation.""" + guard_block = _get_or_add_single_block(self.guard) + with InsertionPoint(guard_block): + guard = guard_fn() + guard_type = support.type_to_pytype(guard.type) + if guard_type.width != 1: + raise ValueError('The guard must be a single bit') + fsm.ReturnOp(operand=guard) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class StateOp(StateOp): + + def __init__(self, name, *, loc=None, ip=None): + attributes = {} + attributes["sym_name"] = StringAttr.get(name) + + OpView.__init__( + self, + self.build_generic(attributes=attributes, + results=[], + operands=[], + successors=None, + regions=2, + loc=loc, + ip=ip)) + + @staticmethod + def create(name): + return fsm.StateOp(name) + + @property + def output(self): + return _get_or_add_single_block(super().output) + + @property + def transitions(self): + return _get_or_add_single_block(super().transitions) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class OutputOp(OutputOp): + + @staticmethod + def create(*operands): + return fsm.OutputOp(operands) diff --git a/lib/Bindings/Python/dialects/hw.py b/lib/Bindings/Python/dialects/hw.py index 4f17ee432acd..5db23a53e3bc 100644 --- a/lib/Bindings/Python/dialects/hw.py +++ b/lib/Bindings/Python/dialects/hw.py @@ -2,5 +2,548 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -from ._hw_ops_gen import * +from __future__ import annotations + +from . import hw +from .. import support from .._mlir_libs._circt._hw import * +from ..dialects._ods_common import _cext as _ods_cext +from ..ir import * +from ._hw_ops_gen import * +from ._hw_ops_gen import _Dialect +from typing import Dict, Type + + +def create_parameters(parameters: dict[str, 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.""" + + def __init__(self, + module, + name, + input_port_mapping, + *, + results=None, + parameters={}, + sym_name=None, + loc=None, + ip=None): + self.module = module + instance_name = StringAttr.get(name) + module_name = FlatSymbolRefAttr.get(StringAttr(module.name).value) + inst_param_array = create_parameters(parameters, module) + if sym_name: + inner_sym = hw.InnerSymAttr.get(StringAttr.get(sym_name)) + else: + inner_sym = None + pre_args = [instance_name, module_name] + post_args = [ + ArrayAttr.get([StringAttr.get(x) for x in self.operand_names()]), + ArrayAttr.get([StringAttr.get(x) for x in self.result_names()]), + ArrayAttr.get(inst_param_array) + ] + if results is None: + results = module.type.output_types + + if not isinstance(module, hw.HWModuleExternOp): + input_name_type_lookup = { + name: support.type_to_pytype(ty) + for name, ty in zip(self.operand_names(), module.type.input_types) + } + for input_name, input_value in input_port_mapping.items(): + if input_name not in input_name_type_lookup: + continue # This error gets caught and raised later. + mod_input_type = input_name_type_lookup[input_name] + if support.type_to_pytype(input_value.type) != mod_input_type: + raise TypeError(f"Input '{input_name}' has type '{input_value.type}' " + f"but expected '{mod_input_type}'") + + super().__init__(hw.InstanceOp, + results, + input_port_mapping, + pre_args, + post_args, + needs_result_type=True, + inner_sym=inner_sym, + loc=loc, + ip=ip) + + def create_default_value(self, index, data_type, arg_name): + type = self.module.type.input_types[index] + return support.BackedgeBuilder.create(type, + arg_name, + self, + instance_of=self.module) + + def operand_names(self): + return self.module.type.input_names + + def result_names(self): + return self.module.type.output_names + + +class ModuleLike: + """Custom Python base class for module-like operations.""" + + def __init__( + self, + name, + input_ports=[], + output_ports=[], + *, + parameters=[], + attributes={}, + body_builder=None, + loc=None, + ip=None, + ): + """ + Create a module-like with the provided `name`, `input_ports`, and + `output_ports`. + - `name` is a string representing the module name. + - `input_ports` is a list of pairs of string names and mlir.ir types. + - `output_ports` is a list of pairs of string names and mlir.ir types. + - `body_builder` is an optional callback, when provided a new entry block + is created and the callback is invoked with the new op as argument within + an InsertionPoint context already set for the block. The callback is + expected to insert a terminator in the block. + """ + # Copy the mutable default arguments. 'Cause python. + input_ports = list(input_ports) + output_ports = list(output_ports) + parameters = list(parameters) + attributes = dict(attributes) + + operands = [] + results = [] + attributes["sym_name"] = StringAttr.get(str(name)) + + module_ports = [] + input_names = [] + port_locs = [] + unknownLoc = Location.unknown().attr + for (i, (port_name, port_type)) in enumerate(input_ports): + input_name = StringAttr.get(str(port_name)) + input_dir = hw.ModulePortDirection.INPUT + input_port = hw.ModulePort(input_name, port_type, input_dir) + module_ports.append(input_port) + input_names.append(input_name) + port_locs.append(unknownLoc) + + output_types = [] + output_names = [] + for (i, (port_name, port_type)) in enumerate(output_ports): + output_name = StringAttr.get(str(port_name)) + output_dir = hw.ModulePortDirection.OUTPUT + output_port = hw.ModulePort(output_name, port_type, output_dir) + module_ports.append(output_port) + output_names.append(output_name) + port_locs.append(unknownLoc) + attributes["port_locs"] = ArrayAttr.get(port_locs) + attributes["per_port_attrs"] = ArrayAttr.get([]) + + if len(parameters) > 0 or "parameters" not in attributes: + attributes["parameters"] = ArrayAttr.get(parameters) + + attributes["module_type"] = TypeAttr.get(hw.ModuleType.get(module_ports)) + + _ods_cext.ir.OpView.__init__( + self, + self.build_generic(attributes=attributes, + results=results, + operands=operands, + loc=loc, + ip=ip)) + + if body_builder: + entry_block = self.add_entry_block() + + with InsertionPoint(entry_block): + with support.BackedgeBuilder(): + outputs = body_builder(self) + _create_output_op(name, output_ports, entry_block, outputs) + + @property + def type(self): + return hw.ModuleType(TypeAttr(self.attributes["module_type"]).value) + + @property + def name(self): + return self.attributes["sym_name"] + + @property + 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 instantiate(self, + name: str, + parameters: Dict[str, object] = {}, + results=None, + sym_name=None, + loc=None, + ip=None, + **kwargs): + return InstanceBuilder(self, + name, + kwargs, + parameters=parameters, + results=results, + sym_name=sym_name, + loc=loc, + ip=ip) + + +def _create_output_op(cls_name, output_ports, entry_block, bb_ret): + """Create the hw.OutputOp from the body_builder return.""" + + # Determine if the body already has an output op. + block_len = len(entry_block.operations) + if block_len > 0: + last_op = entry_block.operations[block_len - 1] + if isinstance(last_op, hw.OutputOp): + # If it does, the return from body_builder must be None. + if bb_ret is not None and bb_ret != last_op: + raise support.ConnectionError( + f"In {cls_name}, cannot return value from body_builder and " + "create hw.OutputOp") + return + + # If builder didn't create an output op and didn't return anything, this op + # mustn't have any outputs. + if bb_ret is None: + if len(output_ports) == 0: + hw.OutputOp([]) + return + raise support.ConnectionError( + f"In {cls_name}, must return module output values") + + # Now create the output op depending on the object type returned + outputs: list[Value] = list() + + # Only acceptable return is a dict of port, value mappings. + if not isinstance(bb_ret, dict): + raise support.ConnectionError( + f"In {cls_name}, can only return a dict of port, value mappings " + "from body_builder.") + + # A dict of `OutputPortName` -> ValueLike must be converted to a list in port + # order. + unconnected_ports = [] + for (name, port_type) in output_ports: + if name not in bb_ret: + unconnected_ports.append(name) + outputs.append(None) + else: + val = support.get_value(bb_ret[name]) + if val is None: + field_type = type(bb_ret[name]) + raise TypeError( + f"In {cls_name}, body_builder return doesn't support type " + f"'{field_type}'") + if val.type != port_type: + if isinstance(port_type, hw.TypeAliasType) and \ + port_type.inner_type == val.type: + val = hw.BitcastOp.create(port_type, val).result + else: + raise TypeError( + f"In {cls_name}, output port '{name}' type ({val.type}) doesn't " + f"match declared type ({port_type})") + outputs.append(val) + bb_ret.pop(name) + if len(unconnected_ports) > 0: + raise support.UnconnectedSignalError(cls_name, unconnected_ports) + if len(bb_ret) > 0: + raise support.ConnectionError( + f"Could not map the following to output ports in {cls_name}: " + + ",".join(bb_ret.keys())) + + hw.OutputOp(outputs) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class HWModuleOp(ModuleLike, HWModuleOp): + """Specialization for the HW module op class.""" + + def __init__( + self, + name, + input_ports=[], + output_ports=[], + *, + parameters=[], + attributes={}, + body_builder=None, + loc=None, + ip=None, + ): + if "comment" not in attributes: + attributes["comment"] = StringAttr.get("") + super().__init__(name, + input_ports, + output_ports, + parameters=parameters, + attributes=attributes, + body_builder=body_builder, + loc=loc, + ip=ip) + + @property + def body(self): + return self.regions[0] + + @property + def entry_block(self): + return self.regions[0].blocks[0] + + @property + def input_indices(self): + indices: dict[int, str] = {} + op_names = self.type.input_names + for idx, name in enumerate(op_names): + indices[name] = idx + return indices + + # Support attribute access to block arguments by name + def __getattr__(self, name): + if name in self.input_indices: + index = self.input_indices[name] + return self.entry_block.arguments[index] + raise AttributeError(f"unknown input port name {name}") + + def inputs(self) -> dict[str:Value]: + ret = {} + for (name, idx) in self.input_indices.items(): + ret[name] = self.entry_block.arguments[idx] + return ret + + def outputs(self) -> dict[str:Type]: + result_names = self.type.output_names + result_types = self.type.output_types + return dict(zip(result_names, result_types)) + + def add_entry_block(self): + if not self.is_external: + raise IndexError('The module already has an entry block') + self.body.blocks.append(*self.type.input_types) + return self.body.blocks[0] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class HWModuleExternOp(ModuleLike, HWModuleExternOp): + """Specialization for the HW module op class.""" + + def __init__( + self, + name, + input_ports=[], + output_ports=[], + *, + parameters=[], + attributes={}, + body_builder=None, + loc=None, + ip=None, + ): + if "comment" not in attributes: + attributes["comment"] = StringAttr.get("") + super().__init__(name, + input_ports, + output_ports, + parameters=parameters, + attributes=attributes, + body_builder=body_builder, + loc=loc, + ip=ip) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ConstantOp(ConstantOp): + + @staticmethod + def create(data_type, value): + return hw.ConstantOp(IntegerAttr.get(data_type, value)) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class BitcastOp(BitcastOp): + + @staticmethod + def create(data_type, value): + value = support.get_value(value) + return hw.BitcastOp(data_type, value) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ArrayGetOp(ArrayGetOp): + + @staticmethod + def create(array_value, idx): + array_value = support.get_value(array_value) + array_type = support.get_self_or_inner(array_value.type) + if isinstance(idx, int): + idx_width = (array_type.size - 1).bit_length() + idx_val = ConstantOp.create(IntegerType.get_signless(idx_width), + idx).result + else: + idx_val = support.get_value(idx) + return hw.ArrayGetOp(array_value, idx_val) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ArraySliceOp(ArraySliceOp): + + @staticmethod + def create(array_value, low_index, ret_type): + array_value = support.get_value(array_value) + array_type = support.get_self_or_inner(array_value.type) + if isinstance(low_index, int): + idx_width = (array_type.size - 1).bit_length() + idx_width = max(1, idx_width) # hw.constant cannot produce i0. + idx_val = ConstantOp.create(IntegerType.get_signless(idx_width), + low_index).result + else: + idx_val = support.get_value(low_index) + return hw.ArraySliceOp(ret_type, array_value, idx_val) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ArrayCreateOp(ArrayCreateOp): + + @staticmethod + def create(elements): + if not elements: + raise ValueError("Cannot 'create' an array of length zero") + vals = [] + type = None + for i, arg in enumerate(elements): + arg_val = support.get_value(arg) + vals.append(arg_val) + if type is None: + type = arg_val.type + elif type != arg_val.type: + raise TypeError( + f"Argument {i} has a different element type ({arg_val.type}) than the element type of the array ({type})" + ) + return hw.ArrayCreateOp(hw.ArrayType.get(type, len(vals)), vals) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ArrayConcatOp(ArrayConcatOp): + + @staticmethod + def create(*sub_arrays): + vals = [] + types = [] + element_type = None + for i, array in enumerate(sub_arrays): + array_value = support.get_value(array) + array_type = support.type_to_pytype(array_value.type) + if array_value is None or not isinstance(array_type, hw.ArrayType): + raise TypeError(f"Cannot concatenate {array_value}") + if element_type is None: + element_type = array_type.element_type + elif element_type != array_type.element_type: + raise TypeError( + f"Argument {i} has a different element type ({element_type}) than the element type of the array ({array_type.element_type})" + ) + + vals.append(array_value) + types.append(array_type) + + size = sum(t.size for t in types) + combined_type = hw.ArrayType.get(element_type, size) + return hw.ArrayConcatOp(combined_type, vals) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class StructCreateOp(StructCreateOp): + + @staticmethod + def create(elements, result_type: Type = None): + elem_name_values = [ + (name, support.get_value(value)) for (name, value) in elements + ] + struct_fields = [(name, value.type) for (name, value) in elem_name_values] + struct_type = hw.StructType.get(struct_fields) + + if result_type is None: + result_type = struct_type + else: + result_type_inner = support.get_self_or_inner(result_type) + if result_type_inner != struct_type: + raise TypeError( + f"result type:\n\t{result_type_inner}\nmust match generated struct type:\n\t{struct_type}" + ) + + return hw.StructCreateOp(result_type, + [value for (_, value) in elem_name_values]) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class StructExtractOp(StructExtractOp): + + @staticmethod + def create(struct_value, field_name: str): + struct_value = support.get_value(struct_value) + struct_type = support.get_self_or_inner(struct_value.type) + field_type = struct_type.get_field(field_name) + return hw.StructExtractOp(field_type, struct_value, + StringAttr.get(field_name)) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class TypedeclOp(TypedeclOp): + + @staticmethod + def create(sym_name: str, type: Type, verilog_name: str = None): + return hw.TypedeclOp(StringAttr.get(sym_name), + TypeAttr.get(type), + verilogName=verilog_name) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class TypeScopeOp(TypeScopeOp): + + @staticmethod + def create(sym_name: str): + op = hw.TypeScopeOp(StringAttr.get(sym_name)) + op.regions[0].blocks.append() + return op + + @property + def body(self): + return self.regions[0].blocks[0] diff --git a/lib/Bindings/Python/dialects/hwarith.py b/lib/Bindings/Python/dialects/hwarith.py index 9a2c914a00fa..019145a2a5ba 100644 --- a/lib/Bindings/Python/dialects/hwarith.py +++ b/lib/Bindings/Python/dialects/hwarith.py @@ -2,4 +2,89 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +from ..dialects._ods_common import _cext as _ods_cext +from ..ir import IntegerAttr, IntegerType +from ..support import NamedValueOpView, get_value from ._hwarith_ops_gen import * +from ._hwarith_ops_gen import _Dialect + + +class BinaryOpBuilder(NamedValueOpView): + + def operand_names(self): + return ["lhs", "rhs"] + + def result_names(self): + return ["result"] + + +def BinaryOp(base): + + class _Class(base): + + @classmethod + def create(cls, lhs=None, rhs=None, result_type=None): + return cls([get_value(lhs), get_value(rhs)]) + + return _Class + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class DivOp(DivOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class SubOp(SubOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class AddOp(AddOp): + pass + + +@BinaryOp +@_ods_cext.register_operation(_Dialect, replace=True) +class MulOp(MulOp): + pass + + +@_ods_cext.register_operation(_Dialect, replace=True) +class CastOp(CastOp): + + @classmethod + def create(cls, value, result_type): + return cls(result_type, value) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ICmpOp(ICmpOp): + # Predicate constants. + + # `==` and `!=` + PRED_EQ = 0b000 + PRED_NE = 0b001 + # `<` and `>=` + PRED_LT = 0b010 + PRED_GE = 0b011 + # `<=` and `>` + PRED_LE = 0b100 + PRED_GT = 0b101 + + @classmethod + def create(cls, pred, a, b): + if isinstance(pred, int): + pred = IntegerAttr.get(IntegerType.get_signless(64), pred) + return cls(pred, a, b) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ConstantOp(ConstantOp): + + @classmethod + def create(cls, data_type, value): + return cls(IntegerAttr.get(data_type, value)) diff --git a/lib/Bindings/Python/dialects/msft.py b/lib/Bindings/Python/dialects/msft.py index 654b99f4d297..401ee7a12113 100644 --- a/lib/Bindings/Python/dialects/msft.py +++ b/lib/Bindings/Python/dialects/msft.py @@ -2,5 +2,67 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -from ._msft_ops_gen import * +from . import hw, msft +from .. import support from .._mlir_libs._circt._msft import * +from ..dialects._ods_common import _cext as _ods_cext +from ..ir import ArrayAttr +from ._msft_ops_gen import * +from ._msft_ops_gen import _Dialect +from typing import Dict, List, Type + + +@_ods_cext.register_operation(_Dialect, replace=True) +class DeclPhysicalRegionOp(DeclPhysicalRegionOp): + + def add_bounds(self, bounds): + existing_bounds = [b for b in ArrayAttr(self.attributes["bounds"])] + existing_bounds.append(bounds) + new_bounds = ArrayAttr.get(existing_bounds) + self.attributes["bounds"] = new_bounds + + +@_ods_cext.register_operation(_Dialect, replace=True) +class InstanceHierarchyOp(InstanceHierarchyOp): + + @staticmethod + def create(root_mod, instance_name=None): + hier = msft.InstanceHierarchyOp(root_mod, instName=instance_name) + hier.body.blocks.append() + return hier + + @property + def top_module_ref(self): + return self.attributes["topModuleRef"] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class DynamicInstanceOp(DynamicInstanceOp): + + @staticmethod + def create(name_ref): + inst = msft.DynamicInstanceOp(name_ref) + inst.body.blocks.append() + return inst + + @property + def instance_path(self): + path = [] + next = self + while isinstance(next, DynamicInstanceOp): + path.append(next.attributes["instanceRef"]) + next = next.operation.parent.opview + path.reverse() + return ArrayAttr.get(path) + + @property + def instanceRef(self): + return self.attributes["instanceRef"] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class PDPhysLocationOp(PDPhysLocationOp): + + @property + def loc(self): + return msft.PhysLocationAttr(self.attributes["loc"]) diff --git a/lib/Bindings/Python/dialects/seq.py b/lib/Bindings/Python/dialects/seq.py index 72879fea11ab..35eba447898d 100644 --- a/lib/Bindings/Python/dialects/seq.py +++ b/lib/Bindings/Python/dialects/seq.py @@ -2,10 +2,14 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +from . import hw +from .._mlir_libs._circt._seq import * +from ..dialects._ods_common import _cext as _ods_cext +from ..ir import IntegerType, OpView, StringAttr +from ..support import BackedgeBuilder, NamedValueOpView from ._seq_ops_gen import * - +from ._seq_ops_gen import _Dialect from .seq import CompRegOp -from .._mlir_libs._circt._seq import * # Create a computational register whose input is the given value, and is clocked @@ -34,3 +38,135 @@ def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None): clk=clock, name=name, sym_name=sym_name).data.value + + +class CompRegLikeBuilder(NamedValueOpView): + + def result_names(self): + return ["data"] + + def create_initial_value(self, index, data_type, arg_name): + if arg_name == "input": + operand_type = data_type + else: + operand_type = IntegerType.get_signless(1) + return BackedgeBuilder.create(operand_type, arg_name, self) + + +class CompRegLike: + + def __init__(self, + data_type, + input, + clk, + clockEnable=None, + *, + reset=None, + reset_value=None, + power_on_value=None, + name=None, + sym_name=None, + loc=None, + ip=None): + operands = [input, clk] + results = [] + attributes = {} + results.append(data_type) + operand_segment_sizes = [1, 1] + if isinstance(self, CompRegOp): + if clockEnable is not None: + raise Exception("Clock enable not supported on compreg") + elif isinstance(self, CompRegClockEnabledOp): + if clockEnable is None: + raise Exception("Clock enable required on compreg.ce") + operands.append(clockEnable) + operand_segment_sizes.append(1) + else: + assert False, "Class not recognized" + if reset is not None and reset_value is not None: + operands.append(reset) + operands.append(reset_value) + operand_segment_sizes += [1, 1] + else: + operand_segment_sizes += [0, 0] + operands += [None, None] + + if power_on_value is not None: + operands.append(power_on_value) + operand_segment_sizes.append(1) + else: + operands.append(None) + operand_segment_sizes.append(0) + if name is None: + attributes["name"] = StringAttr.get("") + else: + attributes["name"] = StringAttr.get(name) + if sym_name is not None: + attributes["inner_sym"] = hw.InnerSymAttr.get(StringAttr.get(sym_name)) + + self._ODS_OPERAND_SEGMENTS = operand_segment_sizes + + OpView.__init__( + self, + self.build_generic( + attributes=attributes, + results=results, + operands=operands, + loc=loc, + ip=ip, + ), + ) + + +class CompRegBuilder(CompRegLikeBuilder): + + def operand_names(self): + return ["input", "clk"] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class CompRegOp(CompRegLike, CompRegOp): + + @classmethod + def create(cls, + result_type, + reset=None, + reset_value=None, + name=None, + sym_name=None, + **kwargs): + return CompRegBuilder(cls, + result_type, + kwargs, + reset=reset, + reset_value=reset_value, + name=name, + sym_name=sym_name, + needs_result_type=True) + + +class CompRegClockEnabledBuilder(CompRegLikeBuilder): + + def operand_names(self): + return ["input", "clk", "clockEnable"] + + +@_ods_cext.register_operation(_Dialect, replace=True) +class CompRegClockEnabledOp(CompRegLike, CompRegClockEnabledOp): + + @classmethod + def create(cls, + result_type, + reset=None, + reset_value=None, + name=None, + sym_name=None, + **kwargs): + return CompRegClockEnabledBuilder(cls, + result_type, + kwargs, + reset=reset, + reset_value=reset_value, + name=name, + sym_name=sym_name, + needs_result_type=True) diff --git a/lib/Bindings/Python/dialects/sv.py b/lib/Bindings/Python/dialects/sv.py index 8c544079dca9..eac42f8cb1ba 100644 --- a/lib/Bindings/Python/dialects/sv.py +++ b/lib/Bindings/Python/dialects/sv.py @@ -2,5 +2,108 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -from ._sv_ops_gen import * +from . import sv, hw +from .. import support from .._mlir_libs._circt._sv import * +from ..dialects._ods_common import _cext as _ods_cext +from ..ir import ArrayAttr, Attribute, FlatSymbolRefAttr, OpView, StringAttr +from ._sv_ops_gen import * +from ._sv_ops_gen import _Dialect + + +@_ods_cext.register_operation(_Dialect, replace=True) +class IfDefOp(IfDefOp): + + def __init__(self, cond: Attribute, *, loc=None, ip=None): + operands = [] + results = [] + attributes = {"cond": cond} + regions = 2 + super().__init__( + self.build_generic(attributes=attributes, + results=results, + operands=operands, + successors=None, + regions=regions, + loc=loc, + ip=ip)) + self.regions[0].blocks.append() + self.regions[1].blocks.append() + + +@_ods_cext.register_operation(_Dialect, replace=True) +class WireOp(WireOp): + + def __init__(self, + data_type, + name, + *, + sym_name=None, + svAttributes=None, + loc=None, + ip=None): + attributes = {"name": StringAttr.get(name)} + if sym_name is not None: + attributes["inner_sym"] = hw.InnerSymAttr.get(StringAttr.get(sym_name)) + if svAttributes is not None: + attributes["svAttributes"] = ArrayAttr.get(svAttributes) + OpView.__init__( + self, + self.build_generic(attributes=attributes, + results=[data_type], + operands=[], + successors=None, + regions=0, + loc=loc, + ip=ip)) + + @staticmethod + def create(data_type, name=None, sym_name=None): + if not isinstance(data_type, hw.InOutType): + data_type = hw.InOutType.get(data_type) + return sv.WireOp(data_type, name, sym_name=sym_name) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class RegOp(RegOp): + + def __init__(self, + data_type, + name, + *, + sym_name=None, + svAttributes=None, + loc=None, + ip=None): + attributes = {"name": StringAttr.get(name)} + if sym_name is not None: + attributes["inner_sym"] = hw.InnerSymAttr.get(StringAttr.get(sym_name)) + if svAttributes is not None: + attributes["svAttributes"] = ArrayAttr.get(svAttributes) + OpView.__init__( + self, + self.build_generic(attributes=attributes, + results=[data_type], + operands=[], + successors=None, + regions=0, + loc=loc, + ip=ip)) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class AssignOp(AssignOp): + + @staticmethod + def create(dest, src): + return sv.AssignOp(dest=dest, src=src) + + +@_ods_cext.register_operation(_Dialect, replace=True) +class ReadInOutOp(ReadInOutOp): + + @staticmethod + def create(value): + value = support.get_value(value) + type = support.get_self_or_inner(value.type).element_type + return sv.ReadInOutOp(value) diff --git a/lib/Conversion/HWToLLVM/HWToLLVM.cpp b/lib/Conversion/HWToLLVM/HWToLLVM.cpp index 27895e2eb604..5caf1897c5b1 100644 --- a/lib/Conversion/HWToLLVM/HWToLLVM.cpp +++ b/lib/Conversion/HWToLLVM/HWToLLVM.cpp @@ -159,11 +159,8 @@ struct ArrayGetOpConversion : public ConvertOpToLLVMPattern { rewriter.create(op->getLoc(), adaptor.getInput(), arrPtr); } + auto arrTy = typeConverter->convertType(op.getInput().getType()); auto elemTy = typeConverter->convertType(op.getResult().getType()); - - auto zeroC = rewriter.create( - op->getLoc(), IntegerType::get(rewriter.getContext(), 32), - rewriter.getI32IntegerAttr(0)); auto zextIndex = zextByOne(op->getLoc(), rewriter, op.getIndex()); // During the ongoing migration to opaque types, use the constructor that @@ -173,11 +170,11 @@ struct ArrayGetOpConversion : public ConvertOpToLLVMPattern { if (cast(arrPtr.getType()).isOpaque()) gep = rewriter.create( op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()), - elemTy, arrPtr, ArrayRef({zeroC, zextIndex})); + arrTy, arrPtr, ArrayRef{0, zextIndex}); else gep = rewriter.create( op->getLoc(), LLVM::LLVMPointerType::get(elemTy), arrPtr, - ArrayRef({zeroC, zextIndex})); + ArrayRef{0, zextIndex}); rewriter.replaceOpWithNewOp(op, elemTy, gep); @@ -202,8 +199,6 @@ struct ArraySliceOpConversion auto elemTy = typeConverter->convertType( op.getDst().getType().cast().getElementType()); - auto zeroC = rewriter.create( - op->getLoc(), rewriter.getI32Type(), rewriter.getI32IntegerAttr(0)); auto oneC = rewriter.create( op->getLoc(), rewriter.getI32Type(), rewriter.getI32IntegerAttr(1)); @@ -223,11 +218,11 @@ struct ArraySliceOpConversion if (cast(arrPtr.getType()).isOpaque()) gep = rewriter.create( op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()), - elemTy, arrPtr, ArrayRef({zeroC, zextIndex})); + dstTy, arrPtr, ArrayRef{0, zextIndex}); else gep = rewriter.create( op->getLoc(), LLVM::LLVMPointerType::get(elemTy), arrPtr, - ArrayRef({zeroC, zextIndex})); + ArrayRef{0, zextIndex}); auto cast = rewriter.create( op->getLoc(), LLVM::LLVMPointerType::get(dstTy), gep); diff --git a/lib/Dialect/FIRRTL/FIRRTLOpInterfaces.cpp b/lib/Dialect/FIRRTL/FIRRTLOpInterfaces.cpp index ebf076baa326..ba8c6676eb97 100644 --- a/lib/Dialect/FIRRTL/FIRRTLOpInterfaces.cpp +++ b/lib/Dialect/FIRRTL/FIRRTLOpInterfaces.cpp @@ -177,7 +177,7 @@ circt::firrtl::detail::replaceWithNewForceability(Forceable op, bool forceable, if (forceable) attributes.push_back(forceableMarker); else { - llvm::erase_value(attributes, forceableMarker); + llvm::erase(attributes, forceableMarker); assert(attributes.size() != op->getAttrs().size()); } diff --git a/lib/Dialect/FIRRTL/NLATable.cpp b/lib/Dialect/FIRRTL/NLATable.cpp index b924d88c533f..5a09a2045e95 100644 --- a/lib/Dialect/FIRRTL/NLATable.cpp +++ b/lib/Dialect/FIRRTL/NLATable.cpp @@ -69,9 +69,9 @@ void NLATable::erase(hw::HierPathOp nla, SymbolTable *symbolTable) { symToOp.erase(nla.getSymNameAttr()); for (auto ent : nla.getNamepath()) if (auto mod = dyn_cast(ent)) - llvm::erase_value(nodeMap[mod.getAttr()], nla); + llvm::erase(nodeMap[mod.getAttr()], nla); else if (auto inr = ent.dyn_cast()) - llvm::erase_value(nodeMap[inr.getModule()], nla); + llvm::erase(nodeMap[inr.getModule()], nla); if (symbolTable) symbolTable->erase(nla); } diff --git a/llvm b/llvm index 5f5faf407b42..7ce613fc77af 160000 --- a/llvm +++ b/llvm @@ -1 +1 @@ -Subproject commit 5f5faf407b42342708ce31a1ca3095ddff10dad8 +Subproject commit 7ce613fc77af092dd6e9db71ce3747b75bc5616e diff --git a/test/Conversion/HWToLLVM/convert_aggregates.mlir b/test/Conversion/HWToLLVM/convert_aggregates.mlir index 4bb967091fbb..0c42a079ae8c 100644 --- a/test/Conversion/HWToLLVM/convert_aggregates.mlir +++ b/test/Conversion/HWToLLVM/convert_aggregates.mlir @@ -36,18 +36,16 @@ func.func @convertArray(%arg0 : i1, %arg1: !hw.array<2xi32>, %arg2: i32, %arg3: // CHECK-NEXT: %[[ONE:.*]] = llvm.mlir.constant(1 : i32) : i32 // CHECK-NEXT: %[[ALLOCA:.*]] = llvm.alloca %[[ONE]] x !llvm.array<2 x i32> {alignment = 4 : i64} : (i32) -> !llvm.ptr> // CHECK-NEXT: llvm.store %[[CAST0]], %[[ALLOCA]] : !llvm.ptr> - // CHECK-NEXT: %[[ZERO:.*]] = llvm.mlir.constant(0 : i32) : i32 // CHECK-NEXT: %[[ZEXT:.*]] = llvm.zext %arg0 : i1 to i2 - // CHECK-NEXT: %[[GEP:.*]] = llvm.getelementptr %[[ALLOCA]][%[[ZERO]], %[[ZEXT]]] : (!llvm.ptr>, i32, i2) -> !llvm.ptr + // CHECK-NEXT: %[[GEP:.*]] = llvm.getelementptr %[[ALLOCA]][0, %[[ZEXT]]] : (!llvm.ptr>, i2) -> !llvm.ptr // CHECK-NEXT: llvm.load %[[GEP]] : !llvm.ptr %0 = hw.array_get %arg1[%arg0] : !hw.array<2xi32>, i1 - // CHECK-NEXT: %[[ZERO1:.*]] = llvm.mlir.constant(0 : i32) : i32 // CHECK-NEXT: %[[ONE4:.*]] = llvm.mlir.constant(1 : i32) : i32 // CHECK-NEXT: %[[ALLOCA1:.*]] = llvm.alloca %[[ONE4]] x !llvm.array<2 x i32> {alignment = 4 : i64} : (i32) -> !llvm.ptr> // CHECK-NEXT: llvm.store %[[CAST0]], %[[ALLOCA1]] : !llvm.ptr> // CHECK-NEXT: %[[ZEXT1:.*]] = llvm.zext %arg0 : i1 to i2 - // CHECK-NEXT: %[[GEP1:.*]] = llvm.getelementptr %[[ALLOCA1]][%[[ZERO1]], %[[ZEXT1]]] : (!llvm.ptr>, i32, i2) -> !llvm.ptr + // CHECK-NEXT: %[[GEP1:.*]] = llvm.getelementptr %[[ALLOCA1]][0, %[[ZEXT1]]] : (!llvm.ptr>, i2) -> !llvm.ptr // CHECK-NEXT: %[[BITCAST:.*]] = llvm.bitcast %[[GEP1]] : !llvm.ptr to !llvm.ptr> // CHECK-NEXT: llvm.load %[[LD:.*]] : !llvm.ptr> %1 = hw.array_slice %arg1[%arg0] : (!hw.array<2xi32>) -> !hw.array<1xi32> @@ -104,9 +102,8 @@ func.func @convertConstArray(%arg0 : i1) { // COM: Test: when the array argument is already a load from a pointer, // COM: then don't allocate on the stack again but take that pointer directly as a shortcut - // CHECK-NEXT: %[[VAL_4:.*]] = llvm.mlir.constant(0 : i32) : i32 // CHECK-NEXT: %[[VAL_5:.*]] = llvm.zext %arg0 : i1 to i2 - // CHECK-NEXT: %[[VAL_6:.*]] = llvm.getelementptr %[[VAL_2]][%[[VAL_4]], %[[VAL_5]]] : (!llvm.ptr, i32, i2) -> !llvm.ptr, i32 + // CHECK-NEXT: %[[VAL_6:.*]] = llvm.getelementptr %[[VAL_2]][0, %[[VAL_5]]] : (!llvm.ptr, i2) -> !llvm.ptr, !llvm.array<2 x i32> // CHECK-NEXT: %{{.+}} = llvm.load %[[VAL_6]] : !llvm.ptr -> i32 %1 = hw.array_get %0[%arg0] : !hw.array<2xi32>, i1