Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
support.py
Go to the documentation of this file.
1# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2# See https://llvm.org/LICENSE.txt for license information.
3# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5from . import ir
6
7from ._mlir_libs._circt._support import _walk_with_filter
8from .ir import Operation
9from contextlib import AbstractContextManager
10from contextvars import ContextVar
11from typing import List
12
13_current_backedge_builder = ContextVar("current_bb")
14
15
17 pass
18
19
21
22 def __init__(self, module: str, port_names: List[str]):
23 super().__init__(
24 f"Ports {port_names} unconnected in design module {module}.")
25
26
27def get_value(obj) -> ir.Value:
28 """Resolve a Value from a few supported types."""
29
30 if isinstance(obj, ir.Value):
31 return obj
32 if hasattr(obj, "result"):
33 return obj.result
34 if hasattr(obj, "value"):
35 return obj.value
36 return None
37
38
39def connect(destination, source):
40 """A convenient way to use BackedgeBuilder."""
41 if not isinstance(destination, OpOperand):
42 raise TypeError(
43 f"cannot connect to destination of type {type(destination)}. "
44 "Must be OpOperand.")
45 value = get_value(source)
46 if value is None:
47 raise TypeError(f"cannot connect from source of type {type(source)}")
48
49 index = destination.index
50 destination.operation.operands[index] = value
51 if destination.backedge_owner and \
52 index in destination.backedge_owner.backedges:
53 destination.backedge_owner.backedges[index].erase()
54 del destination.backedge_owner.backedges[index]
55
56
57def var_to_attribute(obj, none_on_fail: bool = False) -> ir.Attribute:
58 """Create an MLIR attribute from a Python object for a few common cases."""
59 if isinstance(obj, ir.Attribute):
60 return obj
61 if isinstance(obj, bool):
62 return ir.BoolAttr.get(obj)
63 if isinstance(obj, int):
64 attrTy = ir.IntegerType.get_signless(64)
65 return ir.IntegerAttr.get(attrTy, obj)
66 if isinstance(obj, str):
67 return ir.StringAttr.get(obj)
68 if isinstance(obj, list):
69 arr = [var_to_attribute(x, none_on_fail) for x in obj]
70 if all(arr):
71 return ir.ArrayAttr.get(arr)
72 return None
73 if none_on_fail:
74 return None
75 raise TypeError(f"Cannot convert type '{type(obj)}' to MLIR attribute")
76
77
78# There is currently no support in MLIR for querying type types. The
79# conversation regarding how to achieve this is ongoing and I expect it to be a
80# long one. This is a way that works for now.
81def type_to_pytype(t) -> ir.Type:
82
83 if not isinstance(t, ir.Type):
84 raise TypeError("type_to_pytype only accepts MLIR Type objects")
85
86 # If it's not the root type, assume it's already been downcasted and don't do
87 # the expensive probing below.
88 if t.__class__ != ir.Type:
89 return t
90
91 from .dialects import esi, hw, seq, rtg, rtgtest
92 try:
93 return ir.IntegerType(t)
94 except ValueError:
95 pass
96 try:
97 return ir.NoneType(t)
98 except ValueError:
99 pass
100 try:
101 return ir.TupleType(t)
102 except ValueError:
103 pass
104 try:
105 return hw.ArrayType(t)
106 except ValueError:
107 pass
108 try:
109 return hw.StructType(t)
110 except ValueError:
111 pass
112 try:
113 return hw.TypeAliasType(t)
114 except ValueError:
115 pass
116 try:
117 return hw.InOutType(t)
118 except ValueError:
119 pass
120 try:
121 return seq.ClockType(t)
122 except ValueError:
123 pass
124 try:
125 return esi.ChannelType(t)
126 except ValueError:
127 pass
128 try:
129 return esi.AnyType(t)
130 except ValueError:
131 pass
132 try:
133 return esi.BundleType(t)
134 except ValueError:
135 pass
136 try:
137 return rtg.LabelType(t)
138 except ValueError:
139 pass
140 try:
141 return rtg.SetType(t)
142 except ValueError:
143 pass
144 try:
145 return rtg.BagType(t)
146 except ValueError:
147 pass
148 try:
149 return rtg.SequenceType(t)
150 except ValueError:
151 pass
152 try:
153 return rtg.RandomizedSequenceType(t)
154 except ValueError:
155 pass
156 try:
157 return rtg.DictType(t)
158 except ValueError:
159 pass
160 try:
161 return rtg.ImmediateType(t)
162 except ValueError:
163 pass
164 try:
165 return rtg.ArrayType(t)
166 except ValueError:
167 pass
168 try:
169 return rtg.MemoryType(t)
170 except ValueError:
171 pass
172 try:
173 return rtg.MemoryBlockType(t)
174 except ValueError:
175 pass
176 try:
177 return rtgtest.IntegerRegisterType(t)
178 except ValueError:
179 pass
180 try:
181 return rtgtest.CPUType(t)
182 except ValueError:
183 pass
184
185 raise TypeError(f"Cannot convert {repr(t)} to python type")
186
187
188# There is currently no support in MLIR for querying attribute types. The
189# conversation regarding how to achieve this is ongoing and I expect it to be a
190# long one. This is a way that works for now.
191def attribute_to_var(attr):
192
193 if attr is None:
194 return None
195 if not isinstance(attr, ir.Attribute):
196 raise TypeError("attribute_to_var only accepts MLIR Attributes")
197
198 # If it's not the root type, assume it's already been downcasted and don't do
199 # the expensive probing below.
200 if attr.__class__ != ir.Attribute and hasattr(attr, "value"):
201 return attr.value
202
203 from .dialects import hw, om
204 try:
205 return ir.BoolAttr(attr).value
206 except ValueError:
207 pass
208 try:
209 return ir.IntegerAttr(attr).value
210 except ValueError:
211 pass
212 try:
213 return ir.StringAttr(hw.InnerSymAttr(attr).symName).value
214 except ValueError:
215 pass
216 try:
217 return ir.StringAttr(attr).value
218 except ValueError:
219 pass
220 try:
221 return ir.FlatSymbolRefAttr(attr).value
222 except ValueError:
223 pass
224 try:
225 return ir.TypeAttr(attr).value
226 except ValueError:
227 pass
228 try:
229 arr = ir.ArrayAttr(attr)
230 return [attribute_to_var(x) for x in arr]
231 except ValueError:
232 pass
233 try:
234 dict = ir.DictAttr(attr)
235 return {i.name: attribute_to_var(i.attr) for i in dict}
236 except ValueError:
237 pass
238 try:
239 return attribute_to_var(om.ReferenceAttr(attr).inner_ref)
240 except ValueError:
241 pass
242 try:
243 ref = hw.InnerRefAttr(attr)
244 return (ir.StringAttr(ref.module).value, ir.StringAttr(ref.name).value)
245 except ValueError:
246 pass
247 try:
248 return list(map(attribute_to_var, om.ListAttr(attr)))
249 except ValueError:
250 pass
251 try:
252 return {name: attribute_to_var(value) for name, value in om.MapAttr(attr)}
253 except ValueError:
254 pass
255 try:
256 return int(str(om.OMIntegerAttr(attr)))
257 except ValueError:
258 pass
259 try:
260 return om.PathAttr(attr).value
261 except ValueError:
262 pass
263
264 raise TypeError(f"Cannot convert {repr(attr)} to python value")
265
266
267def get_self_or_inner(mlir_type):
268 from .dialects import hw
269 if type(mlir_type) is ir.Type:
270 mlir_type = type_to_pytype(mlir_type)
271 if isinstance(mlir_type, hw.TypeAliasType):
272 return type_to_pytype(mlir_type.inner_type)
273 return mlir_type
274
275
276class BackedgeBuilder(AbstractContextManager):
277
278 class Edge:
279
280 def __init__(self,
281 creator,
282 type: ir.Type,
283 backedge_name: str,
284 op_view,
285 instance_of: ir.Operation,
286 loc: ir.Location = None):
287 self.creator: BackedgeBuilder = creator
288 self.dummy_op = ir.Operation.create("builtin.unrealized_conversion_cast",
289 [type],
290 loc=loc)
291 self.instance_of = instance_of
292 self.op_view = op_view
293 self.port_name = backedge_name
294 self.loc = loc
295 self.erased = False
296
297 @property
298 def result(self):
299 return self.dummy_op.result
300
301 def erase(self):
302 if self.erased:
303 return
304 if self in self.creator.edges:
305 self.creator.edges.remove(self)
306 self.dummy_op.operation.erase()
307
308 def __init__(self, circuit_name: str = ""):
309 self.circuit_name = circuit_name
310 self.edges = set()
311
312 @staticmethod
313 def current():
314 bb = _current_backedge_builder.get(None)
315 if bb is None:
316 raise RuntimeError("No backedge builder found in context!")
317 return bb
318
319 @staticmethod
320 def create(*args, **kwargs):
321 return BackedgeBuilder.current()._create(*args, **kwargs)
322
323 def _create(self,
324 type: ir.Type,
325 port_name: str,
326 op_view,
327 instance_of: ir.Operation = None,
328 loc: ir.Location = None):
329 edge = BackedgeBuilder.Edge(self, type, port_name, op_view, instance_of,
330 loc)
331 self.edges.add(edge)
332 return edge
333
334 def __enter__(self):
335 self.old_bb_token = _current_backedge_builder.set(self)
336
337 def __exit__(self, exc_type, exc_value, traceback):
338 if exc_value is not None:
339 return
340 _current_backedge_builder.reset(self.old_bb_token)
341 errors = []
342 for edge in list(self.edges):
343 # TODO: Make this use `UnconnectedSignalError`.
344 msg = "Backedge: " + edge.port_name + "\n"
345 if edge.instance_of is not None:
346 msg += "InstanceOf: " + str(edge.instance_of).split(" {")[0] + "\n"
347 if edge.op_view is not None:
348 op = edge.op_view.operation
349 msg += "Instance: " + str(op)
350 if edge.loc is not None:
351 msg += "Location: " + str(edge.loc)
352 errors.append(msg)
353
354 if errors:
355 errors.insert(
356 0, f"Uninitialized backedges remain in module '{self.circuit_name}'")
357 raise RuntimeError("\n".join(errors))
358
359
361 __slots__ = ["index", "operation", "value", "backedge_owner"]
362
363 def __init__(self,
364 operation: ir.Operation,
365 index: int,
366 value,
367 backedge_owner=None):
368 if not isinstance(index, int):
369 raise TypeError("Index must be int")
370 self.index = index
371
372 if not hasattr(operation, "operands"):
373 raise TypeError("Operation must be have 'operands' attribute")
374 self.operation = operation
375
376 self.value = value
377 self.backedge_owner = backedge_owner
378
379 @property
380 def type(self):
381 return self.value.type
382
383
385 """Helper class to incrementally construct an instance of an operation that
386 names its operands and results"""
387
388 def __init__(self,
389 cls,
390 data_type=None,
391 input_port_mapping=None,
392 pre_args=None,
393 post_args=None,
394 needs_result_type=False,
395 **kwargs):
396 # Set defaults
397 if input_port_mapping is None:
398 input_port_mapping = {}
399 if pre_args is None:
400 pre_args = []
401 if post_args is None:
402 post_args = []
403
404 # Set result_indices to name each result.
405 result_names = self.result_names()
406 result_indices = {}
407 for i in range(len(result_names)):
408 result_indices[result_names[i]] = i
409
410 # Set operand_indices to name each operand. Give them an initial value,
411 # either from input_port_mapping or a default value.
412 backedges = {}
413 operand_indices = {}
414 operand_values = []
415 operand_names = self.operand_names()
416 for i in range(len(operand_names)):
417 arg_name = operand_names[i]
418 operand_indices[arg_name] = i
419 if arg_name in input_port_mapping:
420 value = get_value(input_port_mapping[arg_name])
421 operand = value
422 else:
423 backedge = self.create_default_value(i, data_type, arg_name)
424 backedges[i] = backedge
425 operand = backedge.result
426 operand_values.append(operand)
427
428 # Some ops take a list of operand values rather than splatting them out.
429 if isinstance(data_type, list):
430 operand_values = [operand_values]
431
432 # In many cases, result types are inferred, and we do not need to pass
433 # data_type to the underlying constructor. It must be provided to
434 # NamedValueOpView in cases where we need to build backedges, but should
435 # generally not be passed to the underlying constructor in this case. There
436 # are some oddball ops that must pass it, even when building backedges, and
437 # these set needs_result_type=True.
438 if data_type is not None and (needs_result_type or len(backedges) == 0):
439 pre_args.insert(0, data_type)
440
441 self.opview = cls(*pre_args, *operand_values, *post_args, **kwargs)
442 self.operand_indices = operand_indices
443 self.result_indices = result_indices
444 self.backedges = backedges
445
446 def __getattr__(self, name):
447 # Check for the attribute in the arg name set.
448 if "operand_indices" in dir(self) and name in self.operand_indices:
449 index = self.operand_indices[name]
450 value = self.opview.operands[index]
451 return OpOperand(self.opview.operation, index, value, self)
452
453 # Check for the attribute in the result name set.
454 if "result_indices" in dir(self) and name in self.result_indices:
455 index = self.result_indices[name]
456 value = self.opview.results[index]
457 return OpOperand(self.opview.operation, index, value, self)
458
459 # Forward "attributes" attribute from the operation.
460 if name == "attributes":
461 return self.opview.operation.attributes
462
463 # If we fell through to here, the name isn't a result.
464 raise AttributeError(f"unknown port name {name}")
465
466 def create_default_value(self, index, data_type, arg_name):
467 return BackedgeBuilder.create(data_type, arg_name, self)
468
469 @property
470 def operation(self):
471 """Get the operation associated with this builder."""
472 return self.opview.operation
473
474
475# Helper function to walk operation with a filter on operation names.
476# `op_views` is a list of operation views to visit. This is a wrapper
477# around the C++ implementation of walk_with_filter.
478def walk_with_filter(operation: Operation, op_views: List[ir.OpView], callback,
479 walk_order):
480 op_names_identifiers = [name.OPERATION_NAME for name in op_views]
481 return _walk_with_filter(operation, op_names_identifiers, callback,
482 walk_order)
__init__(self, creator, ir.Type type, str backedge_name, op_view, ir.Operation instance_of, ir.Location loc=None)
Definition support.py:286
__init__(self, str circuit_name="")
Definition support.py:308
create(*args, **kwargs)
Definition support.py:320
_create(self, ir.Type type, str port_name, op_view, ir.Operation instance_of=None, ir.Location loc=None)
Definition support.py:328
__exit__(self, exc_type, exc_value, traceback)
Definition support.py:337
__init__(self, cls, data_type=None, input_port_mapping=None, pre_args=None, post_args=None, needs_result_type=False, **kwargs)
Definition support.py:395
create_default_value(self, index, data_type, arg_name)
Definition support.py:466
__init__(self, ir.Operation operation, int index, value, backedge_owner=None)
Definition support.py:367
__init__(self, str module, List[str] port_names)
Definition support.py:22
The "any" type is a special type which can be used to represent any type, as identified by the type i...
Definition Types.h:92
Bundles represent a collection of channels.
Definition Types.h:44
Channels are the basic communication primitives.
Definition Types.h:70
get_self_or_inner(mlir_type)
Definition support.py:267
walk_with_filter(Operation operation, List[ir.OpView] op_views, callback, walk_order)
Definition support.py:479
ir.Type type_to_pytype(t)
Definition support.py:81
connect(destination, source)
Definition support.py:39