|
12 | 12 | import os
|
13 | 13 | import sys
|
14 | 14 | import warnings
|
| 15 | +import re |
15 | 16 |
|
16 | 17 | from UpdateTestChecks import common
|
17 | 18 |
|
@@ -64,6 +65,16 @@ def _get_parser():
|
64 | 65 | default="llvm-mca",
|
65 | 66 | help="the binary to use to generate the test case " "(default: llvm-mca)",
|
66 | 67 | )
|
| 68 | + parser.add_argument( |
| 69 | + "--check-sched-info", action="store_true", |
| 70 | + help="check scheduling info if references are given " |
| 71 | + "in comment after each instruction" |
| 72 | + ) |
| 73 | + parser.add_argument( |
| 74 | + "--update-sched-info", action="store_true", |
| 75 | + help="updating scheduling info references given " |
| 76 | + "in comment after each instruction" |
| 77 | + ) |
67 | 78 | parser.add_argument("tests", metavar="<test-path>", nargs="+")
|
68 | 79 | return parser
|
69 | 80 |
|
@@ -244,6 +255,146 @@ def _align_matching_blocks(all_blocks, farthest_indexes):
|
244 | 255 |
|
245 | 256 | return False
|
246 | 257 |
|
| 258 | +def _check_sched_values(line,scheds,units,err,updates): |
| 259 | + """Check for scheduling values differences |
| 260 | + between values reported by llvm-mca with -scheduling-info option |
| 261 | + and values in comment at the end of assembly instruction line (//). |
| 262 | + Reference units must be included in the list reported by llvm-mca. |
| 263 | + """ |
| 264 | + |
| 265 | + _err = [] |
| 266 | + # Got zip of llvm output and values from comment |
| 267 | + infos = ["MicroOps","Latency","Forward latency","Throughput"] |
| 268 | + sched_info = zip(infos,scheds[0].split(),scheds[1].split()) |
| 269 | + for si in sched_info: |
| 270 | + if float(si[1]) != float(si[2]): |
| 271 | + updates.add("sched") |
| 272 | + _err.append( |
| 273 | + "\t=> {} LLVM value {} != reference value in comment {}\n". |
| 274 | + format(si[0],si[1],si[2])) |
| 275 | + |
| 276 | + for u in units[1]: |
| 277 | + if not u in units[0]: |
| 278 | + updates.add("units") |
| 279 | + _err.append( |
| 280 | + "\t=> LLVM units {} != reference units in comment {}\n". |
| 281 | + format(units[0],units[1])) |
| 282 | + break |
| 283 | + |
| 284 | + if len(_err) > 0: |
| 285 | + err.append("{}\n{}".format(line,"".join(_err))) |
| 286 | + return True |
| 287 | + |
| 288 | + return False |
| 289 | + |
| 290 | +def _replace_values(oldvalue,newvalue): |
| 291 | + """Replacing values with the same format (spaces) |
| 292 | + than oldvalue |
| 293 | + """ |
| 294 | + |
| 295 | + fmt = re.sub("[0-9.]+","{}", oldvalue) |
| 296 | + return fmt.format(*newvalue.split()) |
| 297 | + |
| 298 | +def _has_comment(line,cmt_format): |
| 299 | + """Returns True if line contains C++ or C style |
| 300 | + comment. Set cmt_format first and optional second |
| 301 | + comment caracters. |
| 302 | + """ |
| 303 | + |
| 304 | + cpp_comment = re.search("\/\/",line) |
| 305 | + c_comment = re.search("\/\*",line) |
| 306 | + if "//" in line: |
| 307 | + cmt_format.append("//") |
| 308 | + return True |
| 309 | + |
| 310 | + if "/*" in line: |
| 311 | + cmt_format.append("/*") |
| 312 | + cmt_format.append("*/") |
| 313 | + return True |
| 314 | + |
| 315 | + return False |
| 316 | + |
| 317 | +def _sched_info(raw_tool_output, test_path, |
| 318 | + check_sched_info, update_sched_info): |
| 319 | + """Check scheduling info if passed in assembly comment after each |
| 320 | + instructions. |
| 321 | +
|
| 322 | + Recognized form is: |
| 323 | + 1 | 2 | 2 | 4.00 | ABSv1i64 | V1UnitV, | abs d15, d23 |
| 324 | + // ABS <V><d>, <V><n> \\ ASIMD arith, basic \\ 1 2 2 4.0 V1UnitV |
| 325 | +
|
| 326 | + Format: |
| 327 | + [1] // [2] \\ [3] \\ [4] |
| 328 | + [1]: <llvm-mca output> <asm instruction> |
| 329 | + [2]: <Architecture description> |
| 330 | + [3]: <Scheduling info reference> |
| 331 | + [4]: <micro ops> <latency> <forward latency> <throughput> <units> |
| 332 | +
|
| 333 | + <llvm-mca output> with -scheduling-info option: |
| 334 | + <MicroOps> | <latency> | <fwd latency> | <throughput> | |
| 335 | + <side effect> | <llvm opcode> <units> |
| 336 | +
|
| 337 | + The goal is to check [4] regarding llvm-mca output with -scheduling-info [1] |
| 338 | + option. It will allow to check scheduling info easily when |
| 339 | + doing code review of scheduling info merge requests. |
| 340 | + If a difference is found, the comment should be fixed and checked |
| 341 | + against documentation. |
| 342 | + """ |
| 343 | + |
| 344 | + scheduling_info = re.compile("(^\s+|\\\\\s+)([0-9]+[\s|]+[0-9]+" |
| 345 | + "[\s|]+[0-9]+[\s|]+\s+[0-9.]+)") |
| 346 | + units_info = re.compile("(^\s+|\\\\\s+)[0-9]+[\s|]+[0-9]+" |
| 347 | + "[\s|]+[0-9]+[\s|]+\s+[0-9.]+[\s|]+([^|*/]+)") |
| 348 | + |
| 349 | + fixes = {} |
| 350 | + err = [] |
| 351 | + instr_idx = 0 |
| 352 | + for b in raw_tool_output.split("\n\n"): |
| 353 | + for line in b.splitlines(): |
| 354 | + cmt_format = [] |
| 355 | + if _has_comment(line,cmt_format): |
| 356 | + scheds = scheduling_info.findall(line) |
| 357 | + scheds = [s[1].replace("|","") for s in scheds] |
| 358 | + if len(scheds) == 2: |
| 359 | + cmt = cmt_format[0] + line.split(cmt_format[0])[1] |
| 360 | + units = units_info.findall(line) |
| 361 | + c_units = [re.sub("\s","",u[1]).split(",") for u in units] |
| 362 | + updates = set() |
| 363 | + if _check_sched_values(line,scheds,c_units,err,updates): |
| 364 | + if update_sched_info: |
| 365 | + if "sched" in updates: |
| 366 | + cmt = cmt.replace(scheds[1], |
| 367 | + _replace_values(scheds[1],scheds[0])) |
| 368 | + if "units" in updates: |
| 369 | + cmt = cmt.replace(units[1][1],units[0][1]) |
| 370 | + |
| 371 | + fixes[instr_idx] = cmt |
| 372 | + instr_idx = instr_idx + 1 |
| 373 | + |
| 374 | + if update_sched_info: |
| 375 | + with open(test_path) as f: |
| 376 | + # Overwrite test with new fixed comments if any. |
| 377 | + # Test file will be read again just before writing final checking |
| 378 | + output_lines = [] |
| 379 | + instr_idx = 0 |
| 380 | + |
| 381 | + for line in f: |
| 382 | + out = line.rstrip() |
| 383 | + cmt_format = [] |
| 384 | + if _has_comment(line,cmt_format) and not re.match("^#",line): |
| 385 | + if fixes.get(instr_idx) is not None: |
| 386 | + out = line.split(cmt_format[0])[0] + fixes[instr_idx] |
| 387 | + |
| 388 | + instr_idx = instr_idx + 1 |
| 389 | + |
| 390 | + output_lines.append(out) |
| 391 | + |
| 392 | + with open(test_path, "wb") as f: |
| 393 | + f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines]) |
| 394 | + |
| 395 | + if check_sched_info: |
| 396 | + if len(err) > 0: |
| 397 | + raise Error("{}".format("".join(err))) |
247 | 398 |
|
248 | 399 | def _get_block_infos(run_infos, test_path, args, common_prefix): # noqa
|
249 | 400 | """For each run line, run the tool with the specified args and collect the
|
@@ -289,6 +440,17 @@ def _block_key(tool_args, prefixes):
|
289 | 440 | line if line.strip() else "" for line in raw_tool_output.splitlines()
|
290 | 441 | )
|
291 | 442 |
|
| 443 | + # Check if -scheduling-info passed to llvm-mca to check comments if any |
| 444 | + if "-scheduling-info" in tool_args: |
| 445 | + _sched_info(raw_tool_output, test_path, |
| 446 | + args.check_sched_info, args.update_sched_info) |
| 447 | + else: |
| 448 | + if args.check_sched_info: |
| 449 | + _warn("--check-sched-info: ignored: need llvm-mca -scheduling-info") |
| 450 | + if args.update_sched_info: |
| 451 | + _warn("--update-sched-info: ignored: need llvm-mca -scheduling-info") |
| 452 | + |
| 453 | + |
292 | 454 | # Split blocks, stripping all trailing whitespace, but keeping preceding
|
293 | 455 | # whitespace except for newlines so that columns will line up visually.
|
294 | 456 | all_blocks[key] = [
|
@@ -550,6 +712,12 @@ def update_test_file(args, test_path, autogenerated_note):
|
550 | 712 | run_infos = _get_run_infos(run_lines, args)
|
551 | 713 | common_prefix, prefix_pad = _get_useful_prefix_info(run_infos)
|
552 | 714 | block_infos = _get_block_infos(run_infos, test_path, args, common_prefix)
|
| 715 | + |
| 716 | + if args.update_sched_info: |
| 717 | + # Read again input lines in case of changes (scheduling info updates in comments) |
| 718 | + with open(test_path) as f: |
| 719 | + input_lines = [l.rstrip() for l in f] |
| 720 | + |
553 | 721 | _write_output(
|
554 | 722 | test_path,
|
555 | 723 | input_lines,
|
|
0 commit comments