systemc-clang 2.0.0
Parsing SystemC constructs
Loading...
Searching...
No Matches
typedef_expansion.py
Go to the documentation of this file.
1import itertools
2from .top_down import TopDown
3from ..primitives import Primitive
4from .node import TypeDefType
5from ..compound import aggregate
6from lark import Tree, Token
7import copy
8import warnings
9from ..utils import dprint, is_tree_type, get_ids_in_tree, alternate_ids, set_ids_in_tree_dfs
10
11
13 """Expands block assignment of custom types into primitive types"""
14 def __init__(self, types):
15 super().__init__()
16 self.types = types
17 # expanded is the variable stack
18 self.expanded = [dict()]
20
21 def __expand_htype(self, htype):
22 """expand non-primitive htype into primitive sub-field type"""
23 # Note: expansion will stop at custom type boundary,
24 # futher nested type expansion will be handled in the TypeDefType expansion
25 assert htype.data == 'htype'
26 # depth first search, use non-recursive version to better handle copy of the node
27 stack = list()
28 stack.append(htype)
29 expandable = 0
30 while stack:
31 stack_top = stack.pop()
32 assert stack_top.data == 'htype'
33 type_name, *type_params = stack_top.children
34 if type_name in self.types:
35 expandable += 1
36 fields = self.types[type_name].get_fields_with_instantiation(type_params, self.types)
37 else:
38 # expand other htype fields
39 for child in type_params:
40 if isinstance(child, Tree):
41 stack.append(child)
42
43 if expandable == 0:
44 return [('', htype)] # no expansion required
45 elif expandable == 1:
46 res = []
47 for k, v in fields:
48 new_type = copy.deepcopy(htype)
49 stack.append(new_type)
50 while stack:
51 stack_top = stack.pop()
52 type_name, *type_params = stack_top.children
53 if type_name in self.types: # this is the only type that gets expanded
54 stack_top.children = v.children
55 else:
56 for child in type_params:
57 if isinstance(child, Tree):
58 stack.append(child)
59 res.append((k, new_type))
60 return res
61 else:
62 assert False, 'Cannot expand more than 1 custom type'
63
64 def __expand_typeinfo(self, typeinfo):
65 assert typeinfo.data == 'htypeinfo'
66 assert len(typeinfo.children) == 1
67 res = self.__expand_htype(typeinfo.children[0])
68 # reconstruct typeinfo
69 return [(x[0], Tree(typeinfo.data, [x[1]], typeinfo.meta)) for x in res]
70
71 def __expand_helper(self, portdecl, typeinfo):
72 """portdecl is the name:
73 Tree(outportdecl, [Token(ID, 'dout')])
74 typeinfo is the real type info:
75 Tree(htypeinfo, [Tree(htype, [Token(TYPESTR, 'sc_out'), Tree(htype, [Token(TYPESTR, 'fp_t'), 52, 11])])])
76 """
77 res = self.__expand_typeinfo(typeinfo)
78 res_types = []
79 for suffix, tpe in res:
80 children = []
81 for x in portdecl.children:
82 # keep to newly generated name to be a token
83 new_token = copy.deepcopy(x)
84 new_token = new_token.update(value=new_token.value + ('_' + suffix if suffix else ''))
85 children.append(new_token)
86 port = Tree(portdecl.data, children, portdecl.meta)
87 res_types.append([port, tpe])
88 return res_types
89
90 def __expand_portdecltype(self, tree):
91 """The expansion is achieved by first traverse the type subtree to detect the expandable type,
92 then, any parent path is duplicated
93 An example of tree:
94 Tree(portdecltype, [Tree(inportdecl, [Token(ID, 'clk')]), Tree(htypeinfo,
95 [Tree(htype, [Token(TYPESTR, 'sc_in'), Tree(htype, [Token(TYPESTR, '_Bool')])])])])
96 """
97 assert len(tree.children) == 2
98 expanded_ports = self.__expand_helper(tree.children[0], tree.children[1])
99 res = [Tree(tree.data, x, tree.meta) for x in expanded_ports]
100 return res
101
102 def __expand_vardecltype(self, tree):
103 assert tree.data in ['vardeclinit', 'funcparami', 'funcparamio']
104 if len(tree.children) == 2:
105 # the case without initial value
106 expanded_ports = self.__expand_helper(Tree('stub', [tree.children[0]]), tree.children[1])
107 expanded_ports = [[var.children[0], tpe] for var, tpe in expanded_ports]
108 res = [Tree(tree.data, x, tree.meta) for x in expanded_ports]
109 return res
110 elif len(tree.children) == 3:
111 # This case expands the vardecltype with initial value
112 expanded_type = self.__expanded_type(tree.children[0])
113 if expanded_type is None:
114 return [tree]
115 # the case with initial value, and it might be expanded
116 # this is similar to blkassign
117 new_node = copy.copy(tree)
118 new_node.children = tree.children[0:3:2]
119 # a hack to re-use the __expand_blkassign and __expand_helper
120 # a formal way would be to abstract this into a assign-like top class
121 new_node.children[0] = Tree('hliteral', children=[new_node.children[0]])
122 # print(new_node)
123 res = self.__expand_blkassign(new_node)
124 decls = self.__expand_helper(new_node.children[0], Tree(data='htypeinfo', children=[expanded_type]))
125 for x in zip(decls, res):
126 var_name_decl, var_type = x[0]
127 var_name, init_val = x[1].children
128 # print('Testing: ')
129 # print('var_name_decl: ', var_name_decl)
130 assert var_name_decl == var_name
131 x[1].children[0] = x[1].children[0].children[0]
132 x[1].children.insert(1, var_type)
133 # print(res)
134 return res
135 else:
136 assert False, 'vardeclinit/funcparami/funcparamio should contain 2 or 3 sub-trees'
137
138 def __expand_sigdecltype(self, tree):
139 return self.__expand_portdecltype(tree)
140
141 def hsensvars(self, tree):
142 """expand identifiers in sensitivity list with fields"""
143 self.__push_up(tree)
144 new_children = []
145 for sense_var in tree.children:
146 var_type = self.__expanded_type(sense_var)
147 # dprint(sense_var, self.__expanded_type(sense_var))
148 if var_type:
149 var_type = self.__get_expandable_type_from_htype(var_type)
150 type_name = var_type.children[0]
151 type_params = var_type.children[1:]
152 tpe = self.types[type_name]
153 fields = tpe.get_fields_with_instantiation(type_params, self.types)
154 for field_name, _ in fields:
155 new_children.append(sense_var + '_' + field_name)
156 else:
157 new_children.append(sense_var)
158 tree.children = new_children
159 return tree
160
161 def modportsiglist(self, tree):
162 self.__push_up(tree)
163 new_children = []
164 # print('Mod Port Sig List')
165 for node in tree.children:
166 if node.data == 'portdecltype':
167 var_name = node.children[0].children[0]
168 var_type = node.children[1].children[0]
169 var_type_name = var_type.children[1].children[0]
170 if var_type_name in self.types:
171 self.__set_expanded(var_name, var_type)
172 new_children.extend(self.__expand_portdecltype(node))
173 elif node.data == 'sigdecltype':
174 var_name = node.children[0].children[0]
175 var_type = node.children[1].children[0]
176 # dprint(var_name)
177 # dprint(var_type)
178 var_tokens = map(lambda x:
179 filter(lambda y: isinstance(y, str), x.children),
180 var_type.iter_subtrees_topdown())
181 for var_type_name in itertools.chain.from_iterable(var_tokens):
182 if var_type_name in self.types: # detect the first type that is in the typedef list
183 self.__set_expanded(var_name, var_type)
184 break
185 new_children.extend(self.__expand_sigdecltype(node))
186 else:
187 # for vardeclinit, the structure is slightly different
188 # no expansion for now
189 var_name = node.children[0]
190 var_type = node.children[1].children[0]
191 var_tokens = map(lambda x:
192 filter(lambda y: isinstance(y, str), x.children),
193 var_type.iter_subtrees_topdown())
194 type_name = var_type.children[0]
195 # dprint(node)
196 if not Primitive.get_primitive(type_name) and not type_name in self.types:
197 # dprint(tree)
198 # module instantiate
199 new_children.append(Tree('moduleinst', node.children, node.meta))
200 # dprint(new_children[-1])
201 # assert False
202 continue
203 if type_name == 'array':
204 # array of module instantiations
205 sub_type_name = var_type.children[1].children[0]
206 if not Primitive.get_primitive(sub_type_name) and not sub_type_name in self.types:
207 # inst_name, module_name, array_size
208 inst_arr_name = node.children[0]
209 n_inst = var_type.children[2][0]
210 inst_type = Tree('htypeinfo', children=[var_type.children[1]])
211 for i in range(n_inst):
212 inst_name = inst_arr_name + '#' + str(i)
213 new_children.append(Tree('moduleinst', [inst_name, inst_type], node.meta))
214 continue
215 array_of_typedef = False
216 for var_type_name in itertools.chain.from_iterable(var_tokens):
217 if var_type_name in self.types: # detect the first type that is in the typedef list
218 self.__set_expanded(var_name, var_type)
219 break
220 elif var_type_name == "array":
221 continue
222 res = self.__expand_vardecltype(node)
223 new_children.extend(res)
224 # original type
225 # print('Var Name: ', var_name)
226 # print('Var Type: ', var_type)
227 # print()
228 tree.children = new_children
229 # print('---')
230 # for node in tree.children:
231 # print(node)
232 return tree
233
235 # returns the first expandable sub-tree
236 for subtree in htype.iter_subtrees_topdown():
237 if isinstance(subtree, Tree) and subtree.data == 'htype' and subtree.children[0] in self.types:
238 return subtree
239 return None
240
242 assert isinstance(tree, Tree)
243 if tree.data in ['hliteral', 'hvarref']:
244 var_name = tree.children[0]
245 if self.__expanded_type(var_name):
246 return var_name
247 elif tree.data == 'harrayref':
248 # TODO: support for multi-dimension array
249 # Special cases for handling hSigAssignR as array reference
250 if tree.children[0].data == 'hsigassignr':
251 var_name = tree.children[1].children[0]
252 else:
253 var_name = tree.children[0].children[0]
254 if self.__expanded_type(var_name):
255 return var_name
256 elif tree.data == 'hbinop' and tree.children[0] == 'ARRAYSUBSCRIPT': # simmilar to array ref
257 var_name = tree.children[1].children[0]
258 if self.__expanded_type(var_name):
259 return var_name
260 elif tree.data == 'hvarref':
261 var_name = tree.children[0]
262 if self.__expanded_type(var_name):
263 return var_name
264 elif tree.data == 'syscread':
265 # this is only used in statement vardeclinit
266 # syscread can also be performed on hararyref
267 assert tree.children[1].data in ['hliteral', 'hvarref', 'harrayref'], f'Actual: {tree.children[1].data} ({tree})'
268 if tree.children[1].data in ['harrayref']:
269 var_name = self.__get_expandable_var_from_tree(tree.children[1])
270 else:
271 var_name = tree.children[1].children[0]
272 if self.__expanded_type(var_name):
273 return var_name
274 elif tree.data == 'hvarinitlist':
275 # RHS is a list of variable
276 new_children = []
277 for element in tree.children:
278 new_children.append(self.__get_expandable_var_from_tree(element))
279 return new_children
280 return None
281
282 def __append_to_expandable_var_to_tree(self, tree, field_name):
283 """append the field_name to the expandable variable in tree"""
284 # TODO: the self.__expanded_type is excessive
285 assert isinstance(tree, Tree)
286 if tree.data in ['hliteral', 'hvarref']:
287 var_name = tree.children[0]
288 if self.__expanded_type(var_name):
289 tree.children[0] = var_name + '_' + field_name
290 elif tree.data == 'harrayref':
291 var_name = tree.children[0].children[0]
292 if self.__expanded_type(var_name):
293 tree.children[0].children[0] = var_name + '_' + field_name
294 elif tree.data == 'hbinop' and tree.children[0] == 'ARRAYSUBSCRIPT':
295 var_name = tree.children[1].children[0]
296 if self.__expanded_type(var_name):
297 tree.children[1].children[0] = var_name + '_' + field_name
298 elif tree.data == 'syscread':
299 assert tree.children[1].data in ['hliteral', 'hvarref', 'harrayref']
300 if tree.children[1].data in ['harrayref']:
301 var_name = self.__get_expandable_var_from_tree(tree.children[1])
302 else:
303 var_name = tree.children[1].children[0]
304 if self.__expanded_type(var_name):
305 tree.children[1].children[0] = var_name + '_' + field_name
306 elif tree.data == 'hvarinitlist':
307 for t in tree.children:
308 self.__append_to_expandable_var_to_tree(t, field_name)
309
310 def __is_all_none(self, v):
311 """checks if v is None or is a (nested) list containing only none"""
312 if v is None:
313 return True
314 if type(v) == list:
315 return all(map(lambda e: self.__is_all_none(e), v))
316 return False
317
318
319 def __expand_blkassign(self, tree):
320 """detects the expandable variable on lhs and rhs and
321 expand them with the fields"""
322 # Note: we only need fields here, and we don't need the actual type
323 lhs, rhs = tree.children
324 # dprint('LHS ', lhs)
325 # dprint('RHS ', rhs, tree.data)
326 lhs_var = self.__get_expandable_var_from_tree(lhs)
327 rhs_var = self.__get_expandable_var_from_tree(rhs)
328 # dprint('LHS var ', lhs_var)
329 # dprint('isallnone ', self.__is_all_none(rhs_var))
330 if lhs_var is not None and (not self.__is_all_none(rhs_var) or rhs.data == 'hliteral') and (rhs_var is not None or rhs.data == 'hliteral'):
331 lhs_expanded_type = self.__expanded_type(lhs_var)
332 assert lhs_expanded_type is not None, '{} should have expanded type'.format(lhs_var)
333 lhs_type = self.__get_expandable_type_from_htype(lhs_expanded_type)
334 # dprint(rhs_var)
335 if isinstance(rhs_var,list):
336 rhs_type = self.__get_expandable_type_from_htype(self.__expanded_type(rhs_var[0]))
337 if lhs_type.children[0] != rhs_type.children[0]:
338 raise RuntimeError('Type does not match between LHS and RHS')
339 for remaining_rhs_var in rhs_var:
340 rhs_var_type = self.__get_expandable_type_from_htype(self.__expanded_type(remaining_rhs_var))
341 if rhs_type.children[0] != rhs_var_type.children[0]:
342 raise RuntimeError('Type does not match among RHS elements')
343 elif rhs.data != 'hliteral':
344 rhs_type = self.__get_expandable_type_from_htype(self.__expanded_type(rhs_var))
345 # dprint(rhs_type)
346 if lhs_type.children[0] != rhs_type.children[0]:
347 raise RuntimeError('Type does not match between LHS and RHS')
348 else:
349 # warnings.warn('Treating CXXDefaultArgExpr as 0')
350 assert rhs.data == 'hliteral'
351 type_name = lhs_type.children[0]
352 type_params = lhs_type.children[1:]
353 tpe = self.types[type_name]
354 fields = tpe.get_fields_with_instantiation(type_params, self.types)
355 res = []
356 for field_member, _ in fields:
357 new_assign = copy.deepcopy(tree)
358 if tree.data == 'blkassign':
359 new_assign.must_block = tree.must_block
360 assert type(new_assign.must_block) == type(False)
361 new_lhs, new_rhs = new_assign.children
362 self.__append_to_expandable_var_to_tree(new_lhs, field_member)
363 if rhs.data == 'hliteral':
364 new_assign.children[1] = Tree('hliteral', [0], meta=rhs.meta)
365 else:
366 self.__append_to_expandable_var_to_tree(new_rhs, field_member)
367 res.append(new_assign)
368 dprint(res)
369 return res
370 elif lhs_var is None and self.__is_all_none(rhs_var):
371 return [tree]
372 elif lhs_var is not None and self.__is_all_none(rhs_var):
373 return [tree]
374 else:
375 raise RuntimeError('Error while expanding blkassign, LHS and RHS expandability does not match')
376
377
378 def stmt(self, tree):
379 # TODO: expand blkassign for aggregated types
380 self.__push_up(tree)
381 new_children = []
382 for ch in tree.children:
383 if ch.data == 'blkassign':
384 res = self.__expand_blkassign(ch)
385 new_children.extend(res)
386 else:
387 new_children.append(ch)
388 tree.children = new_children
389 return tree
390
391 def __expanded_type(self, var_name):
392 for d in reversed(self.expanded):
393 if var_name in d:
394 return d[var_name]
395 return None
396
397 def __set_expanded(self, var_name, var_type):
398 if self.__expanded_type(var_name):
399 raise RuntimeError('Duplicate variable ', var_name)
400 self.expanded[-1][var_name] = var_type
401
402 def hprocess(self, tree):
403 """add another scope for a process"""
404 self.expanded.append(dict())
405 self.__push_up(tree)
406 self.expanded.pop()
407 return tree
408
409 def hfunction(self, tree):
410 self.expanded.append(dict())
411 self.__push_up(tree)
412 self.expanded.pop()
413 return tree
414
415 def hfunctionparams(self, tree):
416 # self.expanded.append(dict())
417 self.__push_up(tree)
418 tree.children = self.__expand_decl_in_tree_children(tree, ['funcparami', 'funcparamio'])
419 # self.expanded.pop()
420 return tree
421
422 def hmethodcall(self, tree):
423 self.__push_up(tree)
424 new_children = []
425 for sense_var in tree.children[1:]:
426 # add case for function call on array member
427 var_name = self.__get_expandable_var_from_tree(sense_var)
428 var_type = self.__expanded_type(var_name)
429 # dprint(var_name, self.__expanded_type(var_name))
430 if var_type:
431 var_type = self.__get_expandable_type_from_htype(var_type)
432 type_name = var_type.children[0]
433 type_params = var_type.children[1:]
434 tpe = self.types[type_name]
435 fields = tpe.get_fields_with_instantiation(type_params, self.types)
436 for field_name, _ in fields:
437 new_sense_var = copy.deepcopy(sense_var)
438 self.__append_to_expandable_var_to_tree(new_sense_var, field_name)
439 # dprint("Origina...", var_name + '_' + field_name)
440 # dprint(new_sense_var)
441 # new_children.append(var_name + '_' + field_name)
442 new_children.append(new_sense_var)
443 else:
444 new_children.append(sense_var)
445 tree.children[1:] = new_children
446 return tree
447
448 def vardecl(self, tree):
449 """for variable expansion in statement"""
450 self.__push_up(tree)
451 tree.children = self.__expand_decl_in_tree_children(tree)
452 return tree
453
454 def hfunctionlocalvars(self, tree):
455 self.__push_up(tree)
456 tree.children = self.__expand_decl_in_tree_children(tree)
457 return tree
458
459 def __expand_decl_in_tree_children(self, tree, expand_data=None):
460 if expand_data is None:
461 expand_data = ['vardeclinit']
462 new_children = []
463 for node in tree.children:
464 if node.data in expand_data:
465 var_name = node.children[0]
466 var_type = node.children[1].children[0]
467 var_tokens = map(lambda x:
468 filter(lambda y: isinstance(y, str), x.children),
469 var_type.iter_subtrees_topdown())
470 type_name = var_type.children[0]
471 if 'funcparamio' in expand_data:
472 # dprint(var_name, var_type, type_name)
473 pass
474 if not Primitive.get_primitive(type_name) and not type_name in self.types:
475 # module instantiate
476 assert False, 'Type {} not found or module instantiation cannot reside in process: {}, {}'.format(type_name, var_name, type_name)
477 for var_type_name in itertools.chain.from_iterable(var_tokens):
478 if var_type_name in self.types: # detect the first type that is in the typedef list
479 self.__set_expanded(var_name, var_type)
480 break
481 res = self.__expand_vardecltype(node)
482 new_children.extend(res)
483 else:
484 new_children.append(node)
485 return new_children
486
487 def hmodinitblock(self, tree):
488 """
489 expands the hmodinitblock
490 hmodinitblock includes a initialization block and portdecl block, both of which can include
491 aggregated types
492 """
493 self.__push_up(tree)
494 return tree
495
496 def portbindinglist(self, tree):
497 module_name, *bindings = tree.children
498 new_bindings = []
499 for binding in bindings:
500 mod_name, sub, par = binding.children
501 sub_v = sub.children[0]
502 if is_tree_type(par, 'hbindingarrayref'):
503 par_v = get_ids_in_tree(par)[0]
504 else:
505 par_v = par.children[0]
506 typeinfo = self.__expanded_type(par_v.value)
507 if typeinfo: # if the bindinding is on a customized type
508 type_name = self.__get_expandable_type_from_htype(typeinfo).children[0]
509 tpe = self.types[type_name]
510 b = []
511 for field in tpe.fields:
512 new_sub = copy.deepcopy(sub)
513 new_par = copy.deepcopy(par)
514
515 # TODO: this doesn't seem good, should have a more general wrapper
516 def __alternate(x):
517 x.value += '_' + field.children[0]
518
519 alternate_ids(new_sub, [__alternate])
520 alternate_ids(new_par, [__alternate])
521 # new_sub.children[0].value += '_' + field.children[0]
522 # new_par.children[0].value += '_' + field.children[0]
523 new_binding = copy.copy(binding)
524 new_binding.children = [mod_name, new_sub, new_par]
525 b.append(new_binding)
526 new_bindings.extend(b)
527 else:
528 new_bindings.append(binding)
529 tree.children = [module_name, new_bindings]
530 return tree
531
532 def hsenslist(self, tree):
533 self.__push_up(tree)
534 new_children = []
535 # tree.children[0] is the proc_name
536 for sense_var in tree.children[1:]:
537 # var_name = sense_var.children[0].children[0] # hvarref
538 var_ids = get_ids_in_tree(sense_var.children[0])
539 if len(var_ids) != 1:
540 raise ValueError('Sensitivity variable should only have one ID')
541 var_name = var_ids[0]
542 var_type = self.__expanded_type(var_name)
543
544 if var_type:
545 var_type = self.__get_expandable_type_from_htype(var_type)
546 type_name = var_type.children[0]
547 type_params = var_type.children[1:]
548 tpe = self.types[type_name]
549 fields = tpe.get_fields_with_instantiation(type_params, self.types)
550
551 def __alternate(x, y):
552 return x + '_' + y
553
554 for field_name, _ in fields:
555 new_sense_var = copy.deepcopy(sense_var)
556 # alternate_ids(new_sense_var, [lambda x: __alternate(x, field_name)])
557 set_ids_in_tree_dfs(new_sense_var, [lambda x: __alternate(x, field_name)])
558 new_children.append(new_sense_var)
559
560 else:
561 new_children.append(sense_var)
562 tree.children[1:] = new_children
563 return tree
564
565 def hmodule(self, tree):
566 """add another scope for a module"""
567 self.current_module = tree.children[0]
568 self.expanded.append(dict())
569 self.__push_up(tree)
570 self.expanded.pop()
571 return tree
__push_up(self, current_node)
Definition top_down.py:29