2021-01-16 19:24:14 +01:00
|
|
|
#!/usr/bin/env python3
|
2023-12-01 01:58:16 +01:00
|
|
|
# pylint: disable=C0103,C0114,C0115,C0116,C0123,C0209,C0301,C0302,R0902,R0904,R0913,R0914,R0912,R0915,W0511,W0621
|
2006-08-26 13:35:28 +02:00
|
|
|
######################################################################
|
|
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
import argparse
|
|
|
|
|
import glob
|
2022-09-15 14:10:39 +02:00
|
|
|
import os
|
2021-01-16 19:24:14 +01:00
|
|
|
import re
|
|
|
|
|
import sys
|
2022-09-15 20:43:56 +02:00
|
|
|
import textwrap
|
2022-10-04 12:03:41 +02:00
|
|
|
|
2021-03-06 03:59:00 +01:00
|
|
|
# from pprint import pprint, pformat
|
2021-01-16 19:24:14 +01:00
|
|
|
|
2022-09-13 16:57:22 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
# This class is used to represents both AstNode and DfgVertex sub-types
|
2022-09-13 16:57:22 +02:00
|
|
|
class Node:
|
2022-09-28 03:47:45 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def __init__(self, name, superClass, file=None, lineno=None):
|
2022-09-13 16:57:22 +02:00
|
|
|
self._name = name
|
|
|
|
|
self._superClass = superClass
|
|
|
|
|
self._subClasses = [] # Initially list, but tuple after completion
|
|
|
|
|
self._allSuperClasses = None # Computed on demand after completion
|
|
|
|
|
self._allSubClasses = None # Computed on demand after completion
|
|
|
|
|
self._typeId = None # Concrete type identifier number for leaf classes
|
|
|
|
|
self._typeIdMin = None # Lowest type identifier number for class
|
|
|
|
|
self._typeIdMax = None # Highest type identifier number for class
|
2022-09-15 14:10:39 +02:00
|
|
|
self._file = file # File this class is defined in
|
|
|
|
|
self._lineno = lineno # Line this class is defined on
|
|
|
|
|
self._ordIdx = None # Ordering index of this class
|
2022-09-15 20:43:56 +02:00
|
|
|
self._arity = -1 # Arity of node
|
|
|
|
|
self._ops = {} # Operands of node
|
2023-12-01 01:58:16 +01:00
|
|
|
self._ptrs = [] # Pointer members of node (name, types)
|
2022-09-13 16:57:22 +02:00
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def name(self):
|
|
|
|
|
return self._name
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def superClass(self):
|
|
|
|
|
return self._superClass
|
|
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
@property
|
|
|
|
|
def isRoot(self):
|
|
|
|
|
return self.superClass is None
|
|
|
|
|
|
2022-09-13 16:57:22 +02:00
|
|
|
@property
|
|
|
|
|
def isCompleted(self):
|
|
|
|
|
return isinstance(self._subClasses, tuple)
|
|
|
|
|
|
2022-09-15 14:10:39 +02:00
|
|
|
@property
|
|
|
|
|
def file(self):
|
|
|
|
|
return self._file
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def lineno(self):
|
|
|
|
|
return self._lineno
|
|
|
|
|
|
2023-12-01 01:58:16 +01:00
|
|
|
@property
|
|
|
|
|
def ptrs(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
return self._ptrs
|
|
|
|
|
|
2022-09-13 16:57:22 +02:00
|
|
|
# Pre completion methods
|
|
|
|
|
def addSubClass(self, subClass):
|
|
|
|
|
assert not self.isCompleted
|
|
|
|
|
self._subClasses.append(subClass)
|
|
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
def addOp(self, n, name, monad, kind):
|
|
|
|
|
assert 1 <= n <= 4
|
|
|
|
|
self._ops[n] = (name, monad, kind)
|
|
|
|
|
self._arity = max(self._arity, n)
|
|
|
|
|
|
|
|
|
|
def getOp(self, n):
|
|
|
|
|
assert 1 <= n <= 4
|
|
|
|
|
op = self._ops.get(n, None)
|
|
|
|
|
if op is not None:
|
|
|
|
|
return op
|
|
|
|
|
if not self.isRoot:
|
|
|
|
|
return self.superClass.getOp(n)
|
|
|
|
|
return None
|
|
|
|
|
|
2023-12-01 01:58:16 +01:00
|
|
|
def addPtr(self, name, monad, kind):
|
|
|
|
|
name = re.sub(r'^m_', '', name)
|
|
|
|
|
self._ptrs.append({'name': name, 'monad': monad, 'kind': kind})
|
|
|
|
|
|
2022-09-13 16:57:22 +02:00
|
|
|
# Computes derived properties over entire class hierarchy.
|
|
|
|
|
# No more changes to the hierarchy are allowed once this was called
|
2022-09-15 14:10:39 +02:00
|
|
|
def complete(self, typeId=0, ordIdx=0):
|
2022-09-13 16:57:22 +02:00
|
|
|
assert not self.isCompleted
|
|
|
|
|
# Sort sub-classes and convert to tuple, which marks completion
|
2022-09-15 14:10:39 +02:00
|
|
|
self._subClasses = tuple(
|
2024-08-27 03:43:34 +02:00
|
|
|
sorted(self._subClasses, key=lambda _: (bool(_._subClasses), _.name))) # pylint: disable=protected-access
|
2022-09-15 14:10:39 +02:00
|
|
|
|
|
|
|
|
self._ordIdx = ordIdx
|
|
|
|
|
ordIdx = ordIdx + 1
|
|
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
if self.isRoot:
|
|
|
|
|
self._arity = 0
|
|
|
|
|
else:
|
|
|
|
|
self._arity = max(self._arity, self._superClass.arity)
|
|
|
|
|
|
2022-09-13 16:57:22 +02:00
|
|
|
# Leaves
|
|
|
|
|
if self.isLeaf:
|
|
|
|
|
self._typeId = typeId
|
2022-09-15 14:10:39 +02:00
|
|
|
return typeId + 1, ordIdx
|
2022-09-13 16:57:22 +02:00
|
|
|
|
|
|
|
|
# Non-leaves
|
|
|
|
|
for subClass in self._subClasses:
|
2022-09-15 14:10:39 +02:00
|
|
|
typeId, ordIdx = subClass.complete(typeId, ordIdx)
|
|
|
|
|
return typeId, ordIdx
|
2022-09-13 16:57:22 +02:00
|
|
|
|
|
|
|
|
# Post completion methods
|
|
|
|
|
@property
|
|
|
|
|
def subClasses(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
return self._subClasses
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def isLeaf(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
return not self.subClasses
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def allSuperClasses(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
if self._allSuperClasses is None:
|
|
|
|
|
if self.superClass is None:
|
|
|
|
|
self._allSuperClasses = ()
|
|
|
|
|
else:
|
2024-08-27 03:43:34 +02:00
|
|
|
self._allSuperClasses = self.superClass.allSuperClasses + (self.superClass, )
|
2022-09-13 16:57:22 +02:00
|
|
|
return self._allSuperClasses
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def allSubClasses(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
if self._allSubClasses is None:
|
|
|
|
|
if self.isLeaf:
|
|
|
|
|
self._allSubClasses = ()
|
|
|
|
|
else:
|
2024-08-27 03:43:34 +02:00
|
|
|
self._allSubClasses = self.subClasses + tuple(_ for subClass in self.subClasses
|
|
|
|
|
for _ in subClass.allSubClasses)
|
2022-09-13 16:57:22 +02:00
|
|
|
return self._allSubClasses
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def typeId(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
assert self.isLeaf
|
|
|
|
|
return self._typeId
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def typeIdMin(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
if self.isLeaf:
|
|
|
|
|
return self.typeId
|
|
|
|
|
if self._typeIdMin is None:
|
|
|
|
|
self._typeIdMin = min(_.typeIdMin for _ in self.allSubClasses)
|
|
|
|
|
return self._typeIdMin
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def typeIdMax(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
if self.isLeaf:
|
|
|
|
|
return self.typeId
|
|
|
|
|
if self._typeIdMax is None:
|
|
|
|
|
self._typeIdMax = max(_.typeIdMax for _ in self.allSubClasses)
|
|
|
|
|
return self._typeIdMax
|
|
|
|
|
|
2022-09-15 14:10:39 +02:00
|
|
|
@property
|
|
|
|
|
def ordIdx(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
return self._ordIdx
|
|
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
@property
|
|
|
|
|
def arity(self):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
return self._arity
|
|
|
|
|
|
2022-09-13 16:57:22 +02:00
|
|
|
def isSubClassOf(self, other):
|
|
|
|
|
assert self.isCompleted
|
|
|
|
|
if self is other:
|
|
|
|
|
return True
|
|
|
|
|
return self in other.allSubClasses
|
|
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
AstNodes = {}
|
|
|
|
|
AstNodeList = None
|
|
|
|
|
|
|
|
|
|
DfgVertices = {}
|
|
|
|
|
DfgVertexList = None
|
2022-09-13 16:57:22 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
ClassRefs = {}
|
|
|
|
|
Stages = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Cpt:
|
2022-09-28 03:47:45 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
def __init__(self):
|
|
|
|
|
self.did_out_tree = False
|
2021-03-06 04:52:39 +01:00
|
|
|
self.in_filename = ""
|
|
|
|
|
self.in_linenum = 1
|
|
|
|
|
self.out_filename = ""
|
2021-01-16 19:24:14 +01:00
|
|
|
self.out_linenum = 1
|
2021-03-06 04:52:39 +01:00
|
|
|
self.out_lines = []
|
2021-01-16 19:24:14 +01:00
|
|
|
self.tree_skip_visit = {}
|
2021-03-06 04:52:39 +01:00
|
|
|
self.treeop = {}
|
2021-01-16 19:24:14 +01:00
|
|
|
self._exec_nsyms = 0
|
|
|
|
|
self._exec_syms = {}
|
|
|
|
|
|
|
|
|
|
def error(self, txt):
|
2024-08-27 03:43:34 +02:00
|
|
|
sys.exit("%%Error: %s:%d: %s" % (self.in_filename, self.in_linenum, txt))
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
def print(self, txt):
|
|
|
|
|
self.out_lines.append(txt)
|
|
|
|
|
|
|
|
|
|
def output_func(self, func):
|
|
|
|
|
self.out_lines.append(func)
|
|
|
|
|
|
|
|
|
|
def _output_line(self):
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print("#line " + str(self.out_linenum + 2) + " \"" + self.out_filename + "\"\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
def process(self, in_filename, out_filename):
|
|
|
|
|
self.in_filename = in_filename
|
|
|
|
|
self.out_filename = out_filename
|
|
|
|
|
ln = 0
|
|
|
|
|
didln = False
|
|
|
|
|
|
|
|
|
|
# Read the file and parse into list of functions that generate output
|
2022-12-12 03:58:02 +01:00
|
|
|
with open(self.in_filename, "r", encoding="utf8") as fhi:
|
2021-01-16 19:24:14 +01:00
|
|
|
for line in fhi:
|
|
|
|
|
ln += 1
|
|
|
|
|
if not didln:
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print("#line " + str(ln) + " \"" + self.in_filename + "\"\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
didln = True
|
|
|
|
|
match = re.match(r'^\s+(TREE.*)$', line)
|
|
|
|
|
if match:
|
|
|
|
|
func = match.group(1)
|
|
|
|
|
self.in_linenum = ln
|
|
|
|
|
self.print("//" + line)
|
|
|
|
|
self.output_func(lambda self: self._output_line())
|
|
|
|
|
self.tree_line(func)
|
|
|
|
|
didln = False
|
2024-08-27 03:43:34 +02:00
|
|
|
elif not re.match(r'^\s*(#define|/[/\*])\s*TREE', line) and re.search(
|
|
|
|
|
r'\s+TREE', line):
|
2021-01-16 19:24:14 +01:00
|
|
|
self.error("Unknown astgen line: " + line)
|
|
|
|
|
else:
|
|
|
|
|
self.print(line)
|
|
|
|
|
|
|
|
|
|
# Put out the resultant file, if the list has a reference to a
|
|
|
|
|
# function, then call that func to generate output
|
|
|
|
|
with open_file(self.out_filename) as fho:
|
|
|
|
|
togen = self.out_lines
|
|
|
|
|
for line in togen:
|
2023-08-16 13:32:39 +02:00
|
|
|
if isinstance(line, str):
|
2021-01-16 19:24:14 +01:00
|
|
|
self.out_lines = [line]
|
|
|
|
|
else:
|
|
|
|
|
self.out_lines = []
|
|
|
|
|
line(self) # lambda call
|
|
|
|
|
for out in self.out_lines:
|
|
|
|
|
for _ in re.findall(r'\n', out):
|
|
|
|
|
self.out_linenum += 1
|
|
|
|
|
fho.write(out)
|
|
|
|
|
|
|
|
|
|
def tree_line(self, func):
|
|
|
|
|
func = re.sub(r'\s*//.*$', '', func)
|
|
|
|
|
func = re.sub(r'\s*;\s*$', '', func)
|
|
|
|
|
|
|
|
|
|
# doflag "S" indicates an op specifying short-circuiting for a type.
|
|
|
|
|
match = re.search(
|
|
|
|
|
# 1 2 3 4
|
2021-01-17 05:53:49 +01:00
|
|
|
r'TREEOP(1?)([ACSV]?)\s*\(\s*\"([^\"]*)\"\s*,\s*\"([^\"]*)\"\s*\)',
|
2021-01-16 19:24:14 +01:00
|
|
|
func)
|
2024-08-27 03:43:34 +02:00
|
|
|
match_skip = re.search(r'TREE_SKIP_VISIT\s*\(\s*\"([^\"]*)\"\s*\)', func)
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
if match:
|
|
|
|
|
order = match.group(1)
|
|
|
|
|
doflag = match.group(2)
|
|
|
|
|
fromn = match.group(3)
|
|
|
|
|
to = match.group(4)
|
2021-03-06 03:59:00 +01:00
|
|
|
# self.print("// $fromn $to\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
if not self.did_out_tree:
|
|
|
|
|
self.did_out_tree = True
|
|
|
|
|
self.output_func(lambda self: self.tree_match_base())
|
|
|
|
|
match = re.search(r'Ast([a-zA-Z0-9]+)\s*\{(.*)\}\s*$', fromn)
|
|
|
|
|
if not match:
|
|
|
|
|
self.error("Can't parse from function: " + func)
|
|
|
|
|
typen = match.group(1)
|
|
|
|
|
subnodes = match.group(2)
|
2022-10-04 12:03:41 +02:00
|
|
|
if AstNodes[typen].isRoot:
|
2021-01-16 19:24:14 +01:00
|
|
|
self.error("Unknown AstNode typen: " + typen + ": in " + func)
|
|
|
|
|
|
|
|
|
|
mif = ""
|
|
|
|
|
if doflag == '':
|
|
|
|
|
mif = "m_doNConst"
|
|
|
|
|
elif doflag == 'A':
|
|
|
|
|
mif = ""
|
2021-01-17 05:53:49 +01:00
|
|
|
elif doflag == 'C':
|
|
|
|
|
mif = "m_doCpp"
|
2021-01-16 19:24:14 +01:00
|
|
|
elif doflag == 'S':
|
|
|
|
|
mif = "m_doNConst" # Not just for m_doGenerate
|
|
|
|
|
elif doflag == 'V':
|
|
|
|
|
mif = "m_doV"
|
|
|
|
|
else:
|
|
|
|
|
self.error("Unknown flag: " + doflag)
|
|
|
|
|
subnodes = re.sub(r',,', '__ESCAPEDCOMMA__', subnodes)
|
|
|
|
|
for subnode in re.split(r'\s*,\s*', subnodes):
|
|
|
|
|
subnode = re.sub(r'__ESCAPEDCOMMA__', ',', subnode)
|
|
|
|
|
if re.match(r'^\$([a-zA-Z0-9]+)$', subnode):
|
|
|
|
|
continue # "$lhs" is just a comment that this op has a lhs
|
|
|
|
|
subnodeif = subnode
|
2024-08-27 03:43:34 +02:00
|
|
|
subnodeif = re.sub(r'\$([a-zA-Z0-9]+)\.cast([A-Z][A-Za-z0-9]+)$',
|
|
|
|
|
r'VN_IS(nodep->\1(),\2)', subnodeif)
|
|
|
|
|
subnodeif = re.sub(r'\$([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)$', r'nodep->\1()->\2()',
|
|
|
|
|
subnodeif)
|
2021-01-16 19:24:14 +01:00
|
|
|
subnodeif = self.add_nodep(subnodeif)
|
|
|
|
|
if mif != "" and subnodeif != "":
|
|
|
|
|
mif += " && "
|
|
|
|
|
mif += subnodeif
|
|
|
|
|
|
|
|
|
|
exec_func = self.treeop_exec_func(to)
|
2024-08-27 03:43:34 +02:00
|
|
|
exec_func = re.sub(r'([-()a-zA-Z0-9_>]+)->cast([A-Z][A-Za-z0-9]+)\(\)',
|
|
|
|
|
r'VN_CAST(\1,\2)', exec_func)
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
if typen not in self.treeop:
|
|
|
|
|
self.treeop[typen] = []
|
|
|
|
|
n = len(self.treeop[typen])
|
|
|
|
|
typefunc = {
|
|
|
|
|
'order': order,
|
|
|
|
|
'comment': func,
|
|
|
|
|
'match_func': "match_" + typen + "_" + str(n),
|
|
|
|
|
'match_if': mif,
|
|
|
|
|
'exec_func': exec_func,
|
|
|
|
|
'uinfo': re.sub(r'[ \t\"\{\}]+', ' ', func),
|
|
|
|
|
'uinfo_level': (0 if re.match(r'^!', to) else 7),
|
|
|
|
|
'short_circuit': (doflag == 'S'),
|
|
|
|
|
}
|
|
|
|
|
self.treeop[typen].append(typefunc)
|
|
|
|
|
|
|
|
|
|
elif match_skip:
|
|
|
|
|
typen = match_skip.group(1)
|
|
|
|
|
self.tree_skip_visit[typen] = 1
|
2022-10-04 12:03:41 +02:00
|
|
|
if typen not in AstNodes:
|
2021-01-16 19:24:14 +01:00
|
|
|
self.error("Unknown node type: " + typen)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.error("Unknown astgen op: " + func)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-03-06 04:52:39 +01:00
|
|
|
def add_nodep(strg):
|
|
|
|
|
strg = re.sub(r'\$([a-zA-Z0-9]+)', r'nodep->\1()', strg)
|
|
|
|
|
return strg
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
def _exec_syms_recurse(self, aref):
|
|
|
|
|
for sym in aref:
|
2023-10-21 18:48:19 +02:00
|
|
|
if isinstance(sym, list):
|
2021-01-16 19:24:14 +01:00
|
|
|
self._exec_syms_recurse(sym)
|
|
|
|
|
elif re.search(r'^\$.*', sym):
|
|
|
|
|
if sym not in self._exec_syms:
|
|
|
|
|
self._exec_nsyms += 1
|
|
|
|
|
self._exec_syms[sym] = "arg" + str(self._exec_nsyms) + "p"
|
|
|
|
|
|
|
|
|
|
def _exec_new_recurse(self, aref):
|
|
|
|
|
out = "new " + aref[0] + "(nodep->fileline()"
|
|
|
|
|
first = True
|
|
|
|
|
for sym in aref:
|
|
|
|
|
if first:
|
|
|
|
|
first = False
|
|
|
|
|
continue
|
|
|
|
|
out += ", "
|
2023-10-21 18:48:19 +02:00
|
|
|
if isinstance(sym, list):
|
2021-01-16 19:24:14 +01:00
|
|
|
out += self._exec_new_recurse(sym)
|
|
|
|
|
elif re.match(r'^\$.*', sym):
|
|
|
|
|
out += self._exec_syms[sym]
|
|
|
|
|
else:
|
|
|
|
|
out += sym
|
|
|
|
|
return out + ")"
|
|
|
|
|
|
|
|
|
|
def treeop_exec_func(self, func):
|
|
|
|
|
out = ""
|
|
|
|
|
func = re.sub(r'^!', '', func)
|
|
|
|
|
|
|
|
|
|
if re.match(r'^\s*[a-zA-Z0-9]+\s*\(', func): # Function call
|
|
|
|
|
outl = re.sub(r'\$([a-zA-Z0-9]+)', r'nodep->\1()', func)
|
|
|
|
|
out += outl + ";"
|
|
|
|
|
elif re.match(r'^\s*Ast([a-zA-Z0-9]+)\s*\{\s*(.*)\s*\}$', func):
|
|
|
|
|
aref = None
|
|
|
|
|
# Recursive array with structure to form
|
|
|
|
|
astack = []
|
|
|
|
|
forming = ""
|
|
|
|
|
argtext = func + "\000" # EOF character
|
|
|
|
|
for tok in argtext:
|
|
|
|
|
if tok == "\000":
|
2022-03-26 20:57:52 +01:00
|
|
|
pass
|
2021-01-16 19:24:14 +01:00
|
|
|
elif re.match(r'\s+', tok):
|
2022-03-26 20:57:52 +01:00
|
|
|
pass
|
2021-01-16 19:24:14 +01:00
|
|
|
elif tok == "{":
|
|
|
|
|
newref = [forming]
|
|
|
|
|
if not aref:
|
|
|
|
|
aref = []
|
|
|
|
|
aref.append(newref)
|
|
|
|
|
astack.append(aref)
|
|
|
|
|
aref = newref
|
|
|
|
|
forming = ""
|
|
|
|
|
elif tok == "}":
|
|
|
|
|
if forming:
|
|
|
|
|
aref.append(forming)
|
|
|
|
|
if len(astack) == 0:
|
|
|
|
|
self.error("Too many } in execution function: " + func)
|
|
|
|
|
aref = astack.pop()
|
|
|
|
|
forming = ""
|
|
|
|
|
elif tok == ",":
|
|
|
|
|
if forming:
|
|
|
|
|
aref.append(forming)
|
|
|
|
|
forming = ""
|
|
|
|
|
else:
|
|
|
|
|
forming += tok
|
|
|
|
|
if not (aref and len(aref) == 1):
|
|
|
|
|
self.error("Badly formed execution function: " + func)
|
|
|
|
|
aref = aref[0]
|
|
|
|
|
|
|
|
|
|
# Assign numbers to each $ symbol
|
|
|
|
|
self._exec_syms = {}
|
|
|
|
|
self._exec_nsyms = 0
|
|
|
|
|
self._exec_syms_recurse(aref)
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
for sym in sorted(self._exec_syms.keys(), key=lambda val: self._exec_syms[val]):
|
2021-01-16 19:24:14 +01:00
|
|
|
argnp = self._exec_syms[sym]
|
|
|
|
|
arg = self.add_nodep(sym)
|
2022-11-13 21:33:11 +01:00
|
|
|
out += "AstNodeExpr* " + argnp + " = " + arg + "->unlinkFrBack();\n"
|
2021-01-16 19:24:14 +01:00
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
out += "AstNodeExpr* newp = " + self._exec_new_recurse(aref) + ";\n"
|
2021-01-16 19:24:14 +01:00
|
|
|
out += "nodep->replaceWith(newp);"
|
|
|
|
|
out += "VL_DO_DANGLING(nodep->deleteTree(), nodep);"
|
|
|
|
|
elif func == "NEVER":
|
|
|
|
|
out += "nodep->v3fatalSrc(\"Executing transform that was NEVERed\");"
|
|
|
|
|
elif func == "DONE":
|
2022-03-26 20:57:52 +01:00
|
|
|
pass
|
2021-01-16 19:24:14 +01:00
|
|
|
else:
|
|
|
|
|
self.error("Unknown execution function format: " + func + "\n")
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
def tree_match_base(self):
|
|
|
|
|
self.tree_match()
|
|
|
|
|
self.tree_base()
|
|
|
|
|
|
|
|
|
|
def tree_match(self):
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print(" // TREEOP functions, each return true if they matched & transformed\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
for base in sorted(self.treeop.keys()):
|
|
|
|
|
for typefunc in self.treeop[base]:
|
|
|
|
|
self.print(" // Generated by astgen\n")
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print(" bool " + typefunc['match_func'] + "(Ast" + base + "* nodep) {\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
self.print("\t// " + typefunc['comment'] + "\n")
|
|
|
|
|
self.print("\tif (" + typefunc['match_if'] + ") {\n")
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print("\t UINFO(" + str(typefunc['uinfo_level']) + ", cvtToHex(nodep)" +
|
|
|
|
|
" << \" " + typefunc['uinfo'] + "\\n\");\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
self.print("\t " + typefunc['exec_func'] + "\n")
|
|
|
|
|
self.print("\t return true;\n")
|
|
|
|
|
self.print("\t}\n")
|
|
|
|
|
self.print("\treturn false;\n")
|
|
|
|
|
self.print(" }\n", )
|
|
|
|
|
|
|
|
|
|
def tree_base(self):
|
|
|
|
|
self.print(" // TREEOP visitors, call each base type's match\n")
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print(" // Bottom class up, as more simple transforms are generally better\n")
|
2022-10-04 12:03:41 +02:00
|
|
|
for node in AstNodeList:
|
2021-01-16 19:24:14 +01:00
|
|
|
out_for_type_sc = []
|
|
|
|
|
out_for_type = []
|
2022-09-13 16:57:22 +02:00
|
|
|
classes = list(node.allSuperClasses)
|
|
|
|
|
classes.append(node)
|
|
|
|
|
for base in classes:
|
|
|
|
|
base = base.name
|
2021-03-06 03:59:00 +01:00
|
|
|
if base not in self.treeop:
|
2021-01-16 19:24:14 +01:00
|
|
|
continue
|
|
|
|
|
for typefunc in self.treeop[base]:
|
2024-08-27 03:43:34 +02:00
|
|
|
lines = [" if (" + typefunc['match_func'] + "(nodep)) return;\n"]
|
2021-03-06 04:52:39 +01:00
|
|
|
if typefunc['short_circuit']: # short-circuit match fn
|
2021-01-16 19:24:14 +01:00
|
|
|
out_for_type_sc.extend(lines)
|
|
|
|
|
else: # Standard match fn
|
2024-08-27 03:43:34 +02:00
|
|
|
if typefunc['order']: # TREEOP1's go in front of others
|
2021-01-16 19:24:14 +01:00
|
|
|
out_for_type = lines + out_for_type
|
|
|
|
|
else:
|
|
|
|
|
out_for_type.extend(lines)
|
|
|
|
|
|
|
|
|
|
# We need to deal with two cases. For short circuited functions we
|
|
|
|
|
# evaluate the LHS, then apply the short-circuit matches, then
|
|
|
|
|
# evaluate the RHS and possibly THS (ternary operators may
|
|
|
|
|
# short-circuit) and apply all the other matches.
|
|
|
|
|
|
|
|
|
|
# For types without short-circuits, we just use iterateChildren, which
|
|
|
|
|
# saves one comparison.
|
|
|
|
|
if len(out_for_type_sc) > 0: # Short-circuited types
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print(" // Generated by astgen with short-circuiting\n" +
|
|
|
|
|
" void visit(Ast" + node.name + "* nodep) override {\n" +
|
|
|
|
|
" iterateAndNextNull(nodep->{op1}());\n".format(
|
|
|
|
|
op1=node.getOp(1)[0]) + "".join(out_for_type_sc))
|
2021-01-16 19:24:14 +01:00
|
|
|
if out_for_type[0]:
|
2023-11-12 19:30:48 +01:00
|
|
|
self.print(
|
2024-08-27 03:43:34 +02:00
|
|
|
" iterateAndNextNull(nodep->{op2}());\n".format(op2=node.getOp(2)[0]))
|
2022-10-04 12:03:41 +02:00
|
|
|
if node.isSubClassOf(AstNodes["NodeTriop"]):
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print(" iterateAndNextNull(nodep->{op3}());\n".format(
|
|
|
|
|
op3=node.getOp(3)[0]))
|
2021-10-22 18:36:58 +02:00
|
|
|
self.print("".join(out_for_type) + " }\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
elif len(out_for_type) > 0: # Other types with something to print
|
2022-09-13 16:57:22 +02:00
|
|
|
skip = node.name in self.tree_skip_visit
|
2021-01-16 19:24:14 +01:00
|
|
|
gen = "Gen" if skip else ""
|
2022-09-16 12:22:11 +02:00
|
|
|
virtual = "virtual " if skip else ""
|
2021-01-16 19:24:14 +01:00
|
|
|
override = "" if skip else " override"
|
2024-08-27 03:43:34 +02:00
|
|
|
self.print(" // Generated by astgen\n" + " " + virtual + "void visit" + gen +
|
|
|
|
|
"(Ast" + node.name + "* nodep)" + override + " {\n" +
|
|
|
|
|
("" if skip else " iterateChildren(nodep);\n") +
|
|
|
|
|
''.join(out_for_type) + " }\n")
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
######################################################################
|
|
|
|
|
######################################################################
|
2020-01-23 01:07:48 +01:00
|
|
|
|
|
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
def partitionAndStrip(string, separator):
|
|
|
|
|
return map(lambda _: _.strip(), string.partition(separator))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parseOpType(string):
|
|
|
|
|
match = re.match(r'^(\w+)\[(\w+)\]$', string)
|
|
|
|
|
if match:
|
|
|
|
|
monad, kind = match.groups()
|
|
|
|
|
if monad not in ("Optional", "List"):
|
|
|
|
|
return None
|
|
|
|
|
kind = parseOpType(kind)
|
|
|
|
|
if not kind or kind[0]:
|
|
|
|
|
return None
|
|
|
|
|
return monad, kind[1]
|
|
|
|
|
if re.match(r'^Ast(\w+)$', string):
|
|
|
|
|
return "", string[3:]
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def read_types(filename, Nodes, prefix):
|
2022-09-15 20:43:56 +02:00
|
|
|
hasErrors = False
|
|
|
|
|
|
|
|
|
|
def error(lineno, message):
|
|
|
|
|
nonlocal hasErrors
|
2024-08-27 03:43:34 +02:00
|
|
|
print(filename + ":" + str(lineno) + ": %Error: " + message, file=sys.stderr)
|
2022-09-15 20:43:56 +02:00
|
|
|
hasErrors = True
|
|
|
|
|
|
|
|
|
|
node = None
|
|
|
|
|
hasAstgenMembers = False
|
|
|
|
|
|
|
|
|
|
def checkFinishedNode(node):
|
|
|
|
|
nonlocal hasAstgenMembers
|
|
|
|
|
if not node:
|
|
|
|
|
return
|
|
|
|
|
if not hasAstgenMembers:
|
|
|
|
|
error(
|
2022-10-04 12:03:41 +02:00
|
|
|
node.lineno,
|
2024-08-27 03:43:34 +02:00
|
|
|
"'{p}{n}' does not contain 'ASTGEN_MEMBERS_{p}{n};'".format(p=prefix, n=node.name))
|
2022-09-15 20:43:56 +02:00
|
|
|
hasAstgenMembers = False
|
|
|
|
|
|
2022-12-12 03:58:02 +01:00
|
|
|
with open(filename, "r", encoding="utf8") as fh:
|
2022-09-15 14:10:39 +02:00
|
|
|
for (lineno, line) in enumerate(fh, start=1):
|
2022-09-15 20:43:56 +02:00
|
|
|
line = line.strip()
|
|
|
|
|
if not line:
|
2021-01-16 19:24:14 +01:00
|
|
|
continue
|
2022-09-15 20:43:56 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
match = re.search(r'^\s*(class|struct)\s*(\S+)', line)
|
|
|
|
|
if match:
|
|
|
|
|
classn = match.group(2)
|
|
|
|
|
match = re.search(r':\s*public\s+(\S+)', line)
|
2021-10-17 12:40:44 +02:00
|
|
|
supern = match.group(1) if match else ""
|
2022-10-04 12:03:41 +02:00
|
|
|
if re.search(prefix, supern):
|
|
|
|
|
classn = re.sub(r'^' + prefix, '', classn)
|
|
|
|
|
supern = re.sub(r'^' + prefix, '', supern)
|
2022-09-15 14:10:39 +02:00
|
|
|
if not supern:
|
2024-08-27 03:43:34 +02:00
|
|
|
sys.exit("%Error: '{p}{c}' has no super-class".format(p=prefix, c=classn))
|
2022-09-15 20:43:56 +02:00
|
|
|
checkFinishedNode(node)
|
2022-09-15 14:10:39 +02:00
|
|
|
superClass = Nodes[supern]
|
|
|
|
|
node = Node(classn, superClass, filename, lineno)
|
|
|
|
|
superClass.addSubClass(node)
|
2022-09-13 16:57:22 +02:00
|
|
|
Nodes[classn] = node
|
2022-09-15 20:43:56 +02:00
|
|
|
if not node:
|
|
|
|
|
continue
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
if re.match(r'^\s*ASTGEN_MEMBERS_' + prefix + node.name + ';', line):
|
2022-09-15 20:43:56 +02:00
|
|
|
hasAstgenMembers = True
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
if prefix != "Ast":
|
|
|
|
|
continue
|
|
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
match = re.match(r'^\s*//\s*@astgen\s+(.*)$', line)
|
|
|
|
|
if match:
|
|
|
|
|
decl = re.sub(r'//.*$', '', match.group(1))
|
|
|
|
|
what, sep, rest = partitionAndStrip(decl, ":=")
|
|
|
|
|
what = re.sub(r'\s+', ' ', what)
|
|
|
|
|
if not sep:
|
|
|
|
|
error(
|
|
|
|
|
lineno,
|
|
|
|
|
"Malformed '@astgen' directive (expecting '<keywords> := <description>'): "
|
|
|
|
|
+ decl)
|
|
|
|
|
elif what in ("op1", "op2", "op3", "op4"):
|
|
|
|
|
n = int(what[-1])
|
|
|
|
|
ident, sep, kind = partitionAndStrip(rest, ":")
|
|
|
|
|
ident = ident.strip()
|
|
|
|
|
if not sep or not re.match(r'^\w+$', ident):
|
|
|
|
|
error(
|
2024-08-27 03:43:34 +02:00
|
|
|
lineno, "Malformed '@astgen " + what + "' directive (expecting '" +
|
|
|
|
|
what + " := <identifier> : <type>': " + decl)
|
2022-09-15 20:43:56 +02:00
|
|
|
else:
|
|
|
|
|
kind = parseOpType(kind)
|
|
|
|
|
if not kind:
|
|
|
|
|
error(
|
|
|
|
|
lineno, "Bad type for '@astgen " + what +
|
2024-08-27 03:43:34 +02:00
|
|
|
"' (expecting Ast*, Optional[Ast*], or List[Ast*]):" + decl)
|
2022-09-15 20:43:56 +02:00
|
|
|
elif node.getOp(n) is not None:
|
2024-08-27 03:43:34 +02:00
|
|
|
error(lineno, "Already defined " + what + " for " + node.name)
|
2022-09-15 20:43:56 +02:00
|
|
|
else:
|
|
|
|
|
node.addOp(n, ident, *kind)
|
2024-08-27 03:43:34 +02:00
|
|
|
elif what in ("alias op1", "alias op2", "alias op3", "alias op4"):
|
2022-09-15 20:43:56 +02:00
|
|
|
n = int(what[-1])
|
|
|
|
|
ident = rest.strip()
|
|
|
|
|
if not re.match(r'^\w+$', ident):
|
|
|
|
|
error(
|
2024-08-27 03:43:34 +02:00
|
|
|
lineno, "Malformed '@astgen " + what + "' directive (expecting '" +
|
|
|
|
|
what + " := <identifier>': " + decl)
|
2022-09-15 20:43:56 +02:00
|
|
|
else:
|
|
|
|
|
op = node.getOp(n)
|
|
|
|
|
if op is None:
|
2024-08-27 03:43:34 +02:00
|
|
|
error(lineno, "Aliased op" + str(n) + " is not defined")
|
2022-09-15 20:43:56 +02:00
|
|
|
else:
|
|
|
|
|
node.addOp(n, ident, *op[1:])
|
2023-12-01 01:58:16 +01:00
|
|
|
elif what == "ptr":
|
|
|
|
|
ident, sep, kind = partitionAndStrip(rest, ":")
|
|
|
|
|
ident = ident.strip()
|
|
|
|
|
kind = parseOpType(kind)
|
|
|
|
|
if not kind:
|
|
|
|
|
error(
|
|
|
|
|
lineno, "Bad type for '@astgen " + what +
|
2024-08-27 03:43:34 +02:00
|
|
|
"' (expecting Ast*, Optional[Ast*], or List[Ast*]):" + decl)
|
2023-12-01 01:58:16 +01:00
|
|
|
if not re.match(r'^m_(\w+)$', ident):
|
|
|
|
|
error(
|
|
|
|
|
lineno, "Malformed '@astgen ptr'"
|
|
|
|
|
" identifier (expecting m_ in '" + ident + "')")
|
|
|
|
|
else:
|
|
|
|
|
node.addPtr(ident, *kind)
|
|
|
|
|
else:
|
|
|
|
|
error(
|
2024-08-27 03:43:34 +02:00
|
|
|
lineno, "Malformed @astgen what (expecting 'op1'..'op4'," +
|
2023-12-01 01:58:16 +01:00
|
|
|
" 'alias op1'.., 'ptr'): " + what)
|
2022-09-15 20:43:56 +02:00
|
|
|
else:
|
|
|
|
|
line = re.sub(r'//.*$', '', line)
|
|
|
|
|
if re.match(r'.*[Oo]p[1-9].*', line):
|
2024-08-27 03:43:34 +02:00
|
|
|
error(lineno, "Use generated accessors to access op<N> operands")
|
2022-09-15 20:43:56 +02:00
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
if re.match(r'^\s*Ast[A-Z][A-Za-z0-9_]+\s*\*(\s*const)?\s+m_[A-Za-z0-9_]+\s*;', line):
|
|
|
|
|
error(lineno, "Use '@astgen ptr' for Ast pointer members: " + line)
|
2023-12-01 01:58:16 +01:00
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
checkFinishedNode(node)
|
|
|
|
|
if hasErrors:
|
|
|
|
|
sys.exit("%Error: Stopping due to errors reported above")
|
|
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def check_types(sortedTypes, prefix, abstractPrefix):
|
|
|
|
|
baseClass = prefix + abstractPrefix
|
|
|
|
|
|
|
|
|
|
# Check all leaf types are not AstNode* and non-leaves are AstNode*
|
|
|
|
|
for node in sortedTypes:
|
|
|
|
|
if re.match(r'^' + abstractPrefix, node.name):
|
|
|
|
|
if node.isLeaf:
|
2024-08-27 03:43:34 +02:00
|
|
|
sys.exit("%Error: Final {b} subclasses must not be named {b}*: {p}{n}".format(
|
|
|
|
|
b=baseClass, p=prefix, n=node.name))
|
2022-10-04 12:03:41 +02:00
|
|
|
else:
|
|
|
|
|
if not node.isLeaf:
|
2024-08-27 03:43:34 +02:00
|
|
|
sys.exit("%Error: Non-final {b} subclasses must be named {b}*: {p}{n}".format(
|
|
|
|
|
b=baseClass, p=prefix, n=node.name))
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
# Check ordering of node definitions
|
|
|
|
|
hasOrderingError = False
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
files = tuple(sorted(set(_.file for _ in sortedTypes if _.file is not None)))
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
for file in files:
|
|
|
|
|
nodes = tuple(filter(lambda _, f=file: _.file == f, sortedTypes))
|
|
|
|
|
expectOrder = tuple(sorted(nodes, key=lambda _: (_.isLeaf, _.ordIdx)))
|
|
|
|
|
actualOrder = tuple(sorted(nodes, key=lambda _: _.lineno))
|
2024-08-27 03:43:34 +02:00
|
|
|
expect = {node: pred for pred, node in zip((None, ) + expectOrder[:-1], expectOrder)}
|
|
|
|
|
actual = {node: pred for pred, node in zip((None, ) + actualOrder[:-1], actualOrder)}
|
2022-10-04 12:03:41 +02:00
|
|
|
for node in nodes:
|
|
|
|
|
if expect[node] != actual[node]:
|
|
|
|
|
hasOrderingError = True
|
|
|
|
|
pred = expect[node]
|
|
|
|
|
print(
|
2023-11-11 05:25:53 +01:00
|
|
|
"{file}:{lineno}: %Error: Definition of '{p}{n}' is out of order. Should be {where}."
|
2022-10-04 12:03:41 +02:00
|
|
|
.format(file=file,
|
|
|
|
|
lineno=node.lineno,
|
|
|
|
|
p=prefix,
|
|
|
|
|
n=node.name,
|
|
|
|
|
where=("right after '" + prefix + pred.name +
|
|
|
|
|
"'" if pred else "first in file")),
|
|
|
|
|
file=sys.stderr)
|
|
|
|
|
|
|
|
|
|
if hasOrderingError:
|
2024-08-27 03:43:34 +02:00
|
|
|
sys.exit("%Error: Stopping due to out of order definitions listed above")
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
def read_stages(filename):
|
2022-12-12 03:58:02 +01:00
|
|
|
with open(filename, "r", encoding="utf8") as fh:
|
2021-01-16 19:24:14 +01:00
|
|
|
n = 100
|
|
|
|
|
for line in fh:
|
|
|
|
|
line = re.sub(r'//.*$', '', line)
|
|
|
|
|
if re.match(r'^\s*$', line):
|
|
|
|
|
continue
|
2022-01-03 02:54:39 +01:00
|
|
|
match = re.search(r'\s([A-Za-z0-9]+)::', line)
|
2021-01-16 19:24:14 +01:00
|
|
|
if match:
|
|
|
|
|
stage = match.group(1) + ".cpp"
|
|
|
|
|
if stage not in Stages:
|
|
|
|
|
Stages[stage] = n
|
|
|
|
|
n += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def read_refs(filename):
|
|
|
|
|
basename = re.sub(r'.*/', '', filename)
|
2022-12-12 03:58:02 +01:00
|
|
|
with open(filename, "r", encoding="utf8") as fh:
|
2021-01-16 19:24:14 +01:00
|
|
|
for line in fh:
|
|
|
|
|
line = re.sub(r'//.*$', '', line)
|
|
|
|
|
for match in re.finditer(r'\bnew\s*(Ast[A-Za-z0-9_]+)', line):
|
|
|
|
|
ref = match.group(1)
|
|
|
|
|
if ref not in ClassRefs:
|
|
|
|
|
ClassRefs[ref] = {'newed': {}, 'used': {}}
|
|
|
|
|
ClassRefs[ref]['newed'][basename] = 1
|
|
|
|
|
for match in re.finditer(r'\b(Ast[A-Za-z0-9_]+)', line):
|
|
|
|
|
ref = match.group(1)
|
|
|
|
|
if ref not in ClassRefs:
|
|
|
|
|
ClassRefs[ref] = {'newed': {}, 'used': {}}
|
|
|
|
|
ClassRefs[ref]['used'][basename] = 1
|
2024-08-27 03:43:34 +02:00
|
|
|
for match in re.finditer(r'(VN_IS|VN_AS|VN_CAST)\([^.]+, ([A-Za-z0-9_]+)', line):
|
2022-01-03 02:54:39 +01:00
|
|
|
ref = "Ast" + match.group(2)
|
|
|
|
|
if ref not in ClassRefs:
|
|
|
|
|
ClassRefs[ref] = {'newed': {}, 'used': {}}
|
|
|
|
|
ClassRefs[ref]['used'][basename] = 1
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def open_file(filename):
|
2022-12-12 03:58:02 +01:00
|
|
|
fh = open(filename, "w", encoding="utf8") # pylint: disable=consider-using-with
|
2021-01-16 19:24:14 +01:00
|
|
|
if re.search(r'\.txt$', filename):
|
|
|
|
|
fh.write("// Generated by astgen\n")
|
|
|
|
|
else:
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write('// Generated by astgen // -*- mode: C++; c-file-style: "cc-mode" -*-' + "\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
return fh
|
|
|
|
|
|
|
|
|
|
|
2021-03-06 03:59:00 +01:00
|
|
|
# ---------------------------------------------------------------------
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_report(filename):
|
|
|
|
|
with open_file(filename) as fh:
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("Processing stages (approximate, based on order in Verilator.cpp):\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
for classn in sorted(Stages.keys(), key=lambda val: Stages[val]):
|
|
|
|
|
fh.write(" " + classn + "\n")
|
|
|
|
|
|
|
|
|
|
fh.write("\nClasses:\n")
|
2022-10-04 12:03:41 +02:00
|
|
|
for node in AstNodeList:
|
2022-09-13 16:57:22 +02:00
|
|
|
fh.write(" class Ast%-17s\n" % node.name)
|
2022-09-15 20:43:56 +02:00
|
|
|
fh.write(" arity: {}\n".format(node.arity))
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(" parent: ")
|
2022-09-13 16:57:22 +02:00
|
|
|
for superClass in node.allSuperClasses:
|
|
|
|
|
if not superClass.isRoot:
|
|
|
|
|
fh.write("Ast%-12s " % superClass.name)
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write("\n")
|
|
|
|
|
fh.write(" childs: ")
|
2022-09-13 16:57:22 +02:00
|
|
|
for subClass in node.allSubClasses:
|
|
|
|
|
fh.write("Ast%-12s " % subClass.name)
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write("\n")
|
2022-09-13 16:57:22 +02:00
|
|
|
if ("Ast" + node.name) in ClassRefs: # pylint: disable=superfluous-parens
|
|
|
|
|
refs = ClassRefs["Ast" + node.name]
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(" newed: ")
|
|
|
|
|
for stage in sorted(refs['newed'].keys(),
|
2024-08-27 03:43:34 +02:00
|
|
|
key=lambda val: Stages[val] if (val in Stages) else -1):
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(stage + " ")
|
|
|
|
|
fh.write("\n")
|
|
|
|
|
fh.write(" used: ")
|
|
|
|
|
for stage in sorted(refs['used'].keys(),
|
2024-08-27 03:43:34 +02:00
|
|
|
key=lambda val: Stages[val] if (val in Stages) else -1):
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(stage + " ")
|
|
|
|
|
fh.write("\n")
|
|
|
|
|
fh.write("\n")
|
|
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
################################################################################
|
2023-11-11 05:25:53 +01:00
|
|
|
# Common code generation
|
2022-10-04 12:03:41 +02:00
|
|
|
################################################################################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_forward_class_decls(prefix, nodeList):
|
|
|
|
|
with open_file("V3{p}__gen_forward_class_decls.h".format(p=prefix)) as fh:
|
|
|
|
|
for node in nodeList:
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("class {p}{n:<17} // ".format(p=prefix, n=node.name + ";"))
|
2022-09-13 16:57:22 +02:00
|
|
|
for superClass in node.allSuperClasses:
|
2022-10-04 12:03:41 +02:00
|
|
|
fh.write("{p}{n:<12} ".format(p=prefix, n=superClass.name))
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write("\n")
|
|
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def write_visitor_decls(prefix, nodeList):
|
|
|
|
|
with open_file("V3{p}__gen_visitor_decls.h".format(p=prefix)) as fh:
|
|
|
|
|
for node in nodeList:
|
2022-09-13 16:57:22 +02:00
|
|
|
if not node.isRoot:
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("virtual void visit({p}{n}*);\n".format(p=prefix, n=node.name))
|
2022-08-02 17:46:31 +02:00
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def write_visitor_defns(prefix, nodeList, visitor):
|
|
|
|
|
with open_file("V3{p}__gen_visitor_defns.h".format(p=prefix)) as fh:
|
|
|
|
|
variable = "nodep" if prefix == "Ast" else "vtxp"
|
|
|
|
|
for node in nodeList:
|
2022-09-13 16:57:22 +02:00
|
|
|
base = node.superClass
|
|
|
|
|
if base is not None:
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("void {c}::visit({p}{n}* {v}) {{ visit(static_cast<{p}{b}*>({v})); }}\n".
|
|
|
|
|
format(c=visitor, p=prefix, n=node.name, b=base.name, v=variable))
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def write_type_enum(prefix, nodeList):
|
|
|
|
|
root = next(_ for _ in nodeList if _.isRoot)
|
|
|
|
|
with open_file("V3{p}__gen_type_enum.h".format(p=prefix)) as fh:
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
fh.write(" enum en : uint16_t {\n")
|
2024-08-27 03:43:34 +02:00
|
|
|
for node in sorted(filter(lambda _: _.isLeaf, nodeList), key=lambda _: _.typeId):
|
|
|
|
|
fh.write(" at{t} = {n},\n".format(t=node.name, n=node.typeId))
|
2022-10-04 12:03:41 +02:00
|
|
|
fh.write(" _ENUM_END = {n}\n".format(n=root.typeIdMax + 1))
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(" };\n")
|
|
|
|
|
|
|
|
|
|
fh.write(" enum bounds : uint16_t {\n")
|
2024-08-27 03:43:34 +02:00
|
|
|
for node in sorted(filter(lambda _: not _.isLeaf, nodeList), key=lambda _: _.typeIdMin):
|
|
|
|
|
fh.write(" first{t} = {n},\n".format(t=node.name, n=node.typeIdMin))
|
|
|
|
|
fh.write(" last{t} = {n},\n".format(t=node.name, n=node.typeIdMax))
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(" _BOUNDS_END\n")
|
|
|
|
|
fh.write(" };\n")
|
|
|
|
|
|
2023-03-17 00:48:56 +01:00
|
|
|
fh.write(" const char* ascii() const VL_MT_SAFE {\n")
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(" static const char* const names[_ENUM_END + 1] = {\n")
|
2024-08-27 03:43:34 +02:00
|
|
|
for node in sorted(filter(lambda _: _.isLeaf, nodeList), key=lambda _: _.typeId):
|
2022-10-04 12:03:41 +02:00
|
|
|
fh.write(' "{T}",\n'.format(T=node.name.upper()))
|
2021-01-16 19:24:14 +01:00
|
|
|
fh.write(" \"_ENUM_END\"\n")
|
|
|
|
|
fh.write(" };\n")
|
|
|
|
|
fh.write(" return names[m_e];\n")
|
|
|
|
|
fh.write(" }\n")
|
|
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def write_type_tests(prefix, nodeList):
|
|
|
|
|
with open_file("V3{p}__gen_type_tests.h".format(p=prefix)) as fh:
|
|
|
|
|
fh.write("// For internal use. They assume argument is not nullptr.\n")
|
|
|
|
|
if prefix == "Ast":
|
|
|
|
|
base = "AstNode"
|
|
|
|
|
variable = "nodep"
|
|
|
|
|
enum = "VNType"
|
|
|
|
|
elif prefix == "Dfg":
|
|
|
|
|
base = "DfgVertex"
|
|
|
|
|
variable = "vtxp"
|
|
|
|
|
enum = "VDfgType"
|
|
|
|
|
for node in nodeList:
|
|
|
|
|
fh.write(
|
2024-08-27 03:43:34 +02:00
|
|
|
"template<> inline bool {b}::privateTypeTest<{p}{n}>(const {b}* {v}) {{ ".format(
|
|
|
|
|
b=base, p=prefix, n=node.name, v=variable))
|
2022-10-04 12:03:41 +02:00
|
|
|
if node.isRoot:
|
|
|
|
|
fh.write("return true;")
|
|
|
|
|
elif not node.isLeaf:
|
|
|
|
|
fh.write(
|
|
|
|
|
"return static_cast<int>({v}->type()) >= static_cast<int>({e}::first{t}) && static_cast<int>({v}->type()) <= static_cast<int>({e}::last{t});"
|
|
|
|
|
.format(v=variable, e=enum, t=node.name))
|
|
|
|
|
else:
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("return {v}->type() == {e}::at{t};".format(v=variable,
|
|
|
|
|
e=enum,
|
|
|
|
|
t=node.name))
|
2022-10-04 12:03:41 +02:00
|
|
|
fh.write(" }\n")
|
|
|
|
|
|
2021-10-22 20:02:45 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
################################################################################
|
2023-11-11 05:25:53 +01:00
|
|
|
# Ast code generation
|
2022-10-04 12:03:41 +02:00
|
|
|
################################################################################
|
2021-10-22 20:02:45 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
|
2023-08-16 13:32:39 +02:00
|
|
|
def write_ast_type_info(filename):
|
|
|
|
|
with open_file(filename) as fh:
|
2024-08-27 03:43:34 +02:00
|
|
|
for node in sorted(filter(lambda _: _.isLeaf, AstNodeList), key=lambda _: _.typeId):
|
2023-08-16 13:32:39 +02:00
|
|
|
opTypeList = []
|
|
|
|
|
opNameList = []
|
|
|
|
|
for n in range(1, 5):
|
|
|
|
|
op = node.getOp(n)
|
|
|
|
|
if not op:
|
|
|
|
|
opTypeList.append('OP_UNUSED')
|
|
|
|
|
opNameList.append('op{0}p'.format(n))
|
|
|
|
|
else:
|
|
|
|
|
name, monad, _ = op
|
|
|
|
|
if not monad:
|
|
|
|
|
opTypeList.append('OP_USED')
|
|
|
|
|
elif monad == "Optional":
|
|
|
|
|
opTypeList.append('OP_OPTIONAL')
|
|
|
|
|
elif monad == "List":
|
|
|
|
|
opTypeList.append('OP_LIST')
|
|
|
|
|
opNameList.append(name)
|
|
|
|
|
# opTypeStr = ', '.join(opTypeList)
|
2024-08-27 03:43:34 +02:00
|
|
|
opTypeStr = ', '.join(['VNTypeInfo::{0}'.format(s) for s in opTypeList])
|
2023-08-16 13:32:39 +02:00
|
|
|
opNameStr = ', '.join(['"{0}"'.format(s) for s in opNameList])
|
|
|
|
|
fh.write(
|
2024-08-27 03:43:34 +02:00
|
|
|
' {{ "Ast{name}", {{{opTypeStr}}}, {{{opNameStr}}}, sizeof(Ast{name}) }},\n'.
|
|
|
|
|
format(
|
2023-08-16 13:32:39 +02:00
|
|
|
name=node.name,
|
|
|
|
|
opTypeStr=opTypeStr,
|
|
|
|
|
opNameStr=opNameStr,
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
2023-12-01 01:58:16 +01:00
|
|
|
def write_ast_impl(filename):
|
|
|
|
|
with open_file(filename) as fh:
|
|
|
|
|
|
|
|
|
|
def emitBlock(pattern, **fmt):
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write(textwrap.indent(textwrap.dedent(pattern), " ").format(**fmt))
|
2023-12-01 01:58:16 +01:00
|
|
|
|
|
|
|
|
for node in AstNodeList:
|
|
|
|
|
if node.name == "Node":
|
|
|
|
|
continue
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock("const char* Ast{t}::brokenGen() const {{\n", t=node.name)
|
2023-12-02 01:05:21 +01:00
|
|
|
if node.superClass.name != 'Node':
|
|
|
|
|
emitBlock(" BROKEN_BASE_RTN(Ast{base}::brokenGen());\n",
|
|
|
|
|
base=node.superClass.name)
|
2023-12-01 01:58:16 +01:00
|
|
|
for ptr in node.ptrs:
|
|
|
|
|
if ptr['monad'] == 'Optional':
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock(" BROKEN_RTN(m_{name} && !m_{name}->brokeExists());\n",
|
|
|
|
|
name=ptr['name'])
|
2023-12-01 01:58:16 +01:00
|
|
|
else:
|
|
|
|
|
emitBlock(" BROKEN_RTN(!m_{name});\n" +
|
|
|
|
|
" BROKEN_RTN(!m_{name}->brokeExists());\n",
|
|
|
|
|
name=ptr['name'])
|
|
|
|
|
# Node's broken rules can be specialized by declaring broken()
|
|
|
|
|
emitBlock(" return Ast{t}::broken(); }}\n", t=node.name)
|
|
|
|
|
|
|
|
|
|
emitBlock("void Ast{t}::cloneRelinkGen() {{\n", t=node.name)
|
2023-12-02 01:05:21 +01:00
|
|
|
if node.superClass.name != 'Node':
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock(" Ast{base}::cloneRelinkGen();\n", base=node.superClass.name)
|
2023-12-01 01:58:16 +01:00
|
|
|
for ptr in node.ptrs:
|
|
|
|
|
emitBlock(
|
|
|
|
|
" if (m_{name} && m_{name}->clonep()) m_{name} = m_{name}->clonep();\n",
|
|
|
|
|
name=ptr['name'],
|
|
|
|
|
kind=ptr['kind'])
|
|
|
|
|
|
|
|
|
|
emitBlock("}}\n")
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock("void Ast{t}::dumpJsonGen(std::ostream& str) const {{\n", t=node.name)
|
2024-02-09 23:50:09 +01:00
|
|
|
if node.superClass.name != 'Node':
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock(" Ast{base}::dumpJson(str);\n", base=node.superClass.name)
|
2024-02-09 23:50:09 +01:00
|
|
|
for ptr in node.ptrs:
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock(" dumpJsonPtr(str, \"{name}\", m_{name});\n", name=ptr['name'])
|
2024-02-09 23:50:09 +01:00
|
|
|
emitBlock("}}\n")
|
|
|
|
|
|
|
|
|
|
emitBlock(
|
|
|
|
|
"void Ast{t}::dumpTreeJsonOpGen(std::ostream& str, const string& indent) const {{\n",
|
|
|
|
|
t=node.name)
|
|
|
|
|
for i in range(1, 5):
|
|
|
|
|
op = node.getOp(i)
|
|
|
|
|
if op is None:
|
|
|
|
|
continue
|
|
|
|
|
name, _, _ = op
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock(" dumpNodeListJson(str, {name}(), \"{name}\", indent);\n", name=name)
|
2024-02-09 23:50:09 +01:00
|
|
|
emitBlock("}}\n")
|
|
|
|
|
|
2023-12-01 01:58:16 +01:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def write_ast_macros(filename):
|
2021-01-16 19:24:14 +01:00
|
|
|
with open_file(filename) as fh:
|
2022-09-15 20:43:56 +02:00
|
|
|
|
|
|
|
|
def emitBlock(pattern, **fmt):
|
|
|
|
|
fh.write(
|
|
|
|
|
textwrap.indent(textwrap.dedent(pattern),
|
|
|
|
|
" ").format(**fmt).replace("\n", " \\\n"))
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
for node in AstNodeList:
|
|
|
|
|
fh.write("#define ASTGEN_MEMBERS_Ast{t} \\\n".format(t=node.name))
|
2023-12-01 01:58:16 +01:00
|
|
|
any_ptr = False
|
|
|
|
|
for ptr in node.ptrs:
|
|
|
|
|
if not any_ptr:
|
|
|
|
|
fh.write("private: \\\n")
|
|
|
|
|
any_ptr = True
|
2024-08-27 03:43:34 +02:00
|
|
|
emitBlock("Ast{kind}* m_{name} = nullptr;", name=ptr['name'], kind=ptr['kind'])
|
2023-12-01 01:58:16 +01:00
|
|
|
if any_ptr:
|
|
|
|
|
fh.write("public: \\\n")
|
|
|
|
|
# TODO pointer accessors
|
|
|
|
|
# for ptr in node.ptrs:
|
|
|
|
|
# emitBlock(
|
|
|
|
|
# ("{kind}* {name}() const {{ return m_{name}; }}\n" +
|
|
|
|
|
# "void {name}({kind}* nodep) {{ m_{name} = nodep; }}"),
|
|
|
|
|
# name=ptr['name'],
|
|
|
|
|
# kind=ptr['kind'])
|
|
|
|
|
|
2022-09-15 20:43:56 +02:00
|
|
|
emitBlock('''\
|
2022-11-13 21:33:11 +01:00
|
|
|
Ast{t}* unlinkFrBack(VNRelinker* linkerp = nullptr) {{
|
|
|
|
|
return static_cast<Ast{t}*>(AstNode::unlinkFrBack(linkerp));
|
|
|
|
|
}}
|
|
|
|
|
Ast{t}* unlinkFrBackWithNext(VNRelinker* linkerp = nullptr) {{
|
|
|
|
|
return static_cast<Ast{t}*>(AstNode::unlinkFrBackWithNext(linkerp));
|
2022-09-15 20:43:56 +02:00
|
|
|
}}
|
|
|
|
|
Ast{t}* cloneTree(bool cloneNext) {{
|
|
|
|
|
return static_cast<Ast{t}*>(AstNode::cloneTree(cloneNext));
|
|
|
|
|
}}
|
2023-09-17 04:50:54 +02:00
|
|
|
Ast{t}* cloneTreePure(bool cloneNext) {{
|
|
|
|
|
return static_cast<Ast{t}*>(AstNode::cloneTreePure(cloneNext));
|
|
|
|
|
}}
|
2023-03-17 00:48:56 +01:00
|
|
|
Ast{t}* clonep() const {{ return static_cast<Ast{t}*>(AstNode::clonep()); }}
|
2022-09-17 14:48:51 +02:00
|
|
|
Ast{t}* addNext(Ast{t}* nodep) {{ return static_cast<Ast{t}*>(AstNode::addNext(this, nodep)); }}
|
2023-12-01 01:58:16 +01:00
|
|
|
const char* brokenGen() const override;
|
|
|
|
|
void cloneRelinkGen() override;
|
2024-02-09 23:50:09 +01:00
|
|
|
void dumpTreeJsonOpGen(std::ostream& str, const string& indent) const override;
|
|
|
|
|
void dumpJsonGen(std::ostream& str) const;
|
2022-09-15 20:43:56 +02:00
|
|
|
''',
|
|
|
|
|
t=node.name)
|
|
|
|
|
|
|
|
|
|
if node.isLeaf:
|
|
|
|
|
emitBlock('''\
|
2023-03-18 00:58:53 +01:00
|
|
|
void accept(VNVisitorConst& v) override {{ v.visit(this); }}
|
2022-09-16 12:22:11 +02:00
|
|
|
AstNode* clone() override {{ return new Ast{t}(*this); }}
|
2022-09-15 20:43:56 +02:00
|
|
|
''',
|
|
|
|
|
t=node.name)
|
|
|
|
|
|
2023-11-12 19:30:48 +01:00
|
|
|
hiddenMethods = []
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
for n in range(1, 5):
|
2022-09-15 20:43:56 +02:00
|
|
|
op = node.getOp(n)
|
|
|
|
|
if not op:
|
|
|
|
|
continue
|
|
|
|
|
name, monad, kind = op
|
2024-08-27 03:43:34 +02:00
|
|
|
retrieve = ("VN_DBG_AS(op{n}p(), {kind})"
|
|
|
|
|
if kind != "Node" else "op{n}p()").format(n=n, kind=kind)
|
2023-11-12 19:30:48 +01:00
|
|
|
superOp = node.superClass.getOp(n)
|
|
|
|
|
superName = None
|
|
|
|
|
if superOp:
|
|
|
|
|
superName = superOp[0]
|
|
|
|
|
hiddenMethods.append(superName)
|
2022-09-15 20:43:56 +02:00
|
|
|
if monad == "List":
|
|
|
|
|
emitBlock('''\
|
2023-03-17 00:48:56 +01:00
|
|
|
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
|
2022-09-15 20:43:56 +02:00
|
|
|
void add{Name}(Ast{kind}* nodep) {{ addNOp{n}p(reinterpret_cast<AstNode*>(nodep)); }}
|
|
|
|
|
''',
|
|
|
|
|
kind=kind,
|
|
|
|
|
name=name,
|
|
|
|
|
Name=name[0].upper() + name[1:],
|
|
|
|
|
n=n,
|
|
|
|
|
retrieve=retrieve)
|
2023-11-12 19:30:48 +01:00
|
|
|
if superOp:
|
2024-08-27 03:43:34 +02:00
|
|
|
hiddenMethods.append("add" + superName[0].upper() + superName[1:])
|
2022-09-15 20:43:56 +02:00
|
|
|
elif monad == "Optional":
|
|
|
|
|
emitBlock('''\
|
2023-03-17 00:48:56 +01:00
|
|
|
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
|
2022-09-15 20:43:56 +02:00
|
|
|
void {name}(Ast{kind}* nodep) {{ setNOp{n}p(reinterpret_cast<AstNode*>(nodep)); }}
|
|
|
|
|
''',
|
|
|
|
|
kind=kind,
|
|
|
|
|
name=name,
|
|
|
|
|
n=n,
|
|
|
|
|
retrieve=retrieve)
|
|
|
|
|
else:
|
|
|
|
|
emitBlock('''\
|
2023-03-17 00:48:56 +01:00
|
|
|
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
|
2022-09-15 20:43:56 +02:00
|
|
|
void {name}(Ast{kind}* nodep) {{ setOp{n}p(reinterpret_cast<AstNode*>(nodep)); }}
|
|
|
|
|
''',
|
|
|
|
|
kind=kind,
|
|
|
|
|
name=name,
|
|
|
|
|
n=n,
|
|
|
|
|
retrieve=retrieve)
|
|
|
|
|
|
2023-11-12 19:30:48 +01:00
|
|
|
if hiddenMethods:
|
|
|
|
|
fh.write("private: \\\n")
|
|
|
|
|
for method in hiddenMethods:
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write(" using Ast{sup}::{method}; \\\n".format(sup=node.superClass.name,
|
|
|
|
|
method=method))
|
2023-11-12 19:30:48 +01:00
|
|
|
fh.write("public: \\\n")
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
|
2022-09-15 20:43:56 +02:00
|
|
|
|
|
|
|
|
# Only care about leaf classes for the rest
|
|
|
|
|
if node.isLeaf:
|
|
|
|
|
fh.write(
|
2024-08-27 03:43:34 +02:00
|
|
|
"#define ASTGEN_SUPER_{t}(...) Ast{b}(VNType::at{t}, __VA_ARGS__)\n".format(
|
|
|
|
|
t=node.name, b=node.superClass.name))
|
2022-09-15 20:43:56 +02:00
|
|
|
fh.write("\n")
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-05-22 12:14:07 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def write_ast_yystype(filename):
|
|
|
|
|
with open_file(filename) as fh:
|
|
|
|
|
for node in AstNodeList:
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("Ast{t}* {m}p;\n".format(t=node.name, m=node.name[0].lower() + node.name[1:]))
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
################################################################################
|
2023-11-11 05:25:53 +01:00
|
|
|
# DFG code generation
|
2022-10-04 12:03:41 +02:00
|
|
|
################################################################################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_dfg_macros(filename):
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
with open_file(filename) as fh:
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
def emitBlock(pattern, **fmt):
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
fh.write(
|
2022-10-04 12:03:41 +02:00
|
|
|
textwrap.indent(textwrap.dedent(pattern),
|
|
|
|
|
" ").format(**fmt).replace("\n", " \\\n"))
|
|
|
|
|
|
|
|
|
|
for node in DfgVertexList:
|
|
|
|
|
fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name))
|
|
|
|
|
|
|
|
|
|
if node.isLeaf:
|
|
|
|
|
emitBlock('''\
|
|
|
|
|
static constexpr VDfgType dfgType() {{ return VDfgType::at{t}; }};
|
2024-03-06 19:01:52 +01:00
|
|
|
using Super = Dfg{s};
|
2022-10-04 12:03:41 +02:00
|
|
|
void accept(DfgVisitor& v) override {{ v.visit(this); }}
|
|
|
|
|
''',
|
2024-03-06 19:01:52 +01:00
|
|
|
t=node.name,
|
|
|
|
|
s=node.superClass.name)
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
for n in range(1, node.arity + 1):
|
|
|
|
|
name, _, _ = node.getOp(n)
|
|
|
|
|
emitBlock('''\
|
|
|
|
|
DfgVertex* {name}() const {{ return source<{n}>(); }}
|
|
|
|
|
void {name}(DfgVertex* vtxp) {{ relinkSource<{n}>(vtxp); }}
|
|
|
|
|
''',
|
|
|
|
|
name=name,
|
|
|
|
|
n=n - 1)
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1))
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
if operandNames:
|
2022-10-04 12:03:41 +02:00
|
|
|
emitBlock('''\
|
|
|
|
|
const std::string srcName(size_t idx) const override {{
|
|
|
|
|
static const char* names[{a}] = {{ {ns} }};
|
|
|
|
|
return names[idx];
|
|
|
|
|
}}
|
|
|
|
|
''',
|
|
|
|
|
a=node.arity,
|
2024-08-27 03:43:34 +02:00
|
|
|
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames)))
|
|
|
|
|
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def write_dfg_auto_classes(filename):
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
with open_file(filename) as fh:
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
def emitBlock(pattern, **fmt):
|
|
|
|
|
fh.write(textwrap.dedent(pattern).format(**fmt))
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
for node in DfgVertexList:
|
2023-11-11 05:25:53 +01:00
|
|
|
# Only generate code for automatically derived leaf nodes
|
2022-10-04 12:03:41 +02:00
|
|
|
if (node.file is not None) or not node.isLeaf:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
emitBlock('''\
|
|
|
|
|
class Dfg{t} final : public Dfg{s} {{
|
|
|
|
|
public:
|
|
|
|
|
Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
|
|
|
|
: Dfg{s}{{dfg, dfgType(), flp, dtypep}} {{}}
|
|
|
|
|
ASTGEN_MEMBERS_Dfg{t};
|
|
|
|
|
}};
|
|
|
|
|
''',
|
|
|
|
|
t=node.name,
|
|
|
|
|
s=node.superClass.name)
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
fh.write("\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_dfg_ast_to_dfg(filename):
|
|
|
|
|
with open_file(filename) as fh:
|
2022-10-04 12:03:41 +02:00
|
|
|
for node in DfgVertexList:
|
2023-11-11 05:25:53 +01:00
|
|
|
# Only generate code for automatically derived leaf nodes
|
2022-10-04 12:03:41 +02:00
|
|
|
if (node.file is not None) or (not node.isLeaf):
|
|
|
|
|
continue
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("void visit(Ast{t}* nodep) override {{\n".format(t=node.name))
|
|
|
|
|
fh.write(' UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex");\n\n')
|
2022-10-04 12:03:41 +02:00
|
|
|
fh.write(" if (unhandled(nodep)) return;\n\n")
|
|
|
|
|
for i in range(node.arity):
|
|
|
|
|
fh.write(" iterate(nodep->op{j}p());\n".format(j=i + 1))
|
|
|
|
|
fh.write(" if (m_foundUnhandled) return;\n")
|
|
|
|
|
fh.write(
|
|
|
|
|
' UASSERT_OBJ(nodep->op{j}p()->user1p(), nodep, "Child {j} missing Dfg vertex");\n'
|
|
|
|
|
.format(j=i + 1))
|
|
|
|
|
fh.write("\n")
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write(" Dfg{t}* const vtxp = makeVertex<Dfg{t}>(nodep, *m_dfgp);\n".format(
|
|
|
|
|
t=node.name))
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
fh.write(" if (!vtxp) {\n")
|
|
|
|
|
fh.write(" m_foundUnhandled = true;\n")
|
|
|
|
|
fh.write(" ++m_ctx.m_nonRepNode;\n")
|
|
|
|
|
fh.write(" return;\n")
|
|
|
|
|
fh.write(" }\n\n")
|
|
|
|
|
for i in range(node.arity):
|
|
|
|
|
fh.write(
|
2024-08-27 03:43:34 +02:00
|
|
|
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user1u().to<DfgVertex*>());\n".
|
|
|
|
|
format(i=i, j=i + 1))
|
2022-10-04 12:03:41 +02:00
|
|
|
fh.write("\n")
|
|
|
|
|
fh.write(" m_uncommittedVertices.push_back(vtxp);\n")
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
fh.write(" nodep->user1p(vtxp);\n")
|
|
|
|
|
fh.write("}\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_dfg_dfg_to_ast(filename):
|
|
|
|
|
with open_file(filename) as fh:
|
2022-10-04 12:03:41 +02:00
|
|
|
for node in DfgVertexList:
|
2023-11-11 05:25:53 +01:00
|
|
|
# Only generate code for automatically derived leaf nodes
|
2022-10-04 12:03:41 +02:00
|
|
|
if (node.file is not None) or (not node.isLeaf):
|
|
|
|
|
continue
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write("void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
for i in range(node.arity):
|
|
|
|
|
fh.write(
|
2024-03-02 20:49:29 +01:00
|
|
|
" AstNodeExpr* const op{j}p = convertDfgVertexToAstNodeExpr(vtxp->source<{i}>());\n"
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
.format(i=i, j=i + 1))
|
2024-08-27 03:43:34 +02:00
|
|
|
fh.write(" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name))
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
for i in range(node.arity):
|
|
|
|
|
fh.write(", op{j}p".format(j=i + 1))
|
|
|
|
|
fh.write(");\n")
|
|
|
|
|
fh.write("}\n")
|
|
|
|
|
|
|
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
######################################################################
|
|
|
|
|
# main
|
2009-05-04 23:07:57 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
|
allow_abbrev=False,
|
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
|
|
|
description="""Generate V3Ast headers to reduce C++ code duplication.""",
|
2024-08-27 03:43:34 +02:00
|
|
|
epilog="""Copyright 2002-2024 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
can redistribute it and/or modify it under the terms of either the GNU
|
|
|
|
|
Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
Version 2.0.
|
|
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
parser.add_argument('-I', action='store', help='source code include directory')
|
2024-08-27 03:43:34 +02:00
|
|
|
parser.add_argument('--astdef', action='append', help='add AST definition file (relative to -I)')
|
|
|
|
|
parser.add_argument('--dfgdef', action='append', help='add DFG definition file (relative to -I)')
|
|
|
|
|
parser.add_argument('--classes', action='store_true', help='makes class declaration files')
|
2021-01-16 19:24:14 +01:00
|
|
|
parser.add_argument('--debug', action='store_true', help='enable debug')
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
parser.add_argument('infiles', nargs='*', help='list of input .cpp filenames')
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
Args = parser.parse_args()
|
2009-05-04 23:07:57 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
###############################################################################
|
|
|
|
|
# Read AstNode definitions
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
2022-09-15 14:10:39 +02:00
|
|
|
# Set up the root AstNode type. It is standalone so we don't need to parse the
|
|
|
|
|
# sources for this.
|
2022-10-04 12:03:41 +02:00
|
|
|
AstNodes["Node"] = Node("Node", None)
|
2022-09-15 14:10:39 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
# Read AstNode definitions
|
2022-09-15 14:10:39 +02:00
|
|
|
for filename in Args.astdef:
|
2022-10-04 12:03:41 +02:00
|
|
|
read_types(os.path.join(Args.I, filename), AstNodes, "Ast")
|
2022-09-13 16:57:22 +02:00
|
|
|
|
|
|
|
|
# Compute derived properties over the whole AstNode hierarchy
|
2022-10-04 12:03:41 +02:00
|
|
|
AstNodes["Node"].complete()
|
|
|
|
|
|
|
|
|
|
AstNodeList = tuple(map(lambda _: AstNodes[_], sorted(AstNodes.keys())))
|
|
|
|
|
|
|
|
|
|
check_types(AstNodeList, "Ast", "Node")
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
# Read and generate DfgVertex definitions
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
|
|
# Set up the root DfgVertex type and some other hand-written base types.
|
|
|
|
|
# These are standalone so we don't need to parse the sources for this.
|
|
|
|
|
DfgVertices["Vertex"] = Node("Vertex", None)
|
|
|
|
|
DfgVertices["VertexUnary"] = Node("VertexUnary", DfgVertices["Vertex"])
|
|
|
|
|
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexUnary"])
|
|
|
|
|
DfgVertices["VertexBinary"] = Node("VertexBinary", DfgVertices["Vertex"])
|
|
|
|
|
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexBinary"])
|
|
|
|
|
DfgVertices["VertexTernary"] = Node("VertexTernary", DfgVertices["Vertex"])
|
|
|
|
|
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexTernary"])
|
|
|
|
|
DfgVertices["VertexVariadic"] = Node("VertexVariadic", DfgVertices["Vertex"])
|
|
|
|
|
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexVariadic"])
|
|
|
|
|
|
|
|
|
|
# Read DfgVertex definitions
|
|
|
|
|
for filename in Args.dfgdef:
|
|
|
|
|
read_types(os.path.join(Args.I, filename), DfgVertices, "Dfg")
|
|
|
|
|
|
|
|
|
|
# Add the DfgVertex sub-types automatically derived from AstNode sub-types
|
|
|
|
|
for node in AstNodeList:
|
|
|
|
|
# Ignore the hierarchy for now
|
|
|
|
|
if not node.isLeaf:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Ignore any explicitly defined vertex
|
|
|
|
|
if node.name in DfgVertices:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if node.isSubClassOf(AstNodes["NodeUniop"]):
|
|
|
|
|
base = DfgVertices["VertexUnary"]
|
|
|
|
|
elif node.isSubClassOf(AstNodes["NodeBiop"]):
|
|
|
|
|
base = DfgVertices["VertexBinary"]
|
|
|
|
|
elif node.isSubClassOf(AstNodes["NodeTriop"]):
|
|
|
|
|
base = DfgVertices["VertexTernary"]
|
|
|
|
|
else:
|
|
|
|
|
continue
|
2022-09-13 16:57:22 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
vertex = Node(node.name, base)
|
|
|
|
|
DfgVertices[node.name] = vertex
|
|
|
|
|
base.addSubClass(vertex)
|
2022-09-13 16:57:22 +02:00
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
for n in range(1, node.arity + 1):
|
|
|
|
|
op = node.getOp(n)
|
|
|
|
|
if op is not None:
|
|
|
|
|
name, monad, kind = op
|
2023-11-11 05:25:53 +01:00
|
|
|
assert monad == "", "Cannot represent AstNode as DfgVertex"
|
2022-10-04 12:03:41 +02:00
|
|
|
vertex.addOp(n, name, "", "")
|
|
|
|
|
|
|
|
|
|
# Compute derived properties over the whole DfgVertex hierarchy
|
|
|
|
|
DfgVertices["Vertex"].complete()
|
|
|
|
|
|
2024-08-27 03:43:34 +02:00
|
|
|
DfgVertexList = tuple(map(lambda _: DfgVertices[_], sorted(DfgVertices.keys())))
|
2022-10-04 12:03:41 +02:00
|
|
|
|
|
|
|
|
check_types(DfgVertexList, "Dfg", "Vertex")
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
# Read additional files
|
|
|
|
|
###############################################################################
|
2022-09-15 14:10:39 +02:00
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
read_stages(Args.I + "/Verilator.cpp")
|
|
|
|
|
|
|
|
|
|
source_files = glob.glob(Args.I + "/*.y")
|
|
|
|
|
source_files.extend(glob.glob(Args.I + "/*.h"))
|
|
|
|
|
source_files.extend(glob.glob(Args.I + "/*.cpp"))
|
|
|
|
|
for filename in source_files:
|
|
|
|
|
read_refs(filename)
|
|
|
|
|
|
2022-10-04 12:03:41 +02:00
|
|
|
###############################################################################
|
|
|
|
|
# Generate output
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
2021-01-16 19:24:14 +01:00
|
|
|
if Args.classes:
|
|
|
|
|
write_report("V3Ast__gen_report.txt")
|
2022-10-04 12:03:41 +02:00
|
|
|
# Write Ast code
|
|
|
|
|
write_forward_class_decls("Ast", AstNodeList)
|
|
|
|
|
write_visitor_decls("Ast", AstNodeList)
|
2023-03-18 00:58:53 +01:00
|
|
|
write_visitor_defns("Ast", AstNodeList, "VNVisitorConst")
|
2022-10-04 12:03:41 +02:00
|
|
|
write_type_enum("Ast", AstNodeList)
|
|
|
|
|
write_type_tests("Ast", AstNodeList)
|
2023-08-16 13:32:39 +02:00
|
|
|
write_ast_type_info("V3Ast__gen_type_info.h")
|
2023-12-01 01:58:16 +01:00
|
|
|
write_ast_impl("V3Ast__gen_impl.h")
|
2022-10-04 12:03:41 +02:00
|
|
|
write_ast_macros("V3Ast__gen_macros.h")
|
|
|
|
|
write_ast_yystype("V3Ast__gen_yystype.h")
|
|
|
|
|
# Write Dfg code
|
|
|
|
|
write_forward_class_decls("Dfg", DfgVertexList)
|
|
|
|
|
write_visitor_decls("Dfg", DfgVertexList)
|
|
|
|
|
write_visitor_defns("Dfg", DfgVertexList, "DfgVisitor")
|
|
|
|
|
write_type_enum("Dfg", DfgVertexList)
|
|
|
|
|
write_type_tests("Dfg", DfgVertexList)
|
|
|
|
|
write_dfg_macros("V3Dfg__gen_macros.h")
|
|
|
|
|
write_dfg_auto_classes("V3Dfg__gen_auto_classes.h")
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
write_dfg_ast_to_dfg("V3Dfg__gen_ast_to_dfg.h")
|
|
|
|
|
write_dfg_dfg_to_ast("V3Dfg__gen_dfg_to_ast.h")
|
2021-01-16 19:24:14 +01:00
|
|
|
|
|
|
|
|
for cpt in Args.infiles:
|
|
|
|
|
if not re.search(r'.cpp$', cpt):
|
|
|
|
|
sys.exit("%Error: Expected argument to be .cpp file: " + cpt)
|
|
|
|
|
cpt = re.sub(r'.cpp$', '', cpt)
|
2024-08-27 03:43:34 +02:00
|
|
|
Cpt().process(in_filename=Args.I + "/" + cpt + ".cpp", out_filename=cpt + "__gen.cpp")
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
######################################################################
|
2021-03-06 03:59:00 +01:00
|
|
|
# Local Variables:
|
2023-12-01 01:58:16 +01:00
|
|
|
# compile-command: "touch src/V3AstNodeExpr.h ; v4make"
|
2021-03-06 03:59:00 +01:00
|
|
|
# End:
|