diff options
author | Tobias Hieta <tobias@hieta.se> | 2023-05-15 11:02:42 +0200 |
---|---|---|
committer | Tobias Hieta <tobias@hieta.se> | 2023-05-17 10:48:52 +0200 |
commit | b71edfaa4ec3c998aadb35255ce2f60bba2940b0 (patch) | |
tree | e79a8adc4e62ac9e50d889b5cf9a248ceccc65de /llvm/utils/UpdateTestChecks | |
parent | 7beb2ca8fa2aed594bb150c4c5734931d6ea4348 (diff) | |
download | llvm-b71edfaa4ec3c998aadb35255ce2f60bba2940b0.zip llvm-b71edfaa4ec3c998aadb35255ce2f60bba2940b0.tar.gz llvm-b71edfaa4ec3c998aadb35255ce2f60bba2940b0.tar.bz2 |
[NFC][Py Reformat] Reformat python files in llvm
This is the first commit in a series that will reformat
all the python files in the LLVM repository.
Reformatting is done with `black`.
See more information here:
https://discourse.llvm.org/t/rfc-document-and-standardize-python-code-style
Reviewed By: jhenderson, JDevlieghere, MatzeB
Differential Revision: https://reviews.llvm.org/D150545
Diffstat (limited to 'llvm/utils/UpdateTestChecks')
-rw-r--r-- | llvm/utils/UpdateTestChecks/asm.py | 859 | ||||
-rw-r--r-- | llvm/utils/UpdateTestChecks/common.py | 2689 | ||||
-rw-r--r-- | llvm/utils/UpdateTestChecks/isel.py | 91 |
3 files changed, 2066 insertions, 1573 deletions
diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py index 5471922..72ff67b 100644 --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -5,10 +5,12 @@ import sys from . import common if sys.version_info[0] > 2: - class string: - expandtabs = str.expandtabs + + class string: + expandtabs = str.expandtabs + else: - import string + import string # RegEx: this is where the magic happens. @@ -16,525 +18,580 @@ else: ASM_FUNCTION_X86_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*(@"?(?P=func)"?| -- Begin function (?P=func))\n(?:\s*\.?Lfunc_begin[^:\n]*:\n)?' - r'(?:\.L(?P=func)\$local:\n)?' # drop .L<func>$local: - r'(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?' # drop .type .L<func>$local - r'(?:[ \t]*(?:\.cfi_startproc|\.cfi_personality|\.cfi_lsda|\.seh_proc|\.seh_handler)\b[^\n]*\n)*' # drop optional cfi - r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' - r'^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section|#+ -- End function)', - flags=(re.M | re.S)) + r"(?:\.L(?P=func)\$local:\n)?" # drop .L<func>$local: + r"(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?" # drop .type .L<func>$local + r"(?:[ \t]*(?:\.cfi_startproc|\.cfi_personality|\.cfi_lsda|\.seh_proc|\.seh_handler)\b[^\n]*\n)*" # drop optional cfi + r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*" + r"^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section|#+ -- End function)", + flags=(re.M | re.S), +) ASM_FUNCTION_ARM_RE = re.compile( - r'^(?P<func>[0-9a-zA-Z_$]+):\n' # f: (name of function) - r'(?:\.L(?P=func)\$local:\n)?' # drop .L<func>$local: - r'(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?' # drop .type .L<func>$local - r'\s+\.fnstart\n' # .fnstart - r'(?P<body>.*?)' # (body of the function) - r'^.Lfunc_end[0-9]+:', # .Lfunc_end0: or # -- End function - flags=(re.M | re.S)) + r"^(?P<func>[0-9a-zA-Z_$]+):\n" # f: (name of function) + r"(?:\.L(?P=func)\$local:\n)?" # drop .L<func>$local: + r"(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?" # drop .type .L<func>$local + r"\s+\.fnstart\n" # .fnstart + r"(?P<body>.*?)" # (body of the function) + r"^.Lfunc_end[0-9]+:", # .Lfunc_end0: or # -- End function + flags=(re.M | re.S), +) ASM_FUNCTION_AARCH64_RE = re.compile( - r'^_?(?P<func>[^:]+):[ \t]*\/\/[ \t]*@"?(?P=func)"?( (Function|Tail Call))?\n' - r'(?:[ \t]+.cfi_startproc\n)?' # drop optional cfi noise - r'(?P<body>.*?)\n' - # This list is incomplete - r'^\s*(\.Lfunc_end[0-9]+|// -- End function)', - flags=(re.M | re.S)) + r'^_?(?P<func>[^:]+):[ \t]*\/\/[ \t]*@"?(?P=func)"?( (Function|Tail Call))?\n' + r"(?:[ \t]+.cfi_startproc\n)?" # drop optional cfi noise + r"(?P<body>.*?)\n" + # This list is incomplete + r"^\s*(\.Lfunc_end[0-9]+|// -- End function)", + flags=(re.M | re.S), +) ASM_FUNCTION_AMDGPU_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?' - r'(?P<body>.*?)\n' # (body of the function) + r"(?P<body>.*?)\n" # (body of the function) # This list is incomplete - r'^\s*(\.Lfunc_end[0-9]+:\n|\.section)', - flags=(re.M | re.S)) + r"^\s*(\.Lfunc_end[0-9]+:\n|\.section)", + flags=(re.M | re.S), +) ASM_FUNCTION_BPF_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n' - r'(?:[ \t]+.cfi_startproc\n|.seh_proc[^\n]+\n)?' # drop optional cfi - r'(?P<body>.*?)\s*' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"(?:[ \t]+.cfi_startproc\n|.seh_proc[^\n]+\n)?" # drop optional cfi + r"(?P<body>.*?)\s*" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_HEXAGON_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*//[ \t]*@"?(?P=func)"?\n[^:]*?' - r'(?P<body>.*?)\n' # (body of the function) + r"(?P<body>.*?)\n" # (body of the function) # This list is incomplete - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_M68K_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*;[ \t]*@"?(?P=func)"?\n' - r'(?P<body>.*?)\s*' # (body of the function) - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"(?P<body>.*?)\s*" # (body of the function) + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_MIPS_RE = re.compile( - r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n[^:]*?' # f: (name of func) - r'(?:\s*\.?Ltmp[^:\n]*:\n)?[^:]*?' # optional .Ltmp<N> for EH - r'(?:^[ \t]+\.(frame|f?mask|set).*?\n)+' # Mips+LLVM standard asm prologue - r'(?P<body>.*?)\n' # (body of the function) + r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n[^:]*?' # f: (name of func) + r"(?:\s*\.?Ltmp[^:\n]*:\n)?[^:]*?" # optional .Ltmp<N> for EH + r"(?:^[ \t]+\.(frame|f?mask|set).*?\n)+" # Mips+LLVM standard asm prologue + r"(?P<body>.*?)\n" # (body of the function) # Mips+LLVM standard asm epilogue - r'(?:(^[ \t]+\.set[^\n]*?\n)*^[ \t]+\.end.*?\n)' - r'(\$|\.L)func_end[0-9]+:\n', # $func_end0: (mips32 - O32) or - # .Lfunc_end0: (mips64 - NewABI) - flags=(re.M | re.S)) + r"(?:(^[ \t]+\.set[^\n]*?\n)*^[ \t]+\.end.*?\n)" + r"(\$|\.L)func_end[0-9]+:\n", # $func_end0: (mips32 - O32) or + # .Lfunc_end0: (mips64 - NewABI) + flags=(re.M | re.S), +) ASM_FUNCTION_MSP430_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?' - r'(?P<body>.*?)\n' - r'(\$|\.L)func_end[0-9]+:\n', # $func_end0: - flags=(re.M | re.S)) + r"(?P<body>.*?)\n" + r"(\$|\.L)func_end[0-9]+:\n", # $func_end0: + flags=(re.M | re.S), +) ASM_FUNCTION_AVR_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?' - r'(?P<body>.*?)\n' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"(?P<body>.*?)\n" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_PPC_RE = re.compile( - r'#[ \-\t]*Begin function (?P<func>[^.:]+)\n' - r'.*?' + r"#[ \-\t]*Begin function (?P<func>[^.:]+)\n" + r".*?" r'^[_.]?(?P=func):(?:[ \t]*#+[ \t]*@"?(?P=func)"?)?\n' - r'(?:^[^#]*\n)*' - r'(?P<body>.*?)\n' + r"(?:^[^#]*\n)*" + r"(?P<body>.*?)\n" # This list is incomplete - r'(?:^[ \t]*(?:\.(?:long|quad|v?byte)[ \t]+[^\n]+)\n)*' - r'(?:\.Lfunc_end|L\.\.(?P=func))[0-9]+:\n', - flags=(re.M | re.S)) + r"(?:^[ \t]*(?:\.(?:long|quad|v?byte)[ \t]+[^\n]+)\n)*" + r"(?:\.Lfunc_end|L\.\.(?P=func))[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_RISCV_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n' - r'(?:\s*\.?L(?P=func)\$local:\n)?' # optional .L<func>$local: due to -fno-semantic-interposition - r'(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?' # optional .type .L<func>$local - r'(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?' - r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"(?:\s*\.?L(?P=func)\$local:\n)?" # optional .L<func>$local: due to -fno-semantic-interposition + r"(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?" # optional .type .L<func>$local + r"(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?" + r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_LANAI_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*!+[ \t]*@"?(?P=func)"?\n' - r'(?:[ \t]+.cfi_startproc\n)?' # drop optional cfi noise - r'(?P<body>.*?)\s*' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"(?:[ \t]+.cfi_startproc\n)?" # drop optional cfi noise + r"(?P<body>.*?)\s*" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_SPARC_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*!+[ \t]*@"?(?P=func)"?\n' - r'(?P<body>.*?)\s*' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"(?P<body>.*?)\s*" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_SYSTEMZ_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n' - r'(?:[ \t]+.cfi_startproc\n)?' - r'(?P<body>.*?)\n' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"(?:[ \t]+.cfi_startproc\n)?" + r"(?P<body>.*?)\n" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_AARCH64_DARWIN_RE = re.compile( r'^_(?P<func>[^:]+):[ \t]*;[ \t]@"?(?P=func)"?\n' - r'([ \t]*.cfi_startproc\n[\s]*)?' - r'(?P<body>.*?)' - r'([ \t]*.cfi_endproc\n[\s]*)?' - r'^[ \t]*;[ \t]--[ \t]End[ \t]function', - flags=(re.M | re.S)) + r"([ \t]*.cfi_startproc\n[\s]*)?" + r"(?P<body>.*?)" + r"([ \t]*.cfi_endproc\n[\s]*)?" + r"^[ \t]*;[ \t]--[ \t]End[ \t]function", + flags=(re.M | re.S), +) ASM_FUNCTION_ARM_DARWIN_RE = re.compile( - r'@[ \t]--[ \t]Begin[ \t]function[ \t](?P<func>[^ \t]+?)\n' - r'^[ \t]*\.globl[ \t]*_(?P=func)[ \t]*' - r'(?P<directives>.*?)' - r'^_(?P=func):\n[ \t]*' - r'(?P<body>.*?)' - r'^[ \t]*@[ \t]--[ \t]End[ \t]function', - flags=(re.M | re.S )) + r"@[ \t]--[ \t]Begin[ \t]function[ \t](?P<func>[^ \t]+?)\n" + r"^[ \t]*\.globl[ \t]*_(?P=func)[ \t]*" + r"(?P<directives>.*?)" + r"^_(?P=func):\n[ \t]*" + r"(?P<body>.*?)" + r"^[ \t]*@[ \t]--[ \t]End[ \t]function", + flags=(re.M | re.S), +) ASM_FUNCTION_ARM_MACHO_RE = re.compile( - r'^_(?P<func>[^:]+):[ \t]*\n' - r'([ \t]*.cfi_startproc\n[ \t]*)?' - r'(?P<body>.*?)\n' - r'[ \t]*\.cfi_endproc\n', - flags=(re.M | re.S)) + r"^_(?P<func>[^:]+):[ \t]*\n" + r"([ \t]*.cfi_startproc\n[ \t]*)?" + r"(?P<body>.*?)\n" + r"[ \t]*\.cfi_endproc\n", + flags=(re.M | re.S), +) ASM_FUNCTION_THUMBS_DARWIN_RE = re.compile( - r'^_(?P<func>[^:]+):\n' - r'(?P<body>.*?)\n' - r'[ \t]*\.data_region\n', - flags=(re.M | re.S)) + r"^_(?P<func>[^:]+):\n" r"(?P<body>.*?)\n" r"[ \t]*\.data_region\n", + flags=(re.M | re.S), +) ASM_FUNCTION_THUMB_DARWIN_RE = re.compile( - r'^_(?P<func>[^:]+):\n' - r'(?P<body>.*?)\n' - r'^[ \t]*@[ \t]--[ \t]End[ \t]function', - flags=(re.M | re.S)) + r"^_(?P<func>[^:]+):\n" r"(?P<body>.*?)\n" r"^[ \t]*@[ \t]--[ \t]End[ \t]function", + flags=(re.M | re.S), +) ASM_FUNCTION_ARM_IOS_RE = re.compile( - r'^_(?P<func>[^:]+):\n' - r'(?P<body>.*?)' - r'^[ \t]*@[ \t]--[ \t]End[ \t]function', - flags=(re.M | re.S)) + r"^_(?P<func>[^:]+):\n" r"(?P<body>.*?)" r"^[ \t]*@[ \t]--[ \t]End[ \t]function", + flags=(re.M | re.S), +) ASM_FUNCTION_WASM_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n' - r'(?P<body>.*?)\n' - r'^\s*(\.Lfunc_end[0-9]+:\n|end_function)', - flags=(re.M | re.S)) + r"(?P<body>.*?)\n" + r"^\s*(\.Lfunc_end[0-9]+:\n|end_function)", + flags=(re.M | re.S), +) ASM_FUNCTION_VE_RE = re.compile( - r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n' - r'(?:\s*\.?L(?P=func)\$local:\n)?' # optional .L<func>$local: due to -fno-semantic-interposition - r'(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?' # optional .type .L<func>$local - r'(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?' - r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n" + r"(?:\s*\.?L(?P=func)\$local:\n)?" # optional .L<func>$local: due to -fno-semantic-interposition + r"(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?" # optional .type .L<func>$local + r"(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?" + r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_CSKY_RE = re.compile( - r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?' - r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) + r"^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?" + r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) ASM_FUNCTION_NVPTX_RE = re.compile( # function attributes and retval # .visible .func (.param .align 16 .b8 func_retval0[32]) - #r'^(\.visible\s+)?\.func\s+(\([^\)]*\)\s*)?' - r'^(\.(func|visible|weak|entry|noreturn|extern)\s+)+(\([^\)]*\)\s*)?' - + # r'^(\.visible\s+)?\.func\s+(\([^\)]*\)\s*)?' + r"^(\.(func|visible|weak|entry|noreturn|extern)\s+)+(\([^\)]*\)\s*)?" # function name - r'(?P<func>[^\(\n]+)' - + r"(?P<func>[^\(\n]+)" # function name separator (opening brace) - r'(?P<func_name_separator>\()' - + r"(?P<func_name_separator>\()" # function parameters # ( # .param .align 16 .b8 callee_St8x4_param_0[32] # ) // -- Begin function callee_St8x4 - r'[^\)]*\)(\s*//[^\n]*)?\n' - + r"[^\)]*\)(\s*//[^\n]*)?\n" # function body - r'(?P<body>.*?)\n' - + r"(?P<body>.*?)\n" # function body end marker - r'\s*// -- End function', - flags=(re.M | re.S)) + r"\s*// -- End function", + flags=(re.M | re.S), +) ASM_FUNCTION_LOONGARCH_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n' - r'(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?' - r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*' - r'.Lfunc_end[0-9]+:\n', - flags=(re.M | re.S)) - -SCRUB_X86_SHUFFLES_RE = ( - re.compile( - r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$', - flags=re.M)) - -SCRUB_X86_SHUFFLES_NO_MEM_RE = ( - re.compile( - r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = (?!.*(?:mem)).*)$', - flags=re.M)) - -SCRUB_X86_SPILL_RELOAD_RE = ( - re.compile( - r'-?\d+\(%([er])[sb]p\)(.*(?:Spill|Reload))$', - flags=re.M)) -SCRUB_X86_SP_RE = re.compile(r'\d+\(%(esp|rsp)\)') -SCRUB_X86_RIP_RE = re.compile(r'[.\w]+\(%rip\)') -SCRUB_X86_LCP_RE = re.compile(r'\.?LCPI[0-9]+_[0-9]+') -SCRUB_X86_RET_RE = re.compile(r'ret[l|q]') + r"(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?" + r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*" + r".Lfunc_end[0-9]+:\n", + flags=(re.M | re.S), +) + +SCRUB_X86_SHUFFLES_RE = re.compile( + r"^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$", flags=re.M +) + +SCRUB_X86_SHUFFLES_NO_MEM_RE = re.compile( + r"^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = (?!.*(?:mem)).*)$", + flags=re.M, +) + +SCRUB_X86_SPILL_RELOAD_RE = re.compile( + r"-?\d+\(%([er])[sb]p\)(.*(?:Spill|Reload))$", flags=re.M +) +SCRUB_X86_SP_RE = re.compile(r"\d+\(%(esp|rsp)\)") +SCRUB_X86_RIP_RE = re.compile(r"[.\w]+\(%rip\)") +SCRUB_X86_LCP_RE = re.compile(r"\.?LCPI[0-9]+_[0-9]+") +SCRUB_X86_RET_RE = re.compile(r"ret[l|q]") + def scrub_asm_x86(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - - # Detect shuffle asm comments and hide the operands in favor of the comments. - if getattr(args, 'no_x86_scrub_mem_shuffle', True): - asm = SCRUB_X86_SHUFFLES_NO_MEM_RE.sub(r'\1 {{.*#+}} \2', asm) - else: - asm = SCRUB_X86_SHUFFLES_RE.sub(r'\1 {{.*#+}} \2', asm) - - # Detect stack spills and reloads and hide their exact offset and whether - # they used the stack pointer or frame pointer. - asm = SCRUB_X86_SPILL_RELOAD_RE.sub(r'{{[-0-9]+}}(%\1{{[sb]}}p)\2', asm) - if getattr(args, 'x86_scrub_sp', True): - # Generically match the stack offset of a memory operand. - asm = SCRUB_X86_SP_RE.sub(r'{{[0-9]+}}(%\1)', asm) - if getattr(args, 'x86_scrub_rip', False): - # Generically match a RIP-relative memory operand. - asm = SCRUB_X86_RIP_RE.sub(r'{{.*}}(%rip)', asm) - # Generically match a LCP symbol. - asm = SCRUB_X86_LCP_RE.sub(r'{{\.?LCPI[0-9]+_[0-9]+}}', asm) - if getattr(args, 'extra_scrub', False): - # Avoid generating different checks for 32- and 64-bit because of 'retl' vs 'retq'. - asm = SCRUB_X86_RET_RE.sub(r'ret{{[l|q]}}', asm) - # Strip kill operands inserted into the asm. - asm = common.SCRUB_KILL_COMMENT_RE.sub('', asm) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + + # Detect shuffle asm comments and hide the operands in favor of the comments. + if getattr(args, "no_x86_scrub_mem_shuffle", True): + asm = SCRUB_X86_SHUFFLES_NO_MEM_RE.sub(r"\1 {{.*#+}} \2", asm) + else: + asm = SCRUB_X86_SHUFFLES_RE.sub(r"\1 {{.*#+}} \2", asm) + + # Detect stack spills and reloads and hide their exact offset and whether + # they used the stack pointer or frame pointer. + asm = SCRUB_X86_SPILL_RELOAD_RE.sub(r"{{[-0-9]+}}(%\1{{[sb]}}p)\2", asm) + if getattr(args, "x86_scrub_sp", True): + # Generically match the stack offset of a memory operand. + asm = SCRUB_X86_SP_RE.sub(r"{{[0-9]+}}(%\1)", asm) + if getattr(args, "x86_scrub_rip", False): + # Generically match a RIP-relative memory operand. + asm = SCRUB_X86_RIP_RE.sub(r"{{.*}}(%rip)", asm) + # Generically match a LCP symbol. + asm = SCRUB_X86_LCP_RE.sub(r"{{\.?LCPI[0-9]+_[0-9]+}}", asm) + if getattr(args, "extra_scrub", False): + # Avoid generating different checks for 32- and 64-bit because of 'retl' vs 'retq'. + asm = SCRUB_X86_RET_RE.sub(r"ret{{[l|q]}}", asm) + # Strip kill operands inserted into the asm. + asm = common.SCRUB_KILL_COMMENT_RE.sub("", asm) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_amdgpu(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_arm_eabi(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip kill operands inserted into the asm. - asm = common.SCRUB_KILL_COMMENT_RE.sub('', asm) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip kill operands inserted into the asm. + asm = common.SCRUB_KILL_COMMENT_RE.sub("", asm) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_bpf(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_hexagon(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_powerpc(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip unimportant comments, but leave the token '#' in place. - asm = common.SCRUB_LOOP_COMMENT_RE.sub(r'#', asm) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - # Strip the tailing token '#', except the line only has token '#'. - asm = common.SCRUB_TAILING_COMMENT_TOKEN_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip unimportant comments, but leave the token '#' in place. + asm = common.SCRUB_LOOP_COMMENT_RE.sub(r"#", asm) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + # Strip the tailing token '#', except the line only has token '#'. + asm = common.SCRUB_TAILING_COMMENT_TOKEN_RE.sub(r"", asm) + return asm + def scrub_asm_m68k(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_mips(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_msp430(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_avr(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_riscv(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_lanai(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_sparc(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_systemz(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_wasm(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_ve(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_csky(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip kill operands inserted into the asm. - asm = common.SCRUB_KILL_COMMENT_RE.sub('', asm) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip kill operands inserted into the asm. + asm = common.SCRUB_KILL_COMMENT_RE.sub("", asm) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_nvptx(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + def scrub_asm_loongarch(asm, args): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) - # Expand the tabs used for indentation. - asm = string.expandtabs(asm, 2) - # Strip trailing whitespace. - asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) - return asm + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm) + return asm + # Returns a tuple of a scrub function and a function regex. Scrub function is # used to alter function body in some way, for example, remove trailing spaces. # Function regex is used to match function name, body, etc. in raw llc output. def get_run_handler(triple): - target_handlers = { - 'i686': (scrub_asm_x86, ASM_FUNCTION_X86_RE), - 'x86': (scrub_asm_x86, ASM_FUNCTION_X86_RE), - 'i386': (scrub_asm_x86, ASM_FUNCTION_X86_RE), - 'arm64_32-apple-ios': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), - 'arm64_32-apple-watchos2.0.0': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), - 'aarch64': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE), - 'aarch64-apple-darwin': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), - 'aarch64-apple-ios': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), - 'bpf': (scrub_asm_bpf, ASM_FUNCTION_BPF_RE), - 'bpfel': (scrub_asm_bpf, ASM_FUNCTION_BPF_RE), - 'bpfeb': (scrub_asm_bpf, ASM_FUNCTION_BPF_RE), - 'hexagon': (scrub_asm_hexagon, ASM_FUNCTION_HEXAGON_RE), - 'r600': (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE), - 'amdgcn': (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE), - 'arm': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE), - 'arm64': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE), - 'arm64e': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), - 'arm64ec': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE), - 'arm64-apple-ios': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), - 'armv7-apple-ios' : (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE), - 'armv7-apple-darwin': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_DARWIN_RE), - 'thumb': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE), - 'thumb-macho': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE), - 'thumbv5-macho': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE), - 'thumbv7s-apple-darwin' : (scrub_asm_arm_eabi, ASM_FUNCTION_THUMBS_DARWIN_RE), - 'thumbv7-apple-darwin' : (scrub_asm_arm_eabi, ASM_FUNCTION_THUMB_DARWIN_RE), - 'thumbv7-apple-ios' : (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE), - 'm68k': (scrub_asm_m68k, ASM_FUNCTION_M68K_RE), - 'mips': (scrub_asm_mips, ASM_FUNCTION_MIPS_RE), - 'msp430': (scrub_asm_msp430, ASM_FUNCTION_MSP430_RE), - 'avr': (scrub_asm_avr, ASM_FUNCTION_AVR_RE), - 'ppc32': (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE), - 'ppc64': (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE), - 'powerpc': (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE), - 'riscv32': (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE), - 'riscv64': (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE), - 'lanai': (scrub_asm_lanai, ASM_FUNCTION_LANAI_RE), - 'sparc': (scrub_asm_sparc, ASM_FUNCTION_SPARC_RE), - 's390x': (scrub_asm_systemz, ASM_FUNCTION_SYSTEMZ_RE), - 'wasm32': (scrub_asm_wasm, ASM_FUNCTION_WASM_RE), - 'wasm64': (scrub_asm_wasm, ASM_FUNCTION_WASM_RE), - 've': (scrub_asm_ve, ASM_FUNCTION_VE_RE), - 'csky': (scrub_asm_csky, ASM_FUNCTION_CSKY_RE), - 'nvptx': (scrub_asm_nvptx, ASM_FUNCTION_NVPTX_RE), - 'loongarch32': (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE), - 'loongarch64': (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE) - } - handler = None - best_prefix = '' - for prefix, s in target_handlers.items(): - if triple.startswith(prefix) and len(prefix) > len(best_prefix): - handler = s - best_prefix = prefix - - if handler is None: - raise KeyError('Triple %r is not supported' % (triple)) - - return handler + target_handlers = { + "i686": (scrub_asm_x86, ASM_FUNCTION_X86_RE), + "x86": (scrub_asm_x86, ASM_FUNCTION_X86_RE), + "i386": (scrub_asm_x86, ASM_FUNCTION_X86_RE), + "arm64_32-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), + "arm64_32-apple-watchos2.0.0": ( + scrub_asm_arm_eabi, + ASM_FUNCTION_AARCH64_DARWIN_RE, + ), + "aarch64": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE), + "aarch64-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), + "aarch64-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), + "bpf": (scrub_asm_bpf, ASM_FUNCTION_BPF_RE), + "bpfel": (scrub_asm_bpf, ASM_FUNCTION_BPF_RE), + "bpfeb": (scrub_asm_bpf, ASM_FUNCTION_BPF_RE), + "hexagon": (scrub_asm_hexagon, ASM_FUNCTION_HEXAGON_RE), + "r600": (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE), + "amdgcn": (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE), + "arm": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE), + "arm64": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE), + "arm64e": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), + "arm64ec": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE), + "arm64-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), + "armv7-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE), + "armv7-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_DARWIN_RE), + "thumb": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE), + "thumb-macho": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE), + "thumbv5-macho": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE), + "thumbv7s-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_THUMBS_DARWIN_RE), + "thumbv7-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_THUMB_DARWIN_RE), + "thumbv7-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE), + "m68k": (scrub_asm_m68k, ASM_FUNCTION_M68K_RE), + "mips": (scrub_asm_mips, ASM_FUNCTION_MIPS_RE), + "msp430": (scrub_asm_msp430, ASM_FUNCTION_MSP430_RE), + "avr": (scrub_asm_avr, ASM_FUNCTION_AVR_RE), + "ppc32": (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE), + "ppc64": (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE), + "powerpc": (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE), + "riscv32": (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE), + "riscv64": (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE), + "lanai": (scrub_asm_lanai, ASM_FUNCTION_LANAI_RE), + "sparc": (scrub_asm_sparc, ASM_FUNCTION_SPARC_RE), + "s390x": (scrub_asm_systemz, ASM_FUNCTION_SYSTEMZ_RE), + "wasm32": (scrub_asm_wasm, ASM_FUNCTION_WASM_RE), + "wasm64": (scrub_asm_wasm, ASM_FUNCTION_WASM_RE), + "ve": (scrub_asm_ve, ASM_FUNCTION_VE_RE), + "csky": (scrub_asm_csky, ASM_FUNCTION_CSKY_RE), + "nvptx": (scrub_asm_nvptx, ASM_FUNCTION_NVPTX_RE), + "loongarch32": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE), + "loongarch64": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE), + } + handler = None + best_prefix = "" + for prefix, s in target_handlers.items(): + if triple.startswith(prefix) and len(prefix) > len(best_prefix): + handler = s + best_prefix = prefix + + if handler is None: + raise KeyError("Triple %r is not supported" % (triple)) + + return handler + ##### Generator of assembly CHECK lines -def add_checks(output_lines, comment_marker, prefix_list, func_dict, - func_name, global_vars_seen_dict, is_filtered): - # Label format is based on ASM string. - check_label_format = '{} %s-LABEL: %s%s%s%s'.format(comment_marker) - return common.add_checks(output_lines, comment_marker, prefix_list, func_dict, - func_name, check_label_format, True, False, 1, - global_vars_seen_dict, is_filtered=is_filtered) + +def add_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + global_vars_seen_dict, + is_filtered, +): + # Label format is based on ASM string. + check_label_format = "{} %s-LABEL: %s%s%s%s".format(comment_marker) + return common.add_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + check_label_format, + True, + False, + 1, + global_vars_seen_dict, + is_filtered=is_filtered, + ) diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py index aa0b128..b22d7e3 100644 --- a/llvm/utils/UpdateTestChecks/common.py +++ b/llvm/utils/UpdateTestChecks/common.py @@ -16,7 +16,7 @@ from typing import List _verbose = False -_prefix_filecheck_ir_name = '' +_prefix_filecheck_ir_name = "" """ Version changelog: @@ -27,792 +27,1028 @@ Version changelog: """ DEFAULT_VERSION = 2 + class Regex(object): - """Wrap a compiled regular expression object to allow deep copy of a regexp. - This is required for the deep copy done in do_scrub. + """Wrap a compiled regular expression object to allow deep copy of a regexp. + This is required for the deep copy done in do_scrub. - """ - def __init__(self, regex): - self.regex = regex + """ - def __deepcopy__(self, memo): - result = copy.copy(self) - result.regex = self.regex - return result + def __init__(self, regex): + self.regex = regex + + def __deepcopy__(self, memo): + result = copy.copy(self) + result.regex = self.regex + return result + + def search(self, line): + return self.regex.search(line) - def search(self, line): - return self.regex.search(line) + def sub(self, repl, line): + return self.regex.sub(repl, line) - def sub(self, repl, line): - return self.regex.sub(repl, line) + def pattern(self): + return self.regex.pattern - def pattern(self): - return self.regex.pattern + def flags(self): + return self.regex.flags - def flags(self): - return self.regex.flags class Filter(Regex): - """Augment a Regex object with a flag indicating whether a match should be + """Augment a Regex object with a flag indicating whether a match should be added (!is_filter_out) or removed (is_filter_out) from the generated checks. - """ - def __init__(self, regex, is_filter_out): - super(Filter, self).__init__(regex) - self.is_filter_out = is_filter_out + """ + + def __init__(self, regex, is_filter_out): + super(Filter, self).__init__(regex) + self.is_filter_out = is_filter_out + + def __deepcopy__(self, memo): + result = copy.deepcopy(super(Filter, self), memo) + result.is_filter_out = copy.deepcopy(self.is_filter_out, memo) + return result - def __deepcopy__(self, memo): - result = copy.deepcopy(super(Filter, self), memo) - result.is_filter_out = copy.deepcopy(self.is_filter_out, memo) - return result def parse_commandline_args(parser): - class RegexAction(argparse.Action): - """Add a regular expression option value to a list of regular expressions. - This compiles the expression, wraps it in a Regex and adds it to the option - value list.""" - def __init__(self, option_strings, dest, nargs=None, **kwargs): - if nargs is not None: - raise ValueError('nargs not allowed') - super(RegexAction, self).__init__(option_strings, dest, **kwargs) - - def do_call(self, namespace, values, flags): - value_list = getattr(namespace, self.dest) - if value_list is None: - value_list = [] - - try: - value_list.append(Regex(re.compile(values, flags))) - except re.error as error: - raise ValueError('{}: Invalid regular expression \'{}\' ({})'.format( - option_string, error.pattern, error.msg)) - - setattr(namespace, self.dest, value_list) - - def __call__(self, parser, namespace, values, option_string=None): - self.do_call(namespace, values, 0) - - class FilterAction(RegexAction): - """Add a filter to a list of filter option values.""" - def __init__(self, option_strings, dest, nargs=None, **kwargs): - super(FilterAction, self).__init__(option_strings, dest, nargs, **kwargs) - - def __call__(self, parser, namespace, values, option_string=None): - super(FilterAction, self).__call__(parser, namespace, values, option_string) - - value_list = getattr(namespace, self.dest) - - is_filter_out = ( option_string == '--filter-out' ) - - value_list[-1] = Filter(value_list[-1].regex, is_filter_out) - - setattr(namespace, self.dest, value_list) - - filter_group = parser.add_argument_group( - 'filtering', - """Filters are applied to each output line according to the order given. The - first matching filter terminates filter processing for that current line.""") - - filter_group.add_argument('--filter', action=FilterAction, dest='filters', - metavar='REGEX', - help='Only include lines matching REGEX (may be specified multiple times)') - filter_group.add_argument('--filter-out', action=FilterAction, dest='filters', - metavar='REGEX', - help='Exclude lines matching REGEX') - - parser.add_argument('--include-generated-funcs', action='store_true', - help='Output checks for functions not in source') - parser.add_argument('-v', '--verbose', action='store_true', - help='Show verbose output') - parser.add_argument('-u', '--update-only', action='store_true', - help='Only update test if it was already autogened') - parser.add_argument('--force-update', action='store_true', - help='Update test even if it was autogened by a different script') - parser.add_argument('--enable', action='store_true', dest='enabled', default=True, - help='Activate CHECK line generation from this point forward') - parser.add_argument('--disable', action='store_false', dest='enabled', - help='Deactivate CHECK line generation from this point forward') - parser.add_argument('--replace-value-regex', nargs='+', default=[], - help='List of regular expressions to replace matching value names') - parser.add_argument('--prefix-filecheck-ir-name', default='', - help='Add a prefix to FileCheck IR value names to avoid conflicts with scripted names') - parser.add_argument('--global-value-regex', nargs='+', default=[], - help='List of regular expressions that a global value declaration must match to generate a check (has no effect if checking globals is not enabled)') - parser.add_argument('--global-hex-value-regex', nargs='+', default=[], - help='List of regular expressions such that, for matching global value declarations, literal integer values should be encoded in hex in the associated FileCheck directives') - # FIXME: in 3.9, we can use argparse.BooleanOptionalAction. At that point, - # we need to rename the flag to just -generate-body-for-unused-prefixes. - parser.add_argument('--no-generate-body-for-unused-prefixes', - action='store_false', - dest='gen_unused_prefix_body', - default=True, - help='Generate a function body that always matches for unused prefixes. This is useful when unused prefixes are desired, and it avoids needing to annotate each FileCheck as allowing them.') - # This is the default when regenerating existing tests. The default when - # generating new tests is determined by DEFAULT_VERSION. - parser.add_argument('--version', type=int, default=1, - help='The version of output format') - args = parser.parse_args() - global _verbose, _global_value_regex, _global_hex_value_regex - _verbose = args.verbose - _global_value_regex = args.global_value_regex - _global_hex_value_regex = args.global_hex_value_regex - return args + class RegexAction(argparse.Action): + """Add a regular expression option value to a list of regular expressions. + This compiles the expression, wraps it in a Regex and adds it to the option + value list.""" + + def __init__(self, option_strings, dest, nargs=None, **kwargs): + if nargs is not None: + raise ValueError("nargs not allowed") + super(RegexAction, self).__init__(option_strings, dest, **kwargs) + + def do_call(self, namespace, values, flags): + value_list = getattr(namespace, self.dest) + if value_list is None: + value_list = [] + + try: + value_list.append(Regex(re.compile(values, flags))) + except re.error as error: + raise ValueError( + "{}: Invalid regular expression '{}' ({})".format( + option_string, error.pattern, error.msg + ) + ) + + setattr(namespace, self.dest, value_list) + + def __call__(self, parser, namespace, values, option_string=None): + self.do_call(namespace, values, 0) + + class FilterAction(RegexAction): + """Add a filter to a list of filter option values.""" + + def __init__(self, option_strings, dest, nargs=None, **kwargs): + super(FilterAction, self).__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + super(FilterAction, self).__call__(parser, namespace, values, option_string) + + value_list = getattr(namespace, self.dest) + + is_filter_out = option_string == "--filter-out" + + value_list[-1] = Filter(value_list[-1].regex, is_filter_out) + + setattr(namespace, self.dest, value_list) + + filter_group = parser.add_argument_group( + "filtering", + """Filters are applied to each output line according to the order given. The + first matching filter terminates filter processing for that current line.""", + ) + + filter_group.add_argument( + "--filter", + action=FilterAction, + dest="filters", + metavar="REGEX", + help="Only include lines matching REGEX (may be specified multiple times)", + ) + filter_group.add_argument( + "--filter-out", + action=FilterAction, + dest="filters", + metavar="REGEX", + help="Exclude lines matching REGEX", + ) + + parser.add_argument( + "--include-generated-funcs", + action="store_true", + help="Output checks for functions not in source", + ) + parser.add_argument( + "-v", "--verbose", action="store_true", help="Show verbose output" + ) + parser.add_argument( + "-u", + "--update-only", + action="store_true", + help="Only update test if it was already autogened", + ) + parser.add_argument( + "--force-update", + action="store_true", + help="Update test even if it was autogened by a different script", + ) + parser.add_argument( + "--enable", + action="store_true", + dest="enabled", + default=True, + help="Activate CHECK line generation from this point forward", + ) + parser.add_argument( + "--disable", + action="store_false", + dest="enabled", + help="Deactivate CHECK line generation from this point forward", + ) + parser.add_argument( + "--replace-value-regex", + nargs="+", + default=[], + help="List of regular expressions to replace matching value names", + ) + parser.add_argument( + "--prefix-filecheck-ir-name", + default="", + help="Add a prefix to FileCheck IR value names to avoid conflicts with scripted names", + ) + parser.add_argument( + "--global-value-regex", + nargs="+", + default=[], + help="List of regular expressions that a global value declaration must match to generate a check (has no effect if checking globals is not enabled)", + ) + parser.add_argument( + "--global-hex-value-regex", + nargs="+", + default=[], + help="List of regular expressions such that, for matching global value declarations, literal integer values should be encoded in hex in the associated FileCheck directives", + ) + # FIXME: in 3.9, we can use argparse.BooleanOptionalAction. At that point, + # we need to rename the flag to just -generate-body-for-unused-prefixes. + parser.add_argument( + "--no-generate-body-for-unused-prefixes", + action="store_false", + dest="gen_unused_prefix_body", + default=True, + help="Generate a function body that always matches for unused prefixes. This is useful when unused prefixes are desired, and it avoids needing to annotate each FileCheck as allowing them.", + ) + # This is the default when regenerating existing tests. The default when + # generating new tests is determined by DEFAULT_VERSION. + parser.add_argument( + "--version", type=int, default=1, help="The version of output format" + ) + args = parser.parse_args() + global _verbose, _global_value_regex, _global_hex_value_regex + _verbose = args.verbose + _global_value_regex = args.global_value_regex + _global_hex_value_regex = args.global_hex_value_regex + return args + def parse_args(parser, argv): - args = parser.parse_args(argv) - if args.version >= 2: - args.function_signature = True - return args + args = parser.parse_args(argv) + if args.version >= 2: + args.function_signature = True + return args + class InputLineInfo(object): - def __init__(self, line, line_number, args, argv): - self.line = line - self.line_number = line_number - self.args = args - self.argv = argv + def __init__(self, line, line_number, args, argv): + self.line = line + self.line_number = line_number + self.args = args + self.argv = argv class TestInfo(object): - def __init__(self, test, parser, script_name, input_lines, args, argv, - comment_prefix, argparse_callback): - self.parser = parser - self.argparse_callback = argparse_callback - self.path = test - self.args = args - if args.prefix_filecheck_ir_name: - global _prefix_filecheck_ir_name - _prefix_filecheck_ir_name = args.prefix_filecheck_ir_name - self.argv = argv - self.input_lines = input_lines - self.run_lines = find_run_lines(test, self.input_lines) - self.comment_prefix = comment_prefix - if self.comment_prefix is None: - if self.path.endswith('.mir'): - self.comment_prefix = '#' - else: - self.comment_prefix = ';' - self.autogenerated_note_prefix = self.comment_prefix + ' ' + UTC_ADVERT - self.test_autogenerated_note = self.autogenerated_note_prefix + script_name - self.test_autogenerated_note += get_autogennote_suffix(parser, self.args) - self.test_unused_note = self.comment_prefix + self.comment_prefix + ' ' + UNUSED_NOTE - - def ro_iterlines(self): - for line_num, input_line in enumerate(self.input_lines): - args, argv = check_for_command(input_line, self.parser, - self.args, self.argv, self.argparse_callback) - yield InputLineInfo(input_line, line_num, args, argv) - - def iterlines(self, output_lines): - output_lines.append(self.test_autogenerated_note) - for line_info in self.ro_iterlines(): - input_line = line_info.line - # Discard any previous script advertising. - if input_line.startswith(self.autogenerated_note_prefix): - continue - self.args = line_info.args - self.argv = line_info.argv - if not self.args.enabled: - output_lines.append(input_line) - continue - yield line_info - - def get_checks_for_unused_prefixes(self, run_list, used_prefixes: List[str]) -> List[str]: - run_list = [element for element in run_list if element[0] is not None] - unused_prefixes = set([ - prefix for sublist in run_list for prefix in sublist[0] - ]).difference(set(used_prefixes)) - - ret = [] - if not unused_prefixes: - return ret - ret.append(self.test_unused_note) - for unused in sorted(unused_prefixes): - ret.append('{comment} {prefix}: {match_everything}'.format( - comment=self.comment_prefix, - prefix=unused, - match_everything=r"""{{.*}}""" - )) - return ret - -def itertests(test_patterns, parser, script_name, comment_prefix=None, argparse_callback=None): - for pattern in test_patterns: - # On Windows we must expand the patterns ourselves. - tests_list = glob.glob(pattern) - if not tests_list: - warn("Test file pattern '%s' was not found. Ignoring it." % (pattern,)) - continue - for test in tests_list: - with open(test) as f: - input_lines = [l.rstrip() for l in f] - first_line = input_lines[0] if input_lines else "" - is_regenerate = UTC_ADVERT in first_line - - # If we're generating a new test, set the default version to the latest. - argv = sys.argv[:] - if not is_regenerate: - argv.insert(1, '--version=' + str(DEFAULT_VERSION)) - - args = parse_args(parser, argv[1:]) - if argparse_callback is not None: - argparse_callback(args) - if is_regenerate: - if script_name not in first_line and not args.force_update: - warn("Skipping test which wasn't autogenerated by " + script_name, test) - continue - args, argv = check_for_command(first_line, parser, args, argv, argparse_callback) - elif args.update_only: - assert UTC_ADVERT not in first_line - warn("Skipping test which isn't autogenerated: " + test) - continue - final_input_lines = [] - for l in input_lines: - if UNUSED_NOTE in l: - break - final_input_lines.append(l) - yield TestInfo(test, parser, script_name, final_input_lines, args, argv, - comment_prefix, argparse_callback) - - -def should_add_line_to_output(input_line, prefix_set, skip_global_checks = False, comment_marker = ';'): - # Skip any blank comment lines in the IR. - if not skip_global_checks and input_line.strip() == comment_marker: - return False - # Skip a special double comment line we use as a separator. - if input_line.strip() == comment_marker + SEPARATOR: - return False - # Skip any blank lines in the IR. - #if input_line.strip() == '': - # return False - # And skip any CHECK lines. We're building our own. - m = CHECK_RE.match(input_line) - if m and m.group(1) in prefix_set: - if skip_global_checks: - global_ir_value_re = re.compile(r'\[\[', flags=(re.M)) - return not global_ir_value_re.search(input_line) - return False - - return True + def __init__( + self, + test, + parser, + script_name, + input_lines, + args, + argv, + comment_prefix, + argparse_callback, + ): + self.parser = parser + self.argparse_callback = argparse_callback + self.path = test + self.args = args + if args.prefix_filecheck_ir_name: + global _prefix_filecheck_ir_name + _prefix_filecheck_ir_name = args.prefix_filecheck_ir_name + self.argv = argv + self.input_lines = input_lines + self.run_lines = find_run_lines(test, self.input_lines) + self.comment_prefix = comment_prefix + if self.comment_prefix is None: + if self.path.endswith(".mir"): + self.comment_prefix = "#" + else: + self.comment_prefix = ";" + self.autogenerated_note_prefix = self.comment_prefix + " " + UTC_ADVERT + self.test_autogenerated_note = self.autogenerated_note_prefix + script_name + self.test_autogenerated_note += get_autogennote_suffix(parser, self.args) + self.test_unused_note = ( + self.comment_prefix + self.comment_prefix + " " + UNUSED_NOTE + ) + + def ro_iterlines(self): + for line_num, input_line in enumerate(self.input_lines): + args, argv = check_for_command( + input_line, self.parser, self.args, self.argv, self.argparse_callback + ) + yield InputLineInfo(input_line, line_num, args, argv) + + def iterlines(self, output_lines): + output_lines.append(self.test_autogenerated_note) + for line_info in self.ro_iterlines(): + input_line = line_info.line + # Discard any previous script advertising. + if input_line.startswith(self.autogenerated_note_prefix): + continue + self.args = line_info.args + self.argv = line_info.argv + if not self.args.enabled: + output_lines.append(input_line) + continue + yield line_info + + def get_checks_for_unused_prefixes( + self, run_list, used_prefixes: List[str] + ) -> List[str]: + run_list = [element for element in run_list if element[0] is not None] + unused_prefixes = set( + [prefix for sublist in run_list for prefix in sublist[0]] + ).difference(set(used_prefixes)) + + ret = [] + if not unused_prefixes: + return ret + ret.append(self.test_unused_note) + for unused in sorted(unused_prefixes): + ret.append( + "{comment} {prefix}: {match_everything}".format( + comment=self.comment_prefix, + prefix=unused, + match_everything=r"""{{.*}}""", + ) + ) + return ret + + +def itertests( + test_patterns, parser, script_name, comment_prefix=None, argparse_callback=None +): + for pattern in test_patterns: + # On Windows we must expand the patterns ourselves. + tests_list = glob.glob(pattern) + if not tests_list: + warn("Test file pattern '%s' was not found. Ignoring it." % (pattern,)) + continue + for test in tests_list: + with open(test) as f: + input_lines = [l.rstrip() for l in f] + first_line = input_lines[0] if input_lines else "" + is_regenerate = UTC_ADVERT in first_line + + # If we're generating a new test, set the default version to the latest. + argv = sys.argv[:] + if not is_regenerate: + argv.insert(1, "--version=" + str(DEFAULT_VERSION)) + + args = parse_args(parser, argv[1:]) + if argparse_callback is not None: + argparse_callback(args) + if is_regenerate: + if script_name not in first_line and not args.force_update: + warn( + "Skipping test which wasn't autogenerated by " + script_name, + test, + ) + continue + args, argv = check_for_command( + first_line, parser, args, argv, argparse_callback + ) + elif args.update_only: + assert UTC_ADVERT not in first_line + warn("Skipping test which isn't autogenerated: " + test) + continue + final_input_lines = [] + for l in input_lines: + if UNUSED_NOTE in l: + break + final_input_lines.append(l) + yield TestInfo( + test, + parser, + script_name, + final_input_lines, + args, + argv, + comment_prefix, + argparse_callback, + ) + + +def should_add_line_to_output( + input_line, prefix_set, skip_global_checks=False, comment_marker=";" +): + # Skip any blank comment lines in the IR. + if not skip_global_checks and input_line.strip() == comment_marker: + return False + # Skip a special double comment line we use as a separator. + if input_line.strip() == comment_marker + SEPARATOR: + return False + # Skip any blank lines in the IR. + # if input_line.strip() == '': + # return False + # And skip any CHECK lines. We're building our own. + m = CHECK_RE.match(input_line) + if m and m.group(1) in prefix_set: + if skip_global_checks: + global_ir_value_re = re.compile(r"\[\[", flags=(re.M)) + return not global_ir_value_re.search(input_line) + return False + + return True + # Perform lit-like substitutions def getSubstitutions(sourcepath): - sourcedir = os.path.dirname(sourcepath) - return [('%s', sourcepath), - ('%S', sourcedir), - ('%p', sourcedir), - ('%{pathsep}', os.pathsep)] + sourcedir = os.path.dirname(sourcepath) + return [ + ("%s", sourcepath), + ("%S", sourcedir), + ("%p", sourcedir), + ("%{pathsep}", os.pathsep), + ] + def applySubstitutions(s, substitutions): - for a,b in substitutions: - s = s.replace(a, b) - return s + for a, b in substitutions: + s = s.replace(a, b) + return s + # Invoke the tool that is being tested. def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False): - with open(ir) as ir_file: - substitutions = getSubstitutions(ir) - - # TODO Remove the str form which is used by update_test_checks.py and - # update_llc_test_checks.py - # The safer list form is used by update_cc_test_checks.py - if preprocess_cmd: - # Allow pre-processing the IR file (e.g. using sed): - assert isinstance(preprocess_cmd, str) # TODO: use a list instead of using shell - preprocess_cmd = applySubstitutions(preprocess_cmd, substitutions).strip() - if verbose: - print('Pre-processing input file: ', ir, " with command '", - preprocess_cmd, "'", sep="", file=sys.stderr) - # Python 2.7 doesn't have subprocess.DEVNULL: - with open(os.devnull, 'w') as devnull: - pp = subprocess.Popen(preprocess_cmd, shell=True, stdin=devnull, - stdout=subprocess.PIPE) - ir_file = pp.stdout - - if isinstance(cmd_args, list): - args = [applySubstitutions(a, substitutions) for a in cmd_args] - stdout = subprocess.check_output([exe] + args, stdin=ir_file) - else: - stdout = subprocess.check_output(exe + ' ' + applySubstitutions(cmd_args, substitutions), - shell=True, stdin=ir_file) - if sys.version_info[0] > 2: - # FYI, if you crashed here with a decode error, your run line probably - # results in bitcode or other binary format being written to the pipe. - # For an opt test, you probably want to add -S or -disable-output. - stdout = stdout.decode() - # Fix line endings to unix CR style. - return stdout.replace('\r\n', '\n') + with open(ir) as ir_file: + substitutions = getSubstitutions(ir) + + # TODO Remove the str form which is used by update_test_checks.py and + # update_llc_test_checks.py + # The safer list form is used by update_cc_test_checks.py + if preprocess_cmd: + # Allow pre-processing the IR file (e.g. using sed): + assert isinstance( + preprocess_cmd, str + ) # TODO: use a list instead of using shell + preprocess_cmd = applySubstitutions(preprocess_cmd, substitutions).strip() + if verbose: + print( + "Pre-processing input file: ", + ir, + " with command '", + preprocess_cmd, + "'", + sep="", + file=sys.stderr, + ) + # Python 2.7 doesn't have subprocess.DEVNULL: + with open(os.devnull, "w") as devnull: + pp = subprocess.Popen( + preprocess_cmd, shell=True, stdin=devnull, stdout=subprocess.PIPE + ) + ir_file = pp.stdout + + if isinstance(cmd_args, list): + args = [applySubstitutions(a, substitutions) for a in cmd_args] + stdout = subprocess.check_output([exe] + args, stdin=ir_file) + else: + stdout = subprocess.check_output( + exe + " " + applySubstitutions(cmd_args, substitutions), + shell=True, + stdin=ir_file, + ) + if sys.version_info[0] > 2: + # FYI, if you crashed here with a decode error, your run line probably + # results in bitcode or other binary format being written to the pipe. + # For an opt test, you probably want to add -S or -disable-output. + stdout = stdout.decode() + # Fix line endings to unix CR style. + return stdout.replace("\r\n", "\n") -##### LLVM IR parser -RUN_LINE_RE = re.compile(r'^\s*(?://|[;#])\s*RUN:\s*(.*)$') -CHECK_PREFIX_RE = re.compile(r'--?check-prefix(?:es)?[= ](\S+)') -PREFIX_RE = re.compile('^[a-zA-Z0-9_-]+$') -CHECK_RE = re.compile(r'^\s*(?://|[;#])\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL|-SAME|-EMPTY)?:') -UTC_ARGS_KEY = 'UTC_ARGS:' -UTC_ARGS_CMD = re.compile(r'.*' + UTC_ARGS_KEY + '\s*(?P<cmd>.*)\s*$') -UTC_ADVERT = 'NOTE: Assertions have been autogenerated by ' -UNUSED_NOTE = 'NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:' +##### LLVM IR parser +RUN_LINE_RE = re.compile(r"^\s*(?://|[;#])\s*RUN:\s*(.*)$") +CHECK_PREFIX_RE = re.compile(r"--?check-prefix(?:es)?[= ](\S+)") +PREFIX_RE = re.compile("^[a-zA-Z0-9_-]+$") +CHECK_RE = re.compile( + r"^\s*(?://|[;#])\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL|-SAME|-EMPTY)?:" +) + +UTC_ARGS_KEY = "UTC_ARGS:" +UTC_ARGS_CMD = re.compile(r".*" + UTC_ARGS_KEY + "\s*(?P<cmd>.*)\s*$") +UTC_ADVERT = "NOTE: Assertions have been autogenerated by " +UNUSED_NOTE = "NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:" OPT_FUNCTION_RE = re.compile( - r'^(\s*;\s*Function\sAttrs:\s(?P<attrs>[\w\s():,]+?))?\s*define\s+(?P<funcdef_attrs_and_ret>[^@]*)@(?P<func>[\w.$-]+?)\s*' - r'(?P<args_and_sig>\((\)|(.*?[\w.-]+?)\))[^{]*\{)\n(?P<body>.*?)^\}$', - flags=(re.M | re.S)) + r"^(\s*;\s*Function\sAttrs:\s(?P<attrs>[\w\s():,]+?))?\s*define\s+(?P<funcdef_attrs_and_ret>[^@]*)@(?P<func>[\w.$-]+?)\s*" + r"(?P<args_and_sig>\((\)|(.*?[\w.-]+?)\))[^{]*\{)\n(?P<body>.*?)^\}$", + flags=(re.M | re.S), +) ANALYZE_FUNCTION_RE = re.compile( - r'^\s*\'(?P<analysis>[\w\s-]+?)\'\s+for\s+function\s+\'(?P<func>[\w.$-]+?)\':' - r'\s*\n(?P<body>.*)$', - flags=(re.X | re.S)) + r"^\s*\'(?P<analysis>[\w\s-]+?)\'\s+for\s+function\s+\'(?P<func>[\w.$-]+?)\':" + r"\s*\n(?P<body>.*)$", + flags=(re.X | re.S), +) LV_DEBUG_RE = re.compile( - r'^\s*\'(?P<func>[\w.$-]+?)\'[^\n]*' - r'\s*\n(?P<body>.*)$', - flags=(re.X | re.S)) + r"^\s*\'(?P<func>[\w.$-]+?)\'[^\n]*" r"\s*\n(?P<body>.*)$", flags=(re.X | re.S) +) IR_FUNCTION_RE = re.compile(r'^\s*define\s+(?:internal\s+)?[^@]*@"?([\w.$-]+)"?\s*\(') TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$') -TRIPLE_ARG_RE = re.compile(r'-mtriple[= ]([^ ]+)') -MARCH_ARG_RE = re.compile(r'-march[= ]([^ ]+)') -DEBUG_ONLY_ARG_RE = re.compile(r'-debug-only[= ]([^ ]+)') +TRIPLE_ARG_RE = re.compile(r"-mtriple[= ]([^ ]+)") +MARCH_ARG_RE = re.compile(r"-march[= ]([^ ]+)") +DEBUG_ONLY_ARG_RE = re.compile(r"-debug-only[= ]([^ ]+)") -SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)') -SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) -SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) +SCRUB_LEADING_WHITESPACE_RE = re.compile(r"^(\s+)") +SCRUB_WHITESPACE_RE = re.compile(r"(?!^(| \w))[ \t]+", flags=re.M) +SCRUB_TRAILING_WHITESPACE_RE = re.compile(r"[ \t]+$", flags=re.M) SCRUB_TRAILING_WHITESPACE_TEST_RE = SCRUB_TRAILING_WHITESPACE_RE -SCRUB_TRAILING_WHITESPACE_AND_ATTRIBUTES_RE = re.compile(r'([ \t]|(#[0-9]+))+$', flags=re.M) -SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') +SCRUB_TRAILING_WHITESPACE_AND_ATTRIBUTES_RE = re.compile( + r"([ \t]|(#[0-9]+))+$", flags=re.M +) +SCRUB_KILL_COMMENT_RE = re.compile(r"^ *#+ +kill:.*\n") SCRUB_LOOP_COMMENT_RE = re.compile( - r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M) -SCRUB_TAILING_COMMENT_TOKEN_RE = re.compile(r'(?<=\S)+[ \t]*#$', flags=re.M) + r"# =>This Inner Loop Header:.*|# in Loop:.*", flags=re.M +) +SCRUB_TAILING_COMMENT_TOKEN_RE = re.compile(r"(?<=\S)+[ \t]*#$", flags=re.M) + +SEPARATOR = "." -SEPARATOR = '.' def error(msg, test_file=None): - if test_file: - msg = '{}: {}'.format(msg, test_file) - print('ERROR: {}'.format(msg), file=sys.stderr) + if test_file: + msg = "{}: {}".format(msg, test_file) + print("ERROR: {}".format(msg), file=sys.stderr) + def warn(msg, test_file=None): - if test_file: - msg = '{}: {}'.format(msg, test_file) - print('WARNING: {}'.format(msg), file=sys.stderr) + if test_file: + msg = "{}: {}".format(msg, test_file) + print("WARNING: {}".format(msg), file=sys.stderr) + def debug(*args, **kwargs): - # Python2 does not allow def debug(*args, file=sys.stderr, **kwargs): - if 'file' not in kwargs: - kwargs['file'] = sys.stderr - if _verbose: - print(*args, **kwargs) + # Python2 does not allow def debug(*args, file=sys.stderr, **kwargs): + if "file" not in kwargs: + kwargs["file"] = sys.stderr + if _verbose: + print(*args, **kwargs) + def find_run_lines(test, lines): - debug('Scanning for RUN lines in test file:', test) - raw_lines = [m.group(1) - for m in [RUN_LINE_RE.match(l) for l in lines] if m] - run_lines = [raw_lines[0]] if len(raw_lines) > 0 else [] - for l in raw_lines[1:]: - if run_lines[-1].endswith('\\'): - run_lines[-1] = run_lines[-1].rstrip('\\') + ' ' + l - else: - run_lines.append(l) - debug('Found {} RUN lines in {}:'.format(len(run_lines), test)) - for l in run_lines: - debug(' RUN: {}'.format(l)) - return run_lines + debug("Scanning for RUN lines in test file:", test) + raw_lines = [m.group(1) for m in [RUN_LINE_RE.match(l) for l in lines] if m] + run_lines = [raw_lines[0]] if len(raw_lines) > 0 else [] + for l in raw_lines[1:]: + if run_lines[-1].endswith("\\"): + run_lines[-1] = run_lines[-1].rstrip("\\") + " " + l + else: + run_lines.append(l) + debug("Found {} RUN lines in {}:".format(len(run_lines), test)) + for l in run_lines: + debug(" RUN: {}".format(l)) + return run_lines + def get_triple_from_march(march): - triples = { - 'amdgcn': 'amdgcn', - 'r600': 'r600', - 'mips': 'mips', - 'sparc': 'sparc', - 'hexagon': 'hexagon', - 've': 've', - } - for prefix, triple in triples.items(): - if march.startswith(prefix): - return triple - print("Cannot find a triple. Assume 'x86'", file=sys.stderr) - return 'x86' + triples = { + "amdgcn": "amdgcn", + "r600": "r600", + "mips": "mips", + "sparc": "sparc", + "hexagon": "hexagon", + "ve": "ve", + } + for prefix, triple in triples.items(): + if march.startswith(prefix): + return triple + print("Cannot find a triple. Assume 'x86'", file=sys.stderr) + return "x86" + def apply_filters(line, filters): - has_filter = False - for f in filters: - if not f.is_filter_out: - has_filter = True - if f.search(line): - return False if f.is_filter_out else True - # If we only used filter-out, keep the line, otherwise discard it since no - # filter matched. - return False if has_filter else True + has_filter = False + for f in filters: + if not f.is_filter_out: + has_filter = True + if f.search(line): + return False if f.is_filter_out else True + # If we only used filter-out, keep the line, otherwise discard it since no + # filter matched. + return False if has_filter else True + def do_filter(body, filters): - return body if not filters else '\n'.join(filter( - lambda line: apply_filters(line, filters), body.splitlines())) + return ( + body + if not filters + else "\n".join( + filter(lambda line: apply_filters(line, filters), body.splitlines()) + ) + ) + def scrub_body(body): - # Scrub runs of whitespace out of the assembly, but leave the leading - # whitespace in place. - body = SCRUB_WHITESPACE_RE.sub(r' ', body) - # Expand the tabs used for indentation. - body = str.expandtabs(body, 2) - # Strip trailing whitespace. - body = SCRUB_TRAILING_WHITESPACE_TEST_RE.sub(r'', body) - return body + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + body = SCRUB_WHITESPACE_RE.sub(r" ", body) + # Expand the tabs used for indentation. + body = str.expandtabs(body, 2) + # Strip trailing whitespace. + body = SCRUB_TRAILING_WHITESPACE_TEST_RE.sub(r"", body) + return body + def do_scrub(body, scrubber, scrubber_args, extra): - if scrubber_args: - local_args = copy.deepcopy(scrubber_args) - local_args[0].extra_scrub = extra - return scrubber(body, *local_args) - return scrubber(body, *scrubber_args) + if scrubber_args: + local_args = copy.deepcopy(scrubber_args) + local_args[0].extra_scrub = extra + return scrubber(body, *local_args) + return scrubber(body, *scrubber_args) + # Build up a dictionary of all the function bodies. class function_body(object): - def __init__(self, string, extra, funcdef_attrs_and_ret, args_and_sig, attrs, func_name_separator): - self.scrub = string - self.extrascrub = extra - self.funcdef_attrs_and_ret = funcdef_attrs_and_ret - self.args_and_sig = args_and_sig - self.attrs = attrs - self.func_name_separator = func_name_separator - def is_same_except_arg_names(self, extrascrub, funcdef_attrs_and_ret, args_and_sig, attrs, is_backend): - arg_names = set() - def drop_arg_names(match): - arg_names.add(match.group(variable_group_in_ir_value_match)) - if match.group(attribute_group_in_ir_value_match): - attr = match.group(attribute_group_in_ir_value_match) - else: - attr = '' - return match.group(1) + attr + match.group(match.lastindex) - def repl_arg_names(match): - if match.group(variable_group_in_ir_value_match) is not None and match.group(variable_group_in_ir_value_match) in arg_names: - return match.group(1) + match.group(match.lastindex) - return match.group(1) + match.group(2) + match.group(match.lastindex) - if self.funcdef_attrs_and_ret != funcdef_attrs_and_ret: - return False - if self.attrs != attrs: - return False - ans0 = IR_VALUE_RE.sub(drop_arg_names, self.args_and_sig) - ans1 = IR_VALUE_RE.sub(drop_arg_names, args_and_sig) - if ans0 != ans1: - return False - if is_backend: - # Check without replacements, the replacements are not applied to the - # body for backend checks. - return self.extrascrub == extrascrub - - es0 = IR_VALUE_RE.sub(repl_arg_names, self.extrascrub) - es1 = IR_VALUE_RE.sub(repl_arg_names, extrascrub) - es0 = SCRUB_IR_COMMENT_RE.sub(r'', es0) - es1 = SCRUB_IR_COMMENT_RE.sub(r'', es1) - return es0 == es1 - - def __str__(self): - return self.scrub + def __init__( + self, + string, + extra, + funcdef_attrs_and_ret, + args_and_sig, + attrs, + func_name_separator, + ): + self.scrub = string + self.extrascrub = extra + self.funcdef_attrs_and_ret = funcdef_attrs_and_ret + self.args_and_sig = args_and_sig + self.attrs = attrs + self.func_name_separator = func_name_separator + + def is_same_except_arg_names( + self, extrascrub, funcdef_attrs_and_ret, args_and_sig, attrs, is_backend + ): + arg_names = set() + + def drop_arg_names(match): + arg_names.add(match.group(variable_group_in_ir_value_match)) + if match.group(attribute_group_in_ir_value_match): + attr = match.group(attribute_group_in_ir_value_match) + else: + attr = "" + return match.group(1) + attr + match.group(match.lastindex) + + def repl_arg_names(match): + if ( + match.group(variable_group_in_ir_value_match) is not None + and match.group(variable_group_in_ir_value_match) in arg_names + ): + return match.group(1) + match.group(match.lastindex) + return match.group(1) + match.group(2) + match.group(match.lastindex) + + if self.funcdef_attrs_and_ret != funcdef_attrs_and_ret: + return False + if self.attrs != attrs: + return False + ans0 = IR_VALUE_RE.sub(drop_arg_names, self.args_and_sig) + ans1 = IR_VALUE_RE.sub(drop_arg_names, args_and_sig) + if ans0 != ans1: + return False + if is_backend: + # Check without replacements, the replacements are not applied to the + # body for backend checks. + return self.extrascrub == extrascrub + + es0 = IR_VALUE_RE.sub(repl_arg_names, self.extrascrub) + es1 = IR_VALUE_RE.sub(repl_arg_names, extrascrub) + es0 = SCRUB_IR_COMMENT_RE.sub(r"", es0) + es1 = SCRUB_IR_COMMENT_RE.sub(r"", es1) + return es0 == es1 + + def __str__(self): + return self.scrub + class FunctionTestBuilder: - def __init__(self, run_list, flags, scrubber_args, path): - self._verbose = flags.verbose - self._record_args = flags.function_signature - self._check_attributes = flags.check_attributes - # Strip double-quotes if input was read by UTC_ARGS - self._filters = list(map(lambda f: Filter(re.compile(f.pattern().strip('"'), - f.flags()), - f.is_filter_out), - flags.filters)) if flags.filters else [] - self._scrubber_args = scrubber_args - self._path = path - # Strip double-quotes if input was read by UTC_ARGS - self._replace_value_regex = list(map(lambda x: x.strip('"'), flags.replace_value_regex)) - self._func_dict = {} - self._func_order = {} - self._global_var_dict = {} - self._processed_prefixes = set() - for tuple in run_list: - for prefix in tuple[0]: - self._func_dict.update({prefix: dict()}) - self._func_order.update({prefix: []}) - self._global_var_dict.update({prefix: dict()}) - - def finish_and_get_func_dict(self): - for prefix in self.get_failed_prefixes(): - warn('Prefix %s had conflicting output from different RUN lines for all functions in test %s' % (prefix,self._path,)) - return self._func_dict - - def func_order(self): - return self._func_order - - def global_var_dict(self): - return self._global_var_dict - - def is_filtered(self): - return bool(self._filters) - - def process_run_line(self, function_re, scrubber, raw_tool_output, prefixes, is_backend): - build_global_values_dictionary(self._global_var_dict, raw_tool_output, prefixes) - for m in function_re.finditer(raw_tool_output): - if not m: - continue - func = m.group('func') - body = m.group('body') - # func_name_separator is the string that is placed right after function name at the - # beginning of assembly function definition. In most assemblies, that is just a - # colon: `foo:`. But, for example, in nvptx it is a brace: `foo(`. If is_backend is - # False, just assume that separator is an empty string. - if is_backend: - # Use ':' as default separator. - func_name_separator = m.group('func_name_separator') if 'func_name_separator' in m.groupdict() else ':' - else: - func_name_separator = '' - attrs = m.group('attrs') if self._check_attributes else '' - funcdef_attrs_and_ret = m.group('funcdef_attrs_and_ret') if self._record_args else '' - # Determine if we print arguments, the opening brace, or nothing after the - # function name - if self._record_args and 'args_and_sig' in m.groupdict(): - args_and_sig = scrub_body(m.group('args_and_sig').strip()) - elif 'args_and_sig' in m.groupdict(): - args_and_sig = '(' - else: - args_and_sig = '' - filtered_body = do_filter(body, self._filters) - scrubbed_body = do_scrub(filtered_body, scrubber, self._scrubber_args, - extra=False) - scrubbed_extra = do_scrub(filtered_body, scrubber, self._scrubber_args, - extra=True) - if 'analysis' in m.groupdict(): - analysis = m.group('analysis') - if analysis.lower() != 'cost model analysis': - warn('Unsupported analysis mode: %r!' % (analysis,)) - if func.startswith('stress'): - # We only use the last line of the function body for stress tests. - scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:]) - if self._verbose: - print('Processing function: ' + func, file=sys.stderr) - for l in scrubbed_body.splitlines(): - print(' ' + l, file=sys.stderr) - for prefix in prefixes: - # Replace function names matching the regex. - for regex in self._replace_value_regex: - # Pattern that matches capture groups in the regex in leftmost order. - group_regex = re.compile(r'\(.*?\)') - # Replace function name with regex. - match = re.match(regex, func) - if match: - func_repl = regex - # Replace any capture groups with their matched strings. - for g in match.groups(): - func_repl = group_regex.sub(re.escape(g), func_repl, count=1) - func = re.sub(func_repl, '{{' + func_repl + '}}', func) - - # Replace all calls to regex matching functions. - matches = re.finditer(regex, scrubbed_body) - for match in matches: - func_repl = regex - # Replace any capture groups with their matched strings. - for g in match.groups(): - func_repl = group_regex.sub(re.escape(g), func_repl, count=1) - # Substitute function call names that match the regex with the same - # capture groups set. - scrubbed_body = re.sub(func_repl, '{{' + func_repl + '}}', - scrubbed_body) - - if func in self._func_dict[prefix]: - if (self._func_dict[prefix][func] is not None and - (str(self._func_dict[prefix][func]) != scrubbed_body or - self._func_dict[prefix][func].args_and_sig != args_and_sig or - self._func_dict[prefix][func].attrs != attrs or - self._func_dict[prefix][func].funcdef_attrs_and_ret != funcdef_attrs_and_ret)): - if self._func_dict[prefix][func].is_same_except_arg_names( - scrubbed_extra, - funcdef_attrs_and_ret, - args_and_sig, - attrs, - is_backend): - self._func_dict[prefix][func].scrub = scrubbed_extra - self._func_dict[prefix][func].args_and_sig = args_and_sig + def __init__(self, run_list, flags, scrubber_args, path): + self._verbose = flags.verbose + self._record_args = flags.function_signature + self._check_attributes = flags.check_attributes + # Strip double-quotes if input was read by UTC_ARGS + self._filters = ( + list( + map( + lambda f: Filter( + re.compile(f.pattern().strip('"'), f.flags()), f.is_filter_out + ), + flags.filters, + ) + ) + if flags.filters + else [] + ) + self._scrubber_args = scrubber_args + self._path = path + # Strip double-quotes if input was read by UTC_ARGS + self._replace_value_regex = list( + map(lambda x: x.strip('"'), flags.replace_value_regex) + ) + self._func_dict = {} + self._func_order = {} + self._global_var_dict = {} + self._processed_prefixes = set() + for tuple in run_list: + for prefix in tuple[0]: + self._func_dict.update({prefix: dict()}) + self._func_order.update({prefix: []}) + self._global_var_dict.update({prefix: dict()}) + + def finish_and_get_func_dict(self): + for prefix in self.get_failed_prefixes(): + warn( + "Prefix %s had conflicting output from different RUN lines for all functions in test %s" + % ( + prefix, + self._path, + ) + ) + return self._func_dict + + def func_order(self): + return self._func_order + + def global_var_dict(self): + return self._global_var_dict + + def is_filtered(self): + return bool(self._filters) + + def process_run_line( + self, function_re, scrubber, raw_tool_output, prefixes, is_backend + ): + build_global_values_dictionary(self._global_var_dict, raw_tool_output, prefixes) + for m in function_re.finditer(raw_tool_output): + if not m: + continue + func = m.group("func") + body = m.group("body") + # func_name_separator is the string that is placed right after function name at the + # beginning of assembly function definition. In most assemblies, that is just a + # colon: `foo:`. But, for example, in nvptx it is a brace: `foo(`. If is_backend is + # False, just assume that separator is an empty string. + if is_backend: + # Use ':' as default separator. + func_name_separator = ( + m.group("func_name_separator") + if "func_name_separator" in m.groupdict() + else ":" + ) else: - # This means a previous RUN line produced a body for this function - # that is different from the one produced by this current RUN line, - # so the body can't be common across RUN lines. We use None to - # indicate that. - self._func_dict[prefix][func] = None - else: - if prefix not in self._processed_prefixes: - self._func_dict[prefix][func] = function_body( - scrubbed_body, scrubbed_extra, funcdef_attrs_and_ret, - args_and_sig, attrs, func_name_separator) - self._func_order[prefix].append(func) - else: - # An earlier RUN line used this check prefixes but didn't produce - # a body for this function. This happens in Clang tests that use - # preprocesser directives to exclude individual functions from some - # RUN lines. - self._func_dict[prefix][func] = None - - def processed_prefixes(self, prefixes): - """ - Mark a set of prefixes as having had at least one applicable RUN line fully - processed. This is used to filter out function bodies that don't have - outputs for all RUN lines. - """ - self._processed_prefixes.update(prefixes) - - def get_failed_prefixes(self): - # This returns the list of those prefixes that failed to match any function, - # because there were conflicting bodies produced by different RUN lines, in - # all instances of the prefix. - for prefix in self._func_dict: - if (self._func_dict[prefix] and - (not [fct for fct in self._func_dict[prefix] - if self._func_dict[prefix][fct] is not None])): - yield prefix + func_name_separator = "" + attrs = m.group("attrs") if self._check_attributes else "" + funcdef_attrs_and_ret = ( + m.group("funcdef_attrs_and_ret") if self._record_args else "" + ) + # Determine if we print arguments, the opening brace, or nothing after the + # function name + if self._record_args and "args_and_sig" in m.groupdict(): + args_and_sig = scrub_body(m.group("args_and_sig").strip()) + elif "args_and_sig" in m.groupdict(): + args_and_sig = "(" + else: + args_and_sig = "" + filtered_body = do_filter(body, self._filters) + scrubbed_body = do_scrub( + filtered_body, scrubber, self._scrubber_args, extra=False + ) + scrubbed_extra = do_scrub( + filtered_body, scrubber, self._scrubber_args, extra=True + ) + if "analysis" in m.groupdict(): + analysis = m.group("analysis") + if analysis.lower() != "cost model analysis": + warn("Unsupported analysis mode: %r!" % (analysis,)) + if func.startswith("stress"): + # We only use the last line of the function body for stress tests. + scrubbed_body = "\n".join(scrubbed_body.splitlines()[-1:]) + if self._verbose: + print("Processing function: " + func, file=sys.stderr) + for l in scrubbed_body.splitlines(): + print(" " + l, file=sys.stderr) + for prefix in prefixes: + # Replace function names matching the regex. + for regex in self._replace_value_regex: + # Pattern that matches capture groups in the regex in leftmost order. + group_regex = re.compile(r"\(.*?\)") + # Replace function name with regex. + match = re.match(regex, func) + if match: + func_repl = regex + # Replace any capture groups with their matched strings. + for g in match.groups(): + func_repl = group_regex.sub( + re.escape(g), func_repl, count=1 + ) + func = re.sub(func_repl, "{{" + func_repl + "}}", func) + + # Replace all calls to regex matching functions. + matches = re.finditer(regex, scrubbed_body) + for match in matches: + func_repl = regex + # Replace any capture groups with their matched strings. + for g in match.groups(): + func_repl = group_regex.sub( + re.escape(g), func_repl, count=1 + ) + # Substitute function call names that match the regex with the same + # capture groups set. + scrubbed_body = re.sub( + func_repl, "{{" + func_repl + "}}", scrubbed_body + ) + + if func in self._func_dict[prefix]: + if self._func_dict[prefix][func] is not None and ( + str(self._func_dict[prefix][func]) != scrubbed_body + or self._func_dict[prefix][func].args_and_sig != args_and_sig + or self._func_dict[prefix][func].attrs != attrs + or self._func_dict[prefix][func].funcdef_attrs_and_ret + != funcdef_attrs_and_ret + ): + if self._func_dict[prefix][func].is_same_except_arg_names( + scrubbed_extra, + funcdef_attrs_and_ret, + args_and_sig, + attrs, + is_backend, + ): + self._func_dict[prefix][func].scrub = scrubbed_extra + self._func_dict[prefix][func].args_and_sig = args_and_sig + else: + # This means a previous RUN line produced a body for this function + # that is different from the one produced by this current RUN line, + # so the body can't be common across RUN lines. We use None to + # indicate that. + self._func_dict[prefix][func] = None + else: + if prefix not in self._processed_prefixes: + self._func_dict[prefix][func] = function_body( + scrubbed_body, + scrubbed_extra, + funcdef_attrs_and_ret, + args_and_sig, + attrs, + func_name_separator, + ) + self._func_order[prefix].append(func) + else: + # An earlier RUN line used this check prefixes but didn't produce + # a body for this function. This happens in Clang tests that use + # preprocesser directives to exclude individual functions from some + # RUN lines. + self._func_dict[prefix][func] = None + + def processed_prefixes(self, prefixes): + """ + Mark a set of prefixes as having had at least one applicable RUN line fully + processed. This is used to filter out function bodies that don't have + outputs for all RUN lines. + """ + self._processed_prefixes.update(prefixes) + + def get_failed_prefixes(self): + # This returns the list of those prefixes that failed to match any function, + # because there were conflicting bodies produced by different RUN lines, in + # all instances of the prefix. + for prefix in self._func_dict: + if self._func_dict[prefix] and ( + not [ + fct + for fct in self._func_dict[prefix] + if self._func_dict[prefix][fct] is not None + ] + ): + yield prefix ##### Generator of LLVM IR CHECK lines -SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*') +SCRUB_IR_COMMENT_RE = re.compile(r"\s*;.*") # TODO: We should also derive check lines for global, debug, loop declarations, etc.. + class NamelessValue: - def __init__(self, check_prefix, check_key, ir_prefix, ir_regexp, - global_ir_rhs_regexp, *, is_before_functions=False, is_number=False, - replace_number_with_counter=False): - self.check_prefix = check_prefix - self.check_key = check_key - self.ir_prefix = ir_prefix - self.ir_regexp = ir_regexp - self.global_ir_rhs_regexp = global_ir_rhs_regexp - self.is_before_functions = is_before_functions - self.is_number = is_number - # Some variable numbers (e.g. MCINST1234) will change based on unrelated - # modifications to LLVM, replace those with an incrementing counter. - self.replace_number_with_counter = replace_number_with_counter - self.variable_mapping = {} - - # Return true if this kind of IR value is "local", basically if it matches '%{{.*}}'. - def is_local_def_ir_value_match(self, match): - return self.ir_prefix == '%' - - # Return true if this kind of IR value is "global", basically if it matches '#{{.*}}'. - def is_global_scope_ir_value_match(self, match): - return self.global_ir_rhs_regexp is not None - - # Return the IR prefix and check prefix we use for this kind or IR value, - # e.g., (%, TMP) for locals. - def get_ir_prefix_from_ir_value_match(self, match): - return self.ir_prefix, self.check_prefix - - # Return the IR regexp we use for this kind or IR value, e.g., [\w.-]+? for locals - def get_ir_regex_from_ir_value_re_match(self, match): - # for backwards compatibility we check locals with '.*' - if self.is_local_def_ir_value_match(match): - return '.*' - return self.ir_regexp - - # Create a FileCheck variable name based on an IR name. - def get_value_name(self, var: str, check_prefix: str): - var = var.replace('!', '') - if self.replace_number_with_counter: - assert var.isdigit(), var - replacement = self.variable_mapping.get(var, None) - if replacement is None: - # Replace variable with an incrementing counter - replacement = str(len(self.variable_mapping) + 1) - self.variable_mapping[var] = replacement - var = replacement - # This is a nameless value, prepend check_prefix. - if var.isdigit(): - var = check_prefix + var - else: - # This is a named value that clashes with the check_prefix, prepend with - # _prefix_filecheck_ir_name, if it has been defined. - if may_clash_with_default_check_prefix_name(check_prefix, var) and _prefix_filecheck_ir_name: - var = _prefix_filecheck_ir_name + var - var = var.replace('.', '_') - var = var.replace('-', '_') - return var.upper() - - # Create a FileCheck variable from regex. - def get_value_definition(self, var, match): - # for backwards compatibility we check locals with '.*' - varname = self.get_value_name(var, self.check_prefix) - prefix = self.get_ir_prefix_from_ir_value_match(match)[0] - if self.is_number: - regex = '' # always capture a number in the default format - capture_start = '[[#' - else: - regex = self.get_ir_regex_from_ir_value_re_match(match) - capture_start = '[[' - if self.is_local_def_ir_value_match(match): - return capture_start + varname + ':' + prefix + regex + ']]' - return prefix + capture_start + varname + ':' + regex + ']]' - - # Use a FileCheck variable. - def get_value_use(self, var, match, var_prefix=None): - if var_prefix is None: - var_prefix = self.check_prefix - capture_start = '[[#' if self.is_number else '[[' - if self.is_local_def_ir_value_match(match): - return capture_start + self.get_value_name(var, var_prefix) + ']]' - prefix = self.get_ir_prefix_from_ir_value_match(match)[0] - return prefix + capture_start + self.get_value_name(var, var_prefix) + ']]' + def __init__( + self, + check_prefix, + check_key, + ir_prefix, + ir_regexp, + global_ir_rhs_regexp, + *, + is_before_functions=False, + is_number=False, + replace_number_with_counter=False + ): + self.check_prefix = check_prefix + self.check_key = check_key + self.ir_prefix = ir_prefix + self.ir_regexp = ir_regexp + self.global_ir_rhs_regexp = global_ir_rhs_regexp + self.is_before_functions = is_before_functions + self.is_number = is_number + # Some variable numbers (e.g. MCINST1234) will change based on unrelated + # modifications to LLVM, replace those with an incrementing counter. + self.replace_number_with_counter = replace_number_with_counter + self.variable_mapping = {} + + # Return true if this kind of IR value is "local", basically if it matches '%{{.*}}'. + def is_local_def_ir_value_match(self, match): + return self.ir_prefix == "%" + + # Return true if this kind of IR value is "global", basically if it matches '#{{.*}}'. + def is_global_scope_ir_value_match(self, match): + return self.global_ir_rhs_regexp is not None + + # Return the IR prefix and check prefix we use for this kind or IR value, + # e.g., (%, TMP) for locals. + def get_ir_prefix_from_ir_value_match(self, match): + return self.ir_prefix, self.check_prefix + + # Return the IR regexp we use for this kind or IR value, e.g., [\w.-]+? for locals + def get_ir_regex_from_ir_value_re_match(self, match): + # for backwards compatibility we check locals with '.*' + if self.is_local_def_ir_value_match(match): + return ".*" + return self.ir_regexp + + # Create a FileCheck variable name based on an IR name. + def get_value_name(self, var: str, check_prefix: str): + var = var.replace("!", "") + if self.replace_number_with_counter: + assert var.isdigit(), var + replacement = self.variable_mapping.get(var, None) + if replacement is None: + # Replace variable with an incrementing counter + replacement = str(len(self.variable_mapping) + 1) + self.variable_mapping[var] = replacement + var = replacement + # This is a nameless value, prepend check_prefix. + if var.isdigit(): + var = check_prefix + var + else: + # This is a named value that clashes with the check_prefix, prepend with + # _prefix_filecheck_ir_name, if it has been defined. + if ( + may_clash_with_default_check_prefix_name(check_prefix, var) + and _prefix_filecheck_ir_name + ): + var = _prefix_filecheck_ir_name + var + var = var.replace(".", "_") + var = var.replace("-", "_") + return var.upper() + + # Create a FileCheck variable from regex. + def get_value_definition(self, var, match): + # for backwards compatibility we check locals with '.*' + varname = self.get_value_name(var, self.check_prefix) + prefix = self.get_ir_prefix_from_ir_value_match(match)[0] + if self.is_number: + regex = "" # always capture a number in the default format + capture_start = "[[#" + else: + regex = self.get_ir_regex_from_ir_value_re_match(match) + capture_start = "[[" + if self.is_local_def_ir_value_match(match): + return capture_start + varname + ":" + prefix + regex + "]]" + return prefix + capture_start + varname + ":" + regex + "]]" + + # Use a FileCheck variable. + def get_value_use(self, var, match, var_prefix=None): + if var_prefix is None: + var_prefix = self.check_prefix + capture_start = "[[#" if self.is_number else "[[" + if self.is_local_def_ir_value_match(match): + return capture_start + self.get_value_name(var, var_prefix) + "]]" + prefix = self.get_ir_prefix_from_ir_value_match(match)[0] + return prefix + capture_start + self.get_value_name(var, var_prefix) + "]]" + # Description of the different "unnamed" values we match in the IR, e.g., # (local) ssa values, (debug) metadata, etc. ir_nameless_values = [ # check_prefix check_key ir_prefix ir_regexp global_ir_rhs_regexp - NamelessValue(r'TMP' , '%' , r'%' , r'[\w$.-]+?' , None ) , - NamelessValue(r'ATTR' , '#' , r'#' , r'[0-9]+' , None ) , - NamelessValue(r'ATTR' , '#' , r'attributes #' , r'[0-9]+' , r'{[^}]*}' ) , - NamelessValue(r'GLOB' , '@' , r'@' , r'[0-9]+' , None ) , - NamelessValue(r'GLOB' , '@' , r'@' , r'[a-zA-Z0-9_$"\\.-]+' , r'.+' , is_before_functions=True) , - NamelessValue(r'DBG' , '!' , r'!dbg ' , r'![0-9]+' , None ) , - NamelessValue(r'DIASSIGNID' , '!' , r'!DIAssignID ' , r'![0-9]+' , None ) , - NamelessValue(r'PROF' , '!' , r'!prof ' , r'![0-9]+' , None ) , - NamelessValue(r'TBAA' , '!' , r'!tbaa ' , r'![0-9]+' , None ) , - NamelessValue(r'TBAA_STRUCT', '!' , r'!tbaa.struct ' , r'![0-9]+' , None ) , - NamelessValue(r'RNG' , '!' , r'!range ' , r'![0-9]+' , None ) , - NamelessValue(r'LOOP' , '!' , r'!llvm.loop ' , r'![0-9]+' , None ) , - NamelessValue(r'META' , '!' , r'metadata ' , r'![0-9]+' , None ) , - NamelessValue(r'META' , '!' , r'' , r'![0-9]+' , r'(?:distinct |)!.*' ) , - NamelessValue(r'ACC_GRP' , '!' , r'!llvm.access.group ' , r'![0-9]+' , None ) , + NamelessValue(r"TMP", "%", r"%", r"[\w$.-]+?", None), + NamelessValue(r"ATTR", "#", r"#", r"[0-9]+", None), + NamelessValue(r"ATTR", "#", r"attributes #", r"[0-9]+", r"{[^}]*}"), + NamelessValue(r"GLOB", "@", r"@", r"[0-9]+", None), + NamelessValue( + r"GLOB", "@", r"@", r'[a-zA-Z0-9_$"\\.-]+', r".+", is_before_functions=True + ), + NamelessValue(r"DBG", "!", r"!dbg ", r"![0-9]+", None), + NamelessValue(r"DIASSIGNID", "!", r"!DIAssignID ", r"![0-9]+", None), + NamelessValue(r"PROF", "!", r"!prof ", r"![0-9]+", None), + NamelessValue(r"TBAA", "!", r"!tbaa ", r"![0-9]+", None), + NamelessValue(r"TBAA_STRUCT", "!", r"!tbaa.struct ", r"![0-9]+", None), + NamelessValue(r"RNG", "!", r"!range ", r"![0-9]+", None), + NamelessValue(r"LOOP", "!", r"!llvm.loop ", r"![0-9]+", None), + NamelessValue(r"META", "!", r"metadata ", r"![0-9]+", None), + NamelessValue(r"META", "!", r"", r"![0-9]+", r"(?:distinct |)!.*"), + NamelessValue(r"ACC_GRP", "!", r"!llvm.access.group ", r"![0-9]+", None), ] asm_nameless_values = [ - NamelessValue(r'MCINST' , 'Inst#' , '<MCInst #' , r'\d+' , r'.+', is_number=True, replace_number_with_counter=True), - NamelessValue(r'MCREG' , 'Reg:' , '<MCOperand Reg:' , r'\d+' , r'.+', is_number=True, replace_number_with_counter=True), + NamelessValue( + r"MCINST", + "Inst#", + "<MCInst #", + r"\d+", + r".+", + is_number=True, + replace_number_with_counter=True, + ), + NamelessValue( + r"MCREG", + "Reg:", + "<MCOperand Reg:", + r"\d+", + r".+", + is_number=True, + replace_number_with_counter=True, + ), ] + def createOrRegexp(old, new): - if not old: - return new - if not new: - return old - return old + '|' + new + if not old: + return new + if not new: + return old + return old + "|" + new + def createPrefixMatch(prefix_str, prefix_re): - return '(?:' + prefix_str + '(' + prefix_re + '))' + return "(?:" + prefix_str + "(" + prefix_re + "))" + # Build the regexp that matches an "IR value". This can be a local variable, # argument, global, or metadata, anything that is "named". It is important that # the PREFIX and SUFFIX below only contain a single group, if that changes # other locations will need adjustment as well. -IR_VALUE_REGEXP_PREFIX = r'(\s*)' -IR_VALUE_REGEXP_STRING = r'' +IR_VALUE_REGEXP_PREFIX = r"(\s*)" +IR_VALUE_REGEXP_STRING = r"" for nameless_value in ir_nameless_values: - match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp) - if nameless_value.global_ir_rhs_regexp is not None: - match = '^' + match - IR_VALUE_REGEXP_STRING = createOrRegexp(IR_VALUE_REGEXP_STRING, match) -IR_VALUE_REGEXP_SUFFIX = r'([,\s\(\)]|\Z)' -IR_VALUE_RE = re.compile(IR_VALUE_REGEXP_PREFIX + r'(' + IR_VALUE_REGEXP_STRING + r')' + IR_VALUE_REGEXP_SUFFIX) + match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp) + if nameless_value.global_ir_rhs_regexp is not None: + match = "^" + match + IR_VALUE_REGEXP_STRING = createOrRegexp(IR_VALUE_REGEXP_STRING, match) +IR_VALUE_REGEXP_SUFFIX = r"([,\s\(\)]|\Z)" +IR_VALUE_RE = re.compile( + IR_VALUE_REGEXP_PREFIX + + r"(" + + IR_VALUE_REGEXP_STRING + + r")" + + IR_VALUE_REGEXP_SUFFIX +) # Build the regexp that matches an "ASM value" (currently only for --asm-show-inst comments). -ASM_VALUE_REGEXP_STRING = '' +ASM_VALUE_REGEXP_STRING = "" for nameless_value in asm_nameless_values: - match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp) - ASM_VALUE_REGEXP_STRING = createOrRegexp(ASM_VALUE_REGEXP_STRING, match) -ASM_VALUE_REGEXP_SUFFIX = r'([>\s]|\Z)' -ASM_VALUE_RE = re.compile(r'((?:#|//)\s*)' + '(' + ASM_VALUE_REGEXP_STRING + ')' + ASM_VALUE_REGEXP_SUFFIX) + match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp) + ASM_VALUE_REGEXP_STRING = createOrRegexp(ASM_VALUE_REGEXP_STRING, match) +ASM_VALUE_REGEXP_SUFFIX = r"([>\s]|\Z)" +ASM_VALUE_RE = re.compile( + r"((?:#|//)\s*)" + "(" + ASM_VALUE_REGEXP_STRING + ")" + ASM_VALUE_REGEXP_SUFFIX +) # The entire match is group 0, the prefix has one group (=1), the entire # IR_VALUE_REGEXP_STRING is one group (=2), and then the nameless values start. @@ -825,488 +1061,665 @@ attribute_group_in_ir_value_match = 4 # Check a match for IR_VALUE_RE and inspect it to determine if it was a local # value, %..., global @..., debug number !dbg !..., etc. See the PREFIXES above. def get_idx_from_ir_value_match(match): - for i in range(first_nameless_group_in_ir_value_match, match.lastindex): - if match.group(i) is not None: - return i - first_nameless_group_in_ir_value_match - error("Unable to identify the kind of IR value from the match!") - return 0 + for i in range(first_nameless_group_in_ir_value_match, match.lastindex): + if match.group(i) is not None: + return i - first_nameless_group_in_ir_value_match + error("Unable to identify the kind of IR value from the match!") + return 0 + # See get_idx_from_ir_value_match def get_name_from_ir_value_match(match): - return match.group(get_idx_from_ir_value_match(match) + first_nameless_group_in_ir_value_match) + return match.group( + get_idx_from_ir_value_match(match) + first_nameless_group_in_ir_value_match + ) + def get_nameless_value_from_match(match, nameless_values) -> NamelessValue: - return nameless_values[get_idx_from_ir_value_match(match)] + return nameless_values[get_idx_from_ir_value_match(match)] + # Return true if var clashes with the scripted FileCheck check_prefix. def may_clash_with_default_check_prefix_name(check_prefix, var): - return check_prefix and re.match(r'^' + check_prefix + r'[0-9]+?$', var, re.IGNORECASE) - -def generalize_check_lines_common(lines, is_analyze, vars_seen, - global_vars_seen, nameless_values, - nameless_value_regex, is_asm): - # This gets called for each match that occurs in - # a line. We transform variables we haven't seen - # into defs, and variables we have seen into uses. - def transform_line_vars(match): - var = get_name_from_ir_value_match(match) - nameless_value = get_nameless_value_from_match(match, nameless_values) - if may_clash_with_default_check_prefix_name(nameless_value.check_prefix, var): - warn("Change IR value name '%s' or use --prefix-filecheck-ir-name to prevent possible conflict" - " with scripted FileCheck name." % (var,)) - key = (var, nameless_value.check_key) - is_local_def = nameless_value.is_local_def_ir_value_match(match) - if is_local_def and key in vars_seen: - rv = nameless_value.get_value_use(var, match) - elif not is_local_def and key in global_vars_seen: - # We could have seen a different prefix for the global variables first, - # ensure we use that one instead of the prefix for the current match. - rv = nameless_value.get_value_use(var, match, global_vars_seen[key]) - else: - if is_local_def: - vars_seen.add(key) - else: - global_vars_seen[key] = nameless_value.check_prefix - rv = nameless_value.get_value_definition(var, match) - # re.sub replaces the entire regex match - # with whatever you return, so we have - # to make sure to hand it back everything - # including the commas and spaces. - return match.group(1) + rv + match.group(match.lastindex) - - lines_with_def = [] - - for i, line in enumerate(lines): - if not is_asm: - # An IR variable named '%.' matches the FileCheck regex string. - line = line.replace('%.', '%dot') - for regex in _global_hex_value_regex: - if re.match('^@' + regex + ' = ', line): - line = re.sub(r'\bi([0-9]+) ([0-9]+)', - lambda m : 'i' + m.group(1) + ' [[#' + hex(int(m.group(2))) + ']]', - line) - break - # Ignore any comments, since the check lines will too. - scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line) - lines[i] = scrubbed_line - if is_asm or not is_analyze: - # It can happen that two matches are back-to-back and for some reason sub - # will not replace both of them. For now we work around this by - # substituting until there is no more match. - changed = True - while changed: - (lines[i], changed) = nameless_value_regex.subn(transform_line_vars, - lines[i], count=1) - return lines + return check_prefix and re.match( + r"^" + check_prefix + r"[0-9]+?$", var, re.IGNORECASE + ) + + +def generalize_check_lines_common( + lines, + is_analyze, + vars_seen, + global_vars_seen, + nameless_values, + nameless_value_regex, + is_asm, +): + # This gets called for each match that occurs in + # a line. We transform variables we haven't seen + # into defs, and variables we have seen into uses. + def transform_line_vars(match): + var = get_name_from_ir_value_match(match) + nameless_value = get_nameless_value_from_match(match, nameless_values) + if may_clash_with_default_check_prefix_name(nameless_value.check_prefix, var): + warn( + "Change IR value name '%s' or use --prefix-filecheck-ir-name to prevent possible conflict" + " with scripted FileCheck name." % (var,) + ) + key = (var, nameless_value.check_key) + is_local_def = nameless_value.is_local_def_ir_value_match(match) + if is_local_def and key in vars_seen: + rv = nameless_value.get_value_use(var, match) + elif not is_local_def and key in global_vars_seen: + # We could have seen a different prefix for the global variables first, + # ensure we use that one instead of the prefix for the current match. + rv = nameless_value.get_value_use(var, match, global_vars_seen[key]) + else: + if is_local_def: + vars_seen.add(key) + else: + global_vars_seen[key] = nameless_value.check_prefix + rv = nameless_value.get_value_definition(var, match) + # re.sub replaces the entire regex match + # with whatever you return, so we have + # to make sure to hand it back everything + # including the commas and spaces. + return match.group(1) + rv + match.group(match.lastindex) + + lines_with_def = [] + + for i, line in enumerate(lines): + if not is_asm: + # An IR variable named '%.' matches the FileCheck regex string. + line = line.replace("%.", "%dot") + for regex in _global_hex_value_regex: + if re.match("^@" + regex + " = ", line): + line = re.sub( + r"\bi([0-9]+) ([0-9]+)", + lambda m: "i" + + m.group(1) + + " [[#" + + hex(int(m.group(2))) + + "]]", + line, + ) + break + # Ignore any comments, since the check lines will too. + scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r"", line) + lines[i] = scrubbed_line + if is_asm or not is_analyze: + # It can happen that two matches are back-to-back and for some reason sub + # will not replace both of them. For now we work around this by + # substituting until there is no more match. + changed = True + while changed: + (lines[i], changed) = nameless_value_regex.subn( + transform_line_vars, lines[i], count=1 + ) + return lines + # Replace IR value defs and uses with FileCheck variables. def generalize_check_lines(lines, is_analyze, vars_seen, global_vars_seen): - return generalize_check_lines_common(lines, is_analyze, vars_seen, - global_vars_seen, ir_nameless_values, - IR_VALUE_RE, False) + return generalize_check_lines_common( + lines, + is_analyze, + vars_seen, + global_vars_seen, + ir_nameless_values, + IR_VALUE_RE, + False, + ) -def generalize_asm_check_lines(lines, vars_seen, global_vars_seen): - return generalize_check_lines_common(lines, False, vars_seen, - global_vars_seen, asm_nameless_values, - ASM_VALUE_RE, True) - -def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_backend, is_analyze, version, global_vars_seen_dict, is_filtered): - # prefix_exclusions are prefixes we cannot use to print the function because it doesn't exist in run lines that use these prefixes as well. - prefix_exclusions = set() - printed_prefixes = [] - for p in prefix_list: - checkprefixes = p[0] - # If not all checkprefixes of this run line produced the function we cannot check for it as it does not - # exist for this run line. A subset of the check prefixes might know about the function but only because - # other run lines created it. - if any(map(lambda checkprefix: func_name not in func_dict[checkprefix], checkprefixes)): - prefix_exclusions |= set(checkprefixes) - continue - - # prefix_exclusions is constructed, we can now emit the output - for p in prefix_list: - global_vars_seen = {} - checkprefixes = p[0] - for checkprefix in checkprefixes: - if checkprefix in global_vars_seen_dict: - global_vars_seen.update(global_vars_seen_dict[checkprefix]) - else: - global_vars_seen_dict[checkprefix] = {} - if checkprefix in printed_prefixes: - break - - # Check if the prefix is excluded. - if checkprefix in prefix_exclusions: - continue - - # If we do not have output for this prefix we skip it. - if not func_dict[checkprefix][func_name]: - continue - - # Add some space between different check prefixes, but not after the last - # check line (before the test code). - if is_backend: - if len(printed_prefixes) != 0: - output_lines.append(comment_marker) - - if checkprefix not in global_vars_seen_dict: - global_vars_seen_dict[checkprefix] = {} - - global_vars_seen_before = [key for key in global_vars_seen.keys()] - - vars_seen = set() - printed_prefixes.append(checkprefix) - attrs = str(func_dict[checkprefix][func_name].attrs) - attrs = '' if attrs == 'None' else attrs - if version > 1: - funcdef_attrs_and_ret = func_dict[checkprefix][func_name].funcdef_attrs_and_ret - else: - funcdef_attrs_and_ret = '' - - if attrs: - output_lines.append('%s %s: Function Attrs: %s' % (comment_marker, checkprefix, attrs)) - args_and_sig = str(func_dict[checkprefix][func_name].args_and_sig) - if args_and_sig: - args_and_sig = generalize_check_lines([args_and_sig], is_analyze, vars_seen, global_vars_seen)[0] - func_name_separator = func_dict[checkprefix][func_name].func_name_separator - if '[[' in args_and_sig: - output_lines.append(check_label_format % (checkprefix, funcdef_attrs_and_ret, func_name, '', func_name_separator)) - output_lines.append('%s %s-SAME: %s' % (comment_marker, checkprefix, args_and_sig)) - else: - output_lines.append(check_label_format % (checkprefix, funcdef_attrs_and_ret, func_name, args_and_sig, func_name_separator)) - func_body = str(func_dict[checkprefix][func_name]).splitlines() - if not func_body: - # We have filtered everything. - continue - - # For ASM output, just emit the check lines. - if is_backend: - body_start = 1 - if is_filtered: - # For filtered output we don't add "-NEXT" so don't add extra spaces - # before the first line. - body_start = 0 - else: - output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0])) - func_lines = generalize_asm_check_lines(func_body[body_start:], - vars_seen, global_vars_seen) - for func_line in func_lines: - if func_line.strip() == '': - output_lines.append('%s %s-EMPTY:' % (comment_marker, checkprefix)) - else: - check_suffix = '-NEXT' if not is_filtered else '' - output_lines.append('%s %s%s: %s' % (comment_marker, checkprefix, - check_suffix, func_line)) - # Remember new global variables we have not seen before - for key in global_vars_seen: - if key not in global_vars_seen_before: - global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] - break - - # For IR output, change all defs to FileCheck variables, so we're immune - # to variable naming fashions. - func_body = generalize_check_lines(func_body, is_analyze, vars_seen, global_vars_seen) - - # This could be selectively enabled with an optional invocation argument. - # Disabled for now: better to check everything. Be safe rather than sorry. - - # Handle the first line of the function body as a special case because - # it's often just noise (a useless asm comment or entry label). - #if func_body[0].startswith("#") or func_body[0].startswith("entry:"): - # is_blank_line = True - #else: - # output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0])) - # is_blank_line = False - - is_blank_line = False - - for func_line in func_body: - if func_line.strip() == '': - is_blank_line = True - continue - # Do not waste time checking IR comments. - func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line) - - # Skip blank lines instead of checking them. - if is_blank_line: - output_lines.append('{} {}: {}'.format( - comment_marker, checkprefix, func_line)) - else: - check_suffix = '-NEXT' if not is_filtered else '' - output_lines.append('{} {}{}: {}'.format( - comment_marker, checkprefix, check_suffix, func_line)) - is_blank_line = False - - # Add space between different check prefixes and also before the first - # line of code in the test function. - output_lines.append(comment_marker) - - # Remember new global variables we have not seen before - for key in global_vars_seen: - if key not in global_vars_seen_before: - global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] - break - return printed_prefixes - -def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict, - func_name, preserve_names, function_sig, version, - global_vars_seen_dict, is_filtered): - # Label format is based on IR string. - if function_sig and version > 1: - function_def_regex = 'define %s' - elif function_sig: - function_def_regex = 'define {{[^@]+}}%s' - else: - function_def_regex = '%s' - check_label_format = '{} %s-LABEL: {}@%s%s%s'.format(comment_marker, function_def_regex) - return add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, - check_label_format, False, preserve_names, version, - global_vars_seen_dict, - is_filtered) - -def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, is_filtered): - check_label_format = '{} %s-LABEL: \'%s%s%s%s\''.format(comment_marker) - global_vars_seen_dict = {} - return add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, - check_label_format, False, True, 1, global_vars_seen_dict, - is_filtered) -def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes): - for nameless_value in itertools.chain(ir_nameless_values, asm_nameless_values): - if nameless_value.global_ir_rhs_regexp is None: - continue - - lhs_re_str = nameless_value.ir_prefix + nameless_value.ir_regexp - rhs_re_str = nameless_value.global_ir_rhs_regexp - - global_ir_value_re_str = r'^' + lhs_re_str + r'\s=\s' + rhs_re_str + r'$' - global_ir_value_re = re.compile(global_ir_value_re_str, flags=(re.M)) - lines = [] - for m in global_ir_value_re.finditer(raw_tool_output): - lines.append(m.group(0)) - - for prefix in prefixes: - if glob_val_dict[prefix] is None: - continue - if nameless_value.check_prefix in glob_val_dict[prefix]: - if lines == glob_val_dict[prefix][nameless_value.check_prefix]: - continue - if prefix == prefixes[-1]: - warn('Found conflicting asm under the same prefix: %r!' % (prefix,)) - else: - glob_val_dict[prefix][nameless_value.check_prefix] = None - continue - glob_val_dict[prefix][nameless_value.check_prefix] = lines - -def add_global_checks(glob_val_dict, comment_marker, prefix_list, output_lines, global_vars_seen_dict, is_analyze, is_before_functions): - printed_prefixes = set() - for nameless_value in ir_nameless_values: - if nameless_value.global_ir_rhs_regexp is None: - continue - if nameless_value.is_before_functions != is_before_functions: - continue +def generalize_asm_check_lines(lines, vars_seen, global_vars_seen): + return generalize_check_lines_common( + lines, + False, + vars_seen, + global_vars_seen, + asm_nameless_values, + ASM_VALUE_RE, + True, + ) + + +def add_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + check_label_format, + is_backend, + is_analyze, + version, + global_vars_seen_dict, + is_filtered, +): + # prefix_exclusions are prefixes we cannot use to print the function because it doesn't exist in run lines that use these prefixes as well. + prefix_exclusions = set() + printed_prefixes = [] for p in prefix_list: - global_vars_seen = {} - checkprefixes = p[0] - if checkprefixes is None: - continue - for checkprefix in checkprefixes: - if checkprefix in global_vars_seen_dict: - global_vars_seen.update(global_vars_seen_dict[checkprefix]) - else: - global_vars_seen_dict[checkprefix] = {} - if (checkprefix, nameless_value.check_prefix) in printed_prefixes: - break - if not glob_val_dict[checkprefix]: - continue - if nameless_value.check_prefix not in glob_val_dict[checkprefix]: - continue - if not glob_val_dict[checkprefix][nameless_value.check_prefix]: - continue - - check_lines = [] - global_vars_seen_before = [key for key in global_vars_seen.keys()] - for line in glob_val_dict[checkprefix][nameless_value.check_prefix]: - if _global_value_regex: - matched = False - for regex in _global_value_regex: - if re.match('^@' + regex + ' = ', line): - matched = True + checkprefixes = p[0] + # If not all checkprefixes of this run line produced the function we cannot check for it as it does not + # exist for this run line. A subset of the check prefixes might know about the function but only because + # other run lines created it. + if any( + map( + lambda checkprefix: func_name not in func_dict[checkprefix], + checkprefixes, + ) + ): + prefix_exclusions |= set(checkprefixes) + continue + + # prefix_exclusions is constructed, we can now emit the output + for p in prefix_list: + global_vars_seen = {} + checkprefixes = p[0] + for checkprefix in checkprefixes: + if checkprefix in global_vars_seen_dict: + global_vars_seen.update(global_vars_seen_dict[checkprefix]) + else: + global_vars_seen_dict[checkprefix] = {} + if checkprefix in printed_prefixes: break - if not matched: - continue - tmp = generalize_check_lines([line], is_analyze, set(), global_vars_seen) - check_line = '%s %s: %s' % (comment_marker, checkprefix, tmp[0]) - check_lines.append(check_line) - if not check_lines: - continue - output_lines.append(comment_marker + SEPARATOR) - for check_line in check_lines: - output_lines.append(check_line) + # Check if the prefix is excluded. + if checkprefix in prefix_exclusions: + continue + + # If we do not have output for this prefix we skip it. + if not func_dict[checkprefix][func_name]: + continue + + # Add some space between different check prefixes, but not after the last + # check line (before the test code). + if is_backend: + if len(printed_prefixes) != 0: + output_lines.append(comment_marker) + + if checkprefix not in global_vars_seen_dict: + global_vars_seen_dict[checkprefix] = {} + + global_vars_seen_before = [key for key in global_vars_seen.keys()] + + vars_seen = set() + printed_prefixes.append(checkprefix) + attrs = str(func_dict[checkprefix][func_name].attrs) + attrs = "" if attrs == "None" else attrs + if version > 1: + funcdef_attrs_and_ret = func_dict[checkprefix][ + func_name + ].funcdef_attrs_and_ret + else: + funcdef_attrs_and_ret = "" + + if attrs: + output_lines.append( + "%s %s: Function Attrs: %s" % (comment_marker, checkprefix, attrs) + ) + args_and_sig = str(func_dict[checkprefix][func_name].args_and_sig) + if args_and_sig: + args_and_sig = generalize_check_lines( + [args_and_sig], is_analyze, vars_seen, global_vars_seen + )[0] + func_name_separator = func_dict[checkprefix][func_name].func_name_separator + if "[[" in args_and_sig: + output_lines.append( + check_label_format + % ( + checkprefix, + funcdef_attrs_and_ret, + func_name, + "", + func_name_separator, + ) + ) + output_lines.append( + "%s %s-SAME: %s" % (comment_marker, checkprefix, args_and_sig) + ) + else: + output_lines.append( + check_label_format + % ( + checkprefix, + funcdef_attrs_and_ret, + func_name, + args_and_sig, + func_name_separator, + ) + ) + func_body = str(func_dict[checkprefix][func_name]).splitlines() + if not func_body: + # We have filtered everything. + continue + + # For ASM output, just emit the check lines. + if is_backend: + body_start = 1 + if is_filtered: + # For filtered output we don't add "-NEXT" so don't add extra spaces + # before the first line. + body_start = 0 + else: + output_lines.append( + "%s %s: %s" % (comment_marker, checkprefix, func_body[0]) + ) + func_lines = generalize_asm_check_lines( + func_body[body_start:], vars_seen, global_vars_seen + ) + for func_line in func_lines: + if func_line.strip() == "": + output_lines.append( + "%s %s-EMPTY:" % (comment_marker, checkprefix) + ) + else: + check_suffix = "-NEXT" if not is_filtered else "" + output_lines.append( + "%s %s%s: %s" + % (comment_marker, checkprefix, check_suffix, func_line) + ) + # Remember new global variables we have not seen before + for key in global_vars_seen: + if key not in global_vars_seen_before: + global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] + break - printed_prefixes.add((checkprefix, nameless_value.check_prefix)) + # For IR output, change all defs to FileCheck variables, so we're immune + # to variable naming fashions. + func_body = generalize_check_lines( + func_body, is_analyze, vars_seen, global_vars_seen + ) + + # This could be selectively enabled with an optional invocation argument. + # Disabled for now: better to check everything. Be safe rather than sorry. + + # Handle the first line of the function body as a special case because + # it's often just noise (a useless asm comment or entry label). + # if func_body[0].startswith("#") or func_body[0].startswith("entry:"): + # is_blank_line = True + # else: + # output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0])) + # is_blank_line = False + + is_blank_line = False + + for func_line in func_body: + if func_line.strip() == "": + is_blank_line = True + continue + # Do not waste time checking IR comments. + func_line = SCRUB_IR_COMMENT_RE.sub(r"", func_line) + + # Skip blank lines instead of checking them. + if is_blank_line: + output_lines.append( + "{} {}: {}".format(comment_marker, checkprefix, func_line) + ) + else: + check_suffix = "-NEXT" if not is_filtered else "" + output_lines.append( + "{} {}{}: {}".format( + comment_marker, checkprefix, check_suffix, func_line + ) + ) + is_blank_line = False + + # Add space between different check prefixes and also before the first + # line of code in the test function. + output_lines.append(comment_marker) + + # Remember new global variables we have not seen before + for key in global_vars_seen: + if key not in global_vars_seen_before: + global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] + break + return printed_prefixes + + +def add_ir_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + preserve_names, + function_sig, + version, + global_vars_seen_dict, + is_filtered, +): + # Label format is based on IR string. + if function_sig and version > 1: + function_def_regex = "define %s" + elif function_sig: + function_def_regex = "define {{[^@]+}}%s" + else: + function_def_regex = "%s" + check_label_format = "{} %s-LABEL: {}@%s%s%s".format( + comment_marker, function_def_regex + ) + return add_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + check_label_format, + False, + preserve_names, + version, + global_vars_seen_dict, + is_filtered, + ) + + +def add_analyze_checks( + output_lines, comment_marker, prefix_list, func_dict, func_name, is_filtered +): + check_label_format = "{} %s-LABEL: '%s%s%s%s'".format(comment_marker) + global_vars_seen_dict = {} + return add_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + check_label_format, + False, + True, + 1, + global_vars_seen_dict, + is_filtered, + ) - # Remembe new global variables we have not seen before - for key in global_vars_seen: - if key not in global_vars_seen_before: - global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] - break - if printed_prefixes: - output_lines.append(comment_marker + SEPARATOR) - return printed_prefixes +def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes): + for nameless_value in itertools.chain(ir_nameless_values, asm_nameless_values): + if nameless_value.global_ir_rhs_regexp is None: + continue + + lhs_re_str = nameless_value.ir_prefix + nameless_value.ir_regexp + rhs_re_str = nameless_value.global_ir_rhs_regexp + + global_ir_value_re_str = r"^" + lhs_re_str + r"\s=\s" + rhs_re_str + r"$" + global_ir_value_re = re.compile(global_ir_value_re_str, flags=(re.M)) + lines = [] + for m in global_ir_value_re.finditer(raw_tool_output): + lines.append(m.group(0)) + + for prefix in prefixes: + if glob_val_dict[prefix] is None: + continue + if nameless_value.check_prefix in glob_val_dict[prefix]: + if lines == glob_val_dict[prefix][nameless_value.check_prefix]: + continue + if prefix == prefixes[-1]: + warn("Found conflicting asm under the same prefix: %r!" % (prefix,)) + else: + glob_val_dict[prefix][nameless_value.check_prefix] = None + continue + glob_val_dict[prefix][nameless_value.check_prefix] = lines + + +def add_global_checks( + glob_val_dict, + comment_marker, + prefix_list, + output_lines, + global_vars_seen_dict, + is_analyze, + is_before_functions, +): + printed_prefixes = set() + for nameless_value in ir_nameless_values: + if nameless_value.global_ir_rhs_regexp is None: + continue + if nameless_value.is_before_functions != is_before_functions: + continue + for p in prefix_list: + global_vars_seen = {} + checkprefixes = p[0] + if checkprefixes is None: + continue + for checkprefix in checkprefixes: + if checkprefix in global_vars_seen_dict: + global_vars_seen.update(global_vars_seen_dict[checkprefix]) + else: + global_vars_seen_dict[checkprefix] = {} + if (checkprefix, nameless_value.check_prefix) in printed_prefixes: + break + if not glob_val_dict[checkprefix]: + continue + if nameless_value.check_prefix not in glob_val_dict[checkprefix]: + continue + if not glob_val_dict[checkprefix][nameless_value.check_prefix]: + continue + + check_lines = [] + global_vars_seen_before = [key for key in global_vars_seen.keys()] + for line in glob_val_dict[checkprefix][nameless_value.check_prefix]: + if _global_value_regex: + matched = False + for regex in _global_value_regex: + if re.match("^@" + regex + " = ", line): + matched = True + break + if not matched: + continue + tmp = generalize_check_lines( + [line], is_analyze, set(), global_vars_seen + ) + check_line = "%s %s: %s" % (comment_marker, checkprefix, tmp[0]) + check_lines.append(check_line) + if not check_lines: + continue + + output_lines.append(comment_marker + SEPARATOR) + for check_line in check_lines: + output_lines.append(check_line) + + printed_prefixes.add((checkprefix, nameless_value.check_prefix)) + + # Remembe new global variables we have not seen before + for key in global_vars_seen: + if key not in global_vars_seen_before: + global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] + break + + if printed_prefixes: + output_lines.append(comment_marker + SEPARATOR) + return printed_prefixes def check_prefix(prefix): - if not PREFIX_RE.match(prefix): - hint = "" - if ',' in prefix: - hint = " Did you mean '--check-prefixes=" + prefix + "'?" - warn(("Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores." + hint) % - (prefix)) + if not PREFIX_RE.match(prefix): + hint = "" + if "," in prefix: + hint = " Did you mean '--check-prefixes=" + prefix + "'?" + warn( + ( + "Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores." + + hint + ) + % (prefix) + ) + def get_check_prefixes(filecheck_cmd): - check_prefixes = [item for m in CHECK_PREFIX_RE.finditer(filecheck_cmd) - for item in m.group(1).split(',')] - if not check_prefixes: - check_prefixes = ['CHECK'] - return check_prefixes + check_prefixes = [ + item + for m in CHECK_PREFIX_RE.finditer(filecheck_cmd) + for item in m.group(1).split(",") + ] + if not check_prefixes: + check_prefixes = ["CHECK"] + return check_prefixes + def verify_filecheck_prefixes(fc_cmd): - fc_cmd_parts = fc_cmd.split() - for part in fc_cmd_parts: - if "check-prefix=" in part: - prefix = part.split('=', 1)[1] - check_prefix(prefix) - elif "check-prefixes=" in part: - prefixes = part.split('=', 1)[1].split(',') - for prefix in prefixes: - check_prefix(prefix) - if prefixes.count(prefix) > 1: - warn("Supplied prefix '%s' is not unique in the prefix list." % (prefix,)) + fc_cmd_parts = fc_cmd.split() + for part in fc_cmd_parts: + if "check-prefix=" in part: + prefix = part.split("=", 1)[1] + check_prefix(prefix) + elif "check-prefixes=" in part: + prefixes = part.split("=", 1)[1].split(",") + for prefix in prefixes: + check_prefix(prefix) + if prefixes.count(prefix) > 1: + warn( + "Supplied prefix '%s' is not unique in the prefix list." + % (prefix,) + ) def get_autogennote_suffix(parser, args): - autogenerated_note_args = '' - for action in parser._actions: - if not hasattr(args, action.dest): - continue # Ignore options such as --help that aren't included in args - # Ignore parameters such as paths to the binary or the list of tests - if action.dest in ('tests', 'update_only', 'tool_binary', 'opt_binary', - 'llc_binary', 'clang', 'opt', 'llvm_bin', 'verbose', - 'force_update'): - continue - value = getattr(args, action.dest) - if action.const is not None: # action stores a constant (usually True/False) - # Skip actions with different constant values (this happens with boolean - # --foo/--no-foo options) - if value != action.const: - continue - if parser.get_default(action.dest) == value: - continue # Don't add default values - if action.dest == 'function_signature' and args.version >= 2: - continue # Enabled by default in version 2 - if action.dest == 'filters': - # Create a separate option for each filter element. The value is a list - # of Filter objects. - for elem in value: - opt_name = 'filter-out' if elem.is_filter_out else 'filter' - opt_value = elem.pattern() - new_arg = '--%s "%s" ' % (opt_name, opt_value.strip('"')) - if new_arg not in autogenerated_note_args: - autogenerated_note_args += new_arg - else: - autogenerated_note_args += action.option_strings[0] + ' ' - if action.const is None: # action takes a parameter - if action.nargs == '+': - value = ' '.join(map(lambda v: '"' + v.strip('"') + '"', value)) - autogenerated_note_args += '%s ' % value - if autogenerated_note_args: - autogenerated_note_args = ' %s %s' % (UTC_ARGS_KEY, autogenerated_note_args[:-1]) - return autogenerated_note_args + autogenerated_note_args = "" + for action in parser._actions: + if not hasattr(args, action.dest): + continue # Ignore options such as --help that aren't included in args + # Ignore parameters such as paths to the binary or the list of tests + if action.dest in ( + "tests", + "update_only", + "tool_binary", + "opt_binary", + "llc_binary", + "clang", + "opt", + "llvm_bin", + "verbose", + "force_update", + ): + continue + value = getattr(args, action.dest) + if action.const is not None: # action stores a constant (usually True/False) + # Skip actions with different constant values (this happens with boolean + # --foo/--no-foo options) + if value != action.const: + continue + if parser.get_default(action.dest) == value: + continue # Don't add default values + if action.dest == "function_signature" and args.version >= 2: + continue # Enabled by default in version 2 + if action.dest == "filters": + # Create a separate option for each filter element. The value is a list + # of Filter objects. + for elem in value: + opt_name = "filter-out" if elem.is_filter_out else "filter" + opt_value = elem.pattern() + new_arg = '--%s "%s" ' % (opt_name, opt_value.strip('"')) + if new_arg not in autogenerated_note_args: + autogenerated_note_args += new_arg + else: + autogenerated_note_args += action.option_strings[0] + " " + if action.const is None: # action takes a parameter + if action.nargs == "+": + value = " ".join(map(lambda v: '"' + v.strip('"') + '"', value)) + autogenerated_note_args += "%s " % value + if autogenerated_note_args: + autogenerated_note_args = " %s %s" % ( + UTC_ARGS_KEY, + autogenerated_note_args[:-1], + ) + return autogenerated_note_args def check_for_command(line, parser, args, argv, argparse_callback): - cmd_m = UTC_ARGS_CMD.match(line) - if cmd_m: - for option in shlex.split(cmd_m.group('cmd').strip()): - if option: - argv.append(option) - args = parse_args(parser, filter(lambda arg: arg not in args.tests, argv)) - if argparse_callback is not None: - argparse_callback(args) - return args, argv + cmd_m = UTC_ARGS_CMD.match(line) + if cmd_m: + for option in shlex.split(cmd_m.group("cmd").strip()): + if option: + argv.append(option) + args = parse_args(parser, filter(lambda arg: arg not in args.tests, argv)) + if argparse_callback is not None: + argparse_callback(args) + return args, argv + def find_arg_in_test(test_info, get_arg_to_check, arg_string, is_global): - result = get_arg_to_check(test_info.args) - if not result and is_global: - # See if this has been specified via UTC_ARGS. This is a "global" option - # that affects the entire generation of test checks. If it exists anywhere - # in the test, apply it to everything. - saw_line = False - for line_info in test_info.ro_iterlines(): - line = line_info.line - if not line.startswith(';') and line.strip() != '': - saw_line = True - result = get_arg_to_check(line_info.args) - if result: - if warn and saw_line: - # We saw the option after already reading some test input lines. - # Warn about it. - print('WARNING: Found {} in line following test start: '.format(arg_string) - + line, file=sys.stderr) - print('WARNING: Consider moving {} to top of file'.format(arg_string), - file=sys.stderr) - break - return result + result = get_arg_to_check(test_info.args) + if not result and is_global: + # See if this has been specified via UTC_ARGS. This is a "global" option + # that affects the entire generation of test checks. If it exists anywhere + # in the test, apply it to everything. + saw_line = False + for line_info in test_info.ro_iterlines(): + line = line_info.line + if not line.startswith(";") and line.strip() != "": + saw_line = True + result = get_arg_to_check(line_info.args) + if result: + if warn and saw_line: + # We saw the option after already reading some test input lines. + # Warn about it. + print( + "WARNING: Found {} in line following test start: ".format( + arg_string + ) + + line, + file=sys.stderr, + ) + print( + "WARNING: Consider moving {} to top of file".format(arg_string), + file=sys.stderr, + ) + break + return result + def dump_input_lines(output_lines, test_info, prefix_set, comment_string): - for input_line_info in test_info.iterlines(output_lines): - line = input_line_info.line - args = input_line_info.args - if line.strip() == comment_string: - continue - if line.strip() == comment_string + SEPARATOR: - continue - if line.lstrip().startswith(comment_string): - m = CHECK_RE.match(line) - if m and m.group(1) in prefix_set: - continue - output_lines.append(line.rstrip('\n')) - -def add_checks_at_end(output_lines, prefix_list, func_order, - comment_string, check_generator): - added = set() - generated_prefixes = set() - for prefix in prefix_list: - prefixes = prefix[0] - tool_args = prefix[1] - for prefix in prefixes: - for func in func_order[prefix]: - # The func order can contain the same functions multiple times. - # If we see one again we are done. - if (func, prefix) in added: - continue - if added: - output_lines.append(comment_string) - - # The add_*_checks routines expect a run list whose items are - # tuples that have a list of prefixes as their first element and - # tool command args string as their second element. They output - # checks for each prefix in the list of prefixes. By doing so, it - # implicitly assumes that for each function every run line will - # generate something for that function. That is not the case for - # generated functions as some run lines might not generate them - # (e.g. -fopenmp vs. no -fopenmp). - # - # Therefore, pass just the prefix we're interested in. This has - # the effect of generating all of the checks for functions of a - # single prefix before moving on to the next prefix. So checks - # are ordered by prefix instead of by function as in "normal" - # mode. - for generated_prefix in check_generator(output_lines, - [([prefix], tool_args)], func): - added.add((func, generated_prefix)) - generated_prefixes.add(generated_prefix) - return generated_prefixes + for input_line_info in test_info.iterlines(output_lines): + line = input_line_info.line + args = input_line_info.args + if line.strip() == comment_string: + continue + if line.strip() == comment_string + SEPARATOR: + continue + if line.lstrip().startswith(comment_string): + m = CHECK_RE.match(line) + if m and m.group(1) in prefix_set: + continue + output_lines.append(line.rstrip("\n")) + + +def add_checks_at_end( + output_lines, prefix_list, func_order, comment_string, check_generator +): + added = set() + generated_prefixes = set() + for prefix in prefix_list: + prefixes = prefix[0] + tool_args = prefix[1] + for prefix in prefixes: + for func in func_order[prefix]: + # The func order can contain the same functions multiple times. + # If we see one again we are done. + if (func, prefix) in added: + continue + if added: + output_lines.append(comment_string) + + # The add_*_checks routines expect a run list whose items are + # tuples that have a list of prefixes as their first element and + # tool command args string as their second element. They output + # checks for each prefix in the list of prefixes. By doing so, it + # implicitly assumes that for each function every run line will + # generate something for that function. That is not the case for + # generated functions as some run lines might not generate them + # (e.g. -fopenmp vs. no -fopenmp). + # + # Therefore, pass just the prefix we're interested in. This has + # the effect of generating all of the checks for functions of a + # single prefix before moving on to the next prefix. So checks + # are ordered by prefix instead of by function as in "normal" + # mode. + for generated_prefix in check_generator( + output_lines, [([prefix], tool_args)], func + ): + added.add((func, generated_prefix)) + generated_prefixes.add(generated_prefix) + return generated_prefixes diff --git a/llvm/utils/UpdateTestChecks/isel.py b/llvm/utils/UpdateTestChecks/isel.py index c10a555..bdb68e5 100644 --- a/llvm/utils/UpdateTestChecks/isel.py +++ b/llvm/utils/UpdateTestChecks/isel.py @@ -3,10 +3,12 @@ from . import common import sys if sys.version_info[0] > 2: - class string: - expandtabs = str.expandtabs + + class string: + expandtabs = str.expandtabs + else: - import string + import string # Support of isel debug checks # RegEx: this is where the magic happens. @@ -15,43 +17,64 @@ else: # TODO: add function prefix ISEL_FUNCTION_DEFAULT_RE = re.compile( - r'Selected[\s]*selection[\s]*DAG:[\s]*%bb.0[\s]*\'(?P<func>.*?):[^\']*\'*\n' - r'(?P<body>.*?)\n' - r'Total[\s]*amount[\s]*of[\s]*phi[\s]*nodes[\s]*to[\s]*update:[\s]*[0-9]+', - flags=(re.M | re.S)) + r"Selected[\s]*selection[\s]*DAG:[\s]*%bb.0[\s]*\'(?P<func>.*?):[^\']*\'*\n" + r"(?P<body>.*?)\n" + r"Total[\s]*amount[\s]*of[\s]*phi[\s]*nodes[\s]*to[\s]*update:[\s]*[0-9]+", + flags=(re.M | re.S), +) + def scrub_isel_default(isel, args): - # Scrub runs of whitespace out of the iSel debug output, but leave the leading - # whitespace in place. - isel = common.SCRUB_WHITESPACE_RE.sub(r' ', isel) - # Expand the tabs used for indentation. - isel = string.expandtabs(isel, 2) - # Strip trailing whitespace. - isel = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', isel) - return isel + # Scrub runs of whitespace out of the iSel debug output, but leave the leading + # whitespace in place. + isel = common.SCRUB_WHITESPACE_RE.sub(r" ", isel) + # Expand the tabs used for indentation. + isel = string.expandtabs(isel, 2) + # Strip trailing whitespace. + isel = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", isel) + return isel + def get_run_handler(triple): - target_handlers = { - } - handler = None - best_prefix = '' - for prefix, s in target_handlers.items(): - if triple.startswith(prefix) and len(prefix) > len(best_prefix): - handler = s - best_prefix = prefix + target_handlers = {} + handler = None + best_prefix = "" + for prefix, s in target_handlers.items(): + if triple.startswith(prefix) and len(prefix) > len(best_prefix): + handler = s + best_prefix = prefix + + if handler is None: + common.debug("Using default handler.") + handler = (scrub_isel_default, ISEL_FUNCTION_DEFAULT_RE) - if handler is None: - common.debug('Using default handler.') - handler = (scrub_isel_default, ISEL_FUNCTION_DEFAULT_RE) + return handler - return handler ##### Generator of iSel CHECK lines -def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, - global_vars_seen_dict, is_filtered): - # Label format is based on iSel string. - check_label_format = '{} %s-LABEL: %s%s%s%s'.format(comment_marker) - return common.add_checks(output_lines, comment_marker, prefix_list, func_dict, - func_name, check_label_format, True, False, 1, - global_vars_seen_dict, is_filtered=is_filtered) + +def add_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + global_vars_seen_dict, + is_filtered, +): + # Label format is based on iSel string. + check_label_format = "{} %s-LABEL: %s%s%s%s".format(comment_marker) + return common.add_checks( + output_lines, + comment_marker, + prefix_list, + func_dict, + func_name, + check_label_format, + True, + False, + 1, + global_vars_seen_dict, + is_filtered=is_filtered, + ) |