[RISCV] Eliminate dead li after emitting VSETVLIs (#65934)
[llvm-project.git] / llvm / utils / reduce_pipeline.py
blob515fdd22b161726eeb4441a8b0359bee110a1ffb
1 #!/usr/bin/env python3
3 # Automatically formatted with yapf (https://github.com/google/yapf)
5 # Script for automatic 'opt' pipeline reduction for when using the new
6 # pass-manager (NPM). Based around the '-print-pipeline-passes' option.
8 # The reduction algorithm consists of several phases (steps).
10 # Step #0: Verify that input fails with the given pipeline and make note of the
11 # error code.
13 # Step #1: Split pipeline in two starting from front and move forward as long as
14 # first pipeline exits normally and the second pipeline fails with the expected
15 # error code. Move on to step #2 with the IR from the split point and the
16 # pipeline from the second invocation.
18 # Step #2: Remove passes from end of the pipeline as long as the pipeline fails
19 # with the expected error code.
21 # Step #3: Make several sweeps over the remaining pipeline trying to remove one
22 # pass at a time. Repeat sweeps until unable to remove any more passes.
24 # Usage example:
25 # reduce_pipeline.py --opt-binary=./build-all-Debug/bin/opt --input=input.ll --output=output.ll --passes=PIPELINE [EXTRA-OPT-ARGS ...]
27 import argparse
28 import pipeline
29 import shutil
30 import subprocess
31 import tempfile
33 parser = argparse.ArgumentParser(
34 description="Automatic opt pipeline reducer. Unrecognized arguments are forwarded to opt."
36 parser.add_argument("--opt-binary", action="store", dest="opt_binary", default="opt")
37 parser.add_argument("--passes", action="store", dest="passes", required=True)
38 parser.add_argument("--input", action="store", dest="input", required=True)
39 parser.add_argument("--output", action="store", dest="output")
40 parser.add_argument(
41 "--dont-expand-passes",
42 action="store_true",
43 dest="dont_expand_passes",
44 help="Do not expand pipeline before starting reduction.",
46 parser.add_argument(
47 "--dont-remove-empty-pm",
48 action="store_true",
49 dest="dont_remove_empty_pm",
50 help="Do not remove empty pass-managers from the pipeline during reduction.",
52 [args, extra_opt_args] = parser.parse_known_args()
54 print("The following extra args will be passed to opt: {}".format(extra_opt_args))
56 lst = pipeline.fromStr(args.passes)
57 ll_input = args.input
59 # Step #-1
60 # Launch 'opt' once with '-print-pipeline-passes' to expand pipeline before
61 # starting reduction. Allows specifying a default pipelines (e.g.
62 # '-passes=default<O3>').
63 if not args.dont_expand_passes:
64 run_args = [
65 args.opt_binary,
66 "-disable-symbolication",
67 "-disable-output",
68 "-print-pipeline-passes",
69 "-passes={}".format(pipeline.toStr(lst)),
70 ll_input,
72 run_args.extend(extra_opt_args)
73 opt = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
74 if opt.returncode != 0:
75 print("Failed to expand passes. Aborting.")
76 print(run_args)
77 print("exitcode: {}".format(opt.returncode))
78 print(opt.stderr.decode())
79 exit(1)
80 stdout = opt.stdout.decode()
81 stdout = stdout[: stdout.rfind("\n")]
82 lst = pipeline.fromStr(stdout)
83 print("Expanded pass sequence: {}".format(pipeline.toStr(lst)))
85 # Step #0
86 # Confirm that the given input, passes and options result in failure.
87 print("---Starting step #0---")
88 run_args = [
89 args.opt_binary,
90 "-disable-symbolication",
91 "-disable-output",
92 "-passes={}".format(pipeline.toStr(lst)),
93 ll_input,
95 run_args.extend(extra_opt_args)
96 opt = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
97 if opt.returncode >= 0:
98 print("Input does not result in failure as expected. Aborting.")
99 print(run_args)
100 print("exitcode: {}".format(opt.returncode))
101 print(opt.stderr.decode())
102 exit(1)
104 expected_error_returncode = opt.returncode
105 print('-passes="{}"'.format(pipeline.toStr(lst)))
107 # Step #1
108 # Try to narrow down the failing pass sequence by splitting the pipeline in two
109 # opt invocations (A and B) starting with invocation A only running the first
110 # pipeline pass and invocation B the remaining. Keep moving the split point
111 # forward as long as invocation A exits normally and invocation B fails with
112 # the expected error. This will accomplish two things first the input IR will be
113 # further reduced and second, with that IR, the reduced pipeline for invocation
114 # B will be sufficient to reproduce.
115 print("---Starting step #1---")
116 prevLstB = None
117 prevIntermediate = None
118 tmpd = tempfile.TemporaryDirectory()
120 for idx in range(pipeline.count(lst)):
121 [lstA, lstB] = pipeline.split(lst, idx)
122 if not args.dont_remove_empty_pm:
123 lstA = pipeline.prune(lstA)
124 lstB = pipeline.prune(lstB)
126 intermediate = "intermediate-0.ll" if idx % 2 else "intermediate-1.ll"
127 intermediate = tmpd.name + "/" + intermediate
128 run_args = [
129 args.opt_binary,
130 "-disable-symbolication",
131 "-S",
132 "-o",
133 intermediate,
134 "-passes={}".format(pipeline.toStr(lstA)),
135 ll_input,
137 run_args.extend(extra_opt_args)
138 optA = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
139 run_args = [
140 args.opt_binary,
141 "-disable-symbolication",
142 "-disable-output",
143 "-passes={}".format(pipeline.toStr(lstB)),
144 intermediate,
146 run_args.extend(extra_opt_args)
147 optB = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
148 if not (optA.returncode == 0 and optB.returncode == expected_error_returncode):
149 break
150 prevLstB = lstB
151 prevIntermediate = intermediate
152 if prevLstB:
153 lst = prevLstB
154 ll_input = prevIntermediate
155 print('-passes="{}"'.format(pipeline.toStr(lst)))
157 # Step #2
158 # Try removing passes from the end of the remaining pipeline while still
159 # reproducing the error.
160 print("---Starting step #2---")
161 prevLstA = None
162 for idx in reversed(range(pipeline.count(lst))):
163 [lstA, lstB] = pipeline.split(lst, idx)
164 if not args.dont_remove_empty_pm:
165 lstA = pipeline.prune(lstA)
166 run_args = [
167 args.opt_binary,
168 "-disable-symbolication",
169 "-disable-output",
170 "-passes={}".format(pipeline.toStr(lstA)),
171 ll_input,
173 run_args.extend(extra_opt_args)
174 optA = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
175 if optA.returncode != expected_error_returncode:
176 break
177 prevLstA = lstA
178 if prevLstA:
179 lst = prevLstA
180 print('-passes="{}"'.format(pipeline.toStr(lst)))
182 # Step #3
183 # Now that we have a pipeline that is reduced both front and back we do
184 # exhaustive sweeps over the remainder trying to remove one pass at a time.
185 # Repeat as long as reduction is possible.
186 print("---Starting step #3---")
187 while True:
188 keepGoing = False
189 for idx in range(pipeline.count(lst)):
190 candLst = pipeline.remove(lst, idx)
191 if not args.dont_remove_empty_pm:
192 candLst = pipeline.prune(candLst)
193 run_args = [
194 args.opt_binary,
195 "-disable-symbolication",
196 "-disable-output",
197 "-passes={}".format(pipeline.toStr(candLst)),
198 ll_input,
200 run_args.extend(extra_opt_args)
201 opt = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
202 if opt.returncode == expected_error_returncode:
203 lst = candLst
204 keepGoing = True
205 if not keepGoing:
206 break
207 print('-passes="{}"'.format(pipeline.toStr(lst)))
209 print("---FINISHED---")
210 if args.output:
211 shutil.copy(ll_input, args.output)
212 print("Wrote output to '{}'.".format(args.output))
213 print('-passes="{}"'.format(pipeline.toStr(lst)))
214 exit(0)