aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2025-04-10 22:23:08 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2025-04-10 22:23:08 +0300
commit7628ce2432108ae6306457f242e7cc3ac75b9ac0 (patch)
tree32a7b59f0e91b4c82bdeb4de3f75c1a04cc247da /tests
parent0ba52d9043985baff61d4fae225420274d9ad9ab (diff)
parent531d307d310881e69efc8ae8c8119f5f5799e0f9 (diff)
downloadejit-7628ce2432108ae6306457f242e7cc3ac75b9ac0.tar.gz
ejit-7628ce2432108ae6306457f242e7cc3ac75b9ac0.zip
Merge branch 'tail'HEADmaster
Diffstat (limited to 'tests')
-rw-r--r--tests/calli.c41
-rw-r--r--tests/callr_i.c42
-rw-r--r--tests/escapei_10.c30
-rw-r--r--tests/escapei_double.c6
-rw-r--r--tests/escapei_float.c6
-rw-r--r--tests/escapei_immediate_10.c63
-rw-r--r--tests/makefile44
-rw-r--r--tests/maxr_d.c28
-rw-r--r--tests/maxr_f.c28
-rw-r--r--tests/minr_d.c28
-rw-r--r--tests/minr_f.c28
-rw-r--r--tests/sqrtr_d.c23
-rw-r--r--tests/sqrtr_f.c23
-rw-r--r--tests/taili.c39
-rw-r--r--tests/tailr.c41
15 files changed, 425 insertions, 45 deletions
diff --git a/tests/calli.c b/tests/calli.c
new file mode 100644
index 0000000..991e97d
--- /dev/null
+++ b/tests/calli.c
@@ -0,0 +1,41 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+struct ejit_func *compile(bool do_jit)
+{
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_TYPE(long)),
+ EJIT_OPERAND_GPR(1, EJIT_TYPE(long))
+ };
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands);
+ ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1));
+ ejit_retr(f, EJIT_GPR(0));
+ ejit_select_compile_func(f, 2, 0, EJIT_USE64(long), do_jit, true);
+ return f;
+}
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_func *target = compile(do_jit);
+
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_TYPE(long)),
+ EJIT_OPERAND_GPR(1, EJIT_TYPE(long))
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands);
+ ejit_calli(f, target, 2, operands);
+ ejit_retval(f, EJIT_GPR(0));
+ ejit_retr(f, EJIT_GPR(0));
+ ejit_select_compile_func(f, 2, 0, EJIT_USE64(long), do_jit, true);
+
+ assert(erfi2(f,
+ EJIT_ARG(42, long),
+ EJIT_ARG(69, long)) == 111);
+
+ ejit_destroy_func(target);
+ ejit_destroy_func(f);
+}
diff --git a/tests/callr_i.c b/tests/callr_i.c
new file mode 100644
index 0000000..00b5374
--- /dev/null
+++ b/tests/callr_i.c
@@ -0,0 +1,42 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+struct ejit_func *compile(bool do_jit)
+{
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_TYPE(long)),
+ EJIT_OPERAND_GPR(1, EJIT_TYPE(long))
+ };
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands);
+ ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1));
+ ejit_retr(f, EJIT_GPR(0));
+ ejit_select_compile_func(f, 2, 0, EJIT_USE64(long), do_jit, true);
+ return f;
+}
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_func *target = compile(do_jit);
+
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_TYPE(long)),
+ EJIT_OPERAND_GPR(1, EJIT_TYPE(long))
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands);
+ ejit_movi(f, EJIT_GPR(2), (uintptr_t)target);
+ ejit_callr_i(f, EJIT_GPR(2), 2, operands);
+ ejit_retval(f, EJIT_GPR(0));
+ ejit_retr(f, EJIT_GPR(0));
+ ejit_select_compile_func(f, 3, 0, EJIT_USE64(long), do_jit, true);
+
+ assert(erfi2(f,
+ EJIT_ARG(42, long),
+ EJIT_ARG(69, long)) == 111);
+
+ ejit_destroy_func(target);
+ ejit_destroy_func(f);
+}
diff --git a/tests/escapei_10.c b/tests/escapei_10.c
index 4ae00b8..ec48df0 100644
--- a/tests/escapei_10.c
+++ b/tests/escapei_10.c
@@ -21,26 +21,16 @@ static int32_t func(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e,
static long escape_func(size_t argc, const struct ejit_arg args[argc])
{
assert(argc == 10);
- assert(args[0].type == EJIT_INT32);
- assert(args[1].type == EJIT_INT32);
- assert(args[3].type == EJIT_INT32);
- assert(args[4].type == EJIT_INT32);
- assert(args[5].type == EJIT_INT32);
- assert(args[6].type == EJIT_INT32);
- assert(args[7].type == EJIT_INT32);
- assert(args[8].type == EJIT_INT32);
- assert(args[9].type == EJIT_INT32);
-
- int32_t a = args[0].i32;
- int32_t b = args[1].i32;
- int32_t c = args[2].i32;
- int32_t d = args[3].i32;
- int32_t e = args[4].i32;
- int32_t f = args[5].i32;
- int32_t g = args[6].i32;
- int32_t h = args[7].i32;
- int32_t i = args[8].i32;
- int32_t j = args[9].i32;
+ int32_t a = EJIT_PARAM(argc, args, 0, int32_t);
+ int32_t b = EJIT_PARAM(argc, args, 1, int32_t);
+ int32_t c = EJIT_PARAM(argc, args, 2, int32_t);
+ int32_t d = EJIT_PARAM(argc, args, 3, int32_t);
+ int32_t e = EJIT_PARAM(argc, args, 4, int32_t);
+ int32_t f = EJIT_PARAM(argc, args, 5, int32_t);
+ int32_t g = EJIT_PARAM(argc, args, 6, int32_t);
+ int32_t h = EJIT_PARAM(argc, args, 7, int32_t);
+ int32_t i = EJIT_PARAM(argc, args, 8, int32_t);
+ int32_t j = EJIT_PARAM(argc, args, 9, int32_t);
return func(a, b, c, d, e, f, g, h, i, j);
}
diff --git a/tests/escapei_double.c b/tests/escapei_double.c
index 6ea9f90..736e978 100644
--- a/tests/escapei_double.c
+++ b/tests/escapei_double.c
@@ -9,10 +9,8 @@ static double func(int32_t a, double b) {
static double escape_func(size_t argc, const struct ejit_arg args[argc])
{
assert(argc == 2);
- assert(args[0].type == EJIT_INT32);
- assert(args[1].type == EJIT_DOUBLE);
- int32_t a = args[0].i32;
- double b = args[1].d;
+ int32_t a = EJIT_PARAM(argc, args, 0, int32_t);
+ double b = EJIT_PARAM(argc, args, 1, double);
return func(a, b);
}
diff --git a/tests/escapei_float.c b/tests/escapei_float.c
index 7a1b923..7cdc30d 100644
--- a/tests/escapei_float.c
+++ b/tests/escapei_float.c
@@ -9,10 +9,8 @@ static float func(int32_t a, float b) {
static float escape_func(size_t argc, const struct ejit_arg args[argc])
{
assert(argc == 2);
- assert(args[0].type == EJIT_INT32);
- assert(args[1].type == EJIT_FLOAT);
- int32_t a = args[0].i32;
- float b = args[1].f;
+ int32_t a = EJIT_PARAM(argc, args, 0, int32_t);
+ float b = EJIT_PARAM(argc, args, 1, float);
return func(a, b);
}
diff --git a/tests/escapei_immediate_10.c b/tests/escapei_immediate_10.c
new file mode 100644
index 0000000..5517c35
--- /dev/null
+++ b/tests/escapei_immediate_10.c
@@ -0,0 +1,63 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+static int32_t func(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e,
+ int32_t f, int32_t g, int32_t h, int32_t i, int32_t j)
+{
+ assert(a == 0);
+ assert(b == 1);
+ assert(c == 2);
+ assert(d == 3);
+ assert(e == 4);
+ assert(f == 5);
+ assert(g == 6);
+ assert(h == 7);
+ assert(i == 8);
+ assert(j == 9);
+ return 42;
+}
+
+static long escape_func(size_t argc, const struct ejit_arg args[argc])
+{
+ assert(argc == 10);
+ int32_t a = EJIT_PARAM(argc, args, 0, int32_t);
+ int32_t b = EJIT_PARAM(argc, args, 1, int32_t);
+ int32_t c = EJIT_PARAM(argc, args, 2, int32_t);
+ int32_t d = EJIT_PARAM(argc, args, 3, int32_t);
+ int32_t e = EJIT_PARAM(argc, args, 4, int32_t);
+ int32_t f = EJIT_PARAM(argc, args, 5, int32_t);
+ int32_t g = EJIT_PARAM(argc, args, 6, int32_t);
+ int32_t h = EJIT_PARAM(argc, args, 7, int32_t);
+ int32_t i = EJIT_PARAM(argc, args, 8, int32_t);
+ int32_t j = EJIT_PARAM(argc, args, 9, int32_t);
+ return func(a, b, c, d, e, f, g, h, i, j);
+}
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_func *f = ejit_create_func(EJIT_INT32, 0, NULL);
+ struct ejit_operand args[10] = {
+ EJIT_OPERAND_IMM(0, EJIT_INT32),
+ EJIT_OPERAND_IMM(1, EJIT_INT32),
+ EJIT_OPERAND_IMM(2, EJIT_INT32),
+ EJIT_OPERAND_IMM(3, EJIT_INT32),
+ EJIT_OPERAND_IMM(4, EJIT_INT32),
+ EJIT_OPERAND_IMM(5, EJIT_INT32),
+ EJIT_OPERAND_IMM(6, EJIT_INT32),
+ EJIT_OPERAND_IMM(7, EJIT_INT32),
+ EJIT_OPERAND_IMM(8, EJIT_INT32),
+ EJIT_OPERAND_IMM(9, EJIT_INT32),
+ };
+ ejit_escapei_i(f, escape_func, 10, args);
+ ejit_retval(f, EJIT_GPR(0));
+ ejit_retr(f, EJIT_GPR(0));
+
+ ejit_select_compile_func(f, 11, 0, false, do_jit, true);
+
+ assert(ejit_run_func_i(f, 0, NULL) == 42);
+
+ ejit_destroy_func(f);
+}
diff --git a/tests/makefile b/tests/makefile
index 5aeef98..53115de 100644
--- a/tests/makefile
+++ b/tests/makefile
@@ -1,25 +1,35 @@
include ./tests.mk
-LLVM ?= 0
-CROSS_COMPILE :=
-COMPILER != [ "$(LLVM)" != "0" ] \
- && echo clang --target="$(CROSS_COMPILE)" \
- || echo $(CROSS_COMPILE)gcc
+LLVM ?= 0
+COMPILER != [ -n "$(CROSS_COMPILE)" ] \
+ && { \
+ [ "$(LLVM)" != "0" ] \
+ && echo clang --target="$(CROSS_COMPILE)" \
+ || echo $(CROSS_COMPILE)gcc \
+ ; \
+ } \
+ || echo $(CC)
-CFLAGS := -Wall -Wextra -O0 -g
+RELEASE ?= 0
+OPTFLAGS != [ "$(RELEASE)" != "0" ] \
+ && echo "-O2" \
+ || echo "-O0"
+
+LTO ?= 0
+LTOFLAGS != [ "$(LTO)" != "0" ] \
+ && echo "-flto=auto"
+
+DEBUG ?= 1
+DEBUGFLAGS != [ "$(DEBUG)" != "0" ] \
+ && echo "-DDEBUG=1" \
+ || echo "-DNDEBUG=1"
+
+OBFLAGS := -g
+WARNFLAGS := -Wall -Wextra
INCLUDE_FLAGS := -I include
-COMPILE_TEST := $(COMPILER) $(CFLAGS) $(INCLUDE_FLAGS)
+COMPILE_TEST := $(COMPILER) $(WARNFLAGS) $(OPTFLAGS) $(LTOFLAGS) \
+ $(OBFLAGS) $(CFLAGS) $(DEBUGFLAGS) $(INCLUDE_FLAGS)
.PHONY: check
check: $(TESTS)
- @echo "Running bytecode tests..."
- @set -e; for test in $(TESTS); do \
- echo "Testing: $$test"; \
- ./$$test; \
- done
- @echo "Running jit tests..."
- @set -e; for test in $(TESTS); do \
- echo "Testing: $$test"; \
- ./$$test 1; \
- done
@echo "Success!"
diff --git a/tests/maxr_d.c b/tests/maxr_d.c
new file mode 100644
index 0000000..3e35665
--- /dev/null
+++ b/tests/maxr_d.c
@@ -0,0 +1,28 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_FPR(0, EJIT_TYPE(double)),
+ EJIT_OPERAND_FPR(1, EJIT_TYPE(double))
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(double), 2, operands);
+
+ ejit_maxr_d(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1));
+ ejit_retr_d(f, EJIT_FPR(2));
+
+ ejit_select_compile_func(f, 0, 3, EJIT_USE64(double), do_jit, true);
+
+ assert(erfd2(f, EJIT_ARG(42., double), EJIT_ARG(69., double)
+ ) == 69.);
+
+ assert(erfd2(f, EJIT_ARG(-42., double), EJIT_ARG(-69., double)
+ ) == -42.);
+
+ ejit_destroy_func(f);
+}
diff --git a/tests/maxr_f.c b/tests/maxr_f.c
new file mode 100644
index 0000000..581f867
--- /dev/null
+++ b/tests/maxr_f.c
@@ -0,0 +1,28 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_FPR(0, EJIT_TYPE(float)),
+ EJIT_OPERAND_FPR(1, EJIT_TYPE(float))
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(float), 2, operands);
+
+ ejit_maxr_f(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1));
+ ejit_retr_f(f, EJIT_FPR(2));
+
+ ejit_select_compile_func(f, 0, 3, EJIT_USE64(float), do_jit, true);
+
+ assert(erff2(f, EJIT_ARG(42., float), EJIT_ARG(69., float)
+ ) == 69.);
+
+ assert(erff2(f, EJIT_ARG(-42., float), EJIT_ARG(-69., float)
+ ) == -42.);
+
+ ejit_destroy_func(f);
+}
diff --git a/tests/minr_d.c b/tests/minr_d.c
new file mode 100644
index 0000000..d0fb7c8
--- /dev/null
+++ b/tests/minr_d.c
@@ -0,0 +1,28 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_FPR(0, EJIT_TYPE(double)),
+ EJIT_OPERAND_FPR(1, EJIT_TYPE(double))
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(double), 2, operands);
+
+ ejit_minr_d(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1));
+ ejit_retr_d(f, EJIT_FPR(2));
+
+ ejit_select_compile_func(f, 0, 3, EJIT_USE64(double), do_jit, true);
+
+ assert(erfd2(f, EJIT_ARG(42., double), EJIT_ARG(69., double)
+ ) == 42.);
+
+ assert(erfd2(f, EJIT_ARG(-42., double), EJIT_ARG(-69., double)
+ ) == -69.);
+
+ ejit_destroy_func(f);
+}
diff --git a/tests/minr_f.c b/tests/minr_f.c
new file mode 100644
index 0000000..b02ec06
--- /dev/null
+++ b/tests/minr_f.c
@@ -0,0 +1,28 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_FPR(0, EJIT_TYPE(float)),
+ EJIT_OPERAND_FPR(1, EJIT_TYPE(float))
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(float), 2, operands);
+
+ ejit_minr_f(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1));
+ ejit_retr_f(f, EJIT_FPR(2));
+
+ ejit_select_compile_func(f, 0, 3, EJIT_USE64(float), do_jit, true);
+
+ assert(erff2(f, EJIT_ARG(42., float), EJIT_ARG(69., float)
+ ) == 42.);
+
+ assert(erff2(f, EJIT_ARG(-42., float), EJIT_ARG(-69., float)
+ ) == -69.);
+
+ ejit_destroy_func(f);
+}
diff --git a/tests/sqrtr_d.c b/tests/sqrtr_d.c
new file mode 100644
index 0000000..06e7894
--- /dev/null
+++ b/tests/sqrtr_d.c
@@ -0,0 +1,23 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[1] = {
+ EJIT_OPERAND_FPR(0, EJIT_TYPE(double)),
+ };
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(double), 1, operands);
+
+ ejit_sqrtr_d(f, EJIT_FPR(0), EJIT_FPR(0));
+ ejit_retr_d(f, EJIT_FPR(0));
+
+ ejit_select_compile_func(f, 0, 1, EJIT_USE64(double), do_jit, true);
+
+ assert(erfd1(f, EJIT_ARG( 0.0, double)) == 0.0);
+ assert(erfd1(f, EJIT_ARG( 4.0, double)) == 2.0);
+ assert(erfd1(f, EJIT_ARG(-4.0, double))
+ != erfd1(f, EJIT_ARG(-4.0, double))); // nan
+}
diff --git a/tests/sqrtr_f.c b/tests/sqrtr_f.c
new file mode 100644
index 0000000..3baa00d
--- /dev/null
+++ b/tests/sqrtr_f.c
@@ -0,0 +1,23 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[1] = {
+ EJIT_OPERAND_FPR(0, EJIT_TYPE(float)),
+ };
+ struct ejit_func *f = ejit_create_func(EJIT_TYPE(float), 1, operands);
+
+ ejit_sqrtr_f(f, EJIT_FPR(0), EJIT_FPR(0));
+ ejit_retr_f(f, EJIT_FPR(0));
+
+ ejit_select_compile_func(f, 0, 1, EJIT_USE64(float), do_jit, true);
+
+ assert(erff1(f, EJIT_ARG( 0.0, float)) == 0.0);
+ assert(erff1(f, EJIT_ARG( 4.0, float)) == 2.0);
+ assert(erff1(f, EJIT_ARG(-4.0, float))
+ != erff1(f, EJIT_ARG(-4.0, float))); // nan
+}
diff --git a/tests/taili.c b/tests/taili.c
new file mode 100644
index 0000000..cc09f59
--- /dev/null
+++ b/tests/taili.c
@@ -0,0 +1,39 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */
+ EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_INT32, 2, operands);
+
+ /* n == 0, return s */
+ struct ejit_reloc r = ejit_bnei(f, EJIT_GPR(1), 0);
+ ejit_retr(f, EJIT_GPR(0));
+ ejit_patch(f, r, ejit_label(f));
+
+ /* s += n */
+ ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1));
+
+ /* n -= 1 */
+ ejit_subi(f, EJIT_GPR(1), EJIT_GPR(1), 1);
+
+ struct ejit_operand args[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */
+ EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */
+ };
+ ejit_taili(f, f, 2, args);
+
+ ejit_select_compile_func(f, 2, 0, EJIT_USE64(uintptr_t), do_jit, true);
+
+ /* arbitrary number but large enough to most likely cause a stack fault
+ * if the tail call leaks memory or something */
+ assert((int32_t)erfi2(f, EJIT_ARG(0, int32_t), EJIT_ARG(1000000, int32_t)) == 1784293664);
+ ejit_destroy_func(f);
+}
diff --git a/tests/tailr.c b/tests/tailr.c
new file mode 100644
index 0000000..69ad44b
--- /dev/null
+++ b/tests/tailr.c
@@ -0,0 +1,41 @@
+#include <ejit/ejit.h>
+#include <assert.h>
+#include "do_jit.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argv;
+ bool do_jit = argc > 1;
+ struct ejit_operand operands[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */
+ EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */
+ };
+
+ struct ejit_func *f = ejit_create_func(EJIT_INT32, 2, operands);
+
+ /* n == 0, return s */
+ struct ejit_reloc r = ejit_bnei(f, EJIT_GPR(1), 0);
+ ejit_retr(f, EJIT_GPR(0));
+ ejit_patch(f, r, ejit_label(f));
+
+ /* s += n */
+ ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1));
+
+ /* n -= 1 */
+ ejit_subi(f, EJIT_GPR(1), EJIT_GPR(1), 1);
+
+ struct ejit_operand args[2] = {
+ EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */
+ EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */
+ };
+
+ ejit_movi(f, EJIT_GPR(2), (uintptr_t)f);
+ ejit_tailr(f, EJIT_GPR(2), 2, args);
+
+ ejit_select_compile_func(f, 3, 0, EJIT_USE64(uintptr_t), do_jit, true);
+
+ /* arbitrary number but large enough to most likely cause a stack fault
+ * if the tail call leaks memory or something */
+ assert((int32_t)erfi2(f, EJIT_ARG(0, int32_t), EJIT_ARG(1000000, int32_t)) == 1784293664);
+ ejit_destroy_func(f);
+}