llvm-project

Форк
0
/
reduce_pipeline.py 
214 строк · 7.4 Кб
1
#!/usr/bin/env python3
2

3
# Automatically formatted with yapf (https://github.com/google/yapf)
4

5
# Script for automatic 'opt' pipeline reduction for when using the new
6
# 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 the
11
# error code.
12
#
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.
17
#
18
# Step #2: Remove passes from end of the pipeline as long as the pipeline fails
19
# with the expected error code.
20
#
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.
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

27
import argparse
28
import pipeline
29
import shutil
30
import subprocess
31
import tempfile
32

33
parser = argparse.ArgumentParser(
34
    description="Automatic opt pipeline reducer. Unrecognized arguments are forwarded to opt."
35
)
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.",
45
)
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.",
51
)
52
[args, extra_opt_args] = parser.parse_known_args()
53

54
print("The following extra args will be passed to opt: {}".format(extra_opt_args))
55

56
lst = pipeline.fromStr(args.passes)
57
ll_input = args.input
58

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,
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 #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,
94
]
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)
103

104
expected_error_returncode = opt.returncode
105
print('-passes="{}"'.format(pipeline.toStr(lst)))
106

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()
119

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)
125

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,
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
        break
150
    prevLstB = lstB
151
    prevIntermediate = intermediate
152
if prevLstB:
153
    lst = prevLstB
154
    ll_input = prevIntermediate
155
print('-passes="{}"'.format(pipeline.toStr(lst)))
156

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,
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
        break
177
    prevLstA = lstA
178
if prevLstA:
179
    lst = prevLstA
180
print('-passes="{}"'.format(pipeline.toStr(lst)))
181

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,
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 = candLst
204
            keepGoing = True
205
    if not keepGoing:
206
        break
207
print('-passes="{}"'.format(pipeline.toStr(lst)))
208

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)
215

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.