mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 01:26:04 +00:00

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: ./utils/reduce_pipeline.py --opt-binary=./build-all-Debug/bin/opt --input=input.ll --output=output.ll --passes=PIPELINE [EXTRA-OPT-ARGS ...] Differential Revision: https://reviews.llvm.org/D110908
173 lines
4.9 KiB
Python
173 lines
4.9 KiB
Python
# Automatically formatted with yapf (https://github.com/google/yapf)
|
|
"""Utility functions for creating and manipulating LLVM 'opt' NPM pipeline objects."""
|
|
|
|
|
|
def fromStr(pipeStr):
|
|
"""Create pipeline object from string representation."""
|
|
stack = []
|
|
curr = []
|
|
tok = ''
|
|
kind = ''
|
|
for c in pipeStr:
|
|
if c == ',':
|
|
if tok != '':
|
|
curr.append([None, tok])
|
|
tok = ''
|
|
elif c == '(':
|
|
stack.append([kind, curr])
|
|
kind = tok
|
|
curr = []
|
|
tok = ''
|
|
elif c == ')':
|
|
if tok != '':
|
|
curr.append([None, tok])
|
|
tok = ''
|
|
oldKind = kind
|
|
oldCurr = curr
|
|
[kind, curr] = stack.pop()
|
|
curr.append([oldKind, oldCurr])
|
|
else:
|
|
tok += c
|
|
if tok != '':
|
|
curr.append([None, tok])
|
|
return curr
|
|
|
|
|
|
def toStr(pipeObj):
|
|
"""Create string representation of pipeline object."""
|
|
res = ''
|
|
lastIdx = len(pipeObj) - 1
|
|
for i, c in enumerate(pipeObj):
|
|
if c[0]:
|
|
res += c[0] + '('
|
|
res += toStr(c[1])
|
|
res += ')'
|
|
else:
|
|
res += c[1]
|
|
if i != lastIdx:
|
|
res += ','
|
|
return res
|
|
|
|
|
|
def count(pipeObj):
|
|
"""Count number of passes (pass-managers excluded) in pipeline object."""
|
|
cnt = 0
|
|
for c in pipeObj:
|
|
if c[0]:
|
|
cnt += count(c[1])
|
|
else:
|
|
cnt += 1
|
|
return cnt
|
|
|
|
|
|
def split(pipeObj, splitIndex):
|
|
"""Create two new pipeline objects by splitting pipeObj in two directly after pass with index splitIndex."""
|
|
def splitInt(src, splitIndex, dstA, dstB, idx):
|
|
for s in src:
|
|
if s[0]:
|
|
dstA2 = []
|
|
dstB2 = []
|
|
idx = splitInt(s[1], splitIndex, dstA2, dstB2, idx)
|
|
dstA.append([s[0], dstA2])
|
|
dstB.append([s[0], dstB2])
|
|
else:
|
|
if idx <= splitIndex:
|
|
dstA.append([None, s[1]])
|
|
else:
|
|
dstB.append([None, s[1]])
|
|
idx += 1
|
|
return idx
|
|
|
|
listA = []
|
|
listB = []
|
|
splitInt(pipeObj, splitIndex, listA, listB, 0)
|
|
return [listA, listB]
|
|
|
|
|
|
def remove(pipeObj, removeIndex):
|
|
"""Create new pipeline object by removing pass with index removeIndex from pipeObj."""
|
|
def removeInt(src, removeIndex, dst, idx):
|
|
for s in src:
|
|
if s[0]:
|
|
dst2 = []
|
|
idx = removeInt(s[1], removeIndex, dst2, idx)
|
|
dst.append([s[0], dst2])
|
|
else:
|
|
if idx != removeIndex:
|
|
dst.append([None, s[1]])
|
|
idx += 1
|
|
return idx
|
|
|
|
dst = []
|
|
removeInt(pipeObj, removeIndex, dst, 0)
|
|
return dst
|
|
|
|
|
|
def copy(srcPipeObj):
|
|
"""Create copy of pipeline object srcPipeObj."""
|
|
def copyInt(dst, src):
|
|
for s in src:
|
|
if s[0]:
|
|
dst2 = []
|
|
copyInt(dst2, s[1])
|
|
dst.append([s[0], dst2])
|
|
else:
|
|
dst.append([None, s[1]])
|
|
|
|
dstPipeObj = []
|
|
copyInt(dstPipeObj, srcPipeObj)
|
|
return dstPipeObj
|
|
|
|
|
|
def prune(srcPipeObj):
|
|
"""Create new pipeline object by removing empty pass-managers (those with count = 0) from srcPipeObj."""
|
|
def pruneInt(dst, src):
|
|
for s in src:
|
|
if s[0]:
|
|
if count(s[1]):
|
|
dst2 = []
|
|
pruneInt(dst2, s[1])
|
|
dst.append([s[0], dst2])
|
|
else:
|
|
dst.append([None, s[1]])
|
|
|
|
dstPipeObj = []
|
|
pruneInt(dstPipeObj, srcPipeObj)
|
|
return dstPipeObj
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import unittest
|
|
|
|
class Test(unittest.TestCase):
|
|
def test_0(self):
|
|
pipeStr = 'a,b,A(c,B(d,e),f),g'
|
|
pipeObj = fromStr(pipeStr)
|
|
|
|
self.assertEqual(7, count(pipeObj))
|
|
|
|
self.assertEqual(pipeObj, pipeObj)
|
|
self.assertEqual(pipeObj, prune(pipeObj))
|
|
self.assertEqual(pipeObj, copy(pipeObj))
|
|
|
|
self.assertEqual(pipeStr, toStr(pipeObj))
|
|
self.assertEqual(pipeStr, toStr(prune(pipeObj)))
|
|
self.assertEqual(pipeStr, toStr(copy(pipeObj)))
|
|
|
|
[pipeObjA, pipeObjB] = split(pipeObj, 3)
|
|
self.assertEqual('a,b,A(c,B(d))', toStr(pipeObjA))
|
|
self.assertEqual('A(B(e),f),g', toStr(pipeObjB))
|
|
|
|
self.assertEqual('b,A(c,B(d,e),f),g', toStr(remove(pipeObj, 0)))
|
|
self.assertEqual('a,b,A(c,B(d,e),f)', toStr(remove(pipeObj, 6)))
|
|
|
|
pipeObjC = remove(pipeObj, 4)
|
|
self.assertEqual('a,b,A(c,B(d),f),g', toStr(pipeObjC))
|
|
pipeObjC = remove(pipeObjC, 3)
|
|
self.assertEqual('a,b,A(c,B(),f),g', toStr(pipeObjC))
|
|
pipeObjC = prune(pipeObjC)
|
|
self.assertEqual('a,b,A(c,f),g', toStr(pipeObjC))
|
|
|
|
unittest.main()
|
|
exit(0)
|