diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2d256706d..421a82a0b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -198,7 +198,7 @@ jobs: - name: Runt tests working-directory: /home/calyx run: | - runt -x 'cocotb' -d -o fail -j 1 --max-futures 5 + runt -x 'cocotb|profiler' -d -o fail -j 1 --max-futures 5 - name: Run Python Tests working-directory: /home/calyx diff --git a/.gitignore b/.gitignore index 6ca02ce05..1f773c74e 100644 --- a/.gitignore +++ b/.gitignore @@ -55,9 +55,8 @@ results.xml # btor2i ignore tools/btor2/btor2i/build/ -# vcd-parsing tmp files ignore -tools/profiler/tmp/ -tools/profiler/logs +# profiling ignore +tools/profiler/data temp/ @@ -67,3 +66,6 @@ frontends/queues/tests/**/*.expect # for running a venv .venv + +# emacs +*~ diff --git a/Dockerfile b/Dockerfile index 847d67ddd..9b0fef04d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,8 @@ ENV PATH="/opt/venv/bin:$PATH" RUN python3 -m pip install numpy==1.26.4 flit prettytable wheel hypothesis pytest simplejson cocotb==1.6.2 # Current cocotb-bus has a bug that is fixed in more up to date repo RUN python3 -m pip install git+https://github.com/cocotb/cocotb-bus.git cocotbext-axi +# Vcdvcd for profiling +RUN python3 -m pip install vcdvcd # Install clang RUN apt-get install -y clang diff --git a/runt.toml b/runt.toml index 5533ace48..08b46395b 100644 --- a/runt.toml +++ b/runt.toml @@ -686,3 +686,11 @@ make --silent -B TEST_PATH={} COCOTB_LOG_LEVEL=CRITICAL |\ rm -f results.xml """ + +### Profiler tests +[[tests]] +name = "profiler" +paths = ["tests/profiler/*.futil"] +cmd = """ +bash tools/profiler/get-profile-counts-info.sh {} {}.data STDOUT -d +""" diff --git a/tests/profiler/par.expect b/tests/profiler/par.expect new file mode 100644 index 000000000..ee7aa537c --- /dev/null +++ b/tests/profiler/par.expect @@ -0,0 +1,66 @@ +[get-profile-counts-info.sh] Obtaining FSM info from TDCC +[get-profile-counts-info.sh] Obtaining cell information from component-cells backend +[get-profile-counts-info.sh] Obtaining VCD file via simulation +[get-profile-counts-info.sh] Using FSM info and VCD file to obtain cycle level counts +Total clock cycles: 3 +=====SUMMARY===== + +Group TOP.toplevel.main.par0 Summary: + Total cycles: 3 + # of times active: 1 + Avg runtime: 3.0 + +Group TOP.toplevel.main.wr_a Summary: + Total cycles: 1 + # of times active: 1 + Avg runtime: 1.0 + +Group TOP.toplevel.main.wr_b Summary: + Total cycles: 1 + # of times active: 1 + Avg runtime: 1.0 + +Group TOP.toplevel.main.wr_c Summary: + Total cycles: 1 + # of times active: 1 + Avg runtime: 1.0 + +=====DUMP===== + +Group TOP.toplevel.main.par0: + FSM name: TOP.toplevel.main.fsm + FSM state ids: [0] + Total cycles: 3 + # of times active: 1 + Segments: [0, 3) + +Group TOP.toplevel.main.wr_a: + FSM name: None + FSM state ids: None + Total cycles: 1 + # of times active: 1 + Segments: [0, 1) + +Group TOP.toplevel.main.wr_b: + FSM name: None + FSM state ids: None + Total cycles: 1 + # of times active: 1 + Segments: [0, 1) + +Group TOP.toplevel.main.wr_c: + FSM name: None + FSM state ids: None + Total cycles: 1 + # of times active: 1 + Segments: [0, 1) + +Writing dump JSON to /home/ayaka/projects/calyx/tools/profiler/data//tmp/dump.json +Writing summary to STDOUT +name,total-cycles,times-active,avg +TOP.toplevel.main.par0,3,1,3.0 +TOP.toplevel.main.wr_a,1,1,1.0 +TOP.toplevel.main.wr_b,1,1,1.0 +TOP.toplevel.main.wr_c,1,1,1.0 +TOTAL,3,-,- +[get-profile-counts-info.sh] Writing visualization to /home/ayaka/projects/calyx/tools/profiler/data//tmp/visual.json diff --git a/tests/profiler/par.futil b/tests/profiler/par.futil new file mode 100644 index 000000000..1f71a5e15 --- /dev/null +++ b/tests/profiler/par.futil @@ -0,0 +1,41 @@ +import "primitives/core.futil"; +import "primitives/memories/comb.futil"; + +component main() -> () { + cells { + @external(1) a = comb_mem_d1(32, 1, 1); + @external(1) b = comb_mem_d1(32, 1, 1); + @external(1) c = comb_mem_d1(32, 1, 1); + } + + wires { + group wr_a { + a.addr0 = 1'b0; + a.write_en = 1'b1; + a.write_data = 32'd1; + wr_a[done] = a.done; + } + + group wr_b { + b.addr0 = 1'b0; + b.write_en = 1'b1; + b.write_data = 32'd1; + wr_b[done] = b.done; + } + + group wr_c { + c.addr0 = 1'b0; + c.write_en = 1'b1; + c.write_data = 32'd1; + wr_c[done] = c.done; + } + } + + control { + par { + wr_a; + wr_b; + wr_c; + } + } +} diff --git a/tests/profiler/par.futil.data b/tests/profiler/par.futil.data new file mode 100644 index 000000000..4862df80f --- /dev/null +++ b/tests/profiler/par.futil.data @@ -0,0 +1,32 @@ +{ + "a": { + "data": [ + 0 + ], + "format": { + "numeric_type": "bitnum", + "is_signed": true, + "width": 32 + } + }, + "b": { + "data": [ + 0 + ], + "format": { + "numeric_type": "bitnum", + "is_signed": false, + "width": 32 + } + }, + "c": { + "data": [ + 0 + ], + "format": { + "numeric_type": "bitnum", + "is_signed": false, + "width": 32 + } + } +} diff --git a/tests/profiler/simple-par.expect b/tests/profiler/simple-par.expect new file mode 100644 index 000000000..b2fccedf3 --- /dev/null +++ b/tests/profiler/simple-par.expect @@ -0,0 +1,40 @@ +[get-profile-counts-info.sh] Obtaining FSM info from TDCC +[get-profile-counts-info.sh] Obtaining cell information from component-cells backend +[get-profile-counts-info.sh] Obtaining VCD file via simulation +[get-profile-counts-info.sh] Using FSM info and VCD file to obtain cycle level counts +Total clock cycles: 3 +=====SUMMARY===== + +Group TOP.toplevel.main.par0 Summary: + Total cycles: 3 + # of times active: 1 + Avg runtime: 3.0 + +Group TOP.toplevel.main.write Summary: + Total cycles: 1 + # of times active: 1 + Avg runtime: 1.0 + +=====DUMP===== + +Group TOP.toplevel.main.par0: + FSM name: TOP.toplevel.main.fsm + FSM state ids: [0] + Total cycles: 3 + # of times active: 1 + Segments: [0, 3) + +Group TOP.toplevel.main.write: + FSM name: None + FSM state ids: None + Total cycles: 1 + # of times active: 1 + Segments: [0, 1) + +Writing dump JSON to /home/ayaka/projects/calyx/tools/profiler/data//tmp/dump.json +Writing summary to STDOUT +name,total-cycles,times-active,avg +TOP.toplevel.main.par0,3,1,3.0 +TOP.toplevel.main.write,1,1,1.0 +TOTAL,3,-,- +[get-profile-counts-info.sh] Writing visualization to /home/ayaka/projects/calyx/tools/profiler/data//tmp/visual.json diff --git a/tests/profiler/simple-par.futil b/tests/profiler/simple-par.futil new file mode 100644 index 000000000..a66b7ab5e --- /dev/null +++ b/tests/profiler/simple-par.futil @@ -0,0 +1,23 @@ +import "primitives/core.futil"; +import "primitives/memories/comb.futil"; + +component main() -> () { + cells { + @external(1) a = comb_mem_d1(32, 1, 1); + } + + wires { + group write { + a.addr0 = 1'b0; + a.write_en = 1'b1; + a.write_data = 32'd1; + write[done] = a.done; + } + } + + control { + par { + write; + } + } +} diff --git a/tests/profiler/simple-par.futil.data b/tests/profiler/simple-par.futil.data new file mode 100644 index 000000000..cad4ef0e4 --- /dev/null +++ b/tests/profiler/simple-par.futil.data @@ -0,0 +1,12 @@ +{ + "a": { + "data": [ + 0 + ], + "format": { + "numeric_type": "bitnum", + "is_signed": true, + "width": 32 + } + } +} diff --git a/tests/profiler/simple-seq.expect b/tests/profiler/simple-seq.expect new file mode 100644 index 000000000..cd145ed7b --- /dev/null +++ b/tests/profiler/simple-seq.expect @@ -0,0 +1,27 @@ +[get-profile-counts-info.sh] Obtaining FSM info from TDCC +[get-profile-counts-info.sh] Obtaining cell information from component-cells backend +[get-profile-counts-info.sh] Obtaining VCD file via simulation +[get-profile-counts-info.sh] Using FSM info and VCD file to obtain cycle level counts +Total clock cycles: 2 +=====SUMMARY===== + +Group TOP.toplevel.main.write Summary: + Total cycles: 2 + # of times active: 1 + Avg runtime: 2.0 + +=====DUMP===== + +Group TOP.toplevel.main.write: + FSM name: TOP.toplevel.main.fsm + FSM state ids: [0] + Total cycles: 2 + # of times active: 1 + Segments: [0, 2) + +Writing dump JSON to /home/ayaka/projects/calyx/tools/profiler/data//tmp/dump.json +Writing summary to STDOUT +name,total-cycles,times-active,avg +TOP.toplevel.main.write,2,1,2.0 +TOTAL,2,-,- +[get-profile-counts-info.sh] Writing visualization to /home/ayaka/projects/calyx/tools/profiler/data//tmp/visual.json diff --git a/tests/profiler/simple-seq.futil b/tests/profiler/simple-seq.futil new file mode 100644 index 000000000..9bf79820a --- /dev/null +++ b/tests/profiler/simple-seq.futil @@ -0,0 +1,23 @@ +import "primitives/core.futil"; +import "primitives/memories/comb.futil"; + +component main() -> () { + cells { + @external(1) a = comb_mem_d1(32, 1, 1); + } + + wires { + group write { + a.addr0 = 1'b0; + a.write_en = 1'b1; + a.write_data = 32'd1; + write[done] = a.done; + } + } + + control { + seq { + write; + } + } +} diff --git a/tests/profiler/simple-seq.futil.data b/tests/profiler/simple-seq.futil.data new file mode 100644 index 000000000..cad4ef0e4 --- /dev/null +++ b/tests/profiler/simple-seq.futil.data @@ -0,0 +1,12 @@ +{ + "a": { + "data": [ + 0 + ], + "format": { + "numeric_type": "bitnum", + "is_signed": true, + "width": 32 + } + } +} diff --git a/tests/profiler/while-never-true.futil b/tests/profiler/while-never-true.futil new file mode 100644 index 000000000..97805bd8f --- /dev/null +++ b/tests/profiler/while-never-true.futil @@ -0,0 +1,46 @@ +import "primitives/core.futil"; +import "primitives/memories/comb.futil"; + +component main() -> () { + cells { + @external(1) i = comb_mem_d1(32, 1, 1); + lt = std_lt(32); + lt_reg = std_reg(1); + add = std_add(32); + } + + wires { + group cond { + i.addr0 = 1'd0; + lt.left = i.read_data; + lt.right = 32'd0; + lt_reg.in = lt.out; + lt_reg.write_en = 1'b1; + cond[done] = lt_reg.done; + } + + group incr { + add.right = i.read_data; + add.left = 32'd1; + + i.write_data = add.out; + i.addr0 = 1'd0; + i.write_en = 1'b1; + + incr[done] = i.done; + } + } + + control { + seq { + cond; + while lt_reg.out { + seq { + incr; + incr; + cond; + } + } + } + } +} diff --git a/tests/profiler/while-never-true.futil.data b/tests/profiler/while-never-true.futil.data new file mode 100644 index 000000000..a2b95ef52 --- /dev/null +++ b/tests/profiler/while-never-true.futil.data @@ -0,0 +1,10 @@ +{ + "i": { + "data": [0], + "format": { + "numeric_type": "bitnum", + "is_signed": false, + "width": 32 + } + } +} diff --git a/tools/profiler/convert-dump.py b/tools/profiler/convert-dump.py new file mode 100644 index 000000000..8cab93565 --- /dev/null +++ b/tools/profiler/convert-dump.py @@ -0,0 +1,40 @@ +# Takes in a dump file created by parse-vcd.py and creates a JSON file in the Google Trace Event Format +import json +import sys + +# Starting with the JSON array format for now... +# example +# [ {"name": "Asub", "cat": "PERF", "ph": "B", "pid": 22630, "tid": 22630, "ts": 829}, +# {"name": "Asub", "cat": "PERF", "ph": "E", "pid": 22630, "tid": 22630, "ts": 833} ] + +def main(profiler_dump_file, out_file): + profiled_info = json.load(open(profiler_dump_file, "r")) + cat = "C" # Category... might do something with this later + events = [] + id_acc = 1 + ts_multiplier = 100 # some arbitrary number to multiply by so that it's easier to see in the viewer + for group_info in profiled_info: + name = group_info["name"].split("TOP.toplevel.", 1)[1] + for segment in group_info["closed_segments"]: + # beginning of segment + begin_time = segment["start"] * ts_multiplier + events.append({"name": name, "cat": cat, "ph": "B", "pid" : id_acc, "tid": id_acc, "ts" : begin_time}) + # end of segment + end_time = segment["end"] * ts_multiplier + events.append({"name": name, "cat": cat, "ph": "E", "pid": id_acc, "tid": id_acc, "ts": end_time}) + id_acc += 1 + with open(out_file, "w") as out: + json.dump(events, out, indent=4) + +if __name__ == "__main__": + if len(sys.argv) > 2: + profiler_dump_json = sys.argv[1] + visuals_json = sys.argv[2] + main(profiler_dump_json, visuals_json) + else: + args_desc = [ + "PROFILER_JSON", + "VISUALS_JSON" + ] + print(f"Usage: {sys.argv[0]} {' '.join(args_desc)}") + sys.exit(-1) diff --git a/tools/profiler/get-profile-counts-info.sh b/tools/profiler/get-profile-counts-info.sh index 466ba08df..f2d3cc75c 100644 --- a/tools/profiler/get-profile-counts-info.sh +++ b/tools/profiler/get-profile-counts-info.sh @@ -8,22 +8,31 @@ fi SCRIPT_DIR=$( cd $( dirname $0 ) && pwd ) SCRIPT_NAME=$( echo "$0" | rev | cut -d/ -f1 | rev ) CALYX_DIR=$( dirname $( dirname ${SCRIPT_DIR} ) ) -TMP_DIR=${SCRIPT_DIR}/tmp +name=$( echo "${INPUT_FILE}" | rev | cut -d/ -f1 | rev | cut -d. -f1 ) +DATA_DIR=${SCRIPT_DIR}/data/${name} +TMP_DIR=${DATA_DIR}/tmp + +INPUT_FILE=$1 +SIM_DATA_JSON=$2 +if [[ $# -ge 3 ]]; then + OUT_CSV=$3 +else + OUT_CSV=${TMP_DIR}/summary.csv +fi + TMP_VERILOG=${TMP_DIR}/no-opt-verilog.sv FSM_JSON=${TMP_DIR}/fsm.json CELLS_JSON=${TMP_DIR}/cells.json -OUT_CSV=${TMP_DIR}/summary.csv -VCD_FILE=${TMP_DIR}/trace-info.vcd -LOGS_DIR=${SCRIPT_DIR}/logs +OUT_JSON=${TMP_DIR}/dump.json +VISUALS_JSON=${TMP_DIR}/visual.json +VCD_FILE=${TMP_DIR}/trace.vcd +LOGS_DIR=${DATA_DIR}/logs +if [ -d ${DATA_DIR} ]; then + rm -rf ${DATA_DIR} # clean out directory for run each time +fi mkdir -p ${TMP_DIR} ${LOGS_DIR} - rm -f ${TMP_DIR}/* ${LOGS_DIR}/* # remove data from last run -INPUT_FILE=$1 -SIM_DATA_JSON=$2 -if [ $# -eq 3 ]; then - OUT_CSV=$3 -fi # Run TDCC to get the FSM info echo "[${SCRIPT_NAME}] Obtaining FSM info from TDCC" @@ -46,12 +55,23 @@ echo "[${SCRIPT_NAME}] Obtaining cell information from component-cells backend" echo "[${SCRIPT_NAME}] Obtaining VCD file via simulation" ( set -o xtrace - fud2 ${INPUT_FILE} -o ${VCD_FILE} -s calyx.args='-p no-opt' -s sim.data=${SIM_DATA_JSON} + fud2 ${INPUT_FILE} -o ${VCD_FILE} --through verilator -s calyx.args='-p no-opt' -s sim.data=${SIM_DATA_JSON} set +o xtrace ) &> ${LOGS_DIR}/gol-vcd # Run script to get cycle level counts echo "[${SCRIPT_NAME}] Using FSM info and VCD file to obtain cycle level counts" ( - python3 ${SCRIPT_DIR}/parse-vcd.py ${VCD_FILE} ${FSM_JSON} ${CELLS_JSON} ${OUT_CSV} -) # &> ${LOGS_DIR}/gol-process + python3 ${SCRIPT_DIR}/parse-vcd.py ${VCD_FILE} ${FSM_JSON} ${CELLS_JSON} ${OUT_CSV} ${OUT_JSON} +) &> ${LOGS_DIR}/gol-process + +if [ "$4" == "-d" ]; then + cat ${LOGS_DIR}/gol-process +else + tail -2 ${LOGS_DIR}/gol-process +fi + +echo "[${SCRIPT_NAME}] Writing visualization to ${VISUALS_JSON}" +( + python3 ${SCRIPT_DIR}/convert-dump.py ${OUT_JSON} ${VISUALS_JSON} +) &> ${LOGS_DIR}/gol-visuals diff --git a/tools/profiler/parse-vcd.py b/tools/profiler/parse-vcd.py index 6c4361b11..c95c9bd5d 100644 --- a/tools/profiler/parse-vcd.py +++ b/tools/profiler/parse-vcd.py @@ -83,11 +83,8 @@ def __init__(self, fsms, single_enable_names, tdcc_group_names, fsm_group_maps, self.single_enable_names = single_enable_names self.cells_to_components = cells_to_components # Recording the first cycle when the TDCC group became active - # FIXME: remove after fixing enddefinitions self.tdcc_group_active_cycle = {tdcc_group_name : -1 for tdcc_group_name in tdcc_group_names} self.tdcc_group_to_go_id = {tdcc_group_name : None for tdcc_group_name in tdcc_group_names} - # self.tdcc_group_active_cycle = {} # filled in enddefinitions - # self.tdcc_group_to_go_id = {} # filled in enddefinitions self.profiling_info = {} self.signal_to_signal_id = {fsm : None for fsm in fsms} self.signal_to_curr_value = {fsm : 0 for fsm in fsms} @@ -160,7 +157,7 @@ def value( signal_curr_value = self.signal_to_curr_value[signal_name] signal_new_value = int(cur_sig_vals[signal_id], 2) # signal value at this point in time if "_go" in signal_name and signal_new_value == 1: - # start of single enable group + # start of group ground truth group = "_".join(signal_name.split("_")[0:-1]) curr_group_info = self.profiling_info[group] # We want to start a segment regardless of whether it changed @@ -252,7 +249,7 @@ def remap_tdcc_json(json_file, components_to_cells): return fsms, single_enable_names, tdcc_group_names, fsm_group_maps -def main(vcd_filename, groups_json_file, cells_json_file, out_csv): +def main(vcd_filename, groups_json_file, cells_json_file, out_csv, dump_out_json): main_component, components_to_cells = read_component_cell_names_json(cells_json_file) fsms, single_enable_names, tdcc_group_names, fsm_group_maps = remap_tdcc_json(groups_json_file, components_to_cells) converter = VCDConverter(fsms, single_enable_names, tdcc_group_names, fsm_group_maps, components_to_cells, main_component) @@ -264,37 +261,48 @@ def main(vcd_filename, groups_json_file, cells_json_file, out_csv): groups_to_emit.sort(key=lambda x : x.name) # to preserve stability groups_to_emit.sort(key=lambda x : x.total_cycles, reverse=True) csv_acc = [] + dump_json_acc = [] for group_info in groups_to_emit: csv_acc.append(group_info.emit_csv_data()) + dump_json_acc.append(group_info.__dict__) print(group_info.summary()) print("=====DUMP=====") print() for group_info in groups_to_emit: print(group_info) + # emit a json for visualizer script + print(f"Writing dump JSON to {dump_out_json}") + with open(dump_out_json, "w", encoding="utf-8") as dump_file: + dump_file.write(json.dumps(dump_json_acc, indent=4)) # emitting a CSV file for easier eyeballing print(f"Writing summary to {out_csv}") - with open(out_csv, 'w') as csvfile: - csv_keys = ["name", "total-cycles", "times-active", "avg"] - csv_acc.append({ "name": "TOTAL", "total-cycles": converter.clock_cycle_acc, "times-active": "-", "avg": "-"}) - writer = csv.DictWriter(csvfile, csv_keys, lineterminator="\n") - writer.writeheader() - writer.writerows(csv_acc) + csv_keys = ["name", "total-cycles", "times-active", "avg"] + csv_acc.append({ "name": "TOTAL", "total-cycles": converter.clock_cycle_acc, "times-active": "-", "avg": "-"}) + if (out_csv == "STDOUT"): + writer = csv.DictWriter(sys.stdout, csv_keys, lineterminator="\n") + else: + writer = csv.DictWriter(open(out_csv, "w"), csv_keys, lineterminator="\n") + writer.writeheader() + writer.writerows(csv_acc) if __name__ == "__main__": - if len(sys.argv) > 4: + if len(sys.argv) > 5: vcd_filename = sys.argv[1] fsm_json = sys.argv[2] cells_json = sys.argv[3] out_csv = sys.argv[4] - main(vcd_filename, fsm_json, cells_json, out_csv) + dump_out_json = sys.argv[5] + main(vcd_filename, fsm_json, cells_json, out_csv, dump_out_json) else: args_desc = [ "VCD_FILE", "TDCC_JSON", "CELLS_JSON", - "SUMMARY_OUT_CSV" + "SUMMARY_OUT_CSV", + "DUMP_OUT_JSON" ] print(f"Usage: {sys.argv[0]} {' '.join(args_desc)}") print("TDCC_JSON: Run Calyx with `tdcc:dump-fsm-json` option") print("CELLS_JSON: Run Calyx with `component-cells` backend") + print("If SUMMARY_OUT_CSV is STDOUT, then summary CSV will be printed to stdout") sys.exit(-1) diff --git a/tools/profiler/run-up-to-tdcc.sh b/tools/profiler/run-up-to-tdcc.sh new file mode 100644 index 000000000..7c77fae73 --- /dev/null +++ b/tools/profiler/run-up-to-tdcc.sh @@ -0,0 +1,24 @@ +# small script to get the TDCC-translated Calyx program before it goes through any other passes + +if [ $# -lt 1 ]; then + echo "USAGE: bash $0 CALYX_FILE [OPT]" + echo "if OPT is -o, then runs with all optimizations (in august 2024) enabled" + exit +fi + +SCRIPT_DIR=$( cd $( dirname $0 ) && pwd ) +CALYX_DIR=$( dirname $( dirname ${SCRIPT_DIR} ) ) + +if [ "$2" == "-o" ]; then +( + cd ${CALYX_DIR} + cargo run $1 -p well-formed -p papercut -p canonicalize -p infer-data-path -p collapse-control -p compile-sync-without-sync-reg -p group2seq -p dead-assign-removal -p group2invoke -p infer-share -p inline -p comb-prop -p dead-cell-removal -p cell-share -p simplify-with-control -p compile-invoke -p static-inference -p static-promotion -p compile-repeat -p dead-group-removal -p collapse-control -p static-inline -p merge-assigns -p dead-group-removal -p simplify-static-guards -p add-guard -p static-fsm-opts -p compile-static -p dead-group-removal -p tdcc +) +else + +( + cd ${CALYX_DIR} + cargo run $1 -p well-formed -p papercut -p canonicalize -p compile-sync -p simplify-with-control -p compile-invoke -p static-inline -p merge-assigns -p dead-group-removal -p simplify-static-guards -p add-guard -p static-fsm-opts -p compile-static -p dead-group-removal -p tdcc +) + +fi