diff options
author | Johan Tibell <johan.tibell@gmail.com> | 2011-05-31 11:56:58 +0200 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2012-10-09 16:36:31 +0100 |
commit | 5ec6ea3e8f17f993f6a64e5220e9b31b2e23f8bd (patch) | |
tree | 7135a106b5281a159d3e0a9a9fbbb3fdc5230ca5 | |
parent | 5f48b24b303326599147223c64b10b0cfb016a48 (diff) | |
download | haskell-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.
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 (); +} |