Skip to content

Commit b9275eb

Browse files
committed
added a script to update llvm-mc test file
1 parent df159d3 commit b9275eb

File tree

7 files changed

+359
-1
lines changed

7 files changed

+359
-1
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// RUN: llvm-mc -triple=amdgcn -show-encoding %s 2>&1 | FileCheck --check-prefixes=CHECK %s
2+
3+
v_bfrev_b32 v5, v1
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mc_test_check.py UTC_ARGS: --version 5
2+
// RUN: llvm-mc -triple=amdgcn -show-encoding %s 2>&1 | FileCheck --check-prefixes=CHECK %s
3+
4+
// CHECK: v_bfrev_b32_e32 v5, v1 ; encoding: [0x01,0x71,0x0a,0x7e]
5+
v_bfrev_b32 v5, v1
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# RUN: llvm-mc -triple=amdgcn -mcpu=gfx1100 -disassemble -show-encoding %s 2>&1 | FileCheck -check-prefixes=CHECK %s
2+
3+
0x00,0x00,0x00,0x7e
4+
5+
0xfd,0xb8,0x0a,0x7f
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mc_test_check.py UTC_ARGS: --version 5
2+
# RUN: llvm-mc -triple=amdgcn -mcpu=gfx1100 -disassemble -show-encoding %s 2>&1 | FileCheck -check-prefixes=CHECK %s
3+
4+
# CHECK: v_nop ; encoding: [0x00,0x00,0x00,0x7e]
5+
0x00,0x00,0x00,0x7e
6+
7+
# COM: CHECK: warning: invalid instruction encoding
8+
0xfd,0xb8,0x0a,0x7f
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# REQUIRES: amdgpu-registered-target
2+
## Check that basic asm/dasm process is correct
3+
4+
# RUN: cp -f %S/Inputs/amdgpu_asm.s %t.s && %update_mc_test_checks %t.s
5+
# RUN: diff -u %S/Inputs/amdgpu_asm.s.expected %t.s
6+
# RUN: cp -f %S/Inputs/amdgpu_dasm.txt %t.txt && %update_mc_test_checks %t.txt
7+
# RUN: diff -u %S/Inputs/amdgpu_dasm.txt.expected %t.txt

llvm/utils/UpdateTestChecks/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False):
573573

574574
IR_FUNCTION_RE = re.compile(r'^\s*define\s+(?:internal\s+)?[^@]*@"?([\w.$-]+)"?\s*\(')
575575
TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$')
576-
TRIPLE_ARG_RE = re.compile(r"-mtriple[= ]([^ ]+)")
576+
TRIPLE_ARG_RE = re.compile(r"-m?triple[= ]([^ ]+)")
577577
MARCH_ARG_RE = re.compile(r"-march[= ]([^ ]+)")
578578
DEBUG_ONLY_ARG_RE = re.compile(r"-debug-only[= ]([^ ]+)")
579579

