common.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. from __future__ import print_function
  2. import re
  3. import string
  4. import subprocess
  5. import sys
  6. import copy
  7. if sys.version_info[0] > 2:
  8. class string:
  9. expandtabs = str.expandtabs
  10. else:
  11. import string
  12. ##### Common utilities for update_*test_checks.py
  13. def should_add_line_to_output(input_line, prefix_set):
  14. # Skip any blank comment lines in the IR.
  15. if input_line.strip() == ';':
  16. return False
  17. # Skip any blank lines in the IR.
  18. #if input_line.strip() == '':
  19. # return False
  20. # And skip any CHECK lines. We're building our own.
  21. m = CHECK_RE.match(input_line)
  22. if m and m.group(1) in prefix_set:
  23. return False
  24. return True
  25. # Invoke the tool that is being tested.
  26. def invoke_tool(exe, cmd_args, ir):
  27. with open(ir) as ir_file:
  28. # TODO Remove the str form which is used by update_test_checks.py and
  29. # update_llc_test_checks.py
  30. # The safer list form is used by update_cc_test_checks.py
  31. if isinstance(cmd_args, list):
  32. stdout = subprocess.check_output([exe] + cmd_args, stdin=ir_file)
  33. else:
  34. stdout = subprocess.check_output(exe + ' ' + cmd_args,
  35. shell=True, stdin=ir_file)
  36. if sys.version_info[0] > 2:
  37. stdout = stdout.decode()
  38. # Fix line endings to unix CR style.
  39. return stdout.replace('\r\n', '\n')
  40. ##### LLVM IR parser
  41. RUN_LINE_RE = re.compile('^\s*[;#]\s*RUN:\s*(.*)$')
  42. CHECK_PREFIX_RE = re.compile('--?check-prefix(?:es)?[= ](\S+)')
  43. PREFIX_RE = re.compile('^[a-zA-Z0-9_-]+$')
  44. CHECK_RE = re.compile(r'^\s*[;#]\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:')
  45. OPT_FUNCTION_RE = re.compile(
  46. r'^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w-]+?)\s*\('
  47. r'(\s+)?[^)]*[^{]*\{\n(?P<body>.*?)^\}$',
  48. flags=(re.M | re.S))
  49. ANALYZE_FUNCTION_RE = re.compile(
  50. r'^\s*\'(?P<analysis>[\w\s-]+?)\'\s+for\s+function\s+\'(?P<func>[\w-]+?)\':'
  51. r'\s*\n(?P<body>.*)$',
  52. flags=(re.X | re.S))
  53. IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@(\w+)\s*\(')
  54. TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$')
  55. TRIPLE_ARG_RE = re.compile(r'-mtriple[= ]([^ ]+)')
  56. MARCH_ARG_RE = re.compile(r'-march[= ]([^ ]+)')
  57. SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)')
  58. SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M)
  59. SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M)
  60. SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n')
  61. SCRUB_LOOP_COMMENT_RE = re.compile(
  62. r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M)
  63. def scrub_body(body):
  64. # Scrub runs of whitespace out of the assembly, but leave the leading
  65. # whitespace in place.
  66. body = SCRUB_WHITESPACE_RE.sub(r' ', body)
  67. # Expand the tabs used for indentation.
  68. body = string.expandtabs(body, 2)
  69. # Strip trailing whitespace.
  70. body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body)
  71. return body
  72. def do_scrub(body, scrubber, scrubber_args, extra):
  73. if scrubber_args:
  74. local_args = copy.deepcopy(scrubber_args)
  75. local_args[0].extra_scrub = extra
  76. return scrubber(body, *local_args)
  77. return scrubber(body, *scrubber_args)
  78. # Build up a dictionary of all the function bodies.
  79. class function_body(object):
  80. def __init__(self, string, extra):
  81. self.scrub = string
  82. self.extrascrub = extra
  83. def __str__(self):
  84. return self.scrub
  85. def build_function_body_dictionary(function_re, scrubber, scrubber_args, raw_tool_output, prefixes, func_dict, verbose):
  86. for m in function_re.finditer(raw_tool_output):
  87. if not m:
  88. continue
  89. func = m.group('func')
  90. body = m.group('body')
  91. scrubbed_body = do_scrub(body, scrubber, scrubber_args, extra = False)
  92. scrubbed_extra = do_scrub(body, scrubber, scrubber_args, extra = True)
  93. if 'analysis' in m.groupdict():
  94. analysis = m.group('analysis')
  95. if analysis.lower() != 'cost model analysis':
  96. print('WARNING: Unsupported analysis mode: %r!' % (analysis,), file=sys.stderr)
  97. if func.startswith('stress'):
  98. # We only use the last line of the function body for stress tests.
  99. scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:])
  100. if verbose:
  101. print('Processing function: ' + func, file=sys.stderr)
  102. for l in scrubbed_body.splitlines():
  103. print(' ' + l, file=sys.stderr)
  104. for prefix in prefixes:
  105. if func in func_dict[prefix] and str(func_dict[prefix][func]) != scrubbed_body:
  106. if func_dict[prefix][func] and func_dict[prefix][func].extrascrub == scrubbed_extra:
  107. func_dict[prefix][func].scrub = scrubbed_extra
  108. continue
  109. else:
  110. if prefix == prefixes[-1]:
  111. print('WARNING: Found conflicting asm under the '
  112. 'same prefix: %r!' % (prefix,), file=sys.stderr)
  113. else:
  114. func_dict[prefix][func] = None
  115. continue
  116. func_dict[prefix][func] = function_body(scrubbed_body, scrubbed_extra)
  117. ##### Generator of LLVM IR CHECK lines
  118. SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*')
  119. # Match things that look at identifiers, but only if they are followed by
  120. # spaces, commas, paren, or end of the string
  121. IR_VALUE_RE = re.compile(r'(\s+)%([\w\.\-]+?)([,\s\(\)]|\Z)')
  122. # Create a FileCheck variable name based on an IR name.
  123. def get_value_name(var):
  124. if var.isdigit():
  125. var = 'TMP' + var
  126. var = var.replace('.', '_')
  127. var = var.replace('-', '_')
  128. return var.upper()
  129. # Create a FileCheck variable from regex.
  130. def get_value_definition(var):
  131. return '[[' + get_value_name(var) + ':%.*]]'
  132. # Use a FileCheck variable.
  133. def get_value_use(var):
  134. return '[[' + get_value_name(var) + ']]'
  135. # Replace IR value defs and uses with FileCheck variables.
  136. def genericize_check_lines(lines, is_analyze):
  137. # This gets called for each match that occurs in
  138. # a line. We transform variables we haven't seen
  139. # into defs, and variables we have seen into uses.
  140. def transform_line_vars(match):
  141. var = match.group(2)
  142. if var in vars_seen:
  143. rv = get_value_use(var)
  144. else:
  145. vars_seen.add(var)
  146. rv = get_value_definition(var)
  147. # re.sub replaces the entire regex match
  148. # with whatever you return, so we have
  149. # to make sure to hand it back everything
  150. # including the commas and spaces.
  151. return match.group(1) + rv + match.group(3)
  152. vars_seen = set()
  153. lines_with_def = []
  154. for i, line in enumerate(lines):
  155. # An IR variable named '%.' matches the FileCheck regex string.
  156. line = line.replace('%.', '%dot')
  157. # Ignore any comments, since the check lines will too.
  158. scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line)
  159. if is_analyze:
  160. lines[i] = scrubbed_line
  161. else:
  162. lines[i] = IR_VALUE_RE.sub(transform_line_vars, scrubbed_line)
  163. return lines
  164. def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_asm, is_analyze):
  165. printed_prefixes = []
  166. for p in prefix_list:
  167. checkprefixes = p[0]
  168. for checkprefix in checkprefixes:
  169. if checkprefix in printed_prefixes:
  170. break
  171. # TODO func_dict[checkprefix] may be None, '' or not exist.
  172. # Fix the call sites.
  173. if func_name not in func_dict[checkprefix] or not func_dict[checkprefix][func_name]:
  174. continue
  175. # Add some space between different check prefixes, but not after the last
  176. # check line (before the test code).
  177. if is_asm:
  178. if len(printed_prefixes) != 0:
  179. output_lines.append(comment_marker)
  180. printed_prefixes.append(checkprefix)
  181. output_lines.append(check_label_format % (checkprefix, func_name))
  182. func_body = str(func_dict[checkprefix][func_name]).splitlines()
  183. # For ASM output, just emit the check lines.
  184. if is_asm:
  185. output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
  186. for func_line in func_body[1:]:
  187. output_lines.append('%s %s-NEXT: %s' % (comment_marker, checkprefix, func_line))
  188. break
  189. # For IR output, change all defs to FileCheck variables, so we're immune
  190. # to variable naming fashions.
  191. func_body = genericize_check_lines(func_body, is_analyze)
  192. # This could be selectively enabled with an optional invocation argument.
  193. # Disabled for now: better to check everything. Be safe rather than sorry.
  194. # Handle the first line of the function body as a special case because
  195. # it's often just noise (a useless asm comment or entry label).
  196. #if func_body[0].startswith("#") or func_body[0].startswith("entry:"):
  197. # is_blank_line = True
  198. #else:
  199. # output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
  200. # is_blank_line = False
  201. is_blank_line = False
  202. for func_line in func_body:
  203. if func_line.strip() == '':
  204. is_blank_line = True
  205. continue
  206. # Do not waste time checking IR comments.
  207. func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line)
  208. # Skip blank lines instead of checking them.
  209. if is_blank_line:
  210. output_lines.append('{} {}: {}'.format(
  211. comment_marker, checkprefix, func_line))
  212. else:
  213. output_lines.append('{} {}-NEXT: {}'.format(
  214. comment_marker, checkprefix, func_line))
  215. is_blank_line = False
  216. # Add space between different check prefixes and also before the first
  217. # line of code in the test function.
  218. output_lines.append(comment_marker)
  219. break
  220. def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict, func_name):
  221. # Label format is based on IR string.
  222. check_label_format = '{} %s-LABEL: @%s('.format(comment_marker)
  223. add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, False)
  224. def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name):
  225. check_label_format = '{} %s-LABEL: \'%s\''.format(comment_marker)
  226. add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, True)
  227. def check_prefix(prefix):
  228. if not PREFIX_RE.match(prefix):
  229. hint = ""
  230. if ',' in prefix:
  231. hint = " Did you mean '--check-prefixes=" + prefix + "'?"
  232. print(("WARNING: Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores." + hint) %
  233. (prefix), file=sys.stderr)
  234. def verify_filecheck_prefixes(fc_cmd):
  235. fc_cmd_parts = fc_cmd.split()
  236. for part in fc_cmd_parts:
  237. if "check-prefix=" in part:
  238. prefix = part.split('=', 1)[1]
  239. check_prefix(prefix)
  240. elif "check-prefixes=" in part:
  241. prefixes = part.split('=', 1)[1].split(',')
  242. for prefix in prefixes:
  243. check_prefix(prefix)
  244. if prefixes.count(prefix) > 1:
  245. print("WARNING: Supplied prefix '%s' is not unique in the prefix list." %
  246. (prefix,), file=sys.stderr)