brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.4 KiB · 515fdd2 Raw
215 lines · python
1#!/usr/bin/env python32 3# Automatically formatted with yapf (https://github.com/google/yapf)4 5# Script for automatic 'opt' pipeline reduction for when using the new6# pass-manager (NPM). Based around the '-print-pipeline-passes' option.7#8# The reduction algorithm consists of several phases (steps).9#10# Step #0: Verify that input fails with the given pipeline and make note of the11# error code.12#13# Step #1: Split pipeline in two starting from front and move forward as long as14# first pipeline exits normally and the second pipeline fails with the expected15# error code. Move on to step #2 with the IR from the split point and the16# pipeline from the second invocation.17#18# Step #2: Remove passes from end of the pipeline as long as the pipeline fails19# with the expected error code.20#21# Step #3: Make several sweeps over the remaining pipeline trying to remove one22# pass at a time. Repeat sweeps until unable to remove any more passes.23#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 ...]26 27import argparse28import pipeline29import shutil30import subprocess31import tempfile32 33parser = argparse.ArgumentParser(34    description="Automatic opt pipeline reducer. Unrecognized arguments are forwarded to opt."35)36parser.add_argument("--opt-binary", action="store", dest="opt_binary", default="opt")37parser.add_argument("--passes", action="store", dest="passes", required=True)38parser.add_argument("--input", action="store", dest="input", required=True)39parser.add_argument("--output", action="store", dest="output")40parser.add_argument(41    "--dont-expand-passes",42    action="store_true",43    dest="dont_expand_passes",44    help="Do not expand pipeline before starting reduction.",45)46parser.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.",51)52[args, extra_opt_args] = parser.parse_known_args()53 54print("The following extra args will be passed to opt: {}".format(extra_opt_args))55 56lst = pipeline.fromStr(args.passes)57ll_input = args.input58 59# Step #-160# Launch 'opt' once with '-print-pipeline-passes' to expand pipeline before61# starting reduction. Allows specifying a default pipelines (e.g.62# '-passes=default<O3>').63if 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,71    ]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)))84 85# Step #086# Confirm that the given input, passes and options result in failure.87print("---Starting step #0---")88run_args = [89    args.opt_binary,90    "-disable-symbolication",91    "-disable-output",92    "-passes={}".format(pipeline.toStr(lst)),93    ll_input,94]95run_args.extend(extra_opt_args)96opt = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)97if 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)103 104expected_error_returncode = opt.returncode105print('-passes="{}"'.format(pipeline.toStr(lst)))106 107# Step #1108# Try to narrow down the failing pass sequence by splitting the pipeline in two109# opt invocations (A and B) starting with invocation A only running the first110# pipeline pass and invocation B the remaining. Keep moving the split point111# forward as long as invocation A exits normally and invocation B fails with112# the expected error. This will accomplish two things first the input IR will be113# further reduced and second, with that IR, the reduced pipeline for invocation114# B will be sufficient to reproduce.115print("---Starting step #1---")116prevLstB = None117prevIntermediate = None118tmpd = tempfile.TemporaryDirectory()119 120for 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)125 126    intermediate = "intermediate-0.ll" if idx % 2 else "intermediate-1.ll"127    intermediate = tmpd.name + "/" + intermediate128    run_args = [129        args.opt_binary,130        "-disable-symbolication",131        "-S",132        "-o",133        intermediate,134        "-passes={}".format(pipeline.toStr(lstA)),135        ll_input,136    ]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,145    ]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        break150    prevLstB = lstB151    prevIntermediate = intermediate152if prevLstB:153    lst = prevLstB154    ll_input = prevIntermediate155print('-passes="{}"'.format(pipeline.toStr(lst)))156 157# Step #2158# Try removing passes from the end of the remaining pipeline while still159# reproducing the error.160print("---Starting step #2---")161prevLstA = None162for 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,172    ]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        break177    prevLstA = lstA178if prevLstA:179    lst = prevLstA180print('-passes="{}"'.format(pipeline.toStr(lst)))181 182# Step #3183# Now that we have a pipeline that is reduced both front and back we do184# exhaustive sweeps over the remainder trying to remove one pass at a time.185# Repeat as long as reduction is possible.186print("---Starting step #3---")187while True:188    keepGoing = False189    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,199        ]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 = candLst204            keepGoing = True205    if not keepGoing:206        break207print('-passes="{}"'.format(pipeline.toStr(lst)))208 209print("---FINISHED---")210if args.output:211    shutil.copy(ll_input, args.output)212    print("Wrote output to '{}'.".format(args.output))213print('-passes="{}"'.format(pipeline.toStr(lst)))214exit(0)215