diff options
author | Moritz Angermann <moritz.angermann@gmail.com> | 2023-05-01 02:51:27 +0000 |
---|---|---|
committer | Sven Tennie <sven.tennie@wire.com> | 2023-05-01 16:43:15 +0000 |
commit | 8c2b36836376bfd69bd22662250fa59b368a5354 (patch) | |
tree | 213d6fa88c0d9ac4842484cb16fb32bb4dd66a80 | |
parent | 00a8a5ff9abf5bb1a0c2a9225c7bca5ec3bdf306 (diff) | |
download | haskell-8c2b36836376bfd69bd22662250fa59b368a5354.tar.gz |
Bring back old aarch64 test-suite
26 files changed, 847 insertions, 0 deletions
diff --git a/tests/compiler/cmm/README.md b/tests/compiler/cmm/README.md new file mode 100644 index 0000000000..bf4ca87cac --- /dev/null +++ b/tests/compiler/cmm/README.md @@ -0,0 +1,62 @@ +# The Cmm tests + +These tests are primarily targetted towards writing a codegeneration backend. The +`driver.sh` will pickup all `.cmm` files and run them by looking for the `RUN:` +marker in them. The commands will then be executed in sequence. + +A typical file will contain the following header: +``` +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +``` +this instructs the driver, to compie the cmm file `$1`; and pipe the assembly +output into the `FileCheck` tool from the llvm project, which allows us to write +basic assertions about the generated assembly. The `FileCheck` tool will scan +the file for `CHECK-A64:` marker, as well as `CHECK-A64-NEXT:` marker and verify +those in the output (see the `-check-prefix` argument to `FileCheck`). The +`FileCheck` tool has many more options, that might come in handy. + +Some files will also verify that they execute with a zero exit code, or print +some value to stdout. Again we can use the `RUN:` marker for this as the following +example demonstrates: +``` +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$CC" "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" +``` +this will not only compile the assembly, verify that some instructions are +contained, but also convert the produced object file into an executable using +a c compiler, and then run the executable. + +You will most likely also want to include the following: +``` +#include "Cmm.h" +#include "Types.h" +``` +to be able to copy the cmm dumps ghc produces more closely. Those will still +need quite a bit of munging, as the pretty-printer does not roundtrip. + +## Environment Variables: + +- `HC` the haskell compiler to use. +- `CC` the c compiler to use, should be targetting the same architecture as `HC`. +- `EXEC` if we are cross compiling, we might need a tool to execute the foreign + executable. E.g. `wine` or `qemu-<arch>`. + +That is, to execute the whole test-suite (when cross compiling) you may run +``` +EXEC=qemu-aarch64 CC="$TARGET_CC" bash ./driver.sh +``` + +## Missing features + +- [ ] Run selective tests. +- [ ] Pipe command output into log, and cat log on failure. +- [x] Better reporting on which command failed if multiple commands are given. +- [ ] RUN commands should have pipeset fail set. + +## Cmm ppr/parser bugs +- [ ] [Parser] Cannot assing to a fixed value (F1) = ... fails to parse +- [ ] [Parser] F1 + F2, will produce MO_Add F1 F2 instead of MO_F_Add F1 F2 +- [ ] [Parser] Can not construct f(y(x)), the parser will turn that into z = y(x); f(z). +- [ ] [Ppr] keeps pring types all over the place even though the parser doesn't allow for those. +- [ ] [Ppr] will print MO_Add, ... instead of the symbols or primitives understood by the parser.
\ No newline at end of file diff --git a/tests/compiler/cmm/Types.h b/tests/compiler/cmm/Types.h new file mode 100644 index 0000000000..173d659fb0 --- /dev/null +++ b/tests/compiler/cmm/Types.h @@ -0,0 +1,4 @@ +#define F32 float32 +#define F64 float64 +#define W32 bits32 +#define W64 bits64 diff --git a/tests/compiler/cmm/assert.cmm b/tests/compiler/cmm/assert.cmm new file mode 100644 index 0000000000..6288e9b198 --- /dev/null +++ b/tests/compiler/cmm/assert.cmm @@ -0,0 +1,26 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// We need ghc to link this +// RUN: "$HC" -no-hs-main -debug "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" | FileCheck "$1" -check-prefix=CHECK-RUN-A64 + +#define DEBUG + +#include "Cmm.h" + +main () { + // CHECK-RUN-A64: Sp: 0; SpLim: 0 + foreign "C" printf("Sp: %x; SpLim: %x\n", Sp, SpLim); + + Sp = 0x105368; + SpLim = 0x1050c0; + + // XXX: This might just work with qemu, which provides predictable memory + // locations. + // CHECK-RUN-A64: Sp: 105368; SpLim: 1050c0 + foreign "C" printf("Sp: %x; SpLim: %x\n", Sp, SpLim); + + // CHECK-A64: x18, x18, :lo12:_assertFail + ASSERT(SpLim - WDS(RESERVED_STACK_WORDS) <= Sp); + + foreign "C" exit(0::I64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/driver.sh b/tests/compiler/cmm/driver.sh new file mode 100755 index 0000000000..c48cf9202b --- /dev/null +++ b/tests/compiler/cmm/driver.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# +# A simple test driver for the cmm tests. Using FileCheck, a little less +# sofisticated than the LLVM regression test driver. + +# If __PRINT=echo, we'll see the output. +__PRINT="" + +function run_test() { + bash <<CODE +function _run () { + $__PRINT $1 +} +_run $2 +CODE +} + +# Colors! See https://stackoverflow.com/a/5947802 +# .---------- constant part! +# vvvv vvvv-- the code from above +RED='\033[0;31m' +GREEN='\033[0;32m' +GRAY='\033[1;30m' +NC='\033[0m' # No Color + +for cmm in *.cmm; do + if grep "RUN: " $cmm > /dev/null ; then + cmds=$(grep "RUN: " $cmm |sed "s|.*RUN: ||g") + if run_test "$cmds" "$cmm" > /dev/null 2>&1 ; then + echo -e "[${GREEN}\u2713${NC}] $cmm ${GREEN}succeded${NC}" + rm -f "${cmm%%.*}.s" "${cmm%%.*}.o" "${cmm%%.*}.exe" "${cmm%.*}.c" "${cmm%%.*}_stub.o" + else + echo -e "[${RED}\u2717${NC}] $cmm ${RED}failed${NC}" + IFS=$'\n'; arrCmds=($cmds); unset IFS; + for cmd in "${arrCmds[@]}"; do + expandedCmd=$(__PRINT=echo run_test "\"$cmd\"" "$cmm") + if run_test "$cmd" "$cmm" > /dev/null 2>&1 ; then + echo -e "\t[${GREEN}\u2713${NC}] $expandedCmd" + else + echo -e "\t[${RED}\u2717${NC}] $expandedCmd" + fi + done + fi + else + echo -e "[${GRAY}?${NC}] $cmm ${GRAY}has no RUN: marker${NC}" + fi +done
\ No newline at end of file diff --git a/tests/compiler/cmm/encode_values.cmm b/tests/compiler/cmm/encode_values.cmm new file mode 100644 index 0000000000..ef17612d1c --- /dev/null +++ b/tests/compiler/cmm/encode_values.cmm @@ -0,0 +1,30 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$CC" "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" | FileCheck "$1" -check-prefix=CHECK-RUN-A64 + +#include "Cmm.h" +#include "Types.h" + +main () { + R1 = 123454321; + F1 = 0.12345::F32; + D1 = 3.123456789123456789; + D2 = 1.234567891234567890; + + // Let's see that the c string ends up as a symbol + // CHECK-A64: c1_str: + // CHECK-A64-NEXT: .string "%d\n%1.10f\n%.20f\n%.20f\n" + + // CHECK-RUN-A64: 123454321 + // The following is WRONG. We need to promote Float to Double when + // calling a C function. However for now, and for compatibility, we + // don't promote. + + // CHECK-RUN-A64-NEXT: 0.0000000000 + // CHECK-RUN-A64-NEXT: 3.12345678912345681155 + // CHECK-RUN-A64-NEXT: 1.23456789123456789348 + + foreign "C" printf("%d\n%1.10f\n%.20f\n%.20f\n", R1, F1, D1, D2); + + foreign "C" exit(0::I64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/immideate.cmm b/tests/compiler/cmm/immideate.cmm new file mode 100644 index 0000000000..37ca20c8ed --- /dev/null +++ b/tests/compiler/cmm/immideate.cmm @@ -0,0 +1,65 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$HC" -no-hs-main "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" | FileCheck "$1" -check-prefix=CHECK-RUN-A64 + +#include "Cmm.h" + +// Too bad this won't work. We can't construct Cmm Immediates. +// Often ghc will just optimize it away. + +// CHECK-A64: .globl main +// CHECK-A64: main: +main () { + + // CHECK-A64: mov w22, #1 + // CHECK-RUN-A64: 1 = 1 + R1 = 1 :: I64; foreign "C" printf("1 = %ld\n", R1); + + // CHECK-A64: mov w22, #-1 + // CHECK-RUN-A64: -1 = -1 + R1 = -1; foreign "C" printf("-1 = %ld\n", R1); + + // CHECK-A64: mov w22, #4096 + // CHECK-RUN-A64: 4096 = 4096 + R1 = 1 << 12 :: I64; foreign "C" printf("4096 = %ld\n", R1); + + // CHECK-A64: mov w22, #-4096 + // CHECK-RUN-A64: -4096 = -4096 + R1 = -1 << 12 :: I64; foreign "C" printf("-4096 = %ld\n", R1); + + // CHECK-A64: mov w22, #0 + // CHECK-A64-NEXT: movk w22, #1, lsl #16 + // CHECK-RUN-A64: 65536 = 65536 + R1 = 1 << 16 :: I64; foreign "C" printf("65536 = %ld\n", R1); + + // CHECK-A64: mov w22, #0 + // CHECK-A64-NEXT: movk w22, #65535, lsl #16 + // CHECK-RUN-A64: -65536 = -65536 + R1 = -1 << 16 :: I64; foreign "C" printf("-65536 = %ld\n", R1); + + // CHECK-A64: mov w22, #0 + // CHECK-A64-NEXT: movk w22, #256, lsl #16 + // CHECK-RUN-A64: 16777216 = 16777216 + R1 = 1 << 24 :: I64; foreign "C" printf("16777216 = %ld\n", R1); + + // CHECK-A64: mov w22, #0 + // CHECK-A64-NEXT: movk w22, #65280, lsl #16 + // CHECK-RUN-A64: -16777216 = -16777216 + R1 = -1 << 24 :: I64; foreign "C" printf("-16777216 = %ld\n", R1); + + // CHECK-A64: mov x22, #0 + // CHECK-A64-NEXT: movk x22, #0, lsl #16 + // CHECK-A64-NEXT: movk x22, #16, lsl #32 + // CHECK-A64-NEXT: movk x22, #0, lsl #48 + // CHECK-RUN-A64: 68719476736 = 68719476736 + R1 = 1 << 36 :: I64; foreign "C" printf("68719476736 = %ld\n", R1); + + // CHECK-A64: mov x22, #0 + // CHECK-A64-NEXT: movk x22, #0, lsl #16 + // CHECK-A64-NEXT: movk x22, #65520, lsl #32 + // CHECK-A64-NEXT: movk x22, #65535, lsl #48 + // CHECK-RUN-A64: -68719476736 = -68719476736 + R1 = -1 << 36 :: I64; foreign "C" printf("-68719476736 = %ld\n", R1); + + foreign "C" exit(0::I64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/load_large_immediate.cmm b/tests/compiler/cmm/load_large_immediate.cmm new file mode 100644 index 0000000000..42a4621324 --- /dev/null +++ b/tests/compiler/cmm/load_large_immediate.cmm @@ -0,0 +1,28 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +large() { + // CHECK-A64: mov x23, #52428 + // CHECK-A64-NEXT: movk x23, #52428, lsl #16 + // CHECK-A64-NEXT: movk x23, #52428, lsl #32 + // CHECK-A64-NEXT: movk x23, #3276, lsl #48 + R2 = 922337203685477580; + + // CHECK-A64: mov x23, #52429 + R2 = 922337203685477581; + + // CHECK-A64: mov x18, #11544 + // CHECK-A64-NEXT: movk x18, #21572, lsl #16 + // CHECK-A64-NEXT: movk x18, #8699, lsl #32 + // CHECK-A64-NEXT: movk x18, #16393, lsl #48 + // CHECK-A64-NEXT: fmov d12, x18 + D1 = 3.141592653589793; + + // CHECK-A64: mov x18, #11544 + // CHECK-A64-NEXT: movk x18, #21572, lsl #16 + // CHECK-A64-NEXT: movk x18, #8699, lsl #32 + // CHECK-A64-NEXT: movk x18, #16377, lsl #48 + // CHECK-A64-NEXT: fmov d12, x18 + D1 = 1.5707963267948966; +}
\ No newline at end of file diff --git a/tests/compiler/cmm/load_store.cmm b/tests/compiler/cmm/load_store.cmm new file mode 100644 index 0000000000..7b67c87a96 --- /dev/null +++ b/tests/compiler/cmm/load_store.cmm @@ -0,0 +1,9 @@ + +// XXX: todo +main () { + // test load_store + // write 64bit word, load lower 8bits, 16bits, 32bits, + // and ensure we get exactly what we asked for! + + // write byte, half, word, ... load 64bit, verify. +}
\ No newline at end of file diff --git a/tests/compiler/cmm/load_sym_with_large_offset.cmm b/tests/compiler/cmm/load_sym_with_large_offset.cmm new file mode 100644 index 0000000000..dd5298c429 --- /dev/null +++ b/tests/compiler/cmm/load_sym_with_large_offset.cmm @@ -0,0 +1,9 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 + +test () { + // CHECK-A64: mov w18, #4273 + // CHECK-A64: adrp x23, stg_INTLIKE_closure + // CHECK-A64: add x23, x23, :lo12:stg_INTLIKE_closure + // CHECK-A64: add x23, x23, x18 + R2 = stg_INTLIKE_closure+4273; +}
\ No newline at end of file diff --git a/tests/compiler/cmm/main.cmm b/tests/compiler/cmm/main.cmm new file mode 100644 index 0000000000..caad85845d --- /dev/null +++ b/tests/compiler/cmm/main.cmm @@ -0,0 +1,27 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$CC" "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" +/* + * This is a very basic cmm example. For a more in-depth discussion see + * GHC/Cmm/Parser.y. This file emulates a main function in Cmm. Note + * that we can't use return. This will most of the time require a StackSlot, + * which isn't implemented in most code gen backends. + * + * To compile: + * + * $HC -cpp -dcmm-lint -keep-s-file -c main.cmm + * $CC main.o -o main + * + * Note: We use $CC to compile, the reson is that this allows us to depend on + * the default crt0, ..., files; and we get functions like exit. + * + * Note2: The Cmm.h include (which requires the -cpp flag to $HC), this + * allows us to use I64, I32, ... + */ +#include "Cmm.h" + +// CHECK-A64: .type main, @function +// CHECK-A64-NEXT: main: +main ( I32 argc, I32 argv_ptr ) { + foreign "C" exit(0::I64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/mo_f_eq.cmm b/tests/compiler/cmm/mo_f_eq.cmm new file mode 100644 index 0000000000..d217e13e6b --- /dev/null +++ b/tests/compiler/cmm/mo_f_eq.cmm @@ -0,0 +1,10 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +w64 () { + // CHECK-A64: fmov d31, xzr + // CHECK-A64-NEXT: fcmp d12, d31 + // CHECK-A64-NEXT: cset x22, eq + R1 = %feq(D1, 0.0 :: W64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/mo_f_gt.cmm b/tests/compiler/cmm/mo_f_gt.cmm new file mode 100644 index 0000000000..fcb37261ea --- /dev/null +++ b/tests/compiler/cmm/mo_f_gt.cmm @@ -0,0 +1,15 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 + +#include "Cmm.h" +#include "Types.h" + +w64 () { + // CHECK-A64: fmov d31, xzr + R1 = %fgt(D1, 0.0 :: W64); + + if (%fgt(D1, 0.0 :: W64)) { + R2 = 1 :: W64; + } else { + R2 = 0 :: W64; + } +}
\ No newline at end of file diff --git a/tests/compiler/cmm/mo_f_neg.cmm b/tests/compiler/cmm/mo_f_neg.cmm new file mode 100644 index 0000000000..0aeab5ee85 --- /dev/null +++ b/tests/compiler/cmm/mo_f_neg.cmm @@ -0,0 +1,14 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +f_neg_same_reg () { + // CHECK-A64: neg x22, x22 + R1 = %neg(R1); + + // CHECK-A64: fneg s8, s8 + F1 = %fneg(F1); + + // CHECK-A64: fneg d12, d12 + D1 = %fneg(D1); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/mo_ff_conv.cmm b/tests/compiler/cmm/mo_ff_conv.cmm new file mode 100644 index 0000000000..caef960411 --- /dev/null +++ b/tests/compiler/cmm/mo_ff_conv.cmm @@ -0,0 +1,12 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +w64 () { + F64 x; + F32 y; + // CHECK-A64: fmov d31, d12 + x = D1; + // CHECK-A64: fcvt s31, d31 + y = %f2f32(x); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/mo_uu_conv.cmm b/tests/compiler/cmm/mo_uu_conv.cmm new file mode 100644 index 0000000000..5af2b08a3f --- /dev/null +++ b/tests/compiler/cmm/mo_uu_conv.cmm @@ -0,0 +1,20 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -dppr-debug -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +w32_w64 () { + I64 m; + m = 1234 :: I64; + // lobits64 is MO_UU_Conv_W32_W64, when given a I32 + // zx32 is MO_UU_Conv_W64_W32 + // + // First we extract the lower 32 bits from x18 (32 bit value -> 64 bit value) + // CHECK-A64: ubfm x22, x18, #0, #31 + R1 = %lobits64(I32[m]); + + // then we extract the lower 32 bits from x22 (64 bit value -> 32 bit value) + // CHECK-A64-NEXT: ubfm x18, x22, #0, #31 + // and finally extend it again. + // CHECK-A64-NEXT: ubfm x23, x18, #0, #31 + R2 = %lobits64(%zx32(R1)); +} diff --git a/tests/compiler/cmm/nan.cmm b/tests/compiler/cmm/nan.cmm new file mode 100644 index 0000000000..3579387f00 --- /dev/null +++ b/tests/compiler/cmm/nan.cmm @@ -0,0 +1,51 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$HC" -no-hs-main "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" | FileCheck "$1" -check-prefix=CHECK-RUN-A64 + +#include "Cmm.h" +#include "Types.h" + +// CHECK-A64: .globl main +// CHECK-A64: main: + +main () { + F64 y; + I64 z; + + D1 = 0.0 - 7.0; + + (y) = foreign "C" sqrt(D1); + + (z) = foreign "C" isDoubleNaN(y); + + // CHECK-RUN-A64: sqrt(-7.000000) = nan + // CHECK-RUN-A64: isDoubleNaN(nan) = 1 + foreign "C" printf("sqrt(%f) = %f\n", D1, y); + foreign "C" printf("isDoubleNaN(%f) = %d\n", y, z); + + // CHECK-RUN-A64: nan < 0 = 0 + foreign "C" printf("%f < 0 = %d\n", y, y `flt` 0.0); + // CHECK-RUN-A64: nan > 0 = 0 + foreign "C" printf("%f > 0 = %d\n", y, y `fgt` 0.0); + // CHECK-RUN-A64: nan <= 0 = 0 + foreign "C" printf("%f <= 0 = %d\n", y, y `fle` 0.0); + // CHECK-RUN-A64: nan >= 0 = 0 + foreign "C" printf("%f >= 0 = %d\n", y, y `fge` 0.0); + // CHECK-RUN-A64: nan == 0 = 0 + foreign "C" printf("%f == 0 = %d\n", y, y `feq` 0.0); + // CHECK-RUN-A64: nan /= 0 = 1 + foreign "C" printf("%f /= 0 = %d\n", y, y `fne` 0.0); + + F64 x; + x = 0.0; y = 0.0; + foreign "C" printf("%f / %f = %d\n", x, y, x `fquot` y); + + // CHECK-RUN-A64: nan >= 0 + if((x `fquot` y) < x) { + foreign "C" printf("%f < %f\n", x `fquot` y, x); + } else { + foreign "C" printf("%f >= %f\n", x `fquot` y, x); + } + + foreign "C" exit(0::I64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/primops_math.cmm b/tests/compiler/cmm/primops_math.cmm new file mode 100644 index 0000000000..aaee94ec60 --- /dev/null +++ b/tests/compiler/cmm/primops_math.cmm @@ -0,0 +1,111 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$HC" -no-hs-main "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" | FileCheck "$1" -check-prefix=CHECK-RUN-A64 + +#include "Cmm.h" + +// TODO: most of the unsigned test don't really test unsigned. + +// CHECK-A64: .globl main +// CHECK-A64: main: +main () { + R1 = 7; R2 = 3; + R4 = -7; R5 = -3; + + // CHECK-RUN-A64: 7 + 3 => 10 + R3 = R1 `add` R2; + foreign "C" printf("%d + %d => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: 7 - 3 => 4 + R3 = R1 `sub` R2; + foreign "C" printf("%d - %d => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: 7 = 3 => 0 + R3 = R1 `eq` R2; + foreign "C" printf("%d = %d => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: 7 * 3 => 21 + R3 = R1 `mul` R2; + foreign "C" printf("%d * %d => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: -(7+3) => -10 + R3 = %neg(R1+R2); + foreign "C" printf("-(%d+%d) => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: 7 / 3 => 2 + R3 = R1 `quot` R2; + foreign "C" printf("%d / %d => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: 7 % 3 => 1 + R3 = R1 `rem` R2; + foreign "C" printf("%d %% %d => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: 7 / 3 => 2 + R3 = R1 `divu` R2; + foreign "C" printf("(unsigned): %d / %d => %d\n", R1, R2, R3); + + // CHECK-RUN-A64: 7 % 3 => 1 + R3 = R1 `modu` R2; + foreign "C" printf("(unsigned): %d %% %d => %d\n", R1, R2, R3); + + // Signed >=, <=, >, < + // CHECK-RUN-A64: 7 >= 3 => 1 + R3 = R1 `ge` R2; + foreign "C" printf("%d >= %d => %d", R1, R2, R3); + + // CHECK-RUN-A64: 7 <= 3 => 0 + R3 = R1 `le` R2; + foreign "C" printf("%d <= %d => %d", R1, R2, R3); + + // CHECK-RUN-A64: 7 > 3 => 1 + R3 = R1 `gt` R2; + foreign "C" printf("%d > %d => %d", R1, R2, R3); + + // CHECK-RUN-A64: 7 < 3 => 0 + R3 = R1 `lt` R2; + foreign "C" printf("%d < %d => %d", R1, R2, R3); + + // Unsigned >=, <=, >, < + // CHECK-RUN-A64: 7 >= 3 => 1 + R3 = R1 `geu` R2; + foreign "C" printf("(unsigned): %d >= %d => %d", R1, R2, R3); + + // CHECK-RUN-A64: 7 <= 3 => 0 + R3 = R1 `leu` R2; + foreign "C" printf("(unsigned): %d <= %d => %d", R1, R2, R3); + + // CHECK-RUN-A64: 7 > 3 => 1 + R3 = R1 `gtu` R2; + foreign "C" printf("(unsigned): %d > %d => %d", R1, R2, R3); + + // CHECK-RUN-A64: 7 < 3 => 0 + R3 = R1 `ltu` R2; + foreign "C" printf("(unsigned): %d < %d => %d", R1, R2, R3); + + // Logical ops: and, or, xor, com, shl, shrl, shra + R3 = R1 `and` R2; + foreign "C" printf("%d and %d => %d", R1, R2, R3); + + R3 = R1 `or` R2; + foreign "C" printf("%d or %d => %d", R1, R2, R3); + + R3 = R1 `xor` R2; + foreign "C" printf("%d xor %d => %d", R1, R2, R3); + + // R3 = R1 `com` R2; + // foreign "C" printf("%d com %d => %d", R1, R2, R3); + + R3 = R1 `shl` R2; + foreign "C" printf("%d shl %d => %d", R1, R2, R3); + + R3 = R1 `shrl` R2; + foreign "C" printf("%d shrl %d => %d", R1, R2, R3); + + R3 = R1 `shra` R2; + foreign "C" printf("%d shra %d => %d", R1, R2, R3); + + + + + foreign "C" exit(0::I64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/shift_left.cmm b/tests/compiler/cmm/shift_left.cmm new file mode 100644 index 0000000000..15960970ad --- /dev/null +++ b/tests/compiler/cmm/shift_left.cmm @@ -0,0 +1,15 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +shift_left () { + I64 a, b, c, d, e, f; + a = 1; + b = 1; + c = 1; + d = 1; + // CHECK-A64: lsl x18, x18, x14 + // CHECK-A64: lsl x18, x18, x17 + // CHECK-A64: lsl x18, x18, x17 + R2 = (((((a << 1 | b) << 1) | c) << 1) | d); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/shift_left_conditional.cmm b/tests/compiler/cmm/shift_left_conditional.cmm new file mode 100644 index 0000000000..ac13cc8017 --- /dev/null +++ b/tests/compiler/cmm/shift_left_conditional.cmm @@ -0,0 +1,13 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +// Original failing cmm: +// +// R1 = I64[(%MO_F_Gt_W32(F32[Sp + 8], F32[R1 + 7]) << 3) + ghczmprim_GHCziTypes_Bool_closure_tbl]; // CmmAssign +// +shift_left_conditional () { + // CHECK-A64: fcmp s31, s30 + // CHECK-A64-NEXT: cset w18, gt + R1 = %fgt(F32[Sp], F32[R1]) << 3; +}
\ No newline at end of file diff --git a/tests/compiler/cmm/store_neg_float.cmm b/tests/compiler/cmm/store_neg_float.cmm new file mode 100644 index 0000000000..96bf7c5711 --- /dev/null +++ b/tests/compiler/cmm/store_neg_float.cmm @@ -0,0 +1,13 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +store_neg_float_on_heap () { + // CHECK-A64: fneg s31, s8 + // CHECK-A64-NEXT: str s31, [ x21 ] + F32[Hp] = %fneg(F1); + + // CHECK-A64: fneg d31, d12 + // CHECK-A64-NEXT: str d31, [ x21 ] + F64[Hp] = %fneg(D1); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/switch_expr.cmm b/tests/compiler/cmm/switch_expr.cmm new file mode 100644 index 0000000000..fe4babcb2a --- /dev/null +++ b/tests/compiler/cmm/switch_expr.cmm @@ -0,0 +1,52 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$CC" "${1%%.*}.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.cmm}.exe" + +#include "Cmm.h" +#include "Types.h" + + +main (I64 argc) { + _switch: // global + switch [0 .. 7] argc { + case 0 : goto _a0; + case 1 : goto _a1; + case 2 : goto _a2; + case 3 : goto _a3; + case 4 : goto _a4; + case 5 : goto _a5; + case 6 : goto _a6; + case 7 : goto _a7; + default: {goto _c;} + } // CmmSwitch + _a0: // global + foreign "C" printf("A0\n"); + goto _end; + _a1: // global + foreign "C" printf("A1\n"); + goto _end; + _a2: // global + foreign "C" printf("A2\n"); + goto _end; + _a3: // global + foreign "C" printf("A3\n"); + goto _end; + _a4: // global + foreign "C" printf("A4\n"); + goto _end; + _a5: // global + foreign "C" printf("A5\n"); + goto _end; + _a6: // global + foreign "C" printf("A6\n"); + goto _end; + _a7: // global + foreign "C" printf("A7\n"); + goto _end; + _c: // global + foreign "C" printf("C\n"); + goto _end; + + _end: + foreign "C" exit(0::I64); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/unsafe_foreign_calls.cmm b/tests/compiler/cmm/unsafe_foreign_calls.cmm new file mode 100644 index 0000000000..694c56d0c0 --- /dev/null +++ b/tests/compiler/cmm/unsafe_foreign_calls.cmm @@ -0,0 +1,34 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +// Original failing cmm: +// +// (_ckSs::F32) = call MO_F32_ExpM1(_shwV::F32); // CmmUnsafeForeignCall +// +// One issue with the cmm parser is, that we can't build the following expressions +// the parser will deconstruct them into arg0 <- arg0_expr; f(arg0); instead of f(arg0_expr) +// +// (_ckSz::F32) = call MO_F32_Log(%MO_F_Neg_W32(_ckSs::F32)); // CmmUnsafeForeignCall +// (_ckqE::F64) = call MO_F64_Log(2.0 :: W64); // CmmUnsafeForeignCall +// (_cjE1::F64) = call MO_F64_Atan(%MO_F_Quot_W64(_shFv::F64, _shFw::F64)); // CmmUnsafeForeignCall +// +// +foreign_calls () { + F32 x, y; + x = F1; + + // CHECK-A64: adrp x18, expm1f + // CHECK-A64: add x18, x18, :lo12:expm1f + // CHECK-A64: fmov s0, s30 + // CHECK-A64: blr x18 + (y) = prim %expM132f(F1); + // CHECK-A64: fmov s8, s30 + F1 = y; + F2 = y; + (y) = prim %log32f(%fneg(x)); + + prim %log64f(2.0); + + prim %atan64f(%fquot(D1,D2)); +}
\ No newline at end of file diff --git a/tests/compiler/cmm/unsafe_foreign_calls_excess_arguments.cmm b/tests/compiler/cmm/unsafe_foreign_calls_excess_arguments.cmm new file mode 100644 index 0000000000..f76c10e6eb --- /dev/null +++ b/tests/compiler/cmm/unsafe_foreign_calls_excess_arguments.cmm @@ -0,0 +1,36 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$LN" -sf "$1" "${1%%.*}.c" +// RUN: "$CC" -DC -c "${1%%.*}.c" -o "${1%%.*}_stub.o" +// RUN: "$CC" "${1%%.*}.o" "${1%%.*}_stub.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.*}.exe" + +#if defined(C) // the C part +int mega_add(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) { + return a + b + c + d + e + f + g + h + i + j + k; +} +#else // the CMM part +#include "Cmm.h" +#include "Types.h" + +// Let's ensure we can call functions with lots of arguments, and they are +// passed correctly. +main () { + + // create some fake stack space and assign to Sp. + I64 sp; + (sp) = foreign "C" malloc(1024); + Sp = sp; + + I64 x, y; + I64 a, b, c, d, e, f, g, h, i, j, k; + a = 1; b = 2; c = 4; d = 8; e = 16; f = 32; g = 64; h = 128; i = 256; j = 512; k = 1024; + + // We have 3 excess arguments; thus we need 4 slots (32 bytes) + // CHECK-A64: sub sp, sp, #32 + // CHECK-A64: add sp, sp, #32 + (y) = foreign "C" mega_add(a,b,c,d,e,f,g,h,i,j,k); + x = a + b + c + d + e + f + g + h + i + j + k; + + foreign "C" exit(y != x); +} +#endif diff --git a/tests/compiler/cmm/unsafe_foreign_calls_excess_float_arguments.cmm b/tests/compiler/cmm/unsafe_foreign_calls_excess_float_arguments.cmm new file mode 100644 index 0000000000..c82ee97075 --- /dev/null +++ b/tests/compiler/cmm/unsafe_foreign_calls_excess_float_arguments.cmm @@ -0,0 +1,37 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$LN" -sf "$1" "${1%%.*}.c" +// RUN: "$CC" -DC -c "${1%%.*}.c" -o "${1%%.*}_stub.o" +// RUN: "$CC" "${1%%.*}.o" "${1%%.*}_stub.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.*}.exe" + +#if defined(C) // the C part +double mega_fadd(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j, double k) { + return a + b + c + d + e + f + g + h + i + j + k; +} + +#else // the CMM part +#include "Cmm.h" +#include "Types.h" + +// Let's ensure we can call functions with lots of arguments, and they are +// passed correctly. +main () { + + // create some fake stack space and assign to Sp. + I64 sp; + (sp) = foreign "C" malloc(1024); + Sp = sp; + + F64 fx, fy; + F64 fa, fb, fc, fd, fe, ff, fg, fh, fi, fj, fk; + fa = 1.0; fb = 2.0; fc = 4.0; fd = 8.0; fe = 16.0; ff = 32.0; fg = 64.0; fh = 128.0; fi = 256.0; fj = 512.0; fk = 1024.0; + + // We have 3 excess arguments; thus we need 4 slots (32 bytes) + // CHECK-A64: sub sp, sp, #32 + // CHECK-A64: add sp, sp, #32 + (fy) = foreign "C" mega_fadd(fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk); + fx = fa + fb + fc + fd + fe + ff + fg + fh + fi + fj + fk; + + foreign "C" exit(fy != fx); +} +#endif diff --git a/tests/compiler/cmm/unsafe_foreign_calls_excess_mixed_arguments.cmm b/tests/compiler/cmm/unsafe_foreign_calls_excess_mixed_arguments.cmm new file mode 100644 index 0000000000..10e2966ab4 --- /dev/null +++ b/tests/compiler/cmm/unsafe_foreign_calls_excess_mixed_arguments.cmm @@ -0,0 +1,57 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +// RUN: "$LN" -sf "$1" "${1%%.*}.c" +// RUN: "$CC" -DC -c "${1%%.*}.c" -o "${1%%.*}_stub.o" +// RUN: "$CC" "${1%%.*}.o" "${1%%.*}_stub.o" -o "${1%%.*}.exe" +// RUN: "$EXEC" "${1%%.*}.exe" + +#if defined(C) // the C part +#include <stdlib.h> +#include <stdio.h> + +void *mega_mixed_add(int a, double fa, int b, double fb, int c, double fc, + int d, double fd, int e, double fe, int f, double ff, + int g, double fg, int h, double fh, int i, double fi, + int j, double fj, int k, double fk) { + + void *r = (void *)calloc(1,16); + *(int*)r = a + b + c + d + e + f + g + h + i + j + k; + *(double*)(r+8) = fa + fb + fc + fd + fe + ff + fg + fh + fi + fj + fk; + return r; +} + +#else // the CMM part +#include "Cmm.h" +#include "Types.h" + +// Let's ensure we can call functions with lots of arguments, and they are +// passed correctly. +main () { + + // create some fake stack space and assign to Sp. + I64 sp; + (sp) = foreign "C" malloc(1024); + Sp = sp; + + I64 x, y; + I64 a, b, c, d, e, f, g, h, i, j, k; + a = 1; b = 2; c = 4; d = 8; e = 16; f = 32; g = 64; h = 128; i = 256; j = 512; k = 1024; + + F64 fx, fy; + F64 fa, fb, fc, fd, fe, ff, fg, fh, fi, fj, fk; + fa = 1.0; fb = 2.0; fc = 4.0; fd = 8.0; fe = 16.0; ff = 32.0; fg = 64.0; fh = 128.0; fi = 256.0; fj = 512.0; fk = 1024.0; + + // We have 6 excess arguments; thus we need 6 slots (48 bytes) + // CHECK-A64: sub sp, sp, #48 + // CHECK-A64: add sp, sp, #48 + I64 rptr; + (rptr) = foreign "C" mega_mixed_add(a,fa,b,fb,c,fc,d,fd,e,fe,f,ff,g,fg,h,fh, i,fi,j,fj,k,fk); + + y = I64[rptr]; + fy = F64[rptr + 8]; + + x = a + b + c + d + e + f + g + h + i + j + k; + fx = fa + fb + fc + fd + fe + ff + fg + fh + fi + fj + fk; + + foreign "C" exit((fy != fx) == (f != y)); +} +#endif diff --git a/tests/compiler/cmm/unsafe_foreign_calls_spill_regs.cmm b/tests/compiler/cmm/unsafe_foreign_calls_spill_regs.cmm new file mode 100644 index 0000000000..c911267c45 --- /dev/null +++ b/tests/compiler/cmm/unsafe_foreign_calls_spill_regs.cmm @@ -0,0 +1,50 @@ +// RUN: "$HC" -cpp -dcmm-lint -keep-s-file -c "$1" && cat "${1%%.*}.s" | FileCheck "$1" -check-prefix=CHECK-A64 +#include "Cmm.h" +#include "Types.h" + +// Original failing cmm: +// +// (_ckSs::F32) = call MO_F32_ExpM1(_shwV::F32); // CmmUnsafeForeignCall +// +// One issue with the cmm parser is, that we can't build the following expressions +// the parser will deconstruct them into arg0 <- arg0_expr; f(arg0); instead of f(arg0_expr) +// +// (_ckSz::F32) = call MO_F32_Log(%MO_F_Neg_W32(_ckSs::F32)); // CmmUnsafeForeignCall +// (_ckqE::F64) = call MO_F64_Log(2.0 :: W64); // CmmUnsafeForeignCall +// (_cjE1::F64) = call MO_F64_Atan(%MO_F_Quot_W64(_shFv::F64, _shFw::F64)); // CmmUnsafeForeignCall +// +// +foreign_calls () { + F32 x, y; + x = F1; + + // Some variables to ensure we use up all the registers. + F32 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t; + a = F1; b = F1; c = F1; d = F1; e = F1; f = F1; g = F1; h = F1; i = F1; + j = F1; k = F1; l = F1; m = F1; n = F1; o = F1; p = F1; q = F1; r = F1; + s = F1; t = F1; + // Some integer register + I64 _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z; + _a = R1; _b = R1; _c = R1; _d = R1; _e = R1; _f = R1; _g = R1; _h = R1; + _i = R1; _j = R1; _k = R1; _l = R1; _m = R1; _n = R1; _o = R1; _p = R1; + _q = R1; _r = R1; _s = R1; _t = R1; _u = R1; _v = R1; _w = R1; _x = R1; _y = R1; _z = R1; + + // CHECK-A64: adrp x11, expm1f + // CHECK-A64: add x11, x11, :lo12:expm1f + // CHECK-A64: fmov s0, s2 + // CHECK-A64: blr x11 + (y) = prim %expM132f(F1); + // CHECK-A64: fmov s8, s30 + F1 = y; + F2 = y; + (y) = prim %log32f(%fneg(x)); + + prim %log64f(2.0); + + prim %atan64f(%fquot(D1,D2)); + + // make sure a..t stay alive! + F3 = a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t; + R2 = _a + _b + _c + _d + _e + _f + _g + _h + _i + _j + _k + _l + _m + _n + + _o + _p + _q + _r + _s + _t + _u + _v + _w + _x + _y + _z; +}
\ No newline at end of file |