mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 04:46:08 +00:00

This is the first commit in a series that will reformat all the python files in the LLVM repository. Reformatting is done with `black`. See more information here: https://discourse.llvm.org/t/rfc-document-and-standardize-python-code-style Reviewed By: jhenderson, JDevlieghere, MatzeB Differential Revision: https://reviews.llvm.org/D150545
215 lines
7.4 KiB
Python
Executable File
215 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Automatically formatted with yapf (https://github.com/google/yapf)
|
|
|
|
# Script for automatic 'opt' pipeline reduction for when using the new
|
|
# pass-manager (NPM). Based around the '-print-pipeline-passes' option.
|
|
#
|
|
# The reduction algorithm consists of several phases (steps).
|
|
#
|
|
# Step #0: Verify that input fails with the given pipeline and make note of the
|
|
# error code.
|
|
#
|
|
# Step #1: Split pipeline in two starting from front and move forward as long as
|
|
# first pipeline exits normally and the second pipeline fails with the expected
|
|
# error code. Move on to step #2 with the IR from the split point and the
|
|
# pipeline from the second invocation.
|
|
#
|
|
# Step #2: Remove passes from end of the pipeline as long as the pipeline fails
|
|
# with the expected error code.
|
|
#
|
|
# Step #3: Make several sweeps over the remaining pipeline trying to remove one
|
|
# pass at a time. Repeat sweeps until unable to remove any more passes.
|
|
#
|
|
# Usage example:
|
|
# reduce_pipeline.py --opt-binary=./build-all-Debug/bin/opt --input=input.ll --output=output.ll --passes=PIPELINE [EXTRA-OPT-ARGS ...]
|
|
|
|
import argparse
|
|
import pipeline
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Automatic opt pipeline reducer. Unrecognized arguments are forwarded to opt."
|
|
)
|
|
parser.add_argument("--opt-binary", action="store", dest="opt_binary", default="opt")
|
|
parser.add_argument("--passes", action="store", dest="passes", required=True)
|
|
parser.add_argument("--input", action="store", dest="input", required=True)
|
|
parser.add_argument("--output", action="store", dest="output")
|
|
parser.add_argument(
|
|
"--dont-expand-passes",
|
|
action="store_true",
|
|
dest="dont_expand_passes",
|
|
help="Do not expand pipeline before starting reduction.",
|
|
)
|
|
parser.add_argument(
|
|
"--dont-remove-empty-pm",
|
|
action="store_true",
|
|
dest="dont_remove_empty_pm",
|
|
help="Do not remove empty pass-managers from the pipeline during reduction.",
|
|
)
|
|
[args, extra_opt_args] = parser.parse_known_args()
|
|
|
|
print("The following extra args will be passed to opt: {}".format(extra_opt_args))
|
|
|
|
lst = pipeline.fromStr(args.passes)
|
|
ll_input = args.input
|
|
|
|
# Step #-1
|
|
# Launch 'opt' once with '-print-pipeline-passes' to expand pipeline before
|
|
# starting reduction. Allows specifying a default pipelines (e.g.
|
|
# '-passes=default<O3>').
|
|
if not args.dont_expand_passes:
|
|
run_args = [
|
|
args.opt_binary,
|
|
"-disable-symbolication",
|
|
"-disable-output",
|
|
"-print-pipeline-passes",
|
|
"-passes={}".format(pipeline.toStr(lst)),
|
|
ll_input,
|
|
]
|
|
run_args.extend(extra_opt_args)
|
|
opt = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if opt.returncode != 0:
|
|
print("Failed to expand passes. Aborting.")
|
|
print(run_args)
|
|
print("exitcode: {}".format(opt.returncode))
|
|
print(opt.stderr.decode())
|
|
exit(1)
|
|
stdout = opt.stdout.decode()
|
|
stdout = stdout[: stdout.rfind("\n")]
|
|
lst = pipeline.fromStr(stdout)
|
|
print("Expanded pass sequence: {}".format(pipeline.toStr(lst)))
|
|
|
|
# Step #0
|
|
# Confirm that the given input, passes and options result in failure.
|
|
print("---Starting step #0---")
|
|
run_args = [
|
|
args.opt_binary,
|
|
"-disable-symbolication",
|
|
"-disable-output",
|
|
"-passes={}".format(pipeline.toStr(lst)),
|
|
ll_input,
|
|
]
|
|
run_args.extend(extra_opt_args)
|
|
opt = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if opt.returncode >= 0:
|
|
print("Input does not result in failure as expected. Aborting.")
|
|
print(run_args)
|
|
print("exitcode: {}".format(opt.returncode))
|
|
print(opt.stderr.decode())
|
|
exit(1)
|
|
|
|
expected_error_returncode = opt.returncode
|
|
print('-passes="{}"'.format(pipeline.toStr(lst)))
|
|
|
|
# Step #1
|
|
# Try to narrow down the failing pass sequence by splitting the pipeline in two
|
|
# opt invocations (A and B) starting with invocation A only running the first
|
|
# pipeline pass and invocation B the remaining. Keep moving the split point
|
|
# forward as long as invocation A exits normally and invocation B fails with
|
|
# the expected error. This will accomplish two things first the input IR will be
|
|
# further reduced and second, with that IR, the reduced pipeline for invocation
|
|
# B will be sufficient to reproduce.
|
|
print("---Starting step #1---")
|
|
prevLstB = None
|
|
prevIntermediate = None
|
|
tmpd = tempfile.TemporaryDirectory()
|
|
|
|
for idx in range(pipeline.count(lst)):
|
|
[lstA, lstB] = pipeline.split(lst, idx)
|
|
if not args.dont_remove_empty_pm:
|
|
lstA = pipeline.prune(lstA)
|
|
lstB = pipeline.prune(lstB)
|
|
|
|
intermediate = "intermediate-0.ll" if idx % 2 else "intermediate-1.ll"
|
|
intermediate = tmpd.name + "/" + intermediate
|
|
run_args = [
|
|
args.opt_binary,
|
|
"-disable-symbolication",
|
|
"-S",
|
|
"-o",
|
|
intermediate,
|
|
"-passes={}".format(pipeline.toStr(lstA)),
|
|
ll_input,
|
|
]
|
|
run_args.extend(extra_opt_args)
|
|
optA = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
run_args = [
|
|
args.opt_binary,
|
|
"-disable-symbolication",
|
|
"-disable-output",
|
|
"-passes={}".format(pipeline.toStr(lstB)),
|
|
intermediate,
|
|
]
|
|
run_args.extend(extra_opt_args)
|
|
optB = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if not (optA.returncode == 0 and optB.returncode == expected_error_returncode):
|
|
break
|
|
prevLstB = lstB
|
|
prevIntermediate = intermediate
|
|
if prevLstB:
|
|
lst = prevLstB
|
|
ll_input = prevIntermediate
|
|
print('-passes="{}"'.format(pipeline.toStr(lst)))
|
|
|
|
# Step #2
|
|
# Try removing passes from the end of the remaining pipeline while still
|
|
# reproducing the error.
|
|
print("---Starting step #2---")
|
|
prevLstA = None
|
|
for idx in reversed(range(pipeline.count(lst))):
|
|
[lstA, lstB] = pipeline.split(lst, idx)
|
|
if not args.dont_remove_empty_pm:
|
|
lstA = pipeline.prune(lstA)
|
|
run_args = [
|
|
args.opt_binary,
|
|
"-disable-symbolication",
|
|
"-disable-output",
|
|
"-passes={}".format(pipeline.toStr(lstA)),
|
|
ll_input,
|
|
]
|
|
run_args.extend(extra_opt_args)
|
|
optA = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if optA.returncode != expected_error_returncode:
|
|
break
|
|
prevLstA = lstA
|
|
if prevLstA:
|
|
lst = prevLstA
|
|
print('-passes="{}"'.format(pipeline.toStr(lst)))
|
|
|
|
# Step #3
|
|
# Now that we have a pipeline that is reduced both front and back we do
|
|
# exhaustive sweeps over the remainder trying to remove one pass at a time.
|
|
# Repeat as long as reduction is possible.
|
|
print("---Starting step #3---")
|
|
while True:
|
|
keepGoing = False
|
|
for idx in range(pipeline.count(lst)):
|
|
candLst = pipeline.remove(lst, idx)
|
|
if not args.dont_remove_empty_pm:
|
|
candLst = pipeline.prune(candLst)
|
|
run_args = [
|
|
args.opt_binary,
|
|
"-disable-symbolication",
|
|
"-disable-output",
|
|
"-passes={}".format(pipeline.toStr(candLst)),
|
|
ll_input,
|
|
]
|
|
run_args.extend(extra_opt_args)
|
|
opt = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if opt.returncode == expected_error_returncode:
|
|
lst = candLst
|
|
keepGoing = True
|
|
if not keepGoing:
|
|
break
|
|
print('-passes="{}"'.format(pipeline.toStr(lst)))
|
|
|
|
print("---FINISHED---")
|
|
if args.output:
|
|
shutil.copy(ll_input, args.output)
|
|
print("Wrote output to '{}'.".format(args.output))
|
|
print('-passes="{}"'.format(pipeline.toStr(lst)))
|
|
exit(0)
|