|
@@ -0,0 +1,132 @@
|
|
|
+#! /usr/bin/env python3
|
|
|
+"""ExtractIRForPassTest.py - extract IR just before selected pass would be run.
|
|
|
+
|
|
|
+This script automates some operations to make it easier to write IR tests:
|
|
|
+ 1. Gets the pass list for an HLSL compilation using -Odump
|
|
|
+ 2. Compiles HLSL with -fcgl and outputs to intermediate IR
|
|
|
+ 3. Collects list of passes before the desired pass and adds
|
|
|
+ -hlsl-passes-pause to write correct metadata
|
|
|
+ 4. Invokes dxopt to run passes on -fcgl output and write bitcode result
|
|
|
+ 5. Disassembles bitcode to .ll file for use as a test
|
|
|
+ 6. Inserts RUN line with -hlsl-passes-resume and desired pass
|
|
|
+
|
|
|
+Examples:
|
|
|
+ ExtractIRForPassTest.py -p scalarrepl-param-hlsl -o my_test.ll my_test.hlsl -- -T cs_6_0 -Od
|
|
|
+ - stop before 'scalarrepl-param-hlsl' pass; output to my_test.ll
|
|
|
+ ExtractIRForPassTest.py -p simplifycfg -n 2 -o my_test.ll my_test.hlsl -- -T cs_6_0
|
|
|
+ - stop before the second invocation of 'simplifycfg' pass
|
|
|
+
|
|
|
+Use dxc with -Odump to dump the pass sequence for reference.
|
|
|
+"""
|
|
|
+
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import subprocess
|
|
|
+import tempfile
|
|
|
+import argparse
|
|
|
+
|
|
|
+def ParseArgs():
|
|
|
+ parser = argparse.ArgumentParser(
|
|
|
+ formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__)
|
|
|
+ parser.add_argument(
|
|
|
+ '-p', dest='desired_pass', metavar='<desired-pass>', required=True,
|
|
|
+ help='stop before this module pass (per-function prepass not supported).')
|
|
|
+ parser.add_argument(
|
|
|
+ '-n', dest='invocation', metavar='<invocation>', type=int, default=1,
|
|
|
+ help='pass invocation number on which to stop (default=1)')
|
|
|
+ parser.add_argument(
|
|
|
+ 'hlsl_file', metavar='<hlsl-file>',
|
|
|
+ help='input HLSL file path to compile')
|
|
|
+ parser.add_argument(
|
|
|
+ '-o', dest='output_file', metavar='<output-file>', required=True,
|
|
|
+ help='output file name')
|
|
|
+ parser.add_argument(
|
|
|
+ 'compilation_options', nargs='*',
|
|
|
+ metavar='-- <DXC options>',
|
|
|
+ help='set of compilation options needed to compile the HLSL file with DXC')
|
|
|
+ return parser.parse_args()
|
|
|
+
|
|
|
+def SplitAtPass(passes, pass_name, invocation = 1):
|
|
|
+ pass_name = '-' + pass_name
|
|
|
+ before = []
|
|
|
+ fn_passes = True
|
|
|
+ count = 0
|
|
|
+ after = None
|
|
|
+ for line in passes:
|
|
|
+ line = line.strip()
|
|
|
+ if not line or line.startswith('#'):
|
|
|
+ continue
|
|
|
+ if line == '-opt-mod-passes':
|
|
|
+ fn_passes = False
|
|
|
+ if after:
|
|
|
+ after.append(line)
|
|
|
+ continue
|
|
|
+ if not fn_passes:
|
|
|
+ if line == pass_name:
|
|
|
+ count += 1
|
|
|
+ if count >= invocation:
|
|
|
+ after = [line]
|
|
|
+ continue
|
|
|
+ before.append(line)
|
|
|
+ return before, after
|
|
|
+
|
|
|
+def GetTempFilename(*args, **kwargs):
|
|
|
+ "Get temp filename and close the file handle for use by others"
|
|
|
+ fd, name = tempfile.mkstemp(*args, **kwargs)
|
|
|
+ os.close(fd)
|
|
|
+ return name
|
|
|
+
|
|
|
+def main(args):
|
|
|
+ try:
|
|
|
+ # 1. Gets the pass list for an HLSL compilation using -Odump
|
|
|
+ cmd = ['dxc', '/Odump', args.hlsl_file] + args.compilation_options
|
|
|
+ # print(cmd)
|
|
|
+ all_passes = subprocess.check_output(cmd, text=True)
|
|
|
+ all_passes = all_passes.splitlines()
|
|
|
+
|
|
|
+ # 2. Compiles HLSL with -fcgl and outputs to intermediate IR
|
|
|
+ fcgl_file = GetTempFilename('.ll')
|
|
|
+ cmd = (['dxc', '-fcgl', '-Fc', fcgl_file, args.hlsl_file]
|
|
|
+ + args.compilation_options)
|
|
|
+ # print(cmd)
|
|
|
+ subprocess.check_call(cmd)
|
|
|
+
|
|
|
+ # 3. Collects list of passes before the desired pass and adds
|
|
|
+ # -hlsl-passes-pause to write correct metadata
|
|
|
+ passes_before, passes_after = SplitAtPass(
|
|
|
+ all_passes, args.desired_pass, args.invocation)
|
|
|
+ print('\nPasses before: {}\n\nRemaining passes: {}'
|
|
|
+ .format(' '.join(passes_before), ' '.join(passes_after)))
|
|
|
+ passes_before.append('-hlsl-passes-pause')
|
|
|
+
|
|
|
+ # 4. Invokes dxopt to run passes on -fcgl output and write bitcode result
|
|
|
+ bitcode_file = GetTempFilename('.bc')
|
|
|
+ cmd = ['dxopt', '-o=' + bitcode_file, fcgl_file] + passes_before
|
|
|
+ # print(cmd)
|
|
|
+ subprocess.check_call(cmd)
|
|
|
+
|
|
|
+ # 5. Disassembles bitcode to .ll file for use as a test
|
|
|
+ temp_out = GetTempFilename('.ll')
|
|
|
+ cmd = ['dxc', '/dumpbin', '-Fc', temp_out, bitcode_file]
|
|
|
+ # print(cmd)
|
|
|
+ subprocess.check_call(cmd)
|
|
|
+
|
|
|
+ # 6. Inserts RUN line with -hlsl-passes-resume and desired pass
|
|
|
+ with open(args.output_file, 'wt') as f:
|
|
|
+ f.write('; RUN: %opt %s -hlsl-passes-resume {} -S | FileCheck %s\n\n'
|
|
|
+ .format(args.desired_pass))
|
|
|
+ with open(temp_out, 'rt') as f_in:
|
|
|
+ f.write(f_in.read())
|
|
|
+
|
|
|
+ # Clean up temp files
|
|
|
+ os.unlink(fcgl_file)
|
|
|
+ os.unlink(bitcode_file)
|
|
|
+ os.unlink(temp_out)
|
|
|
+ except:
|
|
|
+ print(f'\nSomething went wrong!\nMost recent command and arguments: {cmd}\n')
|
|
|
+ raise
|
|
|
+
|
|
|
+if __name__=='__main__':
|
|
|
+ args = ParseArgs()
|
|
|
+ main(args)
|
|
|
+ print('\nSuccess! See output file:\n{}'.format(args.output_file))
|