verilator/src/astgen

1358 lines
54 KiB
Plaintext
Raw Normal View History

2021-01-16 19:24:14 +01:00
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0115,C0116,C0123,C0209,C0301,C0302,R0902,R0904,R0913,R0914,R0912,R0915,W0511,W0621
######################################################################
2021-01-16 19:24:14 +01:00
import argparse
import glob
import os
2021-01-16 19:24:14 +01:00
import re
import sys
import textwrap
# from pprint import pprint, pformat
2021-01-16 19:24:14 +01:00
# This class is used to represents both AstNode and DfgVertex sub-types
class Node:
def __init__(self, name, superClass, file=None, lineno=None):
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
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
self._arity = -1 # Arity of node
self._ops = {} # Operands of node
self._ptrs = [] # Pointer members of node (name, types)
@property
def name(self):
return self._name
@property
def superClass(self):
return self._superClass
@property
def isRoot(self):
return self.superClass is None
@property
def isCompleted(self):
return isinstance(self._subClasses, tuple)
@property
def file(self):
return self._file
@property
def lineno(self):
return self._lineno
@property
def ptrs(self):
assert self.isCompleted
return self._ptrs
# Pre completion methods
def addSubClass(self, subClass):
assert not self.isCompleted
self._subClasses.append(subClass)
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
def addPtr(self, name, monad, kind):
name = re.sub(r'^m_', '', name)
self._ptrs.append({'name': name, 'monad': monad, 'kind': kind})
# Computes derived properties over entire class hierarchy.
# No more changes to the hierarchy are allowed once this was called
def complete(self, typeId=0, ordIdx=0):
assert not self.isCompleted
# Sort sub-classes and convert to tuple, which marks completion
self._subClasses = tuple(
sorted(self._subClasses, key=lambda _: (bool(_._subClasses), _.name))) # pylint: disable=protected-access
self._ordIdx = ordIdx
ordIdx = ordIdx + 1
if self.isRoot:
self._arity = 0
else:
self._arity = max(self._arity, self._superClass.arity)
# Leaves
if self.isLeaf:
self._typeId = typeId
return typeId + 1, ordIdx
# Non-leaves
for subClass in self._subClasses:
typeId, ordIdx = subClass.complete(typeId, ordIdx)
return typeId, ordIdx
# 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:
self._allSuperClasses = self.superClass.allSuperClasses + (self.superClass, )
return self._allSuperClasses
@property
def allSubClasses(self):
assert self.isCompleted
if self._allSubClasses is None:
if self.isLeaf:
self._allSubClasses = ()
else:
self._allSubClasses = self.subClasses + tuple(_ for subClass in self.subClasses
for _ in subClass.allSubClasses)
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
@property
def ordIdx(self):
assert self.isCompleted
return self._ordIdx
@property
def arity(self):
assert self.isCompleted
return self._arity
def isSubClassOf(self, other):
assert self.isCompleted
if self is other:
return True
return self in other.allSubClasses
AstNodes = {}
AstNodeList = None
DfgVertices = {}
DfgVertexList = None
2021-01-16 19:24:14 +01:00
ClassRefs = {}
Stages = {}
class Cpt:
2021-01-16 19:24:14 +01:00
def __init__(self):
self.did_out_tree = False
self.in_filename = ""
self.in_linenum = 1
self.out_filename = ""
2021-01-16 19:24:14 +01:00
self.out_linenum = 1
self.out_lines = []
2021-01-16 19:24:14 +01:00
self.tree_skip_visit = {}
self.treeop = {}
2021-01-16 19:24:14 +01:00
self._exec_nsyms = 0
self._exec_syms = {}
def error(self, txt):
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):
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:
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
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:
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
r'TREEOP(1?)([ACSV]?)\s*\(\s*\"([^\"]*)\"\s*,\s*\"([^\"]*)\"\s*\)',
2021-01-16 19:24:14 +01:00
func)
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)
# 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)
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 = ""
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
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)
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
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
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":
pass
2021-01-16 19:24:14 +01:00
elif re.match(r'\s+', tok):
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)
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)
out += "AstNodeExpr* " + argnp + " = " + arg + "->unlinkFrBack();\n"
2021-01-16 19:24:14 +01: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":
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):
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")
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")
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")
self.print(" // Bottom class up, as more simple transforms are generally better\n")
for node in AstNodeList:
2021-01-16 19:24:14 +01:00
out_for_type_sc = []
out_for_type = []
classes = list(node.allSuperClasses)
classes.append(node)
for base in classes:
base = base.name
if base not in self.treeop:
2021-01-16 19:24:14 +01:00
continue
for typefunc in self.treeop[base]:
lines = [" if (" + typefunc['match_func'] + "(nodep)) return;\n"]
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
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
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]:
self.print(
" iterateAndNextNull(nodep->{op2}());\n".format(op2=node.getOp(2)[0]))
if node.isSubClassOf(AstNodes["NodeTriop"]):
self.print(" iterateAndNextNull(nodep->{op3}());\n".format(
op3=node.getOp(3)[0]))
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
skip = node.name in self.tree_skip_visit
2021-01-16 19:24:14 +01:00
gen = "Gen" if skip else ""
virtual = "virtual " if skip else ""
2021-01-16 19:24:14 +01:00
override = "" if skip else " override"
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")
2021-01-16 19:24:14 +01: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
def read_types(filename, Nodes, prefix):
hasErrors = False
def error(lineno, message):
nonlocal hasErrors
print(filename + ":" + str(lineno) + ": %Error: " + message, file=sys.stderr)
hasErrors = True
node = None
hasAstgenMembers = False
def checkFinishedNode(node):
nonlocal hasAstgenMembers
if not node:
return
if not hasAstgenMembers:
error(
node.lineno,
"'{p}{n}' does not contain 'ASTGEN_MEMBERS_{p}{n};'".format(p=prefix, n=node.name))
hasAstgenMembers = False
2022-12-12 03:58:02 +01:00
with open(filename, "r", encoding="utf8") as fh:
for (lineno, line) in enumerate(fh, start=1):
line = line.strip()
if not line:
2021-01-16 19:24:14 +01:00
continue
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)
supern = match.group(1) if match else ""
if re.search(prefix, supern):
classn = re.sub(r'^' + prefix, '', classn)
supern = re.sub(r'^' + prefix, '', supern)
if not supern:
sys.exit("%Error: '{p}{c}' has no super-class".format(p=prefix, c=classn))
checkFinishedNode(node)
superClass = Nodes[supern]
node = Node(classn, superClass, filename, lineno)
superClass.addSubClass(node)
Nodes[classn] = node
if not node:
continue
if re.match(r'^\s*ASTGEN_MEMBERS_' + prefix + node.name + ';', line):
hasAstgenMembers = True
if prefix != "Ast":
continue
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(
lineno, "Malformed '@astgen " + what + "' directive (expecting '" +
what + " := <identifier> : <type>': " + decl)
else:
kind = parseOpType(kind)
if not kind:
error(
lineno, "Bad type for '@astgen " + what +
"' (expecting Ast*, Optional[Ast*], or List[Ast*]):" + decl)
elif node.getOp(n) is not None:
error(lineno, "Already defined " + what + " for " + node.name)
else:
node.addOp(n, ident, *kind)
elif what in ("alias op1", "alias op2", "alias op3", "alias op4"):
n = int(what[-1])
ident = rest.strip()
if not re.match(r'^\w+$', ident):
error(
lineno, "Malformed '@astgen " + what + "' directive (expecting '" +
what + " := <identifier>': " + decl)
else:
op = node.getOp(n)
if op is None:
error(lineno, "Aliased op" + str(n) + " is not defined")
else:
node.addOp(n, ident, *op[1:])
elif what == "ptr":
ident, sep, kind = partitionAndStrip(rest, ":")
ident = ident.strip()
kind = parseOpType(kind)
if not kind:
error(
lineno, "Bad type for '@astgen " + what +
"' (expecting Ast*, Optional[Ast*], or List[Ast*]):" + decl)
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(
lineno, "Malformed @astgen what (expecting 'op1'..'op4'," +
" 'alias op1'.., 'ptr'): " + what)
else:
line = re.sub(r'//.*$', '', line)
if re.match(r'.*[Oo]p[1-9].*', line):
error(lineno, "Use generated accessors to access op<N> operands")
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)
checkFinishedNode(node)
if hasErrors:
sys.exit("%Error: Stopping due to errors reported above")
2021-01-16 19:24:14 +01: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:
sys.exit("%Error: Final {b} subclasses must not be named {b}*: {p}{n}".format(
b=baseClass, p=prefix, n=node.name))
else:
if not node.isLeaf:
sys.exit("%Error: Non-final {b} subclasses must be named {b}*: {p}{n}".format(
b=baseClass, p=prefix, n=node.name))
# Check ordering of node definitions
hasOrderingError = False
files = tuple(sorted(set(_.file for _ in sortedTypes if _.file is not None)))
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))
expect = {node: pred for pred, node in zip((None, ) + expectOrder[:-1], expectOrder)}
actual = {node: pred for pred, node in zip((None, ) + actualOrder[:-1], actualOrder)}
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}."
.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:
sys.exit("%Error: Stopping due to out of order definitions listed above")
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
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
for match in re.finditer(r'(VN_IS|VN_AS|VN_CAST)\([^.]+, ([A-Za-z0-9_]+)', line):
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:
fh.write('// Generated by astgen // -*- mode: C++; c-file-style: "cc-mode" -*-' + "\n")
2021-01-16 19:24:14 +01:00
return fh
# ---------------------------------------------------------------------
2021-01-16 19:24:14 +01:00
def write_report(filename):
with open_file(filename) as fh:
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")
for node in AstNodeList:
fh.write(" class Ast%-17s\n" % node.name)
fh.write(" arity: {}\n".format(node.arity))
2021-01-16 19:24:14 +01:00
fh.write(" parent: ")
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: ")
for subClass in node.allSubClasses:
fh.write("Ast%-12s " % subClass.name)
2021-01-16 19:24:14 +01:00
fh.write("\n")
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(),
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(),
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")
################################################################################
2023-11-11 05:25:53 +01:00
# Common code generation
################################################################################
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:
fh.write("class {p}{n:<17} // ".format(p=prefix, n=node.name + ";"))
for superClass in node.allSuperClasses:
fh.write("{p}{n:<12} ".format(p=prefix, n=superClass.name))
2021-01-16 19:24:14 +01:00
fh.write("\n")
def write_visitor_decls(prefix, nodeList):
with open_file("V3{p}__gen_visitor_decls.h".format(p=prefix)) as fh:
for node in nodeList:
if not node.isRoot:
fh.write("virtual void visit({p}{n}*);\n".format(p=prefix, n=node.name))
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:
base = node.superClass
if base is not None:
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
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")
for node in sorted(filter(lambda _: _.isLeaf, nodeList), key=lambda _: _.typeId):
fh.write(" at{t} = {n},\n".format(t=node.name, n=node.typeId))
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")
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")
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")
for node in sorted(filter(lambda _: _.isLeaf, nodeList), key=lambda _: _.typeId):
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")
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(
"template<> inline bool {b}::privateTypeTest<{p}{n}>(const {b}* {v}) {{ ".format(
b=base, p=prefix, n=node.name, v=variable))
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:
fh.write("return {v}->type() == {e}::at{t};".format(v=variable,
e=enum,
t=node.name))
fh.write(" }\n")
################################################################################
2023-11-11 05:25:53 +01:00
# Ast code generation
################################################################################
def write_ast_type_info(filename):
with open_file(filename) as fh:
for node in sorted(filter(lambda _: _.isLeaf, AstNodeList), key=lambda _: _.typeId):
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)
opTypeStr = ', '.join(['VNTypeInfo::{0}'.format(s) for s in opTypeList])
opNameStr = ', '.join(['"{0}"'.format(s) for s in opNameList])
fh.write(
' {{ "Ast{name}", {{{opTypeStr}}}, {{{opNameStr}}}, sizeof(Ast{name}) }},\n'.
format(
name=node.name,
opTypeStr=opTypeStr,
opNameStr=opNameStr,
))
def write_ast_impl(filename):
with open_file(filename) as fh:
def emitBlock(pattern, **fmt):
fh.write(textwrap.indent(textwrap.dedent(pattern), " ").format(**fmt))
for node in AstNodeList:
if node.name == "Node":
continue
emitBlock("const char* Ast{t}::brokenGen() const {{\n", t=node.name)
if node.superClass.name != 'Node':
emitBlock(" BROKEN_BASE_RTN(Ast{base}::brokenGen());\n",
base=node.superClass.name)
for ptr in node.ptrs:
if ptr['monad'] == 'Optional':
emitBlock(" BROKEN_RTN(m_{name} && !m_{name}->brokeExists());\n",
name=ptr['name'])
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)
if node.superClass.name != 'Node':
emitBlock(" Ast{base}::cloneRelinkGen();\n", base=node.superClass.name)
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")
emitBlock("void Ast{t}::dumpJsonGen(std::ostream& str) const {{\n", t=node.name)
if node.superClass.name != 'Node':
emitBlock(" Ast{base}::dumpJson(str);\n", base=node.superClass.name)
for ptr in node.ptrs:
emitBlock(" dumpJsonPtr(str, \"{name}\", m_{name});\n", name=ptr['name'])
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
emitBlock(" dumpNodeListJson(str, {name}(), \"{name}\", indent);\n", name=name)
emitBlock("}}\n")
def write_ast_macros(filename):
2021-01-16 19:24:14 +01:00
with open_file(filename) as fh:
def emitBlock(pattern, **fmt):
fh.write(
textwrap.indent(textwrap.dedent(pattern),
" ").format(**fmt).replace("\n", " \\\n"))
for node in AstNodeList:
fh.write("#define ASTGEN_MEMBERS_Ast{t} \\\n".format(t=node.name))
any_ptr = False
for ptr in node.ptrs:
if not any_ptr:
fh.write("private: \\\n")
any_ptr = True
emitBlock("Ast{kind}* m_{name} = nullptr;", name=ptr['name'], kind=ptr['kind'])
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'])
emitBlock('''\
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));
}}
Ast{t}* cloneTree(bool cloneNext) {{
return static_cast<Ast{t}*>(AstNode::cloneTree(cloneNext));
}}
Ast{t}* cloneTreePure(bool cloneNext) {{
return static_cast<Ast{t}*>(AstNode::cloneTreePure(cloneNext));
}}
Ast{t}* clonep() const {{ return static_cast<Ast{t}*>(AstNode::clonep()); }}
Ast{t}* addNext(Ast{t}* nodep) {{ return static_cast<Ast{t}*>(AstNode::addNext(this, nodep)); }}
const char* brokenGen() const override;
void cloneRelinkGen() override;
void dumpTreeJsonOpGen(std::ostream& str, const string& indent) const override;
void dumpJsonGen(std::ostream& str) const;
''',
t=node.name)
if node.isLeaf:
emitBlock('''\
2023-03-18 00:58:53 +01:00
void accept(VNVisitorConst& v) override {{ v.visit(this); }}
AstNode* clone() override {{ return new Ast{t}(*this); }}
''',
t=node.name)
hiddenMethods = []
for n in range(1, 5):
op = node.getOp(n)
if not op:
continue
name, monad, kind = op
retrieve = ("VN_DBG_AS(op{n}p(), {kind})"
if kind != "Node" else "op{n}p()").format(n=n, kind=kind)
superOp = node.superClass.getOp(n)
superName = None
if superOp:
superName = superOp[0]
hiddenMethods.append(superName)
if monad == "List":
emitBlock('''\
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
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)
if superOp:
hiddenMethods.append("add" + superName[0].upper() + superName[1:])
elif monad == "Optional":
emitBlock('''\
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
void {name}(Ast{kind}* nodep) {{ setNOp{n}p(reinterpret_cast<AstNode*>(nodep)); }}
''',
kind=kind,
name=name,
n=n,
retrieve=retrieve)
else:
emitBlock('''\
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
void {name}(Ast{kind}* nodep) {{ setOp{n}p(reinterpret_cast<AstNode*>(nodep)); }}
''',
kind=kind,
name=name,
n=n,
retrieve=retrieve)
if hiddenMethods:
fh.write("private: \\\n")
for method in hiddenMethods:
fh.write(" using Ast{sup}::{method}; \\\n".format(sup=node.superClass.name,
method=method))
fh.write("public: \\\n")
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
# Only care about leaf classes for the rest
if node.isLeaf:
fh.write(
"#define ASTGEN_SUPER_{t}(...) Ast{b}(VNType::at{t}, __VA_ARGS__)\n".format(
t=node.name, b=node.superClass.name))
fh.write("\n")
2021-05-22 12:14:07 +02:00
def write_ast_yystype(filename):
with open_file(filename) as fh:
for node in AstNodeList:
fh.write("Ast{t}* {m}p;\n".format(t=node.name, m=node.name[0].lower() + node.name[1:]))
################################################################################
2023-11-11 05:25:53 +01:00
# DFG code generation
################################################################################
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:
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(
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}; }};
using Super = Dfg{s};
void accept(DfgVisitor& v) override {{ v.visit(this); }}
''',
t=node.name,
s=node.superClass.name)
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)
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:
emitBlock('''\
const std::string srcName(size_t idx) const override {{
static const char* names[{a}] = {{ {ns} }};
return names[idx];
}}
''',
a=node.arity,
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
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:
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
for node in DfgVertexList:
2023-11-11 05:25:53 +01:00
# Only generate code for automatically derived leaf nodes
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:
for node in DfgVertexList:
2023-11-11 05:25:53 +01:00
# Only generate code for automatically derived leaf nodes
if (node.file is not None) or (not node.isLeaf):
continue
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')
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")
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(
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user1u().to<DfgVertex*>());\n".
format(i=i, j=i + 1))
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:
for node in DfgVertexList:
2023-11-11 05:25:53 +01:00
# Only generate code for automatically derived leaf nodes
if (node.file is not None) or (not node.isLeaf):
continue
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(
" 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))
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
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.""",
epilog="""Copyright 2002-2024 by Wilson Snyder. This program is free software; you
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""")
2021-01-16 19:24:14 +01:00
parser.add_argument('-I', action='store', help='source code include directory')
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')
2021-01-16 19:24:14 +01:00
parser.add_argument('infiles', nargs='*', help='list of input .cpp filenames')
2021-01-16 19:24:14 +01:00
Args = parser.parse_args()
###############################################################################
# Read AstNode definitions
###############################################################################
# Set up the root AstNode type. It is standalone so we don't need to parse the
# sources for this.
AstNodes["Node"] = Node("Node", None)
# Read AstNode definitions
for filename in Args.astdef:
read_types(os.path.join(Args.I, filename), AstNodes, "Ast")
# Compute derived properties over the whole AstNode hierarchy
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
vertex = Node(node.name, base)
DfgVertices[node.name] = vertex
base.addSubClass(vertex)
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"
vertex.addOp(n, name, "", "")
# Compute derived properties over the whole DfgVertex hierarchy
DfgVertices["Vertex"].complete()
DfgVertexList = tuple(map(lambda _: DfgVertices[_], sorted(DfgVertices.keys())))
check_types(DfgVertexList, "Dfg", "Vertex")
###############################################################################
# Read additional files
###############################################################################
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)
###############################################################################
# Generate output
###############################################################################
2021-01-16 19:24:14 +01:00
if Args.classes:
write_report("V3Ast__gen_report.txt")
# 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")
write_type_enum("Ast", AstNodeList)
write_type_tests("Ast", AstNodeList)
write_ast_type_info("V3Ast__gen_type_info.h")
write_ast_impl("V3Ast__gen_impl.h")
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)
Cpt().process(in_filename=Args.I + "/" + cpt + ".cpp", out_filename=cpt + "__gen.cpp")
######################################################################
# Local Variables:
# compile-command: "touch src/V3AstNodeExpr.h ; v4make"
# End: