mirror of
https://github.com/verilator/verilator.git
synced 2024-12-28 18:27:34 +00:00
Internals: Reformat with new settings (last commit). No functional change.
This commit is contained in:
parent
bde4097df2
commit
ae35be9102
@ -16,8 +16,7 @@ parser = argparse.ArgumentParser(
|
||||
|
||||
For documentation see
|
||||
https://verilator.org/guide/latest/exe_verilator_ccache_report.html""",
|
||||
epilog=
|
||||
"""Copyright 2002-2024 by Wilson Snyder. This program is free software; you
|
||||
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.
|
||||
@ -67,30 +66,23 @@ else:
|
||||
wnames = max(len(_) for _ in results) + 1
|
||||
wresults = max(len(_) for _ in results.values()) + 1
|
||||
for k in sorted(results.keys()):
|
||||
args.o.write("{:{wnames}} : {:{wresults}} : {}s\n".format(
|
||||
k,
|
||||
results[k],
|
||||
elapsed[k].total_seconds(),
|
||||
wnames=wnames,
|
||||
wresults=wresults))
|
||||
args.o.write("{:{wnames}} : {:{wresults}} : {}s\n".format(k,
|
||||
results[k],
|
||||
elapsed[k].total_seconds(),
|
||||
wnames=wnames,
|
||||
wresults=wresults))
|
||||
|
||||
args.o.write("\nSummary:\n")
|
||||
counts = collections.Counter(_ for _ in results.values())
|
||||
total = sum(counts.values())
|
||||
for k in sorted(counts.keys()):
|
||||
c = counts[k]
|
||||
args.o.write("{:{width}}| {} ({:.2%})\n".format(k,
|
||||
c,
|
||||
c / total,
|
||||
width=wresults))
|
||||
args.o.write("{:{width}}| {} ({:.2%})\n".format(k, c, c / total, width=wresults))
|
||||
|
||||
args.o.write("\nLongest:\n")
|
||||
longest = sorted(list(elapsed.items()),
|
||||
key=lambda kv: -kv[1].total_seconds())
|
||||
longest = sorted(list(elapsed.items()), key=lambda kv: -kv[1].total_seconds())
|
||||
for i, (k, v) in enumerate(longest):
|
||||
args.o.write("{:{width}}| {}s\n".format(k,
|
||||
v.total_seconds(),
|
||||
width=wnames))
|
||||
args.o.write("{:{width}}| {}s\n".format(k, v.total_seconds(), width=wnames))
|
||||
if i > 4:
|
||||
break
|
||||
|
||||
|
@ -47,8 +47,7 @@ def diff_dir(a, b):
|
||||
diff_file(a, b)
|
||||
anyfile = True
|
||||
if not anyfile:
|
||||
sys.stderr.write(
|
||||
"%Warning: No .tree files found that have similar base names\n")
|
||||
sys.stderr.write("%Warning: No .tree files found that have similar base names\n")
|
||||
|
||||
|
||||
def diff_file(a, b):
|
||||
@ -109,18 +108,14 @@ parser = argparse.ArgumentParser(
|
||||
Verilator_difftree is used for debugging Verilator tree output files.
|
||||
It performs a diff between two files, or all files common between two
|
||||
directories, ignoring irrelevant pointer differences.""",
|
||||
epilog=
|
||||
"""Copyright 2005-2024 by Wilson Snyder. This program is free software; you
|
||||
epilog="""Copyright 2005-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.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('--no-lineno',
|
||||
action='store_false',
|
||||
help='do not show differences in line numbering')
|
||||
|
@ -15,11 +15,7 @@ LongestVcdStrValueLength = 0
|
||||
Threads = collections.defaultdict(lambda: []) # List of records per thread id
|
||||
Mtasks = collections.defaultdict(lambda: {'elapsed': 0, 'end': 0})
|
||||
Cpus = collections.defaultdict(lambda: {'mtask_time': 0})
|
||||
Global = {
|
||||
'args': {},
|
||||
'cpuinfo': collections.defaultdict(lambda: {}),
|
||||
'stats': {}
|
||||
}
|
||||
Global = {'args': {}, 'cpuinfo': collections.defaultdict(lambda: {}), 'stats': {}}
|
||||
ElapsedTime = None # total elapsed time
|
||||
ExecGraphTime = 0 # total elapsed time excuting an exec graph
|
||||
ExecGraphIntervals = [] # list of (start, end) pairs
|
||||
@ -31,8 +27,7 @@ def read_data(filename):
|
||||
with open(filename, "r", encoding="utf8") as fh:
|
||||
re_thread = re.compile(r'^VLPROFTHREAD (\d+)$')
|
||||
re_record = re.compile(r'^VLPROFEXEC (\S+) (\d+)(.*)$')
|
||||
re_payload_mtaskBegin = re.compile(
|
||||
r'id (\d+) predictStart (\d+) cpu (\d+)')
|
||||
re_payload_mtaskBegin = re.compile(r'id (\d+) predictStart (\d+) cpu (\d+)')
|
||||
re_payload_mtaskEnd = re.compile(r'id (\d+) predictCost (\d+)')
|
||||
|
||||
re_arg1 = re.compile(r'VLPROF arg\s+(\S+)\+([0-9.]*)\s*')
|
||||
@ -57,8 +52,7 @@ def read_data(filename):
|
||||
tick = int(tick)
|
||||
payload = payload.strip()
|
||||
if kind == "SECTION_PUSH":
|
||||
LongestVcdStrValueLength = max(LongestVcdStrValueLength,
|
||||
len(payload))
|
||||
LongestVcdStrValueLength = max(LongestVcdStrValueLength, len(payload))
|
||||
SectionStack.append(payload)
|
||||
Sections.append((tick, tuple(SectionStack)))
|
||||
elif kind == "SECTION_POP":
|
||||
@ -66,15 +60,13 @@ def read_data(filename):
|
||||
SectionStack.pop()
|
||||
Sections.append((tick, tuple(SectionStack)))
|
||||
elif kind == "MTASK_BEGIN":
|
||||
mtask, predict_start, ecpu = re_payload_mtaskBegin.match(
|
||||
payload).groups()
|
||||
mtask, predict_start, ecpu = re_payload_mtaskBegin.match(payload).groups()
|
||||
mtask = int(mtask)
|
||||
predict_start = int(predict_start)
|
||||
ecpu = int(ecpu)
|
||||
mTaskThread[mtask] = thread
|
||||
records = Threads[thread]
|
||||
assert not records or records[-1]['start'] <= records[-1][
|
||||
'end'] <= tick
|
||||
assert not records or records[-1]['start'] <= records[-1]['end'] <= tick
|
||||
records.append({
|
||||
'start': tick,
|
||||
'mtask': mtask,
|
||||
@ -85,8 +77,7 @@ def read_data(filename):
|
||||
Mtasks[mtask]['thread'] = thread
|
||||
Mtasks[mtask]['predict_start'] = predict_start
|
||||
elif kind == "MTASK_END":
|
||||
mtask, predict_cost = re_payload_mtaskEnd.match(
|
||||
payload).groups()
|
||||
mtask, predict_cost = re_payload_mtaskEnd.match(payload).groups()
|
||||
mtask = int(mtask)
|
||||
predict_cost = int(predict_cost)
|
||||
begin = Mtasks[mtask]['begin']
|
||||
@ -163,8 +154,7 @@ def report():
|
||||
|
||||
print("\nSummary:")
|
||||
print(" Total elapsed time = {} rdtsc ticks".format(ElapsedTime))
|
||||
print(" Parallelized code = {:.2%} of elapsed time".format(
|
||||
ExecGraphTime / ElapsedTime))
|
||||
print(" Parallelized code = {:.2%} of elapsed time".format(ExecGraphTime / ElapsedTime))
|
||||
print(" Total threads = %d" % nthreads)
|
||||
print(" Total CPUs used = %d" % ncpus)
|
||||
print(" Total mtasks = %d" % len(Mtasks))
|
||||
@ -176,15 +166,12 @@ def report():
|
||||
|
||||
if nthreads > ncpus:
|
||||
print()
|
||||
print("%%Warning: There were fewer CPUs (%d) than threads (%d)." %
|
||||
(ncpus, nthreads))
|
||||
print("%%Warning: There were fewer CPUs (%d) than threads (%d)." % (ncpus, nthreads))
|
||||
print(" : See docs on use of numactl.")
|
||||
else:
|
||||
if 'cpu_socket_cores_warning' in Global:
|
||||
print()
|
||||
print(
|
||||
"%Warning: Multiple threads scheduled on same hyperthreaded core."
|
||||
)
|
||||
print("%Warning: Multiple threads scheduled on same hyperthreaded core.")
|
||||
print(" : See docs on use of numactl.")
|
||||
if 'cpu_sockets_warning' in Global:
|
||||
print()
|
||||
@ -228,8 +215,7 @@ def report_mtasks():
|
||||
serialTime = ElapsedTime - ExecGraphTime
|
||||
|
||||
def subReport(elapsed, work):
|
||||
print(" Thread utilization = {:7.2%}".format(work /
|
||||
(elapsed * nthreads)))
|
||||
print(" Thread utilization = {:7.2%}".format(work / (elapsed * nthreads)))
|
||||
print(" Speedup = {:6.3}x".format(work / elapsed))
|
||||
|
||||
print("\nParallelized code, measured:")
|
||||
@ -256,8 +242,7 @@ def report_mtasks():
|
||||
if Mtasks[mtask]['elapsed'] > 0:
|
||||
if Mtasks[mtask]['predict_cost'] == 0:
|
||||
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
|
||||
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
|
||||
Mtasks[mtask]['elapsed'])
|
||||
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] / Mtasks[mtask]['elapsed'])
|
||||
p2e_ratios.append(p2e_ratio)
|
||||
|
||||
if p2e_ratio > max_p2e:
|
||||
@ -269,18 +254,14 @@ def report_mtasks():
|
||||
|
||||
print("\nMTask statistics:")
|
||||
print(" Longest mtask id = {}".format(long_mtask))
|
||||
print(" Longest mtask time = {:.2%} of time elapsed in parallelized code".
|
||||
format(long_mtask_time / ExecGraphTime))
|
||||
print(" Longest mtask time = {:.2%} of time elapsed in parallelized code".format(
|
||||
long_mtask_time / ExecGraphTime))
|
||||
print(" min log(p2e) = %0.3f" % min_p2e, end="")
|
||||
|
||||
print(" from mtask %d (predict %d," %
|
||||
(min_mtask, Mtasks[min_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" from mtask %d (predict %d," % (min_mtask, Mtasks[min_mtask]['predict_cost']), end="")
|
||||
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
|
||||
print(" max log(p2e) = %0.3f" % max_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(max_mtask, Mtasks[max_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" from mtask %d (predict %d," % (max_mtask, Mtasks[max_mtask]['predict_cost']), end="")
|
||||
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
|
||||
|
||||
stddev = statistics.pstdev(p2e_ratios)
|
||||
@ -315,8 +296,8 @@ def report_cpus():
|
||||
model = cpuinfo['model_name']
|
||||
|
||||
print(" {:3d} | {:7.2%} / {:16d} | {:>6s} | {:>4s} | {}".format(
|
||||
cpu, Cpus[cpu]['mtask_time'] / ElapsedTime,
|
||||
Cpus[cpu]['mtask_time'], socket, core, model))
|
||||
cpu, Cpus[cpu]['mtask_time'] / ElapsedTime, Cpus[cpu]['mtask_time'], socket, core,
|
||||
model))
|
||||
|
||||
if len(Global['cpu_sockets']) > 1:
|
||||
Global['cpu_sockets_warning'] = True
|
||||
@ -366,8 +347,8 @@ def report_sections():
|
||||
|
||||
def printTree(prefix, name, entries, tree):
|
||||
print(" {:7.2%} | {:7.2%} | {:8} | {:10.2f} | {}".format(
|
||||
treeSum(tree) / ElapsedTime, tree[0] / ElapsedTime, tree[2],
|
||||
tree[2] / entries, prefix + name))
|
||||
treeSum(tree) / ElapsedTime, tree[0] / ElapsedTime, tree[2], tree[2] / entries,
|
||||
prefix + name))
|
||||
for k in sorted(tree[1], key=lambda _: -treeSum(tree[1][_])):
|
||||
printTree(prefix + " ", k, tree[2], tree[1][k])
|
||||
|
||||
@ -438,10 +419,8 @@ def write_vcd(filename):
|
||||
addValue(code, start, mtask)
|
||||
addValue(code, end, None)
|
||||
|
||||
tStart = sorted(_['start'] for records in Threads.values()
|
||||
for _ in records)
|
||||
tEnd = sorted(_['end'] for records in Threads.values()
|
||||
for _ in records)
|
||||
tStart = sorted(_['start'] for records in Threads.values() for _ in records)
|
||||
tEnd = sorted(_['end'] for records in Threads.values() for _ in records)
|
||||
|
||||
# Predicted graph
|
||||
for start, end in ExecGraphIntervals:
|
||||
@ -455,11 +434,10 @@ def write_vcd(filename):
|
||||
# Predict mtasks that fill the time the execution occupied
|
||||
for mtask in Mtasks:
|
||||
thread = Mtasks[mtask]['thread']
|
||||
pred_scaled_start = start + int(
|
||||
Mtasks[mtask]['predict_start'] * measured_scaling)
|
||||
pred_scaled_start = start + int(Mtasks[mtask]['predict_start'] * measured_scaling)
|
||||
pred_scaled_end = start + int(
|
||||
(Mtasks[mtask]['predict_start'] +
|
||||
Mtasks[mtask]['predict_cost']) * measured_scaling)
|
||||
(Mtasks[mtask]['predict_start'] + Mtasks[mtask]['predict_cost']) *
|
||||
measured_scaling)
|
||||
if pred_scaled_start == pred_scaled_end:
|
||||
continue
|
||||
|
||||
@ -545,8 +523,7 @@ Verilator_gantt creates a visual representation to help analyze Verilator
|
||||
|
||||
For documentation see
|
||||
https://verilator.org/guide/latest/exe_verilator_gantt.html""",
|
||||
epilog=
|
||||
"""Copyright 2018-2024 by Wilson Snyder. This program is free software; you
|
||||
epilog="""Copyright 2018-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.
|
||||
@ -554,12 +531,8 @@ Version 2.0.
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--debug', action='store_true', help='enable debug')
|
||||
parser.add_argument('--no-vcd',
|
||||
help='disable creating vcd',
|
||||
action='store_true')
|
||||
parser.add_argument('--vcd',
|
||||
help='filename for vcd outpue',
|
||||
default='profile_exec.vcd')
|
||||
parser.add_argument('--no-vcd', help='disable creating vcd', action='store_true')
|
||||
parser.add_argument('--vcd', help='filename for vcd outpue', default='profile_exec.vcd')
|
||||
parser.add_argument('filename',
|
||||
help='input profile_exec.dat filename to process',
|
||||
default='profile_exec.dat')
|
||||
|
@ -34,9 +34,8 @@ def profcfunc(filename):
|
||||
|
||||
# Older gprofs have no call column for single-call functions
|
||||
# %time cumesec selfsec {stuff} name
|
||||
match = re.match(
|
||||
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
||||
line)
|
||||
match = re.match(r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
||||
line)
|
||||
if match:
|
||||
pct = float(match.group(1))
|
||||
sec = float(match.group(2))
|
||||
@ -143,12 +142,9 @@ def profcfunc(filename):
|
||||
print(" These are split into three categories:")
|
||||
print(" C++: Time in non-Verilated C++ code")
|
||||
print(" Prof: Time in profile overhead")
|
||||
print(" VBlock: Time attributable to a block in a" +
|
||||
" Verilog file and line")
|
||||
print(" VCommon: Time in a Verilated module," +
|
||||
" due to all parts of the design")
|
||||
print(" VLib: Time in Verilated common libraries," +
|
||||
" called by the Verilated code")
|
||||
print(" VBlock: Time attributable to a block in a" + " Verilog file and line")
|
||||
print(" VCommon: Time in a Verilated module," + " due to all parts of the design")
|
||||
print(" VLib: Time in Verilated common libraries," + " called by the Verilated code")
|
||||
print()
|
||||
|
||||
print(" % cumulative self ")
|
||||
@ -156,13 +152,11 @@ def profcfunc(filename):
|
||||
"s type filename and line number") % "design")
|
||||
|
||||
cume = 0
|
||||
for func in sorted(vfuncs.keys(),
|
||||
key=lambda f: vfuncs[f]['sec'],
|
||||
reverse=True):
|
||||
for func in sorted(vfuncs.keys(), key=lambda f: vfuncs[f]['sec'], reverse=True):
|
||||
cume += vfuncs[func]['sec']
|
||||
print(("%6.2f %9.2f %8.2f %10d %-" + str(design_width) + "s %s") %
|
||||
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'],
|
||||
vfuncs[func]['calls'], vfuncs[func]['design'], func))
|
||||
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'], vfuncs[func]['calls'],
|
||||
vfuncs[func]['design'], func))
|
||||
|
||||
|
||||
######################################################################
|
||||
@ -180,18 +174,14 @@ in each Verilog block.
|
||||
|
||||
For documentation see
|
||||
https://verilator.org/guide/latest/exe_verilator_profcfunc.html""",
|
||||
epilog=
|
||||
"""Copyright 2002-2024 by Wilson Snyder. This program is free software; you
|
||||
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.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('filename', help='input gprof output to process')
|
||||
|
||||
Args = parser.parse_args()
|
||||
|
@ -23,9 +23,7 @@ class VlSphinxExtract:
|
||||
outname = match.group(1)
|
||||
print("Writing %s" % outname)
|
||||
fhw = open(outname, "w", encoding="utf8") # pylint: disable=consider-using-with
|
||||
fhw.write(
|
||||
".. comment: generated by vl_sphinx_extract from " +
|
||||
filename + "\n")
|
||||
fhw.write(".. comment: generated by vl_sphinx_extract from " + filename + "\n")
|
||||
fhw.write(".. code-block::\n")
|
||||
elif re.match(r'^[=a-zA-Z0-9_]', line):
|
||||
fhw = None
|
||||
@ -39,18 +37,14 @@ parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""Read a file and extract documentation data.""",
|
||||
epilog=
|
||||
""" Copyright 2021-2024 by Wilson Snyder. This package is free software;
|
||||
epilog=""" Copyright 2021-2024 by Wilson Snyder. This package 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.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('path', help='path to extract from')
|
||||
Args = parser.parse_args()
|
||||
|
||||
|
@ -18,8 +18,7 @@ class VlSphinxFix:
|
||||
if os.path.isdir(path):
|
||||
for basefile in os.listdir(path):
|
||||
file = os.path.join(path, basefile)
|
||||
if ((basefile != ".") and (basefile != "..")
|
||||
and basefile not in self.SkipBasenames
|
||||
if ((basefile != ".") and (basefile != "..") and basefile not in self.SkipBasenames
|
||||
and not os.path.islink(file)):
|
||||
self.process(file)
|
||||
elif re.search(r'\.(html|tex)$', path):
|
||||
@ -54,18 +53,14 @@ parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""Post-process Sphinx HTML.""",
|
||||
epilog=
|
||||
""" Copyright 2021-2024 by Wilson Snyder. This package is free software;
|
||||
epilog=""" Copyright 2021-2024 by Wilson Snyder. This package 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.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('path', help='path to edit')
|
||||
Args = parser.parse_args()
|
||||
|
||||
|
@ -23,8 +23,7 @@ def get_vlt_version():
|
||||
filename = "../../Makefile"
|
||||
with open(filename, "r", encoding="utf8") as fh:
|
||||
for line in fh:
|
||||
match = re.search(r"PACKAGE_VERSION *= *([a-z0-9.]+) +([-0-9]+)",
|
||||
line)
|
||||
match = re.search(r"PACKAGE_VERSION *= *([a-z0-9.]+) +([-0-9]+)", line)
|
||||
if match:
|
||||
return match.group(1), match.group(2)
|
||||
match = re.search(r"PACKAGE_VERSION *= *([a-z0-9.]+) +devel", line)
|
||||
@ -75,8 +74,7 @@ extensions = []
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = [
|
||||
'_build', 'Thumbs.db', '.DS_Store', 'internals.rst', 'xml.rst', 'gen/ex_*',
|
||||
'CONTRIBUTING.rst'
|
||||
'_build', 'Thumbs.db', '.DS_Store', 'internals.rst', 'xml.rst', 'gen/ex_*', 'CONTRIBUTING.rst'
|
||||
]
|
||||
|
||||
# Warn about refs
|
||||
|
@ -25,8 +25,7 @@ class VlFileCopy:
|
||||
|
||||
self.debug = debug
|
||||
|
||||
with NamedTemporaryFile() as tree_temp, NamedTemporaryFile(
|
||||
) as meta_temp:
|
||||
with NamedTemporaryFile() as tree_temp, NamedTemporaryFile() as meta_temp:
|
||||
vargs = [
|
||||
'--json-only-output',
|
||||
tree_temp.name,
|
||||
@ -61,8 +60,7 @@ class VlFileCopy:
|
||||
print("\t%s " % command)
|
||||
status = subprocess.call(command, shell=True)
|
||||
if status != 0:
|
||||
raise RuntimeError("Command failed running Verilator with '" +
|
||||
command + "', stopped")
|
||||
raise RuntimeError("Command failed running Verilator with '" + command + "', stopped")
|
||||
|
||||
|
||||
#######################################################################
|
||||
@ -71,8 +69,7 @@ if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
description=
|
||||
"""Example of using Verilator JSON output to copy a list of files to an
|
||||
description="""Example of using Verilator JSON output to copy a list of files to an
|
||||
output directory (-odir, defaults to 'copied'), e.g. to easily create a
|
||||
tarball of the design to pass to others.
|
||||
|
||||
@ -95,11 +92,7 @@ This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
any use, without warranty, 2019 by Wilson Snyder.
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
""")
|
||||
parser.add_argument('-debug',
|
||||
'--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('-debug', '--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('-odir',
|
||||
'--odir',
|
||||
action='store',
|
||||
@ -108,9 +101,7 @@ SPDX-License-Identifier: CC0-1.0
|
||||
help='target output directory')
|
||||
(args, rem) = parser.parse_known_args()
|
||||
|
||||
print(
|
||||
"NOTE: vl_file_copy is only an example starting point for writing your own tool."
|
||||
)
|
||||
print("NOTE: vl_file_copy is only an example starting point for writing your own tool.")
|
||||
# That is:
|
||||
# 1. We will accept basic patches
|
||||
# 2. We are not expecting to make this globally useful. (e.g. we don't cleanup obj_dir)
|
||||
|
@ -24,8 +24,7 @@ class VlHierGraph:
|
||||
self.next_vertex_number = 0
|
||||
self.addr_to_number = {}
|
||||
|
||||
with NamedTemporaryFile() as tree_temp, NamedTemporaryFile(
|
||||
) as meta_temp:
|
||||
with NamedTemporaryFile() as tree_temp, NamedTemporaryFile() as meta_temp:
|
||||
vargs = [
|
||||
'--json-only-output',
|
||||
tree_temp.name,
|
||||
@ -45,9 +44,7 @@ class VlHierGraph:
|
||||
fh.write("digraph {\n")
|
||||
fh.write(" dpi=300;\n")
|
||||
fh.write(" order=LR;\n")
|
||||
fh.write(
|
||||
" node [fontsize=8 shape=\"box\" margin=0.01 width=0 height=0]"
|
||||
)
|
||||
fh.write(" node [fontsize=8 shape=\"box\" margin=0.01 width=0 height=0]")
|
||||
fh.write(" edge [fontsize=6]")
|
||||
# Find cells
|
||||
modules = self.flatten(self.tree, lambda n: n['type'] == "MODULE")
|
||||
@ -101,8 +98,7 @@ class VlHierGraph:
|
||||
print("\t%s " % command)
|
||||
status = subprocess.call(command, shell=True)
|
||||
if status != 0:
|
||||
raise RuntimeError("Command failed running Verilator with '" +
|
||||
command + "', stopped")
|
||||
raise RuntimeError("Command failed running Verilator with '" + command + "', stopped")
|
||||
|
||||
|
||||
#######################################################################
|
||||
@ -111,8 +107,7 @@ if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
description=
|
||||
"""Example of using Verilator JSON output to create a .dot file showing the
|
||||
description="""Example of using Verilator JSON output to create a .dot file showing the
|
||||
design module hierarchy.
|
||||
|
||||
Example usage:
|
||||
@ -134,11 +129,7 @@ This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
any use, without warranty, 2019 by Wilson Snyder.
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
""")
|
||||
parser.add_argument('-debug',
|
||||
'--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('-debug', '--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('-o',
|
||||
'--o',
|
||||
action='store',
|
||||
@ -147,18 +138,14 @@ SPDX-License-Identifier: CC0-1.0
|
||||
help='output filename')
|
||||
(args, rem) = parser.parse_known_args()
|
||||
|
||||
print(
|
||||
"NOTE: vl_hier_graph is only an example starting point for writing your own tool."
|
||||
)
|
||||
print("NOTE: vl_hier_graph is only an example starting point for writing your own tool.")
|
||||
# That is:
|
||||
# 1. We will accept basic patches
|
||||
# 2. We are not expecting to make this globally useful. (e.g. we don't cleanup obj_dir)
|
||||
# 3. "make install" will not install this.
|
||||
# 4. This has not had production-worthy validation.
|
||||
|
||||
fc = VlHierGraph(output_filename=args.o,
|
||||
debug=args.debug,
|
||||
verilator_args=rem)
|
||||
fc = VlHierGraph(output_filename=args.o, debug=args.debug, verilator_args=rem)
|
||||
|
||||
######################################################################
|
||||
# Local Variables:
|
||||
|
@ -106,9 +106,8 @@ class VlAnnotations:
|
||||
|
||||
def is_mt_safe_call(self):
|
||||
return (not self.is_mt_unsafe_call()
|
||||
and (self.mt_safe or self.mt_safe_postinit or self.pure
|
||||
or self.requires or self.excludes or self.acquire
|
||||
or self.release))
|
||||
and (self.mt_safe or self.mt_safe_postinit or self.pure or self.requires
|
||||
or self.excludes or self.acquire or self.release))
|
||||
|
||||
def is_pure_call(self):
|
||||
return self.pure
|
||||
@ -203,9 +202,7 @@ class FunctionInfo:
|
||||
annotations: VlAnnotations
|
||||
ftype: FunctionType
|
||||
|
||||
_hash: Optional[int] = dataclasses.field(default=None,
|
||||
init=False,
|
||||
repr=False)
|
||||
_hash: Optional[int] = dataclasses.field(default=None, init=False, repr=False)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -220,15 +217,13 @@ class FunctionInfo:
|
||||
return self._hash
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.usr == other.usr and self.file == other.file
|
||||
and self.line == other.line)
|
||||
return (self.usr == other.usr and self.file == other.file and self.line == other.line)
|
||||
|
||||
def copy(self, /, **changes):
|
||||
return dataclasses.replace(self, **changes)
|
||||
|
||||
@staticmethod
|
||||
def from_decl_file_line_and_refd_node(file: str, line: int,
|
||||
refd: clang.cindex.Cursor,
|
||||
def from_decl_file_line_and_refd_node(file: str, line: int, refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
file = os.path.abspath(file)
|
||||
refd = refd.canonical
|
||||
@ -277,14 +272,11 @@ class Diagnostic:
|
||||
source_ctx: FunctionInfo
|
||||
kind: DiagnosticKind
|
||||
|
||||
_hash: Optional[int] = dataclasses.field(default=None,
|
||||
init=False,
|
||||
repr=False)
|
||||
_hash: Optional[int] = dataclasses.field(default=None, init=False, repr=False)
|
||||
|
||||
def __hash__(self):
|
||||
if not self._hash:
|
||||
self._hash = hash(
|
||||
hash(self.target) ^ hash(self.source_ctx) ^ hash(self.kind))
|
||||
self._hash = hash(hash(self.target) ^ hash(self.source_ctx) ^ hash(self.kind))
|
||||
return self._hash
|
||||
|
||||
|
||||
@ -292,9 +284,9 @@ class CallAnnotationsValidator:
|
||||
|
||||
def __init__(self, diagnostic_cb: Callable[[Diagnostic], None],
|
||||
is_ignored_top_level: Callable[[clang.cindex.Cursor], bool],
|
||||
is_ignored_def: Callable[
|
||||
[clang.cindex.Cursor, clang.cindex.Cursor], bool],
|
||||
is_ignored_call: Callable[[clang.cindex.Cursor], bool]):
|
||||
is_ignored_def: Callable[[clang.cindex.Cursor, clang.cindex.Cursor],
|
||||
bool], is_ignored_call: Callable[[clang.cindex.Cursor],
|
||||
bool]):
|
||||
self._diagnostic_cb = diagnostic_cb
|
||||
self._is_ignored_top_level = is_ignored_top_level
|
||||
self._is_ignored_call = is_ignored_call
|
||||
@ -329,8 +321,7 @@ class CallAnnotationsValidator:
|
||||
with open(source_file, "r", encoding="utf-8") as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
match = re.fullmatch(
|
||||
r"^#\s*(define\s+(\w+)(?:\s+(.*))?|include\s+.*)$", line)
|
||||
match = re.fullmatch(r"^#\s*(define\s+(\w+)(?:\s+(.*))?|include\s+.*)$", line)
|
||||
if match:
|
||||
if match.group(1).startswith("define"):
|
||||
key = match.group(2)
|
||||
@ -341,16 +332,14 @@ class CallAnnotationsValidator:
|
||||
return defs
|
||||
|
||||
@staticmethod
|
||||
def filter_out_unsupported_compiler_args(
|
||||
args: list[str]) -> tuple[list[str], dict[str, str]]:
|
||||
def filter_out_unsupported_compiler_args(args: list[str]) -> tuple[list[str], dict[str, str]]:
|
||||
filtered_args = []
|
||||
defines = {}
|
||||
args_iter = iter(args)
|
||||
try:
|
||||
while arg := next(args_iter):
|
||||
# Skip positional arguments (input file name).
|
||||
if not arg.startswith("-") and (arg.endswith(".cpp")
|
||||
or arg.endswith(".c")
|
||||
if not arg.startswith("-") and (arg.endswith(".cpp") or arg.endswith(".c")
|
||||
or arg.endswith(".h")):
|
||||
continue
|
||||
|
||||
@ -367,8 +356,7 @@ class CallAnnotationsValidator:
|
||||
# Preserved options with separate value argument.
|
||||
if arg in [
|
||||
"-x"
|
||||
"-Xclang", "-I", "-isystem", "-iquote", "-include",
|
||||
"-include-pch"
|
||||
"-Xclang", "-I", "-isystem", "-iquote", "-include", "-include-pch"
|
||||
]:
|
||||
filtered_args += [arg, next(args_iter)]
|
||||
continue
|
||||
@ -406,14 +394,12 @@ class CallAnnotationsValidator:
|
||||
|
||||
return (filtered_args, defines)
|
||||
|
||||
def compile_and_analyze_file(self, source_file: str,
|
||||
compiler_args: list[str],
|
||||
def compile_and_analyze_file(self, source_file: str, compiler_args: list[str],
|
||||
build_dir: Optional[str]):
|
||||
filename = os.path.abspath(source_file)
|
||||
initial_cwd = "."
|
||||
|
||||
filtered_args, defines = self.filter_out_unsupported_compiler_args(
|
||||
compiler_args)
|
||||
filtered_args, defines = self.filter_out_unsupported_compiler_args(compiler_args)
|
||||
defines.update(self.parse_initial_defines(source_file))
|
||||
|
||||
if build_dir:
|
||||
@ -451,8 +437,7 @@ class CallAnnotationsValidator:
|
||||
self._diagnostic_cb(Diagnostic(target, source, source_ctx, kind))
|
||||
else:
|
||||
self._diagnostic_cb(
|
||||
Diagnostic(FunctionInfo.from_node(target), source, source_ctx,
|
||||
kind))
|
||||
Diagnostic(FunctionInfo.from_node(target), source, source_ctx, kind))
|
||||
|
||||
def iterate_children(self, children: Iterable[clang.cindex.Cursor],
|
||||
handler: Callable[[clang.cindex.Cursor], None]):
|
||||
@ -465,8 +450,7 @@ class CallAnnotationsValidator:
|
||||
@staticmethod
|
||||
def get_referenced_node_info(
|
||||
node: clang.cindex.Cursor
|
||||
) -> tuple[bool, Optional[clang.cindex.Cursor], VlAnnotations,
|
||||
Iterable[clang.cindex.Cursor]]:
|
||||
) -> tuple[bool, Optional[clang.cindex.Cursor], VlAnnotations, Iterable[clang.cindex.Cursor]]:
|
||||
if not node.spelling and not node.displayname:
|
||||
return (False, None, VlAnnotations(), [])
|
||||
|
||||
@ -480,8 +464,7 @@ class CallAnnotationsValidator:
|
||||
annotations = VlAnnotations.from_nodes_list(children)
|
||||
return (True, refd, annotations, children)
|
||||
|
||||
def check_mt_safe_call(self, node: clang.cindex.Cursor,
|
||||
refd: clang.cindex.Cursor,
|
||||
def check_mt_safe_call(self, node: clang.cindex.Cursor, refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
is_mt_safe = False
|
||||
|
||||
@ -513,8 +496,7 @@ class CallAnnotationsValidator:
|
||||
# we are calling local method. It is MT safe
|
||||
# only if this method is also only calling local methods or
|
||||
# MT-safe methods
|
||||
self.iterate_children(refd.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
self.iterate_children(refd.get_children(), self.dispatch_node_inside_definition)
|
||||
is_mt_safe = True
|
||||
# class/struct member
|
||||
elif refn and refn.kind == CursorKind.MEMBER_REF_EXPR and refn.referenced:
|
||||
@ -525,18 +507,15 @@ class CallAnnotationsValidator:
|
||||
if self.is_constructor_context() and refn.semantic_parent:
|
||||
# we are in constructor, so calling local members is MT_SAFE,
|
||||
# make sure object that we are calling is local to the constructor
|
||||
constructor_class = self._constructor_context[
|
||||
-1].semantic_parent
|
||||
constructor_class = self._constructor_context[-1].semantic_parent
|
||||
if refn.semantic_parent.spelling == constructor_class.spelling:
|
||||
if check_class_member_exists(constructor_class, refn):
|
||||
is_mt_safe = True
|
||||
else:
|
||||
# check if this class inherits from some base class
|
||||
base_class = get_base_class(constructor_class,
|
||||
refn.semantic_parent)
|
||||
base_class = get_base_class(constructor_class, refn.semantic_parent)
|
||||
if base_class:
|
||||
if check_class_member_exists(
|
||||
base_class.get_declaration(), refn):
|
||||
if check_class_member_exists(base_class.get_declaration(), refn):
|
||||
is_mt_safe = True
|
||||
# variable
|
||||
elif refn and refn.kind == CursorKind.DECL_REF_EXPR and refn.referenced:
|
||||
@ -567,8 +546,7 @@ class CallAnnotationsValidator:
|
||||
|
||||
# Call handling
|
||||
|
||||
def process_method_call(self, node: clang.cindex.Cursor,
|
||||
refd: clang.cindex.Cursor,
|
||||
def process_method_call(self, node: clang.cindex.Cursor, refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
assert self._call_location
|
||||
ctx = self._call_location.annotations
|
||||
@ -576,58 +554,48 @@ class CallAnnotationsValidator:
|
||||
# MT-safe context
|
||||
if ctx.is_mt_safe_context():
|
||||
if not self.check_mt_safe_call(node, refd, annotations):
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX)
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX)
|
||||
|
||||
# stable tree context
|
||||
if ctx.is_stabe_tree_context():
|
||||
if annotations.is_mt_unsafe_call() or not (
|
||||
annotations.is_stabe_tree_call()
|
||||
or annotations.is_pure_call()
|
||||
annotations.is_stabe_tree_call() or annotations.is_pure_call()
|
||||
or self.check_mt_safe_call(node, refd, annotations)):
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX)
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX)
|
||||
|
||||
# pure context
|
||||
if ctx.is_pure_context():
|
||||
if not annotations.is_pure_call():
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX)
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX)
|
||||
|
||||
def process_function_call(self, refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
def process_function_call(self, refd: clang.cindex.Cursor, annotations: VlAnnotations):
|
||||
assert self._call_location
|
||||
ctx = self._call_location.annotations
|
||||
|
||||
# MT-safe context
|
||||
if ctx.is_mt_safe_context():
|
||||
if not annotations.is_mt_safe_call():
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX)
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX)
|
||||
|
||||
# stable tree context
|
||||
if ctx.is_stabe_tree_context():
|
||||
if annotations.is_mt_unsafe_call() or not (
|
||||
annotations.is_pure_call()
|
||||
or annotations.is_mt_safe_call()
|
||||
or annotations.is_stabe_tree_call()):
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX)
|
||||
if annotations.is_mt_unsafe_call() or not (annotations.is_pure_call()
|
||||
or annotations.is_mt_safe_call()
|
||||
or annotations.is_stabe_tree_call()):
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX)
|
||||
|
||||
# pure context
|
||||
if ctx.is_pure_context():
|
||||
if not annotations.is_pure_call():
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX)
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX)
|
||||
|
||||
def process_constructor_call(self, refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
def process_constructor_call(self, refd: clang.cindex.Cursor, annotations: VlAnnotations):
|
||||
assert self._call_location
|
||||
ctx = self._call_location.annotations
|
||||
|
||||
@ -635,31 +603,26 @@ class CallAnnotationsValidator:
|
||||
# only if they call local methods or MT-safe functions.
|
||||
if ctx.is_mt_safe_context() or self.is_constructor_context():
|
||||
self._constructor_context.append(refd)
|
||||
self.iterate_children(refd.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
self.iterate_children(refd.get_children(), self.dispatch_node_inside_definition)
|
||||
self._constructor_context.pop()
|
||||
|
||||
# stable tree context
|
||||
if ctx.is_stabe_tree_context():
|
||||
self._constructor_context.append(refd)
|
||||
self.iterate_children(refd.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
self.iterate_children(refd.get_children(), self.dispatch_node_inside_definition)
|
||||
self._constructor_context.pop()
|
||||
|
||||
# pure context
|
||||
if ctx.is_pure_context():
|
||||
if not annotations.is_pure_call(
|
||||
) and not refd.is_default_constructor():
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX)
|
||||
if not annotations.is_pure_call() and not refd.is_default_constructor():
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX)
|
||||
|
||||
def dispatch_call_node(self, node: clang.cindex.Cursor):
|
||||
[supported, refd, annotations, _] = self.get_referenced_node_info(node)
|
||||
|
||||
if not supported:
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
self.iterate_children(node.get_children(), self.dispatch_node_inside_definition)
|
||||
return True
|
||||
|
||||
assert refd is not None
|
||||
@ -676,19 +639,14 @@ class CallAnnotationsValidator:
|
||||
|
||||
assert self._call_location is not None
|
||||
node_file = os.path.abspath(node.location.file.name)
|
||||
self._call_location = self._call_location.copy(file=node_file,
|
||||
line=node.location.line)
|
||||
self._call_location = self._call_location.copy(file=node_file, line=node.location.line)
|
||||
|
||||
# Standalone functions and static class methods
|
||||
if (refd.kind == CursorKind.FUNCTION_DECL
|
||||
or refd.kind == CursorKind.CXX_METHOD
|
||||
and refd.is_static_method()):
|
||||
or refd.kind == CursorKind.CXX_METHOD and refd.is_static_method()):
|
||||
self.process_function_call(refd, annotations)
|
||||
# Function pointer
|
||||
elif refd.kind in [
|
||||
CursorKind.VAR_DECL, CursorKind.FIELD_DECL,
|
||||
CursorKind.PARM_DECL
|
||||
]:
|
||||
elif refd.kind in [CursorKind.VAR_DECL, CursorKind.FIELD_DECL, CursorKind.PARM_DECL]:
|
||||
self.process_function_call(refd, annotations)
|
||||
# Non-static class methods
|
||||
elif refd.kind == CursorKind.CXX_METHOD:
|
||||
@ -726,14 +684,13 @@ class CallAnnotationsValidator:
|
||||
if self.dispatch_call_node(node) is False:
|
||||
return None
|
||||
elif node.is_definition() and node.kind in [
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
|
||||
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, CursorKind.CONSTRUCTOR,
|
||||
CursorKind.CONVERSION_FUNCTION
|
||||
]:
|
||||
self.process_function_definition(node)
|
||||
return None
|
||||
|
||||
return self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node_inside_definition)
|
||||
|
||||
def process_function_definition(self, node: clang.cindex.Cursor):
|
||||
[supported, refd, annotations, _] = self.get_referenced_node_info(node)
|
||||
@ -768,14 +725,12 @@ class CallAnnotationsValidator:
|
||||
self._caller = FunctionInfo.from_node(node, refd, def_annotations)
|
||||
self._call_location = self._caller
|
||||
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
|
||||
self.emit_diagnostic(FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
|
||||
|
||||
# Use concatenation of definition and declaration annotations
|
||||
# for calls validation.
|
||||
self._caller = FunctionInfo.from_node(node, refd,
|
||||
def_annotations | annotations)
|
||||
self._caller = FunctionInfo.from_node(node, refd, def_annotations | annotations)
|
||||
prev_call_location = self._call_location
|
||||
self._call_location = self._caller
|
||||
|
||||
@ -793,8 +748,7 @@ class CallAnnotationsValidator:
|
||||
if declarations:
|
||||
del self._external_decls[usr]
|
||||
|
||||
self.iterate_children(node_children,
|
||||
self.dispatch_node_inside_definition)
|
||||
self.iterate_children(node_children, self.dispatch_node_inside_definition)
|
||||
|
||||
self._call_location = prev_call_location
|
||||
self._caller = prev_call_location
|
||||
@ -805,8 +759,8 @@ class CallAnnotationsValidator:
|
||||
|
||||
def dispatch_node(self, node: clang.cindex.Cursor):
|
||||
if node.kind in [
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
|
||||
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, CursorKind.CONSTRUCTOR,
|
||||
CursorKind.CONVERSION_FUNCTION
|
||||
]:
|
||||
if node.is_definition():
|
||||
return self.process_function_definition(node)
|
||||
@ -815,14 +769,12 @@ class CallAnnotationsValidator:
|
||||
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node)
|
||||
|
||||
def process_translation_unit(
|
||||
self, translation_unit: clang.cindex.TranslationUnit):
|
||||
def process_translation_unit(self, translation_unit: clang.cindex.TranslationUnit):
|
||||
self._level += 1
|
||||
kv_defines = sorted([f"{k}={v}" for k, v in self._defines.items()])
|
||||
concat_defines = '\n'.join(kv_defines)
|
||||
# List of headers already processed in a TU with specified set of defines.
|
||||
tu_processed_headers = self._processed_headers.setdefault(
|
||||
concat_defines, set())
|
||||
tu_processed_headers = self._processed_headers.setdefault(concat_defines, set())
|
||||
for child in translation_unit.cursor.get_children():
|
||||
if self._is_ignored_top_level(child):
|
||||
continue
|
||||
@ -833,10 +785,8 @@ class CallAnnotationsValidator:
|
||||
self.dispatch_node(child)
|
||||
self._level -= 1
|
||||
|
||||
tu_processed_headers.update([
|
||||
os.path.abspath(str(hdr.source))
|
||||
for hdr in translation_unit.get_includes()
|
||||
])
|
||||
tu_processed_headers.update(
|
||||
[os.path.abspath(str(hdr.source)) for hdr in translation_unit.get_includes()])
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -857,8 +807,7 @@ def get_filter_funcs(verilator_root: str):
|
||||
filename = os.path.abspath(node.location.file.name)
|
||||
return not filename.startswith(verilator_root)
|
||||
|
||||
def is_ignored_def(node: clang.cindex.Cursor,
|
||||
refd: clang.cindex.Cursor) -> bool:
|
||||
def is_ignored_def(node: clang.cindex.Cursor, refd: clang.cindex.Cursor) -> bool:
|
||||
# __*
|
||||
if str(refd.spelling).startswith("__"):
|
||||
return True
|
||||
@ -901,8 +850,7 @@ def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
|
||||
os.chdir(compile_command.directory)
|
||||
|
||||
index = Index.create()
|
||||
translation_unit = index.parse(compile_command.filename,
|
||||
compile_command.args)
|
||||
translation_unit = index.parse(compile_command.filename, compile_command.args)
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
||||
errors.append(str(diag))
|
||||
@ -910,23 +858,20 @@ def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
|
||||
if len(errors) == 0:
|
||||
pch_file = os.path.join(
|
||||
tmp_dir,
|
||||
f"{compile_command.refid:02}_{os.path.basename(compile_command.filename)}.pch"
|
||||
)
|
||||
f"{compile_command.refid:02}_{os.path.basename(compile_command.filename)}.pch")
|
||||
translation_unit.save(pch_file)
|
||||
|
||||
if pch_file:
|
||||
return pch_file
|
||||
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError,
|
||||
OSError) as exception:
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError, OSError) as exception:
|
||||
print(f"%Warning: {exception}", file=sys.stderr)
|
||||
|
||||
finally:
|
||||
os.chdir(initial_cwd)
|
||||
|
||||
print(
|
||||
f"%Warning: Precompilation failed, skipping: {compile_command.filename}",
|
||||
file=sys.stderr)
|
||||
print(f"%Warning: Precompilation failed, skipping: {compile_command.filename}",
|
||||
file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
return ""
|
||||
@ -934,10 +879,8 @@ def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
|
||||
|
||||
# Compile and analyze inputs in a single process.
|
||||
def run_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
|
||||
diagnostic_cb: Callable[[Diagnostic],
|
||||
None], verilator_root: str):
|
||||
(is_ignored_top_level, is_ignored_def,
|
||||
is_ignored_call) = get_filter_funcs(verilator_root)
|
||||
diagnostic_cb: Callable[[Diagnostic], None], verilator_root: str):
|
||||
(is_ignored_top_level, is_ignored_def, is_ignored_call) = get_filter_funcs(verilator_root)
|
||||
|
||||
prefix = "verilator_clang_check_attributes_"
|
||||
with tempfile.TemporaryDirectory(prefix=prefix) as tmp_dir:
|
||||
@ -947,8 +890,8 @@ def run_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
|
||||
if pch_file:
|
||||
extra_args += ["-include-pch", pch_file]
|
||||
|
||||
cav = CallAnnotationsValidator(diagnostic_cb, is_ignored_top_level,
|
||||
is_ignored_def, is_ignored_call)
|
||||
cav = CallAnnotationsValidator(diagnostic_cb, is_ignored_top_level, is_ignored_def,
|
||||
is_ignored_call)
|
||||
for compile_command in ccl:
|
||||
cav.compile_and_analyze_file(compile_command.filename,
|
||||
extra_args + compile_command.args,
|
||||
@ -963,12 +906,11 @@ class ParallelAnalysisProcess:
|
||||
|
||||
@staticmethod
|
||||
def init_data(verilator_root: str, tmp_dir: str):
|
||||
(is_ignored_top_level, is_ignored_def,
|
||||
is_ignored_call) = get_filter_funcs(verilator_root)
|
||||
(is_ignored_top_level, is_ignored_def, is_ignored_call) = get_filter_funcs(verilator_root)
|
||||
|
||||
ParallelAnalysisProcess.cav = CallAnnotationsValidator(
|
||||
ParallelAnalysisProcess._diagnostic_handler, is_ignored_top_level,
|
||||
is_ignored_def, is_ignored_call)
|
||||
ParallelAnalysisProcess._diagnostic_handler, is_ignored_top_level, is_ignored_def,
|
||||
is_ignored_call)
|
||||
ParallelAnalysisProcess.tmp_dir = tmp_dir
|
||||
|
||||
@staticmethod
|
||||
@ -979,31 +921,27 @@ class ParallelAnalysisProcess:
|
||||
def analyze_cpp_file(compile_command: CompileCommand) -> set[Diagnostic]:
|
||||
ParallelAnalysisProcess.diags = set()
|
||||
assert ParallelAnalysisProcess.cav is not None
|
||||
ParallelAnalysisProcess.cav.compile_and_analyze_file(
|
||||
compile_command.filename, compile_command.args,
|
||||
compile_command.directory)
|
||||
ParallelAnalysisProcess.cav.compile_and_analyze_file(compile_command.filename,
|
||||
compile_command.args,
|
||||
compile_command.directory)
|
||||
return ParallelAnalysisProcess.diags
|
||||
|
||||
@staticmethod
|
||||
def precompile_header(compile_command: CompileCommand) -> str:
|
||||
return precompile_header(compile_command,
|
||||
ParallelAnalysisProcess.tmp_dir)
|
||||
return precompile_header(compile_command, ParallelAnalysisProcess.tmp_dir)
|
||||
|
||||
|
||||
# Compile and analyze inputs in multiple processes.
|
||||
def run_parallel_analysis(ccl: Iterable[CompileCommand],
|
||||
pccl: Iterable[CompileCommand],
|
||||
diagnostic_cb: Callable[[Diagnostic], None],
|
||||
jobs_count: int, verilator_root: str):
|
||||
def run_parallel_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
|
||||
diagnostic_cb: Callable[[Diagnostic],
|
||||
None], jobs_count: int, verilator_root: str):
|
||||
prefix = "verilator_clang_check_attributes_"
|
||||
with tempfile.TemporaryDirectory(prefix=prefix) as tmp_dir:
|
||||
with multiprocessing.Pool(
|
||||
processes=jobs_count,
|
||||
initializer=ParallelAnalysisProcess.init_data,
|
||||
initargs=[verilator_root, tmp_dir]) as pool:
|
||||
with multiprocessing.Pool(processes=jobs_count,
|
||||
initializer=ParallelAnalysisProcess.init_data,
|
||||
initargs=[verilator_root, tmp_dir]) as pool:
|
||||
extra_args = []
|
||||
for pch_file in pool.imap_unordered(
|
||||
ParallelAnalysisProcess.precompile_header, pccl):
|
||||
for pch_file in pool.imap_unordered(ParallelAnalysisProcess.precompile_header, pccl):
|
||||
if pch_file:
|
||||
extra_args += ["-include-pch", pch_file]
|
||||
|
||||
@ -1011,8 +949,7 @@ def run_parallel_analysis(ccl: Iterable[CompileCommand],
|
||||
for compile_command in ccl:
|
||||
compile_command.args = compile_command.args + extra_args
|
||||
|
||||
for diags in pool.imap_unordered(
|
||||
ParallelAnalysisProcess.analyze_cpp_file, ccl, 1):
|
||||
for diags in pool.imap_unordered(ParallelAnalysisProcess.analyze_cpp_file, ccl, 1):
|
||||
for diag in diags:
|
||||
diagnostic_cb(diag)
|
||||
|
||||
@ -1057,8 +994,7 @@ class TopDownSummaryPrinter():
|
||||
row_groups: dict[str, list[list[str]]] = {}
|
||||
column_widths = [0, 0]
|
||||
for func in sorted(self._funcs.values(),
|
||||
key=lambda func:
|
||||
(func.info.file, func.info.line, func.info.usr)):
|
||||
key=lambda func: (func.info.file, func.info.line, func.info.usr)):
|
||||
func_info = func.info
|
||||
relfile = os.path.relpath(func_info.file, root_dir)
|
||||
|
||||
@ -1082,31 +1018,23 @@ class TopDownSummaryPrinter():
|
||||
if func.mismatch:
|
||||
mrelfile = os.path.relpath(func.mismatch.file, root_dir)
|
||||
row_group.append([
|
||||
f"{mrelfile}:{func.mismatch.line}:",
|
||||
f"[{func.mismatch.annotations}]",
|
||||
f"{mrelfile}:{func.mismatch.line}:", f"[{func.mismatch.annotations}]",
|
||||
func.mismatch.name + " [declaration]"
|
||||
])
|
||||
|
||||
row_group.append([
|
||||
f"{relfile}:{func_info.line}:", f"[{func_info.annotations}]",
|
||||
func_info.name
|
||||
])
|
||||
row_group.append(
|
||||
[f"{relfile}:{func_info.line}:", f"[{func_info.annotations}]", func_info.name])
|
||||
|
||||
for callee in sorted(func.calees,
|
||||
key=lambda func:
|
||||
(func.file, func.line, func.usr)):
|
||||
for callee in sorted(func.calees, key=lambda func: (func.file, func.line, func.usr)):
|
||||
crelfile = os.path.relpath(callee.file, root_dir)
|
||||
row_group.append([
|
||||
f"{crelfile}:{callee.line}:", f"[{callee.annotations}]",
|
||||
" " + callee.name
|
||||
])
|
||||
row_group.append(
|
||||
[f"{crelfile}:{callee.line}:", f"[{callee.annotations}]", " " + callee.name])
|
||||
|
||||
row_groups[name] = row_group
|
||||
|
||||
for row in row_group:
|
||||
for row_id, value in enumerate(row[0:-1]):
|
||||
column_widths[row_id] = max(column_widths[row_id],
|
||||
len(value))
|
||||
column_widths[row_id] = max(column_widths[row_id], len(value))
|
||||
|
||||
for label, rows in sorted(row_groups.items(), key=lambda kv: kv[0]):
|
||||
self.begin_group(label)
|
||||
@ -1114,21 +1042,17 @@ class TopDownSummaryPrinter():
|
||||
print(f"{row[0]:<{column_widths[0]}} "
|
||||
f"{row[1]:<{column_widths[1]}} "
|
||||
f"{row[2]}")
|
||||
print(
|
||||
f"Number of functions reported unsafe: {len(self._unsafe_in_safe)}"
|
||||
)
|
||||
print(f"Number of functions reported unsafe: {len(self._unsafe_in_safe)}")
|
||||
|
||||
|
||||
def main():
|
||||
default_verilator_root = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), ".."))
|
||||
default_verilator_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""Check function annotations for correctness""",
|
||||
epilog=
|
||||
"""Copyright 2022-2024 by Wilson Snyder. Verilator is free software;
|
||||
epilog="""Copyright 2022-2024 by Wilson Snyder. Verilator 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 Apache License 2.0.
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Apache-2.0""")
|
||||
@ -1142,29 +1066,23 @@ def main():
|
||||
type=int,
|
||||
default=0,
|
||||
help="Number of parallel jobs to use.")
|
||||
parser.add_argument(
|
||||
"--compile-commands-dir",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Path to directory containing compile_commands.json.")
|
||||
parser.add_argument("--compile-commands-dir",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Path to directory containing compile_commands.json.")
|
||||
parser.add_argument("--cxxflags",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Extra flags passed to clang++.")
|
||||
parser.add_argument(
|
||||
"--compilation-root",
|
||||
type=str,
|
||||
default=os.getcwd(),
|
||||
help="Directory used as CWD when compiling source files.")
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--precompile",
|
||||
action="append",
|
||||
help="Header file to be precompiled and cached at the start.")
|
||||
parser.add_argument("file",
|
||||
parser.add_argument("--compilation-root",
|
||||
type=str,
|
||||
nargs="+",
|
||||
help="Source file to analyze.")
|
||||
default=os.getcwd(),
|
||||
help="Directory used as CWD when compiling source files.")
|
||||
parser.add_argument("-c",
|
||||
"--precompile",
|
||||
action="append",
|
||||
help="Header file to be precompiled and cached at the start.")
|
||||
parser.add_argument("file", type=str, nargs="+", help="Source file to analyze.")
|
||||
|
||||
cmdline = parser.parse_args()
|
||||
|
||||
@ -1179,8 +1097,7 @@ def main():
|
||||
|
||||
compdb: Optional[CompilationDatabase] = None
|
||||
if cmdline.compile_commands_dir:
|
||||
compdb = CompilationDatabase.fromDirectory(
|
||||
cmdline.compile_commands_dir)
|
||||
compdb = CompilationDatabase.fromDirectory(cmdline.compile_commands_dir)
|
||||
|
||||
if cmdline.cxxflags is not None:
|
||||
common_cxxflags = shlex.split(cmdline.cxxflags)
|
||||
@ -1230,8 +1147,7 @@ def main():
|
||||
summary_printer.handle_diagnostic, verilator_root)
|
||||
else:
|
||||
run_parallel_analysis(compile_commands_list, precompile_commands_list,
|
||||
summary_printer.handle_diagnostic, cmdline.jobs,
|
||||
verilator_root)
|
||||
summary_printer.handle_diagnostic, cmdline.jobs, verilator_root)
|
||||
|
||||
summary_printer.print_summary(verilator_root)
|
||||
|
||||
|
@ -58,15 +58,12 @@ def test():
|
||||
if not Args.scenarios or re.match('dist', Args.scenarios):
|
||||
run("make examples VERILATOR_NO_OPT_BUILD=1")
|
||||
run("make test_regress VERILATOR_NO_OPT_BUILD=1" +
|
||||
(" SCENARIOS='" + Args.scenarios +
|
||||
"'" if Args.scenarios else "") +
|
||||
(" DRIVER_HASHSET='--hashset=" + Args.hashset +
|
||||
"'" if Args.hashset else "") +
|
||||
(" SCENARIOS='" + Args.scenarios + "'" if Args.scenarios else "") +
|
||||
(" DRIVER_HASHSET='--hashset=" + Args.hashset + "'" if Args.hashset else "") +
|
||||
('' if Args.stop else ' || true'))
|
||||
else:
|
||||
for test in Args.tests:
|
||||
if not os.path.exists(test) and os.path.exists(
|
||||
"test_regress/t/" + test):
|
||||
if not os.path.exists(test) and os.path.exists("test_regress/t/" + test):
|
||||
test = "test_regress/t/" + test
|
||||
run(test)
|
||||
ci_fold_end()
|
||||
@ -78,8 +75,7 @@ def test():
|
||||
os.makedirs(cc_dir, exist_ok=True)
|
||||
os.makedirs(cc_dir + "/info", exist_ok=True)
|
||||
|
||||
with subprocess.Popen("find . -print | grep .gcda",
|
||||
shell=True,
|
||||
with subprocess.Popen("find . -print | grep .gcda", shell=True,
|
||||
stdout=subprocess.PIPE) as sp:
|
||||
datout = sp.stdout.read()
|
||||
|
||||
@ -98,8 +94,7 @@ def test():
|
||||
del dats[dat]
|
||||
break
|
||||
|
||||
with subprocess.Popen("find . -print | grep .gcno",
|
||||
shell=True,
|
||||
with subprocess.Popen("find . -print | grep .gcno", shell=True,
|
||||
stdout=subprocess.PIPE) as sp:
|
||||
datout = sp.stdout.read()
|
||||
|
||||
@ -116,8 +111,7 @@ def test():
|
||||
if gbase in gcnos:
|
||||
os.symlink(gcnos[gbase], gcno)
|
||||
else:
|
||||
print("MISSING .gcno for a .gcda: " + gcno,
|
||||
file=sys.stderr)
|
||||
print("MISSING .gcno for a .gcda: " + gcno, file=sys.stderr)
|
||||
ci_fold_end()
|
||||
|
||||
if Args.stage_enabled[5]:
|
||||
@ -142,8 +136,7 @@ def test():
|
||||
if Args.stage_enabled[11]:
|
||||
ci_fold_start("dirs")
|
||||
print("Stage 11: Cleanup paths")
|
||||
cleanup_abs_paths_info(cc_dir, cc_dir + "/app_total.info",
|
||||
cc_dir + "/app_total.info")
|
||||
cleanup_abs_paths_info(cc_dir, cc_dir + "/app_total.info", cc_dir + "/app_total.info")
|
||||
ci_fold_end()
|
||||
|
||||
if Args.stage_enabled[12]:
|
||||
@ -164,17 +157,15 @@ def test():
|
||||
inc = "--include " + inc
|
||||
if exc != '':
|
||||
exc = "--exclude " + exc
|
||||
run("cd " + cc_dir + " ; " + RealPath +
|
||||
"/fastcov.py -C app_total.info " + inc + " " + exc +
|
||||
" -x --lcov -o app_total_f.info")
|
||||
run("cd " + cc_dir + " ; " + RealPath + "/fastcov.py -C app_total.info " + inc + " " +
|
||||
exc + " -x --lcov -o app_total_f.info")
|
||||
ci_fold_end()
|
||||
|
||||
if Args.stage_enabled[17]:
|
||||
ci_fold_start("report")
|
||||
print("Stage 17: Create HTML")
|
||||
run("cd " + cc_dir + " ; genhtml app_total_f.info --demangle-cpp" +
|
||||
" --rc lcov_branch_coverage=1 --rc genhtml_hi_limit=100 --output-directory html"
|
||||
)
|
||||
" --rc lcov_branch_coverage=1 --rc genhtml_hi_limit=100 --output-directory html")
|
||||
ci_fold_end()
|
||||
|
||||
if Args.stage_enabled[18]:
|
||||
@ -186,8 +177,7 @@ def test():
|
||||
# So, remove gcno files before calling codecov
|
||||
upload_dir = "nodist/obj_dir/upload"
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
cmd = ("ci/codecov -v upload-process -Z" + " -f " + cc_dir +
|
||||
"/app_total.info )")
|
||||
cmd = ("ci/codecov -v upload-process -Z" + " -f " + cc_dir + "/app_total.info )")
|
||||
print("print: Not running:")
|
||||
print(" export CODECOV_TOKEN=<hidden>")
|
||||
print(" find . -name '*.gcno' -exec rm {} \\;")
|
||||
@ -198,9 +188,7 @@ def test():
|
||||
print("*-* All Finished *-*")
|
||||
print("")
|
||||
print("* See report in " + cc_dir + "/html/index.html")
|
||||
print(
|
||||
"* Remember to make distclean && ./configure before working on non-coverage"
|
||||
)
|
||||
print("* Remember to make distclean && ./configure before working on non-coverage")
|
||||
|
||||
|
||||
def clone_sources(cc_dir):
|
||||
@ -209,9 +197,8 @@ def clone_sources(cc_dir):
|
||||
for globf in Source_Globs:
|
||||
for infile in glob.glob(globf):
|
||||
if re.match(r'^/', infile):
|
||||
sys.exit(
|
||||
"%Error: source globs should be relative not absolute filenames, "
|
||||
+ infile)
|
||||
sys.exit("%Error: source globs should be relative not absolute filenames, " +
|
||||
infile)
|
||||
outfile = cc_dir + "/" + infile
|
||||
outpath = re.sub(r'/[^/]*$', '', outfile, count=1)
|
||||
os.makedirs(outpath, exist_ok=True)
|
||||
@ -252,10 +239,8 @@ def clone_sources(cc_dir):
|
||||
done = True
|
||||
|
||||
ofh.write(line + "\n")
|
||||
print("Number of source lines automatically LCOV_EXCL_LINE'ed: %d" %
|
||||
excluded_lines)
|
||||
print("Number of source lines automatically LCOV_EXCL_BR_LINE'ed: %d" %
|
||||
excluded_br_lines)
|
||||
print("Number of source lines automatically LCOV_EXCL_LINE'ed: %d" % excluded_lines)
|
||||
print("Number of source lines automatically LCOV_EXCL_BR_LINE'ed: %d" % excluded_br_lines)
|
||||
|
||||
|
||||
def cleanup_abs_paths_info(cc_dir, infile, outfile):
|
||||
@ -263,20 +248,11 @@ def cleanup_abs_paths_info(cc_dir, infile, outfile):
|
||||
with open(infile, "r", encoding="utf8") as fh:
|
||||
for line in fh:
|
||||
if re.search(r'^SF:', line) and not re.search(r'^SF:/usr/', line):
|
||||
line = re.sub(os.environ['VERILATOR_ROOT'] + '/',
|
||||
'',
|
||||
line,
|
||||
count=1)
|
||||
line = re.sub(os.environ['VERILATOR_ROOT'] + '/', '', line, count=1)
|
||||
line = re.sub(cc_dir + '/', '', line, count=1)
|
||||
line = re.sub(r'^SF:.*?/include/',
|
||||
'SF:include/',
|
||||
line,
|
||||
count=1)
|
||||
line = re.sub(r'^SF:.*?/include/', 'SF:include/', line, count=1)
|
||||
line = re.sub(r'^SF:.*?/src/', 'SF:src/', line, count=1)
|
||||
line = re.sub(r'^SF:.*?/test_regress/',
|
||||
'SF:test_regress/',
|
||||
line,
|
||||
count=1)
|
||||
line = re.sub(r'^SF:.*?/test_regress/', 'SF:test_regress/', line, count=1)
|
||||
line = re.sub(r'obj_dbg/verilog.y$', 'verilog.y', line)
|
||||
# print("Remaining SF: "+line)
|
||||
lines.append(line)
|
||||
@ -358,15 +334,13 @@ def ci_fold_end():
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=
|
||||
"""code_coverage builds Verilator with C++ coverage support and runs
|
||||
description="""code_coverage builds Verilator with C++ coverage support and runs
|
||||
tests with coverage enabled. This will rebuild the current object
|
||||
files. Run as:
|
||||
|
||||
cd $VERILATOR_ROOT
|
||||
nodist/code_coverage""",
|
||||
epilog=
|
||||
"""Copyright 2019-2024 by Wilson Snyder. This program is free software; you
|
||||
epilog="""Copyright 2019-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.
|
||||
@ -380,19 +354,16 @@ parser.add_argument('--hashset',
|
||||
parser.add_argument('--scenarios',
|
||||
action='store',
|
||||
help='pass test scenarios onto driver.pl test harness')
|
||||
parser.add_argument(
|
||||
'--stages',
|
||||
'--stage',
|
||||
action='store',
|
||||
help='runs a specific stage or range of stages (see the script)')
|
||||
parser.add_argument('--stages',
|
||||
'--stage',
|
||||
action='store',
|
||||
help='runs a specific stage or range of stages (see the script)')
|
||||
parser.add_argument(
|
||||
'--tests',
|
||||
'--test',
|
||||
action='append',
|
||||
default=[],
|
||||
help=
|
||||
'Instead of normal regressions, run the specified test(s), may be used multiple times'
|
||||
)
|
||||
help='Instead of normal regressions, run the specified test(s), may be used multiple times')
|
||||
parser.add_argument('--no-stop',
|
||||
dest='stop',
|
||||
action='store_false',
|
||||
|
@ -20,8 +20,7 @@ def dotread(filename):
|
||||
vnum = 0
|
||||
|
||||
vertex_re = re.compile(r'^\t([a-zA-Z0-9_]+)\t(.*)$')
|
||||
edge_re = re.compile(
|
||||
r'^\t([a-zA-Z0-9_]+)\s+->\s+([a-zA-Z0-9_]+)\s*(.*)$')
|
||||
edge_re = re.compile(r'^\t([a-zA-Z0-9_]+)\s+->\s+([a-zA-Z0-9_]+)\s*(.*)$')
|
||||
|
||||
for line in fh:
|
||||
vertex_match = re.search(vertex_re, line)
|
||||
@ -29,11 +28,7 @@ def dotread(filename):
|
||||
if vertex_match:
|
||||
if vertex_match.group(1) != 'nTITLE':
|
||||
header = False
|
||||
Vertexes.append({
|
||||
'num': vnum,
|
||||
'line': line,
|
||||
'name': vertex_match.group(1)
|
||||
})
|
||||
Vertexes.append({'num': vnum, 'line': line, 'name': vertex_match.group(1)})
|
||||
vnum += 1
|
||||
elif edge_match:
|
||||
fromv = edge_match.group(1)
|
||||
@ -65,14 +60,13 @@ def cwrite(filename):
|
||||
fh.write("void V3GraphTestImport::dotImport() {\n")
|
||||
fh.write(" auto* gp = &m_graph;\n")
|
||||
for ver in sorted(Vertexes, key=lambda ver: ver['num']):
|
||||
fh.write(
|
||||
" auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n"
|
||||
% (ver['name'], ver['name'], ver['name']))
|
||||
fh.write(" auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n" %
|
||||
(ver['name'], ver['name'], ver['name']))
|
||||
fh.write("\n")
|
||||
for edge in Edges:
|
||||
fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" %
|
||||
(edge['from'], edge['to'], edge['weight'],
|
||||
"true" if edge['cutable'] else "false"))
|
||||
fh.write(
|
||||
" new V3GraphEdge{gp, %s, %s, %s, %s};\n" %
|
||||
(edge['from'], edge['to'], edge['weight'], "true" if edge['cutable'] else "false"))
|
||||
fh.write("}\n")
|
||||
|
||||
|
||||
@ -82,22 +76,17 @@ def cwrite(filename):
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=
|
||||
"""dot_importer takes a graphvis .dot file and converts into .cpp file.
|
||||
description="""dot_importer takes a graphvis .dot file and converts into .cpp file.
|
||||
This x.cpp file is then manually included in V3GraphTest.cpp to verify
|
||||
various xsub-algorithms.""",
|
||||
epilog=
|
||||
"""Copyright 2005-2024 by Wilson Snyder. This program is free software; you
|
||||
epilog="""Copyright 2005-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.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('filename', help='input .dot filename to process')
|
||||
|
||||
Args = parser.parse_args()
|
||||
|
@ -19,9 +19,12 @@ from argparse import ArgumentParser
|
||||
|
||||
|
||||
def interesting(s):
|
||||
if 'assert' in s: return 1
|
||||
if 'Assert' in s: return 1
|
||||
if 'Aborted' in s: return 1
|
||||
if 'assert' in s:
|
||||
return 1
|
||||
if 'Assert' in s:
|
||||
return 1
|
||||
if 'Aborted' in s:
|
||||
return 1
|
||||
if 'terminate' in s:
|
||||
if 'unterminated' in s:
|
||||
return 0
|
||||
@ -41,8 +44,7 @@ def main():
|
||||
for infile in glob(args.dir + '/*'):
|
||||
# Input filenames are known not to contain spaces or other unusual
|
||||
# characters, therefore this works.
|
||||
status, output = getstatusoutput('../../bin/verilator_bin --cc ' +
|
||||
infile)
|
||||
status, output = getstatusoutput('../../bin/verilator_bin --cc ' + infile)
|
||||
if interesting(output):
|
||||
print(infile)
|
||||
print(status)
|
||||
|
@ -51,9 +51,11 @@ def write_file(filename, contents):
|
||||
|
||||
def parse_line(s):
|
||||
# str->maybe str
|
||||
if len(s) == 0: return None
|
||||
if len(s) == 0:
|
||||
return None
|
||||
part = skip_while(lambda x: x != '"', s)
|
||||
if len(part) == 0 or part[0] != '"': return None
|
||||
if len(part) == 0 or part[0] != '"':
|
||||
return None
|
||||
literal_part = take_while(lambda x: x != '"', part[1:])
|
||||
return ''.join(filter(lambda x: x != '\\', literal_part))
|
||||
|
||||
|
@ -41,8 +41,7 @@ def test():
|
||||
run("/bin/mkdir -p " + prefix)
|
||||
run("cd " + blddir + " && make install")
|
||||
run("test -e " + prefix + "/share/man/man1/verilator.1")
|
||||
run("test -e " + prefix +
|
||||
"/share/verilator/examples/make_tracing_c/Makefile")
|
||||
run("test -e " + prefix + "/share/verilator/examples/make_tracing_c/Makefile")
|
||||
run("test -e " + prefix + "/share/verilator/include/verilated.h")
|
||||
run("test -e " + prefix + "/bin/verilator")
|
||||
run("test -e " + prefix + "/bin/verilator_bin")
|
||||
@ -58,10 +57,8 @@ def test():
|
||||
run("/bin/mkdir -p " + odir)
|
||||
path = prefix + "/bin" + ":" + prefix + "/share/bin"
|
||||
write_verilog(odir)
|
||||
run("cd " + odir + " && PATH=" + path +
|
||||
":$PATH verilator --cc top.v --exe sim_main.cpp")
|
||||
run("cd " + odir + "/obj_dir && PATH=" + path +
|
||||
":$PATH make -f Vtop.mk")
|
||||
run("cd " + odir + " && PATH=" + path + ":$PATH verilator --cc top.v --exe sim_main.cpp")
|
||||
run("cd " + odir + "/obj_dir && PATH=" + path + ":$PATH make -f Vtop.mk")
|
||||
run("cd " + odir + " && PATH=" + path + ":$PATH obj_dir/Vtop")
|
||||
|
||||
# run a test using exact path to binary
|
||||
@ -72,8 +69,7 @@ def test():
|
||||
run("/bin/mkdir -p " + odir)
|
||||
write_verilog(odir)
|
||||
bin1 = prefix + "/bin"
|
||||
run("cd " + odir + " && " + bin1 +
|
||||
"/verilator --cc top.v --exe sim_main.cpp")
|
||||
run("cd " + odir + " && " + bin1 + "/verilator --cc top.v --exe sim_main.cpp")
|
||||
run("cd " + odir + "/obj_dir && make -f Vtop.mk")
|
||||
run("cd " + odir + "/obj_dir && ./Vtop")
|
||||
|
||||
@ -88,8 +84,7 @@ def write_verilog(odir):
|
||||
|
||||
def cleanenv():
|
||||
for var in os.environ:
|
||||
if var in ('VERILATOR_ROOT', 'VERILATOR_INCLUDE',
|
||||
'VERILATOR_NO_OPT_BUILD'):
|
||||
if var in ('VERILATOR_ROOT', 'VERILATOR_INCLUDE', 'VERILATOR_NO_OPT_BUILD'):
|
||||
print("unset %s # Was '%s'" % (var, os.environ[var]))
|
||||
del os.environ[var]
|
||||
|
||||
@ -113,21 +108,16 @@ def run(command):
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=
|
||||
"""install_test performs several make-and-install iterations to verify the
|
||||
description="""install_test performs several make-and-install iterations to verify the
|
||||
Verilator kit. It isn't part of the normal "make test" due to the number
|
||||
of builds required.""",
|
||||
epilog=
|
||||
"""Copyright 2009-2024 by Wilson Snyder. This program is free software; you
|
||||
epilog="""Copyright 2009-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.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
|
||||
parser.add_argument('--stage',
|
||||
type=int,
|
||||
default=0,
|
||||
|
@ -101,8 +101,7 @@ parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
prog="log_changes",
|
||||
description="Create example entries for 'Changes' from parsing 'git log'",
|
||||
epilog=
|
||||
"""Copyright 2019-2024 by Wilson Snyder. This program is free software; you
|
||||
epilog="""Copyright 2019-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.
|
||||
|
@ -47,9 +47,12 @@ class AstseeCmd(gdb.Command):
|
||||
|
||||
def _null_check(self, old, new):
|
||||
err = ""
|
||||
if old == "<nullptr>\n": err += "old == <nullptr>\n"
|
||||
if new == "<nullptr>\n": err += "new == <nullptr>"
|
||||
if err: raise gdb.GdbError(err.strip("\n"))
|
||||
if old == "<nullptr>\n":
|
||||
err += "old == <nullptr>\n"
|
||||
if new == "<nullptr>\n":
|
||||
err += "new == <nullptr>"
|
||||
if err:
|
||||
raise gdb.GdbError(err.strip("\n"))
|
||||
|
||||
def invoke(self, arg_str, from_tty):
|
||||
from astsee import verilator_cli as astsee # pylint: disable=import-error,import-outside-toplevel
|
||||
@ -58,8 +61,8 @@ class AstseeCmd(gdb.Command):
|
||||
# We hack `astsee_verilator`'s arg parser to find arguments with nodes
|
||||
# After finding them, we replace them with proper files
|
||||
astsee_args = astsee.parser.parse_args(gdb.string_to_argv(arg_str))
|
||||
with _vltgdb_tmpfile() as oldfile, _vltgdb_tmpfile(
|
||||
) as newfile, _vltgdb_tmpfile() as metafile:
|
||||
with _vltgdb_tmpfile() as oldfile, _vltgdb_tmpfile() as newfile, _vltgdb_tmpfile(
|
||||
) as metafile:
|
||||
if astsee_args.file:
|
||||
_vltgdb_fwrite(oldfile, _vltgdb_get_dump(astsee_args.file))
|
||||
astsee_args.file = oldfile.name
|
||||
@ -68,8 +71,7 @@ class AstseeCmd(gdb.Command):
|
||||
astsee_args.newfile = newfile.name
|
||||
if astsee_args.meta is None:
|
||||
# pass
|
||||
gdb.execute(
|
||||
f'call AstNode::dumpJsonMetaFileGdb("{metafile.name}")')
|
||||
gdb.execute(f'call AstNode::dumpJsonMetaFileGdb("{metafile.name}")')
|
||||
astsee_args.meta = metafile.name
|
||||
try:
|
||||
astsee.main(astsee_args)
|
||||
|
351
src/astgen
351
src/astgen
@ -89,8 +89,7 @@ class Node:
|
||||
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
|
||||
sorted(self._subClasses, key=lambda _: (bool(_._subClasses), _.name))) # pylint: disable=protected-access
|
||||
|
||||
self._ordIdx = ordIdx
|
||||
ordIdx = ordIdx + 1
|
||||
@ -128,8 +127,7 @@ class Node:
|
||||
if self.superClass is None:
|
||||
self._allSuperClasses = ()
|
||||
else:
|
||||
self._allSuperClasses = self.superClass.allSuperClasses + (
|
||||
self.superClass, )
|
||||
self._allSuperClasses = self.superClass.allSuperClasses + (self.superClass, )
|
||||
return self._allSuperClasses
|
||||
|
||||
@property
|
||||
@ -139,9 +137,8 @@ class Node:
|
||||
if self.isLeaf:
|
||||
self._allSubClasses = ()
|
||||
else:
|
||||
self._allSubClasses = self.subClasses + tuple(
|
||||
_ for subClass in self.subClasses
|
||||
for _ in subClass.allSubClasses)
|
||||
self._allSubClasses = self.subClasses + tuple(_ for subClass in self.subClasses
|
||||
for _ in subClass.allSubClasses)
|
||||
return self._allSubClasses
|
||||
|
||||
@property
|
||||
@ -210,8 +207,7 @@ class Cpt:
|
||||
self._exec_syms = {}
|
||||
|
||||
def error(self, txt):
|
||||
sys.exit("%%Error: %s:%d: %s" %
|
||||
(self.in_filename, self.in_linenum, txt))
|
||||
sys.exit("%%Error: %s:%d: %s" % (self.in_filename, self.in_linenum, txt))
|
||||
|
||||
def print(self, txt):
|
||||
self.out_lines.append(txt)
|
||||
@ -220,8 +216,7 @@ class Cpt:
|
||||
self.out_lines.append(func)
|
||||
|
||||
def _output_line(self):
|
||||
self.print("#line " + str(self.out_linenum + 2) + " \"" +
|
||||
self.out_filename + "\"\n")
|
||||
self.print("#line " + str(self.out_linenum + 2) + " \"" + self.out_filename + "\"\n")
|
||||
|
||||
def process(self, in_filename, out_filename):
|
||||
self.in_filename = in_filename
|
||||
@ -234,8 +229,7 @@ class Cpt:
|
||||
for line in fhi:
|
||||
ln += 1
|
||||
if not didln:
|
||||
self.print("#line " + str(ln) + " \"" + self.in_filename +
|
||||
"\"\n")
|
||||
self.print("#line " + str(ln) + " \"" + self.in_filename + "\"\n")
|
||||
didln = True
|
||||
match = re.match(r'^\s+(TREE.*)$', line)
|
||||
if match:
|
||||
@ -245,8 +239,8 @@ class Cpt:
|
||||
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):
|
||||
elif not re.match(r'^\s*(#define|/[/\*])\s*TREE', line) and re.search(
|
||||
r'\s+TREE', line):
|
||||
self.error("Unknown astgen line: " + line)
|
||||
else:
|
||||
self.print(line)
|
||||
@ -275,8 +269,7 @@ class Cpt:
|
||||
# 1 2 3 4
|
||||
r'TREEOP(1?)([ACSV]?)\s*\(\s*\"([^\"]*)\"\s*,\s*\"([^\"]*)\"\s*\)',
|
||||
func)
|
||||
match_skip = re.search(r'TREE_SKIP_VISIT\s*\(\s*\"([^\"]*)\"\s*\)',
|
||||
func)
|
||||
match_skip = re.search(r'TREE_SKIP_VISIT\s*\(\s*\"([^\"]*)\"\s*\)', func)
|
||||
|
||||
if match:
|
||||
order = match.group(1)
|
||||
@ -314,20 +307,18 @@ class Cpt:
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
exec_func = re.sub(r'([-()a-zA-Z0-9_>]+)->cast([A-Z][A-Za-z0-9]+)\(\)',
|
||||
r'VN_CAST(\1,\2)', exec_func)
|
||||
|
||||
if typen not in self.treeop:
|
||||
self.treeop[typen] = []
|
||||
@ -431,14 +422,12 @@ class Cpt:
|
||||
self._exec_nsyms = 0
|
||||
self._exec_syms_recurse(aref)
|
||||
|
||||
for sym in sorted(self._exec_syms.keys(),
|
||||
key=lambda val: self._exec_syms[val]):
|
||||
for sym in sorted(self._exec_syms.keys(), key=lambda val: self._exec_syms[val]):
|
||||
argnp = self._exec_syms[sym]
|
||||
arg = self.add_nodep(sym)
|
||||
out += "AstNodeExpr* " + argnp + " = " + arg + "->unlinkFrBack();\n"
|
||||
|
||||
out += "AstNodeExpr* newp = " + self._exec_new_recurse(
|
||||
aref) + ";\n"
|
||||
out += "AstNodeExpr* newp = " + self._exec_new_recurse(aref) + ";\n"
|
||||
out += "nodep->replaceWith(newp);"
|
||||
out += "VL_DO_DANGLING(nodep->deleteTree(), nodep);"
|
||||
elif func == "NEVER":
|
||||
@ -454,19 +443,15 @@ class Cpt:
|
||||
self.tree_base()
|
||||
|
||||
def tree_match(self):
|
||||
self.print(
|
||||
" // TREEOP functions, each return true if they matched & transformed\n"
|
||||
)
|
||||
self.print(" // TREEOP functions, each return true if they matched & transformed\n")
|
||||
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")
|
||||
self.print(" bool " + typefunc['match_func'] + "(Ast" + base + "* nodep) {\n")
|
||||
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")
|
||||
self.print("\t UINFO(" + str(typefunc['uinfo_level']) + ", cvtToHex(nodep)" +
|
||||
" << \" " + typefunc['uinfo'] + "\\n\");\n")
|
||||
self.print("\t " + typefunc['exec_func'] + "\n")
|
||||
self.print("\t return true;\n")
|
||||
self.print("\t}\n")
|
||||
@ -475,9 +460,7 @@ class Cpt:
|
||||
|
||||
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"
|
||||
)
|
||||
self.print(" // Bottom class up, as more simple transforms are generally better\n")
|
||||
for node in AstNodeList:
|
||||
out_for_type_sc = []
|
||||
out_for_type = []
|
||||
@ -488,15 +471,11 @@ class Cpt:
|
||||
if base not in self.treeop:
|
||||
continue
|
||||
for typefunc in self.treeop[base]:
|
||||
lines = [
|
||||
" if (" + typefunc['match_func'] +
|
||||
"(nodep)) return;\n"
|
||||
]
|
||||
lines = [" if (" + typefunc['match_func'] + "(nodep)) return;\n"]
|
||||
if typefunc['short_circuit']: # short-circuit match fn
|
||||
out_for_type_sc.extend(lines)
|
||||
else: # Standard match fn
|
||||
if typefunc[
|
||||
'order']: # TREEOP1's go in front of others
|
||||
if typefunc['order']: # TREEOP1's go in front of others
|
||||
out_for_type = lines + out_for_type
|
||||
else:
|
||||
out_for_type.extend(lines)
|
||||
@ -509,32 +488,26 @@ class Cpt:
|
||||
# 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))
|
||||
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))
|
||||
if out_for_type[0]:
|
||||
self.print(
|
||||
" iterateAndNextNull(nodep->{op2}());\n".format(
|
||||
op2=node.getOp(2)[0]))
|
||||
" 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(" iterateAndNextNull(nodep->{op3}());\n".format(
|
||||
op3=node.getOp(3)[0]))
|
||||
self.print("".join(out_for_type) + " }\n")
|
||||
elif len(out_for_type) > 0: # Other types with something to print
|
||||
skip = node.name in self.tree_skip_visit
|
||||
gen = "Gen" if skip else ""
|
||||
virtual = "virtual " if skip else ""
|
||||
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")
|
||||
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")
|
||||
|
||||
|
||||
######################################################################
|
||||
@ -565,8 +538,7 @@ def read_types(filename, Nodes, prefix):
|
||||
|
||||
def error(lineno, message):
|
||||
nonlocal hasErrors
|
||||
print(filename + ":" + str(lineno) + ": %Error: " + message,
|
||||
file=sys.stderr)
|
||||
print(filename + ":" + str(lineno) + ": %Error: " + message, file=sys.stderr)
|
||||
hasErrors = True
|
||||
|
||||
node = None
|
||||
@ -579,8 +551,7 @@ def read_types(filename, Nodes, prefix):
|
||||
if not hasAstgenMembers:
|
||||
error(
|
||||
node.lineno,
|
||||
"'{p}{n}' does not contain 'ASTGEN_MEMBERS_{p}{n};'".format(
|
||||
p=prefix, n=node.name))
|
||||
"'{p}{n}' does not contain 'ASTGEN_MEMBERS_{p}{n};'".format(p=prefix, n=node.name))
|
||||
hasAstgenMembers = False
|
||||
|
||||
with open(filename, "r", encoding="utf8") as fh:
|
||||
@ -598,8 +569,7 @@ def read_types(filename, Nodes, prefix):
|
||||
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))
|
||||
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)
|
||||
@ -608,8 +578,7 @@ def read_types(filename, Nodes, prefix):
|
||||
if not node:
|
||||
continue
|
||||
|
||||
if re.match(r'^\s*ASTGEN_MEMBERS_' + prefix + node.name + ';',
|
||||
line):
|
||||
if re.match(r'^\s*ASTGEN_MEMBERS_' + prefix + node.name + ';', line):
|
||||
hasAstgenMembers = True
|
||||
|
||||
if prefix != "Ast":
|
||||
@ -631,36 +600,29 @@ def read_types(filename, Nodes, prefix):
|
||||
ident = ident.strip()
|
||||
if not sep or not re.match(r'^\w+$', ident):
|
||||
error(
|
||||
lineno, "Malformed '@astgen " + what +
|
||||
"' directive (expecting '" + what +
|
||||
" := <identifier> : <type>': " + decl)
|
||||
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)
|
||||
"' (expecting Ast*, Optional[Ast*], or List[Ast*]):" + decl)
|
||||
elif node.getOp(n) is not None:
|
||||
error(
|
||||
lineno, "Already defined " + what + " for " +
|
||||
node.name)
|
||||
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"):
|
||||
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)
|
||||
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")
|
||||
error(lineno, "Aliased op" + str(n) + " is not defined")
|
||||
else:
|
||||
node.addOp(n, ident, *op[1:])
|
||||
elif what == "ptr":
|
||||
@ -670,8 +632,7 @@ def read_types(filename, Nodes, prefix):
|
||||
if not kind:
|
||||
error(
|
||||
lineno, "Bad type for '@astgen " + what +
|
||||
"' (expecting Ast*, Optional[Ast*], or List[Ast*]):"
|
||||
+ decl)
|
||||
"' (expecting Ast*, Optional[Ast*], or List[Ast*]):" + decl)
|
||||
if not re.match(r'^m_(\w+)$', ident):
|
||||
error(
|
||||
lineno, "Malformed '@astgen ptr'"
|
||||
@ -680,20 +641,15 @@ def read_types(filename, Nodes, prefix):
|
||||
node.addPtr(ident, *kind)
|
||||
else:
|
||||
error(
|
||||
lineno,
|
||||
"Malformed @astgen what (expecting 'op1'..'op4'," +
|
||||
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")
|
||||
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)
|
||||
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:
|
||||
@ -707,33 +663,24 @@ def check_types(sortedTypes, prefix, abstractPrefix):
|
||||
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))
|
||||
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))
|
||||
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)))
|
||||
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)
|
||||
}
|
||||
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
|
||||
@ -749,8 +696,7 @@ def check_types(sortedTypes, prefix, abstractPrefix):
|
||||
file=sys.stderr)
|
||||
|
||||
if hasOrderingError:
|
||||
sys.exit(
|
||||
"%Error: Stopping due to out of order definitions listed above")
|
||||
sys.exit("%Error: Stopping due to out of order definitions listed above")
|
||||
|
||||
|
||||
def read_stages(filename):
|
||||
@ -783,8 +729,7 @@ def read_refs(filename):
|
||||
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):
|
||||
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': {}}
|
||||
@ -796,9 +741,7 @@ def open_file(filename):
|
||||
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")
|
||||
fh.write('// Generated by astgen // -*- mode: C++; c-file-style: "cc-mode" -*-' + "\n")
|
||||
return fh
|
||||
|
||||
|
||||
@ -808,9 +751,7 @@ def open_file(filename):
|
||||
def write_report(filename):
|
||||
with open_file(filename) as fh:
|
||||
|
||||
fh.write(
|
||||
"Processing stages (approximate, based on order in Verilator.cpp):\n"
|
||||
)
|
||||
fh.write("Processing stages (approximate, based on order in Verilator.cpp):\n")
|
||||
for classn in sorted(Stages.keys(), key=lambda val: Stages[val]):
|
||||
fh.write(" " + classn + "\n")
|
||||
|
||||
@ -831,14 +772,12 @@ def write_report(filename):
|
||||
refs = ClassRefs["Ast" + node.name]
|
||||
fh.write(" newed: ")
|
||||
for stage in sorted(refs['newed'].keys(),
|
||||
key=lambda val: Stages[val]
|
||||
if (val in Stages) else -1):
|
||||
key=lambda val: Stages[val] if (val in Stages) else -1):
|
||||
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):
|
||||
key=lambda val: Stages[val] if (val in Stages) else -1):
|
||||
fh.write(stage + " ")
|
||||
fh.write("\n")
|
||||
fh.write("\n")
|
||||
@ -852,8 +791,7 @@ def write_report(filename):
|
||||
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 + ";"))
|
||||
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))
|
||||
fh.write("\n")
|
||||
@ -863,8 +801,7 @@ 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))
|
||||
fh.write("virtual void visit({p}{n}*);\n".format(p=prefix, n=node.name))
|
||||
|
||||
|
||||
def write_visitor_defns(prefix, nodeList, visitor):
|
||||
@ -873,13 +810,8 @@ def write_visitor_defns(prefix, nodeList, visitor):
|
||||
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))
|
||||
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))
|
||||
|
||||
|
||||
def write_type_enum(prefix, nodeList):
|
||||
@ -887,27 +819,21 @@ def write_type_enum(prefix, nodeList):
|
||||
with open_file("V3{p}__gen_type_enum.h".format(p=prefix)) as fh:
|
||||
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
fh.write(" _BOUNDS_END\n")
|
||||
fh.write(" };\n")
|
||||
|
||||
fh.write(" const char* ascii() const VL_MT_SAFE {\n")
|
||||
fh.write(" static const char* const names[_ENUM_END + 1] = {\n")
|
||||
for node in sorted(filter(lambda _: _.isLeaf, nodeList),
|
||||
key=lambda _: _.typeId):
|
||||
for node in sorted(filter(lambda _: _.isLeaf, nodeList), key=lambda _: _.typeId):
|
||||
fh.write(' "{T}",\n'.format(T=node.name.upper()))
|
||||
fh.write(" \"_ENUM_END\"\n")
|
||||
fh.write(" };\n")
|
||||
@ -928,8 +854,8 @@ def write_type_tests(prefix, nodeList):
|
||||
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))
|
||||
"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:
|
||||
@ -937,8 +863,9 @@ def write_type_tests(prefix, nodeList):
|
||||
"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("return {v}->type() == {e}::at{t};".format(v=variable,
|
||||
e=enum,
|
||||
t=node.name))
|
||||
fh.write(" }\n")
|
||||
|
||||
|
||||
@ -949,8 +876,7 @@ def write_type_tests(prefix, nodeList):
|
||||
|
||||
def write_ast_type_info(filename):
|
||||
with open_file(filename) as fh:
|
||||
for node in sorted(filter(lambda _: _.isLeaf, AstNodeList),
|
||||
key=lambda _: _.typeId):
|
||||
for node in sorted(filter(lambda _: _.isLeaf, AstNodeList), key=lambda _: _.typeId):
|
||||
opTypeList = []
|
||||
opNameList = []
|
||||
for n in range(1, 5):
|
||||
@ -968,12 +894,11 @@ def write_ast_type_info(filename):
|
||||
opTypeList.append('OP_LIST')
|
||||
opNameList.append(name)
|
||||
# opTypeStr = ', '.join(opTypeList)
|
||||
opTypeStr = ', '.join(
|
||||
['VNTypeInfo::{0}'.format(s) for s in 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(
|
||||
' {{ "Ast{name}", {{{opTypeStr}}}, {{{opNameStr}}}, sizeof(Ast{name}) }},\n'.
|
||||
format(
|
||||
name=node.name,
|
||||
opTypeStr=opTypeStr,
|
||||
opNameStr=opNameStr,
|
||||
@ -984,23 +909,19 @@ def write_ast_impl(filename):
|
||||
with open_file(filename) as fh:
|
||||
|
||||
def emitBlock(pattern, **fmt):
|
||||
fh.write(
|
||||
textwrap.indent(textwrap.dedent(pattern),
|
||||
" ").format(**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)
|
||||
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'])
|
||||
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",
|
||||
@ -1010,8 +931,7 @@ def write_ast_impl(filename):
|
||||
|
||||
emitBlock("void Ast{t}::cloneRelinkGen() {{\n", t=node.name)
|
||||
if node.superClass.name != 'Node':
|
||||
emitBlock(" Ast{base}::cloneRelinkGen();\n",
|
||||
base=node.superClass.name)
|
||||
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",
|
||||
@ -1020,14 +940,11 @@ def write_ast_impl(filename):
|
||||
|
||||
emitBlock("}}\n")
|
||||
|
||||
emitBlock("void Ast{t}::dumpJsonGen(std::ostream& str) const {{\n",
|
||||
t=node.name)
|
||||
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)
|
||||
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(" dumpJsonPtr(str, \"{name}\", m_{name});\n", name=ptr['name'])
|
||||
emitBlock("}}\n")
|
||||
|
||||
emitBlock(
|
||||
@ -1038,9 +955,7 @@ def write_ast_impl(filename):
|
||||
if op is None:
|
||||
continue
|
||||
name, _, _ = op
|
||||
emitBlock(
|
||||
" dumpNodeListJson(str, {name}(), \"{name}\", indent);\n",
|
||||
name=name)
|
||||
emitBlock(" dumpNodeListJson(str, {name}(), \"{name}\", indent);\n", name=name)
|
||||
emitBlock("}}\n")
|
||||
|
||||
|
||||
@ -1059,9 +974,7 @@ def write_ast_macros(filename):
|
||||
if not any_ptr:
|
||||
fh.write("private: \\\n")
|
||||
any_ptr = True
|
||||
emitBlock("Ast{kind}* m_{name} = nullptr;",
|
||||
name=ptr['name'],
|
||||
kind=ptr['kind'])
|
||||
emitBlock("Ast{kind}* m_{name} = nullptr;", name=ptr['name'], kind=ptr['kind'])
|
||||
if any_ptr:
|
||||
fh.write("public: \\\n")
|
||||
# TODO pointer accessors
|
||||
@ -1108,8 +1021,8 @@ def write_ast_macros(filename):
|
||||
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)
|
||||
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:
|
||||
@ -1126,8 +1039,7 @@ def write_ast_macros(filename):
|
||||
n=n,
|
||||
retrieve=retrieve)
|
||||
if superOp:
|
||||
hiddenMethods.append("add" + superName[0].upper() +
|
||||
superName[1:])
|
||||
hiddenMethods.append("add" + superName[0].upper() + superName[1:])
|
||||
elif monad == "Optional":
|
||||
emitBlock('''\
|
||||
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
|
||||
@ -1150,27 +1062,24 @@ def write_ast_macros(filename):
|
||||
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(" 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
|
||||
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))
|
||||
"#define ASTGEN_SUPER_{t}(...) Ast{b}(VNType::at{t}, __VA_ARGS__)\n".format(
|
||||
t=node.name, b=node.superClass.name))
|
||||
fh.write("\n")
|
||||
|
||||
|
||||
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:]))
|
||||
fh.write("Ast{t}* {m}p;\n".format(t=node.name, m=node.name[0].lower() + node.name[1:]))
|
||||
|
||||
|
||||
################################################################################
|
||||
@ -1207,8 +1116,7 @@ def write_dfg_macros(filename):
|
||||
name=name,
|
||||
n=n - 1)
|
||||
|
||||
operandNames = tuple(
|
||||
node.getOp(n)[0] for n in range(1, node.arity + 1))
|
||||
operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1))
|
||||
if operandNames:
|
||||
emitBlock('''\
|
||||
const std::string srcName(size_t idx) const override {{
|
||||
@ -1217,10 +1125,8 @@ def write_dfg_macros(filename):
|
||||
}}
|
||||
''',
|
||||
a=node.arity,
|
||||
ns=", ".join(
|
||||
map(lambda _: '"' + _ + '"', operandNames)))
|
||||
fh.write(
|
||||
" static_assert(true, \"\")\n") # Swallowing the semicolon
|
||||
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames)))
|
||||
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
|
||||
|
||||
|
||||
def write_dfg_auto_classes(filename):
|
||||
@ -1254,11 +1160,8 @@ def write_dfg_ast_to_dfg(filename):
|
||||
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("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))
|
||||
@ -1267,9 +1170,8 @@ def write_dfg_ast_to_dfg(filename):
|
||||
' 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))
|
||||
fh.write(" Dfg{t}* const vtxp = makeVertex<Dfg{t}>(nodep, *m_dfgp);\n".format(
|
||||
t=node.name))
|
||||
fh.write(" if (!vtxp) {\n")
|
||||
fh.write(" m_foundUnhandled = true;\n")
|
||||
fh.write(" ++m_ctx.m_nonRepNode;\n")
|
||||
@ -1277,8 +1179,8 @@ def write_dfg_ast_to_dfg(filename):
|
||||
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))
|
||||
" 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")
|
||||
fh.write(" nodep->user1p(vtxp);\n")
|
||||
@ -1292,14 +1194,12 @@ def write_dfg_dfg_to_ast(filename):
|
||||
if (node.file is not None) or (not node.isLeaf):
|
||||
continue
|
||||
|
||||
fh.write(
|
||||
"void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
|
||||
fh.write("void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
|
||||
for i in range(node.arity):
|
||||
fh.write(
|
||||
" AstNodeExpr* const op{j}p = convertDfgVertexToAstNodeExpr(vtxp->source<{i}>());\n"
|
||||
.format(i=i, j=i + 1))
|
||||
fh.write(
|
||||
" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name))
|
||||
fh.write(" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name))
|
||||
for i in range(node.arity):
|
||||
fh.write(", op{j}p".format(j=i + 1))
|
||||
fh.write(");\n")
|
||||
@ -1313,8 +1213,7 @@ 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
|
||||
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.
|
||||
@ -1322,15 +1221,9 @@ Version 2.0.
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
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')
|
||||
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')
|
||||
parser.add_argument('--debug', action='store_true', help='enable debug')
|
||||
|
||||
parser.add_argument('infiles', nargs='*', help='list of input .cpp filenames')
|
||||
@ -1409,8 +1302,7 @@ for node in AstNodeList:
|
||||
# Compute derived properties over the whole DfgVertex hierarchy
|
||||
DfgVertices["Vertex"].complete()
|
||||
|
||||
DfgVertexList = tuple(map(lambda _: DfgVertices[_],
|
||||
sorted(DfgVertices.keys())))
|
||||
DfgVertexList = tuple(map(lambda _: DfgVertices[_], sorted(DfgVertices.keys())))
|
||||
|
||||
check_types(DfgVertexList, "Dfg", "Vertex")
|
||||
|
||||
@ -1457,8 +1349,7 @@ 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")
|
||||
Cpt().process(in_filename=Args.I + "/" + cpt + ".cpp", out_filename=cpt + "__gen.cpp")
|
||||
|
||||
######################################################################
|
||||
# Local Variables:
|
||||
|
123
src/bisonpre
123
src/bisonpre
@ -28,15 +28,13 @@ def process():
|
||||
+ (" -d" if Args.definitions else "") #
|
||||
+ (" -k" if Args.token_table else "") #
|
||||
+ (" -v" if Args.verbose else "") #
|
||||
+ (" --report=itemset --report=lookahead" if
|
||||
(Args.verbose and supports_report) else "")
|
||||
+ (" --report=itemset --report=lookahead" if (Args.verbose and supports_report) else "")
|
||||
# Useful but slow:
|
||||
# (" -Wcounterexamples" if
|
||||
# (Args.verbose and supports_counter_examples) else "")
|
||||
#
|
||||
# -p required for GLR parsers; they write to -p basename, not -o
|
||||
+ ((" -p " + Args.name_prefix) if Args.name_prefix else "") + " -b " +
|
||||
tmp_prefix() #
|
||||
+ ((" -p " + Args.name_prefix) if Args.name_prefix else "") + " -b " + tmp_prefix() #
|
||||
+ " -o " + tmp_prefix() + ".c" #
|
||||
+ " " + tmp_prefix() + ".y")
|
||||
|
||||
@ -44,11 +42,10 @@ def process():
|
||||
status = subprocess.call(command, shell=True)
|
||||
if status != 0:
|
||||
unlink_outputs()
|
||||
sys.exit("bisonpre: %Error: " + Args.yacc + " version " +
|
||||
str(Bison_Version) + " run failed due to errors\n")
|
||||
sys.exit("bisonpre: %Error: " + Args.yacc + " version " + str(Bison_Version) +
|
||||
" run failed due to errors\n")
|
||||
|
||||
clean_output(tmp_prefix() + ".output",
|
||||
output_prefix() + ".output", True, False)
|
||||
clean_output(tmp_prefix() + ".output", output_prefix() + ".output", True, False)
|
||||
warning_check(output_prefix() + ".output")
|
||||
|
||||
clean_output(tmp_prefix() + ".c", output_prefix() + ".c", False, True)
|
||||
@ -88,9 +85,7 @@ def unlink_outputs():
|
||||
|
||||
|
||||
def bison_version_check():
|
||||
with subprocess.Popen(Args.yacc + " --version",
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE) as sp:
|
||||
with subprocess.Popen(Args.yacc + " --version", shell=True, stdout=subprocess.PIPE) as sp:
|
||||
out = str(sp.stdout.read())
|
||||
match = re.search(r'([0-9]+\.[0-9]+)', out)
|
||||
if match:
|
||||
@ -102,8 +97,7 @@ def bison_version_check():
|
||||
Bison_Version = v
|
||||
return
|
||||
|
||||
sys.exit("bisonpre: %Error: '" + Args.yacc +
|
||||
"' is not installed, or not working\n")
|
||||
sys.exit("bisonpre: %Error: '" + Args.yacc + "' is not installed, or not working\n")
|
||||
|
||||
|
||||
def clean_output(filename, outname, is_output, is_c):
|
||||
@ -141,8 +135,7 @@ def clean_output(filename, outname, is_output, is_c):
|
||||
if is_c:
|
||||
token_values = {}
|
||||
for line in lines:
|
||||
if re.search(r'enum\s+yytokentype',
|
||||
line) and not re.search(r';', line):
|
||||
if re.search(r'enum\s+yytokentype', line) and not re.search(r';', line):
|
||||
match = re.search(r'\b(\S+) = (\d+)', line)
|
||||
if match:
|
||||
token_values[match.group(2)] = match.group(1)
|
||||
@ -151,8 +144,7 @@ def clean_output(filename, outname, is_output, is_c):
|
||||
if _enaline(line) and re.search(r'BISONPRE_TOKEN_NAMES', line):
|
||||
out.append(line)
|
||||
for tv in sorted(token_values.keys()):
|
||||
out.append("\tcase %d: return \"%s\";\n" %
|
||||
(tv, token_values[tv]))
|
||||
out.append("\tcase %d: return \"%s\";\n" % (tv, token_values[tv]))
|
||||
continue
|
||||
out.append(line)
|
||||
lines = out
|
||||
@ -165,8 +157,7 @@ def clean_output(filename, outname, is_output, is_c):
|
||||
# Fix bison 2.3 and GCC 4.2.1
|
||||
line = re.sub(r'\(YY_\("', '(YY_((char*)"', line)
|
||||
# Fix bison 2.3 glr-parser warning about yyerrorloc.YYTYPE::yydummy uninit
|
||||
line = re.sub(r'(YYLTYPE yyerrloc;)',
|
||||
r'\1 yyerrloc.yydummy=0;/*bisonpre*/', line)
|
||||
line = re.sub(r'(YYLTYPE yyerrloc;)', r'\1 yyerrloc.yydummy=0;/*bisonpre*/', line)
|
||||
# Fix bison 3.6.1 unexpected nested-comment
|
||||
line = re.sub(r'/\* "/\*.*\*/" \*/', '', line)
|
||||
fh.write(line)
|
||||
@ -177,11 +168,8 @@ def warning_check(filename):
|
||||
linenum = 0
|
||||
for line in fh:
|
||||
linenum += 1
|
||||
if re.search(r'(conflicts|warning:|^useless)',
|
||||
line,
|
||||
flags=re.IGNORECASE):
|
||||
sys.exit("%Error: " + filename + ":" + str(linenum) + ": " +
|
||||
line + "\n")
|
||||
if re.search(r'(conflicts|warning:|^useless)', line, flags=re.IGNORECASE):
|
||||
sys.exit("%Error: " + filename + ":" + str(linenum) + ": " + line + "\n")
|
||||
|
||||
|
||||
######################################################################
|
||||
@ -214,12 +202,9 @@ def clean_input(filename, outname):
|
||||
# ^/ to prevent comments from matching
|
||||
if re.match(r'^[a-zA-Z0-9_<>]+:[^/]*[a-zA-Z]', line):
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Move text on rule line to next line: " + line +
|
||||
"\n")
|
||||
": Move text on rule line to next line: " + line + "\n")
|
||||
|
||||
matcha = re.match(r'^([a-zA-Z0-9_]+)<(\S*)>(.*)$',
|
||||
line,
|
||||
flags=re.DOTALL)
|
||||
matcha = re.match(r'^([a-zA-Z0-9_]+)<(\S*)>(.*)$', line, flags=re.DOTALL)
|
||||
matchb = re.match(r'^([a-zA-Z0-9_]+):', line)
|
||||
|
||||
if re.match(r'^%%', line):
|
||||
@ -231,8 +216,8 @@ def clean_input(filename, outname):
|
||||
dtype = matcha.group(2)
|
||||
line = name + matcha.group(3)
|
||||
if name in Rules:
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Redeclaring '" + name + "': " + line)
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) + ": Redeclaring '" + name +
|
||||
"': " + line)
|
||||
if dtype not in types:
|
||||
types[dtype] = {}
|
||||
types[dtype][name] = 1
|
||||
@ -250,8 +235,8 @@ def clean_input(filename, outname):
|
||||
name = matchb.group(1)
|
||||
if name not in ('public', 'private'):
|
||||
if name in Rules:
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Redeclaring '" + name + "': " + line)
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) + ": Redeclaring '" +
|
||||
name + "': " + line)
|
||||
Rules[name] = {
|
||||
'name': name,
|
||||
'type': "",
|
||||
@ -268,8 +253,7 @@ def clean_input(filename, outname):
|
||||
cline = re.sub(r'//.*$', '\n', line)
|
||||
if re.match(r'^\s*;', cline):
|
||||
if not last_rule:
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Stray semicolon\n")
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) + ": Stray semicolon\n")
|
||||
last_rule = None
|
||||
elif last_rule:
|
||||
Rules[last_rule]['rules_and_productions'] += cline
|
||||
@ -279,8 +263,8 @@ def clean_input(filename, outname):
|
||||
dtype = match.group(1)
|
||||
tok = match.group(2)
|
||||
if tok in tokens:
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Redeclaring '" + tok + "': " + line)
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) + ": Redeclaring '" + tok +
|
||||
"': " + line)
|
||||
tokens[tok] = dtype
|
||||
|
||||
for tok in re.split(r'[^a-zA-Z0-9_]+', cline):
|
||||
@ -299,17 +283,16 @@ def clean_input(filename, outname):
|
||||
lineno += 1
|
||||
if _enaline(line) and re.search(r'BISONPRE_VERSION', line):
|
||||
# 1 2 3 4
|
||||
match = re.search(
|
||||
r'BISONPRE_VERSION\((\S+)\s*,\s*((\S+)\s*,)?\s*([^\),]+)\)\s*$',
|
||||
line)
|
||||
match = re.search(r'BISONPRE_VERSION\((\S+)\s*,\s*((\S+)\s*,)?\s*([^\),]+)\)\s*$',
|
||||
line)
|
||||
if not match:
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Bad form of BISONPRE_VERSION: " + line)
|
||||
ver = match.group(1)
|
||||
ver_max = match.group(3)
|
||||
cmd = match.group(4)
|
||||
if Bison_Version >= float(ver) and (
|
||||
not ver_max or Bison_Version <= float(ver_max)):
|
||||
if Bison_Version >= float(ver) and (not ver_max
|
||||
or Bison_Version <= float(ver_max)):
|
||||
line = cmd + "\n"
|
||||
else:
|
||||
line = "//NOP: " + line
|
||||
@ -323,10 +306,9 @@ def clean_input(filename, outname):
|
||||
for line in linesin:
|
||||
lineno += 1
|
||||
if _enaline(line) and re.search(r'BISONPRE_NOT', line):
|
||||
match = re.search(
|
||||
r'(.*)BISONPRE_NOT\((\S+)\)\s*(\{[^}]+})\s*(.*)$',
|
||||
line,
|
||||
flags=re.DOTALL)
|
||||
match = re.search(r'(.*)BISONPRE_NOT\((\S+)\)\s*(\{[^}]+})\s*(.*)$',
|
||||
line,
|
||||
flags=re.DOTALL)
|
||||
if not match:
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Bad form of BISONPRE_NOT: " + line)
|
||||
@ -337,8 +319,7 @@ def clean_input(filename, outname):
|
||||
for etok in endtoks:
|
||||
if etok not in tokens:
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) +
|
||||
": Can't find definition for token: " + etok +
|
||||
"\n")
|
||||
": Can't find definition for token: " + etok + "\n")
|
||||
# Push it all onto one line to avoid error messages changing
|
||||
pipe = ""
|
||||
for tok in sorted(tokens.keys()):
|
||||
@ -397,10 +378,8 @@ def clean_input(filename, outname):
|
||||
# Bison doesn't have a #line directive, so we need somewhere to insert into
|
||||
line = re.sub(r'^\s*//.*$', '', line)
|
||||
if not re.match(r'^\s*$', line):
|
||||
sys.exit(
|
||||
"%Error: " + filename + ":" + str(lineno) + ": Need " +
|
||||
str(needmore) +
|
||||
" more blank lines to keep line numbers constant\n")
|
||||
sys.exit("%Error: " + filename + ":" + str(lineno) + ": Need " +
|
||||
str(needmore) + " more blank lines to keep line numbers constant\n")
|
||||
needmore -= 1
|
||||
else:
|
||||
lines.append(line)
|
||||
@ -418,8 +397,8 @@ def _bisonpre_copy(text, lineno, depth):
|
||||
text,
|
||||
flags=re.DOTALL)
|
||||
if not match:
|
||||
sys.exit("%Error: " + Filename + ":" + str(lineno) +
|
||||
": Bad form of BISONPRE_NOT: " + text)
|
||||
sys.exit("%Error: " + Filename + ":" + str(lineno) + ": Bad form of BISONPRE_NOT: " +
|
||||
text)
|
||||
text = match.group(1) + '{HERE}' + match.group(5)
|
||||
once = match.group(2)
|
||||
rule = match.group(3)
|
||||
@ -448,8 +427,7 @@ def _bisonpre_copy(text, lineno, depth):
|
||||
insert = re.sub(left, right, insert)
|
||||
|
||||
insert = re.sub(r'[ \t\n]+\n', "\n", insert)
|
||||
insert = re.sub(r'\n', " ",
|
||||
insert) # Optional - preserve line numbering
|
||||
insert = re.sub(r'\n', " ", insert) # Optional - preserve line numbering
|
||||
text = re.sub(r'{HERE}', insert, text)
|
||||
depth += 1
|
||||
return text
|
||||
@ -465,8 +443,7 @@ def _enaline(line):
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=
|
||||
"""Bisonpre is a wrapper for the Bison YACC replacement. Input to Bison is
|
||||
description="""Bisonpre is a wrapper for the Bison YACC replacement. Input to Bison is
|
||||
preprocessed with substitution as described below under EXTENSIONS. Output
|
||||
from Bison is checked for additional errors, and corrected to work around
|
||||
various compile warnings.""",
|
||||
@ -522,35 +499,17 @@ parser.add_argument('--yacc',
|
||||
help='name of the bison executable, defaults to "bison"')
|
||||
|
||||
# Arguments passed through to bison
|
||||
parser.add_argument('-b',
|
||||
'--file-prefix',
|
||||
action='store',
|
||||
help='Passed to bison.')
|
||||
parser.add_argument('-d',
|
||||
'--definitions',
|
||||
action='store_true',
|
||||
help='Passed to bison.')
|
||||
parser.add_argument('-k',
|
||||
'--token-table',
|
||||
action='store_true',
|
||||
help='Passed to bison.')
|
||||
parser.add_argument('-b', '--file-prefix', action='store', help='Passed to bison.')
|
||||
parser.add_argument('-d', '--definitions', action='store_true', help='Passed to bison.')
|
||||
parser.add_argument('-k', '--token-table', action='store_true', help='Passed to bison.')
|
||||
parser.add_argument('-o',
|
||||
'--output',
|
||||
action='store',
|
||||
required=True,
|
||||
help='Passed to bison. Sets output file name')
|
||||
parser.add_argument('-p',
|
||||
'--name-prefix',
|
||||
action='store',
|
||||
help='Passed to bison.')
|
||||
parser.add_argument('-t',
|
||||
'--debug',
|
||||
action='store_true',
|
||||
help='Passed to bison.')
|
||||
parser.add_argument('-v',
|
||||
'--verbose',
|
||||
action='store_true',
|
||||
help='Passed to bison.')
|
||||
parser.add_argument('-p', '--name-prefix', action='store', help='Passed to bison.')
|
||||
parser.add_argument('-t', '--debug', action='store_true', help='Passed to bison.')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='Passed to bison.')
|
||||
|
||||
parser.add_argument('input', help='Passed to bison. Input grammar file.')
|
||||
|
||||
|
@ -115,11 +115,9 @@ def _suppress(filename, linenum, eid):
|
||||
return True
|
||||
if eid == 'constParameter' and re.search(r'gtkwave/', filename):
|
||||
return True
|
||||
if eid == 'ctuOneDefinitionRuleViolation' and re.search(
|
||||
r'vltstd/', filename):
|
||||
if eid == 'ctuOneDefinitionRuleViolation' and re.search(r'vltstd/', filename):
|
||||
return True
|
||||
if eid == 'duplicateConditionalAssign' and re.search(
|
||||
r'gtkwave/', filename):
|
||||
if eid == 'duplicateConditionalAssign' and re.search(r'gtkwave/', filename):
|
||||
return True
|
||||
if eid == 'knownConditionTrueFalse' and re.search(r'gtkwave/', filename):
|
||||
return True
|
||||
@ -151,8 +149,7 @@ def _suppress(filename, linenum, eid):
|
||||
return True
|
||||
|
||||
if not os.path.exists(filename):
|
||||
print("%Warning: " + filename + " does not exist, ignored",
|
||||
file=sys.stderr)
|
||||
print("%Warning: " + filename + " does not exist, ignored", file=sys.stderr)
|
||||
return False
|
||||
|
||||
with open(filename, "r", encoding="utf8") as fh:
|
||||
@ -160,13 +157,11 @@ def _suppress(filename, linenum, eid):
|
||||
for line in fh:
|
||||
lineno += 1
|
||||
if (lineno + 1) == linenum:
|
||||
match = re.search(
|
||||
r'(cppcheck|cppcheck-has-bug|cppverilator)-suppress((\s+\S+)+)',
|
||||
line)
|
||||
match = re.search(r'(cppcheck|cppcheck-has-bug|cppverilator)-suppress((\s+\S+)+)',
|
||||
line)
|
||||
if match:
|
||||
for supid in match.group(2).split():
|
||||
if (supid == eid or (eid in SuppressMap
|
||||
and supid == SuppressMap[eid])):
|
||||
if (supid == eid or (eid in SuppressMap and supid == SuppressMap[eid])):
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -182,8 +177,7 @@ filters out unnecessary warnings related to Verilator. Run as:
|
||||
|
||||
cd $VERILATOR_ROOT
|
||||
make -k cppcheck""",
|
||||
epilog=
|
||||
"""Copyright 2014-2024 by Wilson Snyder. This program is free software; you
|
||||
epilog="""Copyright 2014-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.
|
||||
|
21
src/flexfix
21
src/flexfix
@ -16,23 +16,19 @@ import sys
|
||||
|
||||
for line in sys.stdin:
|
||||
# Fix flex 2.6.1 warning
|
||||
line = re.sub(
|
||||
r'for \( i = 0; i < _yybytes_len; \+\+i \)',
|
||||
r'for ( i = 0; (yy_size_t)(i) < (yy_size_t)(_yybytes_len); ++i )',
|
||||
line)
|
||||
line = re.sub(r'for \( i = 0; i < _yybytes_len; \+\+i \)',
|
||||
r'for ( i = 0; (yy_size_t)(i) < (yy_size_t)(_yybytes_len); ++i )', line)
|
||||
# Fix flex 2.6.0 warning
|
||||
line = re.sub(
|
||||
r'\(\(int\) \(\(yy_n_chars\) \+ number_to_move\) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size\)',
|
||||
r'((int) ((yy_n_chars) + number_to_move) > (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size)',
|
||||
line)
|
||||
line = re.sub(r' number_to_move == YY_MORE_ADJ ',
|
||||
r' (int)number_to_move == (int)YY_MORE_ADJ ', line)
|
||||
# Fix flex 2.5.4 namespace omission
|
||||
line = re.sub(r'^class istream;',
|
||||
'#include <iostream>\nusing namespace std;\n', line)
|
||||
# Fix flex 2.5.31 redefinition
|
||||
line = re.sub(r'(\#define\s+yyFlexLexer\s+yyFlexLexer)', r'//flexfix: \1',
|
||||
line = re.sub(r' number_to_move == YY_MORE_ADJ ', r' (int)number_to_move == (int)YY_MORE_ADJ ',
|
||||
line)
|
||||
# Fix flex 2.5.4 namespace omission
|
||||
line = re.sub(r'^class istream;', '#include <iostream>\nusing namespace std;\n', line)
|
||||
# Fix flex 2.5.31 redefinition
|
||||
line = re.sub(r'(\#define\s+yyFlexLexer\s+yyFlexLexer)', r'//flexfix: \1', line)
|
||||
# Fix flex 2.5.1 yytext_ptr undef
|
||||
line = re.sub(r'(\#undef\s+yytext_ptr)', r'//flexfix: \1', line)
|
||||
# Fix flex 2.5.4 and GCC 4.1.0 warn_unused_result
|
||||
@ -41,8 +37,7 @@ for line in sys.stdin:
|
||||
line = re.sub(r'for \( n = 0; n < max_size && ',
|
||||
r'for ( n = 0; ((size_t)n < (size_t)max_size) && ', line)
|
||||
# Fix flex 2.5.4 and GCC 4.0.2 under FLEX_DEBUG
|
||||
line = re.sub(r'--accepting rule at line %d ',
|
||||
r'--accepting rule at line %ld ', line)
|
||||
line = re.sub(r'--accepting rule at line %d ', r'--accepting rule at line %ld ', line)
|
||||
# Fix compiler warning filenames
|
||||
line = re.sub(r'(#line \d+ ".*)_pretmp', r'\1', line)
|
||||
# Fix 'register' storage class specifier is deprecated and incompatible with C++17
|
||||
|
21
src/vlcovgen
21
src/vlcovgen
@ -19,11 +19,9 @@ def read_keys(filename):
|
||||
if re.match(r'^\s*$', line):
|
||||
continue
|
||||
if re.search(r'^\s*VLCOVGEN_ITEM', line):
|
||||
match = re.search(r'^\s*VLCOVGEN_ITEM *\( *"([^"]+)" *\)',
|
||||
line)
|
||||
match = re.search(r'^\s*VLCOVGEN_ITEM *\( *"([^"]+)" *\)', line)
|
||||
if not match:
|
||||
sys.exit("%Error: " + filename +
|
||||
": vlcovgen misformed VLCOVGEN_ITEM line")
|
||||
sys.exit("%Error: " + filename + ": vlcovgen misformed VLCOVGEN_ITEM line")
|
||||
code = "{" + match.group(1) + "}"
|
||||
data = eval(code)
|
||||
# pprint(data)
|
||||
@ -59,9 +57,8 @@ def write_keys(filename):
|
||||
deleting = True
|
||||
out.append(line)
|
||||
for keyref in sorted(Items, key=lambda a: a['name']):
|
||||
out.append(
|
||||
" if (key == \"%s\") return VL_CIK_%s;\n" %
|
||||
(keyref['name'], keyref['name'].upper()))
|
||||
out.append(" if (key == \"%s\") return VL_CIK_%s;\n" %
|
||||
(keyref['name'], keyref['name'].upper()))
|
||||
elif re.search(r'VLCOVGEN_.*AUTO_EDIT_END', line):
|
||||
deleting = False
|
||||
out.append(line)
|
||||
@ -80,19 +77,15 @@ def write_keys(filename):
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=
|
||||
"""Generate verilated_cov headers to reduce C++ code duplication.""",
|
||||
epilog=
|
||||
"""Copyright 2002-2024 by Wilson Snyder. This program is free software; you
|
||||
description="""Generate verilated_cov 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.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--srcdir',
|
||||
action='store',
|
||||
help='directory containing Verilator sources')
|
||||
parser.add_argument('--srcdir', action='store', help='directory containing Verilator sources')
|
||||
|
||||
parser.set_defaults(srcdir=".")
|
||||
|
||||
|
@ -42,8 +42,7 @@ for cmd in sys.stdin:
|
||||
if 'example_lint' in line:
|
||||
# We don't have a way to specify this yet, so just for now
|
||||
# sys.stderr.write($line)
|
||||
prefixes.append("int lint_off_line_" + str(lineno) +
|
||||
" = 1;\n")
|
||||
prefixes.append("int lint_off_line_" + str(lineno) + " = 1;\n")
|
||||
|
||||
lineno += 1
|
||||
pos = newpos + 1
|
||||
|
Loading…
Reference in New Issue
Block a user