summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoritz Angermann <moritz.angermann@gmail.com>2023-05-01 02:51:27 +0000
committerSven Tennie <sven.tennie@wire.com>2023-05-01 16:43:15 +0000
commit8c2b36836376bfd69bd22662250fa59b368a5354 (patch)
tree213d6fa88c0d9ac4842484cb16fb32bb4dd66a80
parent00a8a5ff9abf5bb1a0c2a9225c7bca5ec3bdf306 (diff)
downloadhaskell-8c2b36836376bfd69bd22662250fa59b368a5354.tar.gz
Bring back old aarch64 test-suite
-rw-r--r--tests/compiler/cmm/README.md62
-rw-r--r--tests/compiler/cmm/Types.h4
-rw-r--r--tests/compiler/cmm/assert.cmm26
-rwxr-xr-xtests/compiler/cmm/driver.sh47
-rw-r--r--tests/compiler/cmm/encode_values.cmm30
-rw-r--r--tests/compiler/cmm/immideate.cmm65
-rw-r--r--tests/compiler/cmm/load_large_immediate.cmm28
-rw-r--r--tests/compiler/cmm/load_store.cmm9
-rw-r--r--tests/compiler/cmm/load_sym_with_large_offset.cmm9
-rw-r--r--tests/compiler/cmm/main.cmm27
-rw-r--r--tests/compiler/cmm/mo_f_eq.cmm10
-rw-r--r--tests/compiler/cmm/mo_f_gt.cmm15
-rw-r--r--tests/compiler/cmm/mo_f_neg.cmm14
-rw-r--r--tests/compiler/cmm/mo_ff_conv.cmm12
-rw-r--r--tests/compiler/cmm/mo_uu_conv.cmm20
-rw-r--r--tests/compiler/cmm/nan.cmm51
-rw-r--r--tests/compiler/cmm/primops_math.cmm111
-rw-r--r--tests/compiler/cmm/shift_left.cmm15
-rw-r--r--tests/compiler/cmm/shift_left_conditional.cmm13
-rw-r--r--tests/compiler/cmm/store_neg_float.cmm13
-rw-r--r--tests/compiler/cmm/switch_expr.cmm52
-rw-r--r--tests/compiler/cmm/unsafe_foreign_calls.cmm34
-rw-r--r--tests/compiler/cmm/unsafe_foreign_calls_excess_arguments.cmm36
-rw-r--r--tests/compiler/cmm/unsafe_foreign_calls_excess_float_arguments.cmm37
-rw-r--r--tests/compiler/cmm/unsafe_foreign_calls_excess_mixed_arguments.cmm57
-rw-r--r--tests/compiler/cmm/unsafe_foreign_calls_spill_regs.cmm50
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