llvm/utils/update_mc_test_check.py

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
#!/usr/bin/env python3
2+
"""
3+
A test update script. This script is a utility to update LLVM 'llvm-mc' based test cases with new FileCheck patterns.
4+
"""
5+
6+
from __future__ import print_function
7+
8+
import argparse
9+
import os # Used to advertise this file's name ("autogenerated_note").
10+
11+
from UpdateTestChecks import common
12+
13+
import subprocess
14+
import re
15+
16+
mc_LIKE_TOOLS = [
17+
"llvm-mc",
18+
]
19+
20+
ERROR_RE = re.compile(r"(warning|error): .*")
21+
ERROR_CHECK_RE = re.compile(r"# COM: .*")
22+
OUTPUT_SKIPPED_RE = re.compile(r"(.text)")
23+
COMMENT = {
24+
"asm" : "//",
25+
"dasm" : "#"
26+
}
27+
28+
29+
def invoke_tool(exe, cmd_args, testline, verbose=False):
30+
if isinstance(cmd_args, list):
31+
args = [applySubstitutions(a, substitutions) for a in cmd_args]
32+
else:
33+
args = cmd_args
34+
35+
cmd = "echo \"" + testline + "\" | " + exe + " " + args
36+
if verbose:
37+
print("Command: ", cmd)
38+
out = subprocess.check_output(cmd, shell=True)
39+
# Fix line endings to unix CR style.
40+
return out.decode().replace("\r\n", "\n")
41+
42+
43+
# create tests line-by-line, here we just filter out the check lines and comments
44+
# and treat all others as tests
45+
def isTestLine(input_line, mc_mode):
46+
# Skip comment lines
47+
if input_line.strip(' \t\r').startswith(COMMENT[mc_mode]):
48+
return False
49+
elif input_line.strip(' \t\r') == '':
50+
return False
51+
# skip any CHECK lines.
52+
elif common.CHECK_RE.match(input_line):
53+
return False
54+
return True
55+
56+
def hasErr(err):
57+
if err is None or len(err) == 0:
58+
return False
59+
if ERROR_RE.search(err):
60+
return True
61+
return False
62+
63+
def getErrString(err):
64+
if err is None or len(err) == 0:
65+
return ""
66+
67+
lines = err.split('\n')
68+
# take the first match
69+
for line in lines:
70+
s = ERROR_RE.search(line)
71+
if s:
72+
return s.group(0)
73+
return ""
74+
75+
def getOutputString(out):
76+
if out is None or len(out) == 0:
77+
return ""
78+
lines = out.split('\n')
79+
output = ""
80+
81+
for line in lines:
82+
if OUTPUT_SKIPPED_RE.search(line):
83+
continue
84+
if line.strip('\t ') == '':
85+
continue
86+
output += line.lstrip('\t ')
87+
return output
88+
89+
def should_add_line_to_output(input_line, prefix_set, mc_mode):
90+
# special check line
91+
if mc_mode == 'dasm' and ERROR_CHECK_RE.search(input_line):
92+
return False
93+
else:
94+
return common.should_add_line_to_output(input_line, prefix_set, comment_marker=COMMENT[mc_mode])
95+
96+
97+
def getStdCheckLine(prefix, output, mc_mode):
98+
lines = output.split('\n')
99+
output = ""
100+
for line in lines:
101+
output += COMMENT[mc_mode] + ' ' + prefix + ": " + line + '\n'
102+
return output
103+
104+
def getErrCheckLine(prefix, output, mc_mode):
105+
if mc_mode == 'asm':
106+
return COMMENT[mc_mode] + ' ' + prefix + ": " + output + '\n'
107+
elif mc_mode == 'dasm':
108+
return COMMENT[mc_mode] + ' COM: ' + prefix + ": " + output + '\n'
109+
110+
def main():
111+
parser = argparse.ArgumentParser(description=__doc__)
112+
parser.add_argument(
113+
"--mc-binary",
114+
default=None,
115+
help='The "mc" binary to use to generate the test case',
116+
)
117+
parser.add_argument(
118+
"--tool",
119+
default=None,
120+
help="Treat the given tool name as an mc-like tool for which check lines should be generated",
121+
)
122+
parser.add_argument(
123+
"--default-march",
124+
default=None,
125+
help="Set a default -march for when neither triple nor arch are found in a RUN line",
126+
)
127+
parser.add_argument("tests", nargs="+")
128+
initial_args = common.parse_commandline_args(parser)
129+
130+
script_name = os.path.basename(__file__)
131+
132+
for ti in common.itertests(
133+
initial_args.tests, parser, script_name="utils/" + script_name
134+
):
135+
if ti.path.endswith('.s'):
136+
mc_mode = "asm"
137+
elif ti.path.endswith('.txt'):
138+
mc_mode = "dasm"
139+
else:
140+
common.warn("Expected .s and .txt, Skipping file : ", ti.path)
141+
continue
142+
143+
triple_in_ir = None
144+
for l in ti.input_lines:
145+
m = common.TRIPLE_IR_RE.match(l)
146+
if m:
147+
triple_in_ir = m.groups()[0]
148+
break
149+
150+
run_list = []
151+
for l in ti.run_lines:
152+
if "|" not in l:
153+
common.warn("Skipping unparsable RUN line: " + l)
154+
continue
155+
156+
commands = [cmd.strip() for cmd in l.split("|")]
157+
assert len(commands) >= 2
158+
mc_cmd = " | ".join(commands[:-1])
159+
filecheck_cmd = commands[-1]
160+
mc_tool = mc_cmd.split(" ")[0]
161+
162+
triple_in_cmd = None
163+
m = common.TRIPLE_ARG_RE.search(mc_cmd)
164+
if m:
165+
triple_in_cmd = m.groups()[0]
166+
167+
march_in_cmd = ti.args.default_march
168+
m = common.MARCH_ARG_RE.search(mc_cmd)
169+
if m:
170+
march_in_cmd = m.groups()[0]
171+
172+
common.verify_filecheck_prefixes(filecheck_cmd)
173+
174+
mc_like_tools = mc_LIKE_TOOLS[:]
175+
if ti.args.tool:
176+
mc_like_tools.append(ti.args.tool)
177+
if mc_tool not in mc_like_tools:
178+
common.warn("Skipping non-mc RUN line: " + l)
179+
continue
180+
181+
if not filecheck_cmd.startswith("FileCheck "):
182+
common.warn("Skipping non-FileChecked RUN line: " + l)
183+
continue
184+
185+
mc_cmd_args = mc_cmd[len(mc_tool) :].strip()
186+
mc_cmd_args = mc_cmd_args.replace("< %s", "").replace("%s", "").strip()
187+
check_prefixes = common.get_check_prefixes(filecheck_cmd)
188+
189+
run_list.append(
190+
(
191+
check_prefixes,
192+
mc_tool,
193+
mc_cmd_args,
194+
triple_in_cmd,
195+
march_in_cmd,
196+
)
197+
)
198+
199+
200+
# find all test line from input
201+
testlines = [l for l in ti.input_lines if isTestLine(l, mc_mode)]
202+
run_list_size = len(run_list)
203+
testnum = len(testlines)
204+
205+
raw_output = []
206+
raw_prefixes = []
207+
for (
208+
prefixes,
209+
mc_tool,
210+
mc_args,
211+
triple_in_cmd,
212+
march_in_cmd,
213+
) in run_list:
214+
common.debug("Extracted mc cmd:", mc_tool, mc_args)
215+
common.debug("Extracted FileCheck prefixes:", str(prefixes))
216+
common.debug("Extracted triple :", str(triple_in_cmd))
217+
common.debug("Extracted march:", str(march_in_cmd))
218+
219+
triple = triple_in_cmd or triple_in_ir
220+
if not triple:
221+
triple = common.get_triple_from_march(march_in_cmd)
222+
223+
raw_output.append([])
224+
for line in testlines:
225+
# get output for each testline
226+
out = invoke_tool(
227+
ti.args.mc_binary or mc_tool,
228+
mc_args,
229+
line,
230+
verbose=ti.args.verbose,
231+
)
232+
raw_output[-1].append(out)
233+
234+
common.debug("Collect raw tool lines:", str(len(raw_output[-1])))
235+
236+
raw_prefixes.append(prefixes)
237+
238+
output_lines = []
239+
generated_prefixes = []
240+
used_prefixes = set()
241+
prefix_set = set([prefix for p in run_list for prefix in p[0]])
242+
common.debug("Rewriting FileCheck prefixes:", str(prefix_set))
243+
244+
for test_id in range(testnum):
245+
input_line = testlines[test_id]
246+
247+
# a {prefix : output, [runid] } dict
248+
# insert output to a prefix-key dict, and do a max sorting
249+
# to select the most-used prefix which share the same output string
250+
p_dict = {}
251+
for run_id in range(run_list_size):
252+
out = raw_output[run_id][test_id]
253+
254+
if hasErr(out):
255+
o = getErrString(out)
256+
else:
257+
o = getOutputString(out)
258+
259+
prefixes = raw_prefixes[run_id]
260+
261+
for p in prefixes:
262+
if p not in p_dict:
263+
p_dict[p] = o, [run_id]
264+
else:
265+
if p_dict[p] == (None, []):
266+
continue
267+
268+
prev_o, run_ids = p_dict[p]
269+
if o == prev_o:
270+
run_ids.append(run_id)
271+
p_dict[p] = o, run_ids
272+
else:
273+
# conflict, discard
274+
p_dict[p] = None, []
275+
276+
p_dict_sorted = dict(sorted(p_dict.items(), key=lambda item: -len(item[1][1])))
277+
278+
# prefix is selected and generated with most shared output lines
279+
# each run_id can only be used once
280+
gen_prefix = ""
281+
used_runid = set()
282+
for prefix, tup in p_dict_sorted.items():
283+
o, run_ids = tup
284+
285+
if len(run_ids) == 0:
286+
continue
287+
288+
skip = False
289+
for i in run_ids:
290+
if i in used_runid:
291+
skip = True
292+
else:
293+
used_runid.add(i)
294+
if not skip:
295+
used_prefixes.add(prefix)
296+
297+
if hasErr(o):
298+
gen_prefix += getErrCheckLine(prefix, o, mc_mode)
299+
else:
300+
gen_prefix += getStdCheckLine(prefix, o, mc_mode)
301+
302+
generated_prefixes.append(gen_prefix.rstrip('\n'))
303+
304+
# write output
305+
prefix_id = 0
306+
for input_info in ti.iterlines(output_lines):
307+
input_line = input_info.line
308+
if isTestLine(input_line, mc_mode):
309+
output_lines.append(generated_prefixes[prefix_id])
310+
output_lines.append(input_line)
311+
prefix_id += 1
312+
313+
elif should_add_line_to_output(input_line, prefix_set, mc_mode):
314+
output_lines.append(input_line)
315+
316+
elif input_line in ti.run_lines or input_line == "":
317+
output_lines.append(input_line)
318+
319+
if ti.args.gen_unused_prefix_body:
320+
output_lines.extend(
321+
ti.get_checks_for_unused_prefixes(run_list, used_prefixes)
322+
)
323+
324+
common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path))
325+
with open(ti.path, "wb") as f:
326+
f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])
327+
328+
329+
if __name__ == "__main__":
330+
main()

0 commit comments

Comments
 (0)