summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohan Tibell <johan.tibell@gmail.com>2011-05-31 11:56:58 +0200
committerSimon Marlow <marlowsd@gmail.com>2012-10-09 16:36:31 +0100
commit5ec6ea3e8f17f993f6a64e5220e9b31b2e23f8bd (patch)
tree7135a106b5281a159d3e0a9a9fbbb3fdc5230ca5
parent5f48b24b303326599147223c64b10b0cfb016a48 (diff)
downloadhaskell-5ec6ea3e8f17f993f6a64e5220e9b31b2e23f8bd.tar.gz
Make it possible to test the generated assembly
This test framework feature is inspired by a similar feature in LLVM. The programmer writes a bit of Cmm #include "Cmm.h" // Large memcpy's should lower to calls. callMemcpy { W_ dst, src; prim %memcpy(dst "ptr", src "ptr", 1024, 4) []; } and asserts what the generated assembly should look like, modulo register naming. callMemcpy: movq ; Move arguments into place movq movl movl call memcpy Patch edited and updated by Simon Marlow, and I also added a test for unrolling memcpy and a simple constant-propagation test.
-rw-r--r--testsuite/driver/testlib.py54
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/Makefile3
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/all.T3
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.asm21
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.cmm14
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.asm18
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.cmm8
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/memcpy.asm9
-rw-r--r--testsuite/tests/codeGen/should_gen_asm/memcpy.cmm8
9 files changed, 137 insertions, 1 deletions
diff --git a/testsuite/driver/testlib.py b/testsuite/driver/testlib.py
index 5853413871..4531801bb4 100644
--- a/testsuite/driver/testlib.py
+++ b/testsuite/driver/testlib.py
@@ -329,6 +329,12 @@ def if_arch( arch, f ):
else:
return normal
+def unless_arch( arch, f ):
+ if config.arch == arch:
+ return normal
+ else:
+ return f
+
def if_wordsize( ws, f ):
if config.wordsize == str(ws):
return f
@@ -941,6 +947,33 @@ def do_compile( name, way, should_fail, top_mod, extra_mods, extra_hc_opts ):
# no problems found, this test passed
return passed()
+def compile_cmp_asm( name, way, extra_hc_opts ):
+ print 'Compile only, extra args = ', extra_hc_opts
+ pretest_cleanup(name)
+ result = simple_build( name + '.cmm', way, '-keep-s-files -O ' + extra_hc_opts, 0, '', 0, 0, 0)
+
+ if badResult(result):
+ return result
+
+ # the actual stderr should always match the expected, regardless
+ # of whether we expected the compilation to fail or not (successful
+ # compilations may generate warnings).
+
+ if getTestOpts().with_namebase == None:
+ namebase = name
+ else:
+ namebase = getTestOpts().with_namebase
+
+ (platform_specific, expected_asm_file) = platform_wordsize_qualify(namebase, 'asm')
+ actual_asm_file = qualify(name, 's')
+
+ if not compare_outputs('asm', two_normalisers(normalise_errmsg, normalise_asm), \
+ expected_asm_file, actual_asm_file):
+ return failBecause('asm mismatch')
+
+ # no problems found, this test passed
+ return passed()
+
# -----------------------------------------------------------------------------
# Compile-and-run tests
@@ -1674,6 +1707,26 @@ def normalise_output( str ):
str = re.sub('([^\\s])\\.exe', '\\1', str)
return str
+def normalise_asm( str ):
+ lines = str.split('\n')
+ # Only keep instructions and labels not starting with a dot.
+ metadata = re.compile('^[ \t]*\\..*$')
+ out = []
+ for line in lines:
+ # Drop metadata directives (e.g. ".type")
+ if not metadata.match(line):
+ instr = line.lstrip().split()
+ # Drop empty lines.
+ if not instr:
+ continue
+ # Drop operands, except for call instructions.
+ elif instr[0] == 'call':
+ out.append(instr[0] + ' ' + instr[1])
+ else:
+ out.append(instr[0])
+ out = '\n'.join(out)
+ return out
+
def if_verbose( n, str ):
if config.verbose >= n:
print str
@@ -2118,4 +2171,3 @@ def getStdout(cmd):
return stdout
else:
raise Exception("Need subprocess to get stdout, but don't have it")
-
diff --git a/testsuite/tests/codeGen/should_gen_asm/Makefile b/testsuite/tests/codeGen/should_gen_asm/Makefile
new file mode 100644
index 0000000000..9101fbd40a
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/Makefile
@@ -0,0 +1,3 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
diff --git a/testsuite/tests/codeGen/should_gen_asm/all.T b/testsuite/tests/codeGen/should_gen_asm/all.T
new file mode 100644
index 0000000000..c262255342
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/all.T
@@ -0,0 +1,3 @@
+test('memcpy', unless_arch('x86_64',skip), compile_cmp_asm, [''])
+test('memcpy-unroll', unless_arch('x86_64',skip), compile_cmp_asm, [''])
+test('memcpy-unroll-conprop', unless_arch('x86_64',skip), compile_cmp_asm, [''])
diff --git a/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.asm b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.asm
new file mode 100644
index 0000000000..1bafb34ce9
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.asm
@@ -0,0 +1,21 @@
+.text
+ .align 8
+.globl callMemcpy
+.type callMemcpy, @object
+callMemcpy:
+.Lc8:
+ testq %rbx,%rbx
+ je .Lcb
+ movl 0(%r14),%eax
+ movl %eax,0(%rbx)
+ movl 4(%r14),%eax
+ movl %eax,4(%rbx)
+ movl 8(%r14),%eax
+ movl %eax,8(%rbx)
+ movl 12(%r14),%eax
+ movl %eax,12(%rbx)
+.Lcb:
+ jmp *(%rbp)
+ .size callMemcpy, .-callMemcpy
+.section .note.GNU-stack,"",@progbits
+.ident "GHC 7.7.20121009"
diff --git a/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.cmm b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.cmm
new file mode 100644
index 0000000000..be4883d34f
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll-conprop.cmm
@@ -0,0 +1,14 @@
+#include "Cmm.h"
+
+// Check that we propagate the constants into the branch
+callMemcpy (W_ dst, W_ src)
+{
+ W_ size;
+ W_ alig;
+ size = 16;
+ alig = 4;
+ if (dst != 0) {
+ prim %memcpy(dst, src, size, alig);
+ }
+ return ();
+}
diff --git a/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.asm b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.asm
new file mode 100644
index 0000000000..ffb27e70b7
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.asm
@@ -0,0 +1,18 @@
+.text
+ .align 8
+.globl callMemcpy
+.type callMemcpy, @object
+callMemcpy:
+.Lc3:
+ movl 0(%r14),%eax
+ movl %eax,0(%rbx)
+ movl 4(%r14),%eax
+ movl %eax,4(%rbx)
+ movl 8(%r14),%eax
+ movl %eax,8(%rbx)
+ movl 12(%r14),%eax
+ movl %eax,12(%rbx)
+ jmp *(%rbp)
+ .size callMemcpy, .-callMemcpy
+.section .note.GNU-stack,"",@progbits
+.ident "GHC 7.7.20121009"
diff --git a/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.cmm b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.cmm
new file mode 100644
index 0000000000..95656b4410
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/memcpy-unroll.cmm
@@ -0,0 +1,8 @@
+#include "Cmm.h"
+
+// Small memcpies should unroll
+callMemcpy (W_ dst, W_ src)
+{
+ prim %memcpy(dst, src, 16, 4);
+ return ();
+}
diff --git a/testsuite/tests/codeGen/should_gen_asm/memcpy.asm b/testsuite/tests/codeGen/should_gen_asm/memcpy.asm
new file mode 100644
index 0000000000..1fb604fc43
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/memcpy.asm
@@ -0,0 +1,9 @@
+callMemcpy:
+ movq ; Move arguments into place
+ movq
+ movl
+ subq
+ movl
+ call memcpy
+ addq
+ jmp
diff --git a/testsuite/tests/codeGen/should_gen_asm/memcpy.cmm b/testsuite/tests/codeGen/should_gen_asm/memcpy.cmm
new file mode 100644
index 0000000000..0ff16639ef
--- /dev/null
+++ b/testsuite/tests/codeGen/should_gen_asm/memcpy.cmm
@@ -0,0 +1,8 @@
+#include "Cmm.h"
+
+// Large memcpy's should lower to calls.
+callMemcpy (W_ dst, W_ src)
+{
+ prim %memcpy(dst, src, 1024, 4);
+ return ();
+}