common.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 error(msg, test_file=None):
  64. if test_file:
  65. msg = '{}: {}'.format(msg, test_file)
  66. print('ERROR: {}'.format(msg), file=sys.stderr)
  67. def warn(msg, test_file=None):
  68. if test_file:
  69. msg = '{}: {}'.format(msg, test_file)
  70. print('WARNING: {}'.format(msg), file=sys.stderr)
  71. def scrub_body(body):
  72. # Scrub runs of whitespace out of the assembly, but leave the leading
  73. # whitespace in place.
  74. body = SCRUB_WHITESPACE_RE.sub(r' ', body)
  75. # Expand the tabs used for indentation.
  76. body = string.expandtabs(body, 2)
  77. # Strip trailing whitespace.
  78. body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body)
  79. return body
  80. def do_scrub(body, scrubber, scrubber_args, extra):
  81. if scrubber_args:
  82. local_args = copy.deepcopy(scrubber_args)
  83. local_args[0].extra_scrub = extra
  84. return scrubber(body, *local_args)
  85. return scrubber(body, *scrubber_args)
  86. # Build up a dictionary of all the function bodies.
  87. class function_body(object):
  88. def __init__(self, string, extra):
  89. self.scrub = string
  90. self.extrascrub = extra
  91. def __str__(self):
  92. return self.scrub
  93. def build_function_body_dictionary(function_re, scrubber, scrubber_args, raw_tool_output, prefixes, func_dict, verbose):
  94. for m in function_re.finditer(raw_tool_output):
  95. if not m:
  96. continue
  97. func = m.group('func')
  98. body = m.group('body')
  99. scrubbed_body = do_scrub(body, scrubber, scrubber_args, extra = False)
  100. scrubbed_extra = do_scrub(body, scrubber, scrubber_args, extra = True)
  101. if 'analysis' in m.groupdict():
  102. analysis = m.group('analysis')
  103. if analysis.lower() != 'cost model analysis':
  104. warn('Unsupported analysis mode: %r!' % (analysis,))
  105. if func.startswith('stress'):
  106. # We only use the last line of the function body for stress tests.
  107. scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:])
  108. if verbose:
  109. print('Processing function: ' + func, file=sys.stderr)
  110. for l in scrubbed_body.splitlines():
  111. print(' ' + l, file=sys.stderr)
  112. for prefix in prefixes:
  113. if func in func_dict[prefix] and str(func_dict[prefix][func]) != scrubbed_body:
  114. if func_dict[prefix][func] and func_dict[prefix][func].extrascrub == scrubbed_extra:
  115. func_dict[prefix][func].scrub = scrubbed_extra
  116. continue
  117. else:
  118. if prefix == prefixes[-1]:
  119. warn('Found conflicting asm under the same prefix: %r!' % (prefix,))
  120. else:
  121. func_dict[prefix][func] = None
  122. continue
  123. func_dict[prefix][func] = function_body(scrubbed_body, scrubbed_extra)
  124. ##### Generator of LLVM IR CHECK lines
  125. SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*')
  126. # Match things that look at identifiers, but only if they are followed by
  127. # spaces, commas, paren, or end of the string
  128. IR_VALUE_RE = re.compile(r'(\s+)%([\w\.\-]+?)([,\s\(\)]|\Z)')
  129. # Create a FileCheck variable name based on an IR name.
  130. def get_value_name(var):
  131. if var.isdigit():
  132. var = 'TMP' + var
  133. var = var.replace('.', '_')
  134. var = var.replace('-', '_')
  135. return var.upper()
  136. # Create a FileCheck variable from regex.
  137. def get_value_definition(var):
  138. return '[[' + get_value_name(var) + ':%.*]]'
  139. # Use a FileCheck variable.
  140. def get_value_use(var):
  141. return '[[' + get_value_name(var) + ']]'
  142. # Replace IR value defs and uses with FileCheck variables.
  143. def genericize_check_lines(lines, is_analyze):
  144. # This gets called for each match that occurs in
  145. # a line. We transform variables we haven't seen
  146. # into defs, and variables we have seen into uses.
  147. def transform_line_vars(match):
  148. var = match.group(2)
  149. if var in vars_seen:
  150. rv = get_value_use(var)
  151. else:
  152. vars_seen.add(var)
  153. rv = get_value_definition(var)
  154. # re.sub replaces the entire regex match
  155. # with whatever you return, so we have
  156. # to make sure to hand it back everything
  157. # including the commas and spaces.
  158. return match.group(1) + rv + match.group(3)
  159. vars_seen = set()
  160. lines_with_def = []
  161. for i, line in enumerate(lines):
  162. # An IR variable named '%.' matches the FileCheck regex string.
  163. line = line.replace('%.', '%dot')
  164. # Ignore any comments, since the check lines will too.
  165. scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line)
  166. if is_analyze:
  167. lines[i] = scrubbed_line
  168. else:
  169. lines[i] = IR_VALUE_RE.sub(transform_line_vars, scrubbed_line)
  170. return lines
  171. def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_asm, is_analyze):
  172. printed_prefixes = []
  173. for p in prefix_list:
  174. checkprefixes = p[0]
  175. for checkprefix in checkprefixes:
  176. if checkprefix in printed_prefixes:
  177. break
  178. # TODO func_dict[checkprefix] may be None, '' or not exist.
  179. # Fix the call sites.
  180. if func_name not in func_dict[checkprefix] or not func_dict[checkprefix][func_name]:
  181. continue
  182. # Add some space between different check prefixes, but not after the last
  183. # check line (before the test code).
  184. if is_asm:
  185. if len(printed_prefixes) != 0:
  186. output_lines.append(comment_marker)
  187. printed_prefixes.append(checkprefix)
  188. output_lines.append(check_label_format % (checkprefix, func_name))
  189. func_body = str(func_dict[checkprefix][func_name]).splitlines()
  190. # For ASM output, just emit the check lines.
  191. if is_asm:
  192. output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
  193. for func_line in func_body[1:]:
  194. output_lines.append('%s %s-NEXT: %s' % (comment_marker, checkprefix, func_line))
  195. break
  196. # For IR output, change all defs to FileCheck variables, so we're immune
  197. # to variable naming fashions.
  198. func_body = genericize_check_lines(func_body, is_analyze)
  199. # This could be selectively enabled with an optional invocation argument.
  200. # Disabled for now: better to check everything. Be safe rather than sorry.
  201. # Handle the first line of the function body as a special case because
  202. # it's often just noise (a useless asm comment or entry label).
  203. #if func_body[0].startswith("#") or func_body[0].startswith("entry:"):
  204. # is_blank_line = True
  205. #else:
  206. # output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
  207. # is_blank_line = False
  208. is_blank_line = False
  209. for func_line in func_body:
  210. if func_line.strip() == '':
  211. is_blank_line = True
  212. continue
  213. # Do not waste time checking IR comments.
  214. func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line)
  215. # Skip blank lines instead of checking them.
  216. if is_blank_line:
  217. output_lines.append('{} {}: {}'.format(
  218. comment_marker, checkprefix, func_line))
  219. else:
  220. output_lines.append('{} {}-NEXT: {}'.format(
  221. comment_marker, checkprefix, func_line))
  222. is_blank_line = False
  223. # Add space between different check prefixes and also before the first
  224. # line of code in the test function.
  225. output_lines.append(comment_marker)
  226. break
  227. def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict, func_name):
  228. # Label format is based on IR string.
  229. check_label_format = '{} %s-LABEL: @%s('.format(comment_marker)
  230. add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, False)
  231. def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name):
  232. check_label_format = '{} %s-LABEL: \'%s\''.format(comment_marker)
  233. add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, True)
  234. def check_prefix(prefix):
  235. if not PREFIX_RE.match(prefix):
  236. hint = ""
  237. if ',' in prefix:
  238. hint = " Did you mean '--check-prefixes=" + prefix + "'?"
  239. warn(("Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores." + hint) %
  240. (prefix))
  241. def verify_filecheck_prefixes(fc_cmd):
  242. fc_cmd_parts = fc_cmd.split()
  243. for part in fc_cmd_parts:
  244. if "check-prefix=" in part:
  245. prefix = part.split('=', 1)[1]
  246. check_prefix(prefix)
  247. elif "check-prefixes=" in part:
  248. prefixes = part.split('=', 1)[1].split(',')
  249. for prefix in prefixes:
  250. check_prefix(prefix)
  251. if prefixes.count(prefix) > 1:
  252. warn("Supplied prefix '%s' is not unique in the prefix list." % (prefix,))