+2019-07-29 Richard Sandiford <richard.sandiford@arm.com>
+
+ * doc/sourcebuild.texi (check-function-bodies): Document.
+
2019-07-29 Richard Sandiford <richard.sandiford@arm.com>
* simplify-rtx.c (simplify_const_unary_operation): Fold a
@item scan-not-hidden @var{symbol} [@{ target/xfail @var{selector} @}]
Passes if @var{symbol} is not defined as a hidden symbol in the test's
assembly output.
+
+@item check-function-bodies @var{prefix} @var{terminator} [@var{option}]
+Looks through the source file for comments that give the expected assembly
+output for selected functions. Each line of expected output starts with the
+prefix string @var{prefix} and the expected output for a function as a whole
+is followed by a line that starts with the string @var{terminator}.
+Specifying an empty terminator is equivalent to specifying @samp{"*/"}.
+
+If @var{option} is specified, the test only applies to command lines
+that contain @var{option}. This can be useful if a source file is compiled
+both with and without optimization, since it is rarely useful to check the
+assembly output for unoptimized code.
+
+The first line of the expected output for a function @var{fn} has the form:
+
+@smallexample
+@var{prefix} @var{fn}: [@{ target/xfail @var{selector} @}]
+@end smallexample
+
+Subsequent lines of the expected output also start with @var{prefix}.
+In both cases, whitespace after @var{prefix} is not significant.
+
+The test discards assembly directives such as @code{.cfi_startproc}
+and local label definitions such as @code{.LFB0} from the compiler's
+assembly output. It then matches the result against the expected
+output for a function as a single regular expression. This means that
+later lines can use backslashes to refer back to @samp{(@dots{})}
+captures on earlier lines. For example:
+
+@smallexample
+/* @{ dg-final @{ check-function-bodies "**" "" "-DCHECK_ASM" @} @} */
+@dots{}
+/*
+** add_w0_s8_m:
+** mov (z[0-9]+\.b), w0
+** add z0\.b, p0/m, z0\.b, \1
+** ret
+*/
+svint8_t add_w0_s8_m (@dots{}) @{ @dots{} @}
+@dots{}
+/*
+** add_b0_s8_m:
+** mov (z[0-9]+\.b), b0
+** add z1\.b, p0/m, z1\.b, \1
+** ret
+*/
+svint8_t add_b0_s8_m (@dots{}) @{ @dots{} @}
+@end smallexample
+
+checks whether the implementations of @code{add_w0_s8_m} and
+@code{add_b0_s8_m} match the regular expressions given. The test only
+runs when @samp{-DCHECK_ASM} is passed on the command line.
+
+It is possible to create non-capturing multi-line regular expression
+groups of the form @samp{(@var{a}|@var{b}|@dots{})} by putting the
+@samp{(}, @samp{|} and @samp{)} on separate lines (each still using
+@var{prefix}). For example:
+
+@smallexample
+/*
+** cmple_f16_tied:
+** (
+** fcmge p0\.h, p0/z, z1\.h, z0\.h
+** |
+** fcmle p0\.h, p0/z, z0\.h, z1\.h
+** )
+** ret
+*/
+svbool_t cmple_f16_tied (@dots{}) @{ @dots{} @}
+@end smallexample
+
+checks whether @code{cmple_f16_tied} is implemented by the
+@code{fcmge} instruction followed by @code{ret} or by the
+@code{fcmle} instruction followed by @code{ret}. The test is
+still a single regular rexpression.
+
+A line containing just:
+
+@smallexample
+@var{prefix} ...
+@end smallexample
+
+stands for zero or more unmatched lines; the whitespace after
+@var{prefix} is again not significant.
+
@end table
@subsubsection Scan optimization dump files
+2019-07-29 Richard Sandiford <richard.sandiford@arm.com>
+
+ * lib/scanasm.exp (parse_function_bodies, check_function_body)
+ (check-function-bodies): New procedures.
+ * gcc.target/aarch64/sve/init_1.c: Use check-function-bodies
+ instead of scan-assembler.
+ * gcc.target/aarch64/sve/init_2.c: Likewise.
+ * gcc.target/aarch64/sve/init_3.c: Likewise.
+ * gcc.target/aarch64/sve/init_4.c: Likewise.
+ * gcc.target/aarch64/sve/init_5.c: Likewise.
+ * gcc.target/aarch64/sve/init_6.c: Likewise.
+ * gcc.target/aarch64/sve/init_7.c: Likewise.
+ * gcc.target/aarch64/sve/init_8.c: Likewise.
+ * gcc.target/aarch64/sve/init_9.c: Likewise.
+ * gcc.target/aarch64/sve/init_10.c: Likewise.
+ * gcc.target/aarch64/sve/init_11.c: Likewise.
+ * gcc.target/aarch64/sve/init_12.c: Likewise.
+
2019-07-28 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
* g++.dg/lto/pr89330_0.C (dg-lto-options): Add -fPIC.
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 1.1: Trailing constants with stepped sequence. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** index (z[0-9]+\.s), #1, #1
+** insr \1, w1
+** insr \1, w0
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b)
{
return (vnx4si) { a, b, 1, 2, 3, 4, 5, 6 };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- index z0.s, #1, #1
- insr z0.s, w1
- insr z0.s, w0
- ret
-*/
-
-/* { dg-final { scan-assembler {\tindex\t(z[0-9]+\.s), #1, #1\n\tinsr\t\1, w1\n\tinsr\t\1, w0} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 5.4: Interleaved repeating elements and non-repeating elements. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** mov (z[0-9]+\.s), w3
+** mov (z[0-9]+\.s), w2
+** insr \2, w1
+** insr \2, w0
+** zip1 \2, \2, \1
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b, int c, int f)
{
return (vnx4si) { a, f, b, f, c, f, c, f };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- mov z1.s, w3
- mov z0.s, w2
- insr z0.s, w1
- insr z0.s, w0
- zip1 z0.s, z0.s, z1.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tmov\t(z[0-9]+\.s), w3\n\tmov\t(z[0-9]+\.s), w2\n\tinsr\t\2, w1\n\tinsr\t\2, w0\n\tzip1\t\2, \2, \1} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 5.5: Interleaved repeating elements and trailing same elements. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** mov (z[0-9]+\.s), w1
+** insr \1, w0
+** mov (z[0-9]+\.s), w2
+** zip1 \1, \1, \2
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b, int f)
{
return (vnx4si) { a, f, b, f, b, f, b, f };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- mov z0.s, w1
- insr z0.s, w0
- mov z1.s, w2
- zip1 z0.s, z0.s, z1.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tmov\t(z[0-9]+\.s), w1\n\tinsr\t\1, w0\n\tmov\t(z[0-9]+\.s), w2\n\tzip1\t\1, \1, \2} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 5.5: Interleaved repeating elements and trailing same elements. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** mov (z[0-9]+\.s), w2
+** mov (z[0-9]+\.s), w0
+** insr \2, w1
+** insr \2, w1
+** insr \2, w1
+** zip1 \2, \2, \1
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b, int f)
{
return (vnx4si) { b, f, b, f, b, f, a, f };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- mov z1.s, w2
- mov z0.s, w0
- insr z0.s, w1
- insr z0.s, w1
- insr z0.s, w1
- zip1 z0.s, z0.s, z1.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tmov\t(z[0-9]+\.s), w2\n\tmov\t(z[0-9]+\.s), w0\n\tinsr\t\2, w1\n\tinsr\t\2, w1\n\tinsr\t\2, w1\n\tzip1\t\2, \2, \1} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 1.2: Trailing constants with repeating sequence. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** ...
+** ld1w (z[0-9]+\.s), p[0-9]+/z, \[x[0-9]+\]
+** insr \1, w1
+** insr \1, w0
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b)
{
return (vnx4si) { a, b, 2, 3, 2, 3, 2, 3 };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- ptrue p0.s, vl8
- adrp x2, .LANCHOR0
- add x2, x2, :lo12:.LANCHOR0
- ld1w z0.s, p0/z, [x2]
- insr z0.s, w1
- insr z0.s, w0
- ret
-*/
-
-/* { dg-final { scan-assembler {\tld1w\t(z[0-9]+\.s), p[0-9]+/z, \[x[0-9]+\]\n\tinsr\t\1, w1\n\tinsr\t\1, w0} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 2.1: Leading constants with stepped sequence. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** index (z[0-9]+\.s), #6, #-1
+** insr \1, w0
+** insr \1, w1
+** rev \1, \1
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b)
{
return (vnx4si) { 1, 2, 3, 4, 5, 6, a, b };
}
-/*
-foo:
-.LFB0:
- .cfi_startproc
- index z0.s, #6, #-1
- insr z0.s, w0
- insr z0.s, w1
- rev z0.s, z0.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tindex\t(z[0-9]+\.s), #6, #-1\n\tinsr\t\1, w0\n\tinsr\t\1, w1\n\trev\t\1, \1} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 2.2: Leading constants with stepped sequence. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** ...
+** ld1w (z[0-9]+\.s), p[0-9]+/z, \[x[0-9]+\]
+** insr \1, w1
+** insr \1, w0
+** rev \1, \1
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b)
{
return (vnx4si) { 3, 2, 3, 2, 3, 2, b, a };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- ptrue p0.s, vl8
- adrp x2, .LANCHOR0
- add x2, x2, :lo12:.LANCHOR0
- ld1w z0.s, p0/z, [x2]
- insr z0.s, w1
- insr z0.s, w0
- rev z0.s, z0.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tld1w\t(z[0-9]+\.s), p[0-9]+/z, \[x[0-9]+\]\n\tinsr\t\1, w1\n\tinsr\t\1, w0\n\trev\t\1, \1} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 3: Trailing same element. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** mov (z[0-9]+\.s), w2
+** insr \1, w1
+** insr \1, w0
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b, int c)
{
return (vnx4si) { a, b, c, c, c, c, c, c };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- mov z0.s, w2
- insr z0.s, w1
- insr z0.s, w0
- ret
-*/
-
-/* { dg-final { scan-assembler {\tmov\t(z[0-9]+\.s), w2\n\tinsr\t\1, w1\n\tinsr\t\1, w0} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 3: Trailing same element. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** mov (z[0-9]+\.s), w2
+** insr \1, w1
+** insr \1, w0
+** rev \1, \1
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b, int c)
{
return (vnx4si) { c, c, c, c, c, c, b, a };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- mov z0.s, w2
- insr z0.s, w1
- insr z0.s, w0
- rev z0.s, z0.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tmov\t(z[0-9]+\.s), w2\n\tinsr\t\1, w1\n\tinsr\t\1, w0\n\trev\t\1, \1} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 5.1: All elements. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** mov (z[0-9]+\.s), w7
+** insr \1, w6
+** insr \1, w5
+** insr \1, w4
+** insr \1, w3
+** insr \1, w2
+** insr \1, w1
+** insr \1, w0
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b, int c, int d, int e, int f, int g, int h)
{
return (vnx4si) { a, b, c, d, e, f, g, h };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- mov z0.s, w7
- insr z0.s, w6
- insr z0.s, w5
- insr z0.s, w4
- insr z0.s, w3
- insr z0.s, w2
- insr z0.s, w1
- insr z0.s, w0
- ret
-*/
-
-/* { dg-final { scan-assembler {\tmov\t(z[0-9]+\.s), w7\n\tinsr\t\1, w6\n\tinsr\t\1, w5\n\tinsr\t\1, w4\n\tinsr\t\1, w3\n\tinsr\t\1, w2\n\tinsr\t\1, w1\n\tinsr\t\1, w0} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 5.2: Interleaved elements and constants. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** ...
+** ld1w (z[0-9]+\.s), p[0-9]+/z, \[x[0-9]+\]
+** mov (z[0-9]+\.s), w3
+** insr \2, w2
+** insr \2, w1
+** insr \2, w0
+** zip1 \2, \2, \1
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b, int c, int d)
{
return (vnx4si) { a, 1, b, 2, c, 3, d, 4 };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- ptrue p0.s, vl8
- adrp x4, .LANCHOR0
- add x4, x4, :lo12:.LANCHOR0
- ld1w z1.s, p0/z, [x4]
- mov z0.s, w3
- insr z0.s, w2
- insr z0.s, w1
- insr z0.s, w0
- zip1 z0.s, z0.s, z1.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tld1w\t(z[0-9]+\.s), p[0-9]+/z, \[x[0-9]+\]\n\tmov\t(z[0-9]+\.s), w3\n\tinsr\t\2, w2\n\tinsr\t\2, w1\n\tinsr\t\2, w0\n\tzip1\t\2, \2, \1} } } */
/* { dg-do assemble { target aarch64_asm_sve_ok } } */
/* { dg-options "-O -msve-vector-bits=256 --save-temps" } */
+/* { dg-final { check-function-bodies "**" "" } } */
/* Case 5.3: Repeated elements. */
typedef int32_t vnx4si __attribute__((vector_size (32)));
+/*
+** foo:
+** mov (z[0-9]+\.s), w0
+** mov (z[0-9]+\.s), w1
+** zip1 \1, \1, \2
+** ...
+*/
__attribute__((noipa))
vnx4si foo(int a, int b)
{
return (vnx4si) { a, b, a, b, a, b, a, b };
}
-
-/*
-foo:
-.LFB0:
- .cfi_startproc
- mov z0.s, w0
- mov z1.s, w1
- zip1 z0.s, z0.s, z1.s
- ret
-*/
-
-/* { dg-final { scan-assembler {\tmov\t(z[0-9]+\.s), w0\n\tmov\t(z[0-9]+\.s), w1\n\tzip1\t\1, \1, \2} } } */
verbose "output_file: $output_file"
dg-scan "scan-lto-assembler" 1 $testcase $output_file $args
}
+
+# Read assembly file FILENAME and store a mapping from function names
+# to function bodies in array RESULT. FILENAME has already been uploaded
+# locally where necessary and is known to exist.
+
+proc parse_function_bodies { filename result } {
+ upvar $result up_result
+
+ # Regexp for the start of a function definition (name in \1).
+ set label {^([a-zA-Z_]\S+):$}
+
+ # Regexp for the end of a function definition.
+ set terminator {^\s*\.size}
+
+ # Regexp for lines that aren't interesting.
+ set fluff {^\s*(?:\.|//)}
+
+ set fd [open $filename r]
+ set in_function 0
+ while { [gets $fd line] >= 0 } {
+ if { [regexp $label $line dummy function_name] } {
+ set in_function 1
+ set function_body ""
+ } elseif { $in_function } {
+ if { [regexp $terminator $line] } {
+ set up_result($function_name) $function_body
+ set in_function 0
+ } elseif { ![regexp $fluff $line] } {
+ append function_body $line "\n"
+ }
+ }
+ }
+ close $fd
+}
+
+# FUNCTIONS is an array that maps function names to function bodies.
+# Return true if it contains a definition of function NAME and if
+# that definition matches BODY_REGEXP.
+
+proc check_function_body { functions name body_regexp } {
+ upvar $functions up_functions
+
+ if { ![info exists up_functions($name)] } {
+ return 0
+ }
+ return [regexp "^$body_regexp\$" $up_functions($name)]
+}
+
+# Check the implementations of functions against expected output. Used as:
+#
+# { dg-do { check-function-bodies PREFIX TERMINATOR[ OPTION] } }
+#
+# See sourcebuild.texi for details.
+
+proc check-function-bodies { args } {
+ if { [llength $args] < 2 } {
+ error "too few arguments to check-function-bodies"
+ }
+ if { [llength $args] > 3 } {
+ error "too many arguments to check-function-bodies"
+ }
+
+ if { [llength $args] == 3 } {
+ set required_flag [lindex $args 2]
+
+ upvar 2 dg-extra-tool-flags extra_tool_flags
+ set flags $extra_tool_flags
+
+ global torture_current_flags
+ if { [info exists torture_current_flags] } {
+ append flags " " $torture_current_flags
+ }
+ if { ![regexp " $required_flag " $flags] } {
+ return
+ }
+ }
+
+ set testcase [testname-for-summary]
+ # The name might include a list of options; extract the file name.
+ set filename [lindex $testcase 0]
+
+ global srcdir
+ set input_filename "$srcdir/$filename"
+ set output_filename "[file rootname [file tail $filename]].s"
+
+ set prefix [lindex $args 0]
+ set prefix_len [string length $prefix]
+ set terminator [lindex $args 1]
+ if { [string equal $terminator ""] } {
+ set terminator "*/"
+ }
+ set terminator_len [string length $terminator]
+
+ set have_bodies 0
+ if { [is_remote host] } {
+ remote_upload host "$filename"
+ }
+ if { [file exists $output_filename] } {
+ parse_function_bodies $output_filename functions
+ set have_bodies 1
+ } else {
+ verbose -log "$testcase: output file does not exist"
+ }
+
+ set count 0
+ set function_regexp ""
+ set label {^(\S+):$}
+
+ set lineno 1
+ set fd [open $input_filename r]
+ set in_function 0
+ while { [gets $fd line] >= 0 } {
+ if { [string equal -length $prefix_len $line $prefix] } {
+ set line [string trim [string range $line $prefix_len end]]
+ if { !$in_function } {
+ if { [regexp "^(.*\\S)\\s+{(.*)}\$" $line dummy \
+ line selector] } {
+ set selector [dg-process-target $selector]
+ } else {
+ set selector "P"
+ }
+ if { ![regexp $label $line dummy function_name] } {
+ close $fd
+ error "check-function-bodies: line $lineno does not have a function label"
+ }
+ set in_function 1
+ set function_regexp ""
+ } elseif { [string equal $line "("] } {
+ append function_regexp "(?:"
+ } elseif { [string equal $line "|"] } {
+ append function_regexp "|"
+ } elseif { [string equal $line ")"] } {
+ append function_regexp ")"
+ } elseif { [string equal $line "..."] } {
+ append function_regexp ".*"
+ } else {
+ append function_regexp "\t" $line "\n"
+ }
+ } elseif { [string equal -length $terminator_len $line $terminator] } {
+ if { ![string equal $selector "N"] } {
+ if { [string equal $selector "F"] } {
+ setup_xfail "*-*-*"
+ }
+ set testname "$testcase check-function-bodies $function_name"
+ if { !$have_bodies } {
+ unresolved $testname
+ } elseif { [check_function_body functions $function_name \
+ $function_regexp] } {
+ pass $testname
+ } else {
+ fail $testname
+ }
+ }
+ set in_function 0
+ incr count
+ }
+ incr lineno
+ }
+ close $fd
+ if { $in_function } {
+ error "check-function-bodies: missing \"$terminator\""
+ }
+ if { $count == 0 } {
+ error "check-function-bodies: no matches found"
+ }
+}