From 4d4bdcf368d72c7dbedbc58fb3ebcad8447ffcd8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 21 Oct 2021 13:06:30 +0900 Subject: Move the test file --- doc/yjit/yjit.md | 2 +- misc/test_yjit_asm.sh | 2 +- misc/yjit_asm_tests.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++ yjit_asm_tests.c | 433 -------------------------------------------------- 4 files changed, 435 insertions(+), 435 deletions(-) create mode 100644 misc/yjit_asm_tests.c delete mode 100644 yjit_asm_tests.c diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md index 476f162d8f..f84b989f6d 100644 --- a/doc/yjit/yjit.md +++ b/doc/yjit/yjit.md @@ -166,13 +166,13 @@ you can contribute things we will want to merge into YJIT. The YJIT source code is divided between: - `yjit_asm.c`: x86 in-memory assembler we use to generate machine code -- `yjit_asm_tests.c`: tests for the in-memory assembler - `yjit_codegen.c`: logic for translating Ruby bytecode to machine code - `yjit_core.c`: basic block versioning logic, core structure of YJIT - `yjit_iface.c`: code YJIT uses to interface with the rest of CRuby - `yjit.h`: C definitions YJIT exposes to the rest of the CRuby - `yjit.rb`: `YJIT` Ruby module that is exposed to Ruby - `misc/test_yjit_asm.sh`: script to compile and run the in-memory assembler tests +- `misc/yjit_asm_tests.c`: tests for the in-memory assembler The core of CRuby's interpreter logic is found in: - `insns.def`: defines Ruby's bytecode instructions (gets compiled into `vm.inc`) diff --git a/misc/test_yjit_asm.sh b/misc/test_yjit_asm.sh index 0d79ccadf0..cf1ae7bee5 100755 --- a/misc/test_yjit_asm.sh +++ b/misc/test_yjit_asm.sh @@ -3,7 +3,7 @@ set -e set -x -clang -std=gnu99 -Wall -Werror -Wno-error=unused-function -Wshorten-64-to-32 -I ${0%/*/*} ${0%/*/*}/yjit_asm_tests.c -o asm_test +clang -std=gnu99 -Wall -Werror -Wno-error=unused-function -Wshorten-64-to-32 -I ${0%/*/*} ${0%/*}/yjit_asm_tests.c -o asm_test ./asm_test diff --git a/misc/yjit_asm_tests.c b/misc/yjit_asm_tests.c new file mode 100644 index 0000000000..7650505fb3 --- /dev/null +++ b/misc/yjit_asm_tests.c @@ -0,0 +1,433 @@ +// For MAP_ANONYMOUS on GNU/Linux +#define _GNU_SOURCE 1 + +#include +#include +#include +#include + +// This test executable doesn't compile with the rest of Ruby +// so we need to define a rb_bug(). +_Noreturn +static void rb_bug(char *message) +{ + fprintf(stderr, "%s\n", message); + abort(); +} + +#include "yjit_asm.c" + +// Print the bytes in a code block +void print_bytes(codeblock_t* cb) +{ + for (uint32_t i = 0; i < cb->write_pos; ++i) + { + printf("%02X", (int)cb->mem_block[i]); + } + + printf("\n"); +} + +// Check that the code block contains the given sequence of bytes +void check_bytes(codeblock_t* cb, const char* bytes) +{ + printf("checking encoding: %s\n", bytes); + + size_t len = strlen(bytes); + assert (len % 2 == 0); + size_t num_bytes = len / 2; + + if (cb->write_pos != num_bytes) + { + fprintf(stderr, "incorrect encoding length, expected %ld, got %d\n", + num_bytes, + cb->write_pos + ); + printf("%s\n", bytes); + print_bytes(cb); + exit(-1); + } + + for (uint32_t i = 0; i < num_bytes; ++i) + { + char byte_str[] = {0, 0, 0, 0}; + strncpy(byte_str, bytes + (2 * i), 2); + char* endptr; + long int byte = strtol(byte_str, &endptr, 16); + + uint8_t cb_byte = cb->mem_block[i]; + + if (cb_byte != byte) + { + fprintf(stderr, "incorrect encoding at position %d, expected %02X, got %02X\n", + i, + (int)byte, + (int)cb_byte + ); + printf("%s\n", bytes); + print_bytes(cb); + exit(-1); + } + } +} + +void run_assembler_tests() +{ + printf("Running assembler tests\n"); + + codeblock_t cb_obj; + codeblock_t* cb = &cb_obj; + uint8_t* mem_block = alloc_exec_mem(4096); + cb_init(cb, mem_block, 4096); + + // add + cb_set_pos(cb, 0); add(cb, CL, imm_opnd(3)); check_bytes(cb, "80C103"); + cb_set_pos(cb, 0); add(cb, CL, BL); check_bytes(cb, "00D9"); + cb_set_pos(cb, 0); add(cb, CL, SPL); check_bytes(cb, "4000E1"); + cb_set_pos(cb, 0); add(cb, CX, BX); check_bytes(cb, "6601D9"); + cb_set_pos(cb, 0); add(cb, RAX, RBX); check_bytes(cb, "4801D8"); + cb_set_pos(cb, 0); add(cb, ECX, EDX); check_bytes(cb, "01D1"); + cb_set_pos(cb, 0); add(cb, RDX, R14); check_bytes(cb, "4C01F2"); + cb_set_pos(cb, 0); add(cb, mem_opnd(64, RAX, 0), RDX); check_bytes(cb, "480110"); + cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 0)); check_bytes(cb, "480310"); + cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 8)); check_bytes(cb, "48035008"); + cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 255)); check_bytes(cb, "480390FF000000"); + cb_set_pos(cb, 0); add(cb, mem_opnd(64, RAX, 127), imm_opnd(255)); check_bytes(cb, "4881407FFF000000"); + cb_set_pos(cb, 0); add(cb, mem_opnd(32, RAX, 0), EDX); check_bytes(cb, "0110"); + cb_set_pos(cb, 0); add(cb, RSP, imm_opnd(8)); check_bytes(cb, "4883C408"); + cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(8)); check_bytes(cb, "83C108"); + cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(255)); check_bytes(cb, "81C1FF000000"); + + // and + cb_set_pos(cb, 0); and(cb, EBP, R12D); check_bytes(cb, "4421E5"); + cb_set_pos(cb, 0); and(cb, mem_opnd(64, RAX, 0), imm_opnd(0x08)); check_bytes(cb, "48832008"); + + // call + { + cb_set_pos(cb, 0); + uint32_t fn_label = cb_new_label(cb, "foo"); + call_label(cb, fn_label); + cb_link_labels(cb); + check_bytes(cb, "E8FBFFFFFF"); + } + cb_set_pos(cb, 0); call(cb, RAX); check_bytes(cb, "FFD0"); + cb_set_pos(cb, 0); call(cb, mem_opnd(64, RSP, 8)); check_bytes(cb, "FF542408"); + + // cmovcc + cb_set_pos(cb, 0); cmovg(cb, ESI, EDI); check_bytes(cb, "0F4FF7"); + cb_set_pos(cb, 0); cmovg(cb, ESI, mem_opnd(32, RBP, 12)); check_bytes(cb, "0F4F750C"); + cb_set_pos(cb, 0); cmovl(cb, EAX, ECX); check_bytes(cb, "0F4CC1"); + cb_set_pos(cb, 0); cmovl(cb, RBX, RBP); check_bytes(cb, "480F4CDD"); + cb_set_pos(cb, 0); cmovle(cb, ESI, mem_opnd(32, RSP, 4)); check_bytes(cb, "0F4E742404"); + + // cmp + cb_set_pos(cb, 0); cmp(cb, CL, DL); check_bytes(cb, "38D1"); + cb_set_pos(cb, 0); cmp(cb, ECX, EDI); check_bytes(cb, "39F9"); + cb_set_pos(cb, 0); cmp(cb, RDX, mem_opnd(64, R12, 0)); check_bytes(cb, "493B1424"); + cb_set_pos(cb, 0); cmp(cb, RAX, imm_opnd(2)); check_bytes(cb, "4883F802"); + + // cqo + cb_set_pos(cb, 0); cqo(cb); check_bytes(cb, "4899"); + + // div + /* + test( + delegate void (CodeBlock cb) { cb.div(X86Opnd(EDX)); }, + "F7F2" + ); + test( + delegate void (CodeBlock cb) { cb.div(X86Opnd(32, RSP, -12)); }, + "F77424F4" + ); + */ + + // jcc to label + { + cb_set_pos(cb, 0); + uint32_t loop_label = cb_new_label(cb, "loop"); + jge_label(cb, loop_label); + cb_link_labels(cb); + check_bytes(cb, "0F8DFAFFFFFF"); + } + { + cb_set_pos(cb, 0); + uint32_t loop_label = cb_new_label(cb, "loop"); + jo_label(cb, loop_label); + cb_link_labels(cb); + check_bytes(cb, "0F80FAFFFFFF"); + } + + // jmp to label + { + cb_set_pos(cb, 0); + uint32_t loop_label = cb_new_label(cb, "loop"); + jmp_label(cb, loop_label); + cb_link_labels(cb); + check_bytes(cb, "E9FBFFFFFF"); + } + + // jmp with RM operand + cb_set_pos(cb, 0); jmp_rm(cb, R12); check_bytes(cb, "41FFE4"); + + // lea + cb_set_pos(cb, 0); lea(cb, RDX, mem_opnd(64, RCX, 8)); check_bytes(cb, "488D5108"); + cb_set_pos(cb, 0); lea(cb, RAX, mem_opnd(8, RIP, 0)); check_bytes(cb, "488D0500000000"); + cb_set_pos(cb, 0); lea(cb, RAX, mem_opnd(8, RIP, 5)); check_bytes(cb, "488D0505000000"); + cb_set_pos(cb, 0); lea(cb, RDI, mem_opnd(8, RIP, 5)); check_bytes(cb, "488D3D05000000"); + + // mov + cb_set_pos(cb, 0); mov(cb, EAX, imm_opnd(7)); check_bytes(cb, "B807000000"); + cb_set_pos(cb, 0); mov(cb, EAX, imm_opnd(-3)); check_bytes(cb, "B8FDFFFFFF"); + cb_set_pos(cb, 0); mov(cb, R15, imm_opnd(3)); check_bytes(cb, "49BF0300000000000000"); + cb_set_pos(cb, 0); mov(cb, EAX, EBX); check_bytes(cb, "89D8"); + cb_set_pos(cb, 0); mov(cb, EAX, ECX); check_bytes(cb, "89C8"); + cb_set_pos(cb, 0); mov(cb, EDX, mem_opnd(32, RBX, 128)); check_bytes(cb, "8B9380000000"); + /* + test( + delegate void (CodeBlock cb) { cb.mov(X86Opnd(AL), X86Opnd(8, RCX, 0, 1, RDX)); }, + "8A0411" + ); + */ + cb_set_pos(cb, 0); mov(cb, CL, R9B); check_bytes(cb, "4488C9"); + cb_set_pos(cb, 0); mov(cb, RBX, RAX); check_bytes(cb, "4889C3"); + cb_set_pos(cb, 0); mov(cb, RDI, RBX); check_bytes(cb, "4889DF"); + cb_set_pos(cb, 0); mov(cb, SIL, imm_opnd(11)); check_bytes(cb, "40B60B"); + cb_set_pos(cb, 0); mov(cb, mem_opnd(8, RSP, 0), imm_opnd(-3)); check_bytes(cb, "C60424FD"); + cb_set_pos(cb, 0); mov(cb, mem_opnd(64, RDI, 8), imm_opnd(1)); check_bytes(cb, "48C7470801000000"); + + // movsx + cb_set_pos(cb, 0); movsx(cb, AX, AL); check_bytes(cb, "660FBEC0"); + cb_set_pos(cb, 0); movsx(cb, EDX, AL); check_bytes(cb, "0FBED0"); + cb_set_pos(cb, 0); movsx(cb, RAX, BL); check_bytes(cb, "480FBEC3"); + cb_set_pos(cb, 0); movsx(cb, ECX, AX); check_bytes(cb, "0FBFC8"); + cb_set_pos(cb, 0); movsx(cb, R11, CL); check_bytes(cb, "4C0FBED9"); + cb_set_pos(cb, 0); movsx(cb, R10, mem_opnd(32, RSP, 12)); check_bytes(cb, "4C6354240C"); + cb_set_pos(cb, 0); movsx(cb, RAX, mem_opnd(8, RSP, 0)); check_bytes(cb, "480FBE0424"); + + // neg + cb_set_pos(cb, 0); neg(cb, RAX); check_bytes(cb, "48F7D8"); + + // nop + cb_set_pos(cb, 0); nop(cb, 1); check_bytes(cb, "90"); + + // not + cb_set_pos(cb, 0); not(cb, AX); check_bytes(cb, "66F7D0"); + cb_set_pos(cb, 0); not(cb, EAX); check_bytes(cb, "F7D0"); + cb_set_pos(cb, 0); not(cb, mem_opnd(64, R12, 0)); check_bytes(cb, "49F71424"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 301)); check_bytes(cb, "F794242D010000"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 0)); check_bytes(cb, "F71424"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 3)); check_bytes(cb, "F7542403"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RBP, 0)); check_bytes(cb, "F75500"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RBP, 13)); check_bytes(cb, "F7550D"); + cb_set_pos(cb, 0); not(cb, RAX); check_bytes(cb, "48F7D0"); + cb_set_pos(cb, 0); not(cb, R11); check_bytes(cb, "49F7D3"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RAX, 0)); check_bytes(cb, "F710"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSI, 0)); check_bytes(cb, "F716"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDI, 0)); check_bytes(cb, "F717"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, 55)); check_bytes(cb, "F75237"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, 1337)); check_bytes(cb, "F79239050000"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, -55)); check_bytes(cb, "F752C9"); + cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, -555)); check_bytes(cb, "F792D5FDFFFF"); + /* + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 0, 1, RBX)); }, + "F71418" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 0, 1, R12)); }, + "42F71420" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 0, 1, R12)); }, + "43F71427" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 1, R12)); }, + "43F7542705" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 8, R12)); }, + "43F754E705" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 8, R13)); }, + "43F754EF05" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R12, 5, 4, R9)); }, + "43F7548C05" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R12, 301, 4, R9)); }, + "43F7948C2D010000" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 5, 4, RDX)); }, + "F7549005" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(64, RAX, 0, 2, RDX)); }, + "48F71450" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RSP, 0, 1, RBX)); }, + "F7141C" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RSP, 3, 1, RBX)); }, + "F7541C03" + ); + test( + delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RBP, 13, 1, RDX)); }, + "F754150D" + ); + */ + + // or + cb_set_pos(cb, 0); or(cb, EDX, ESI); check_bytes(cb, "09F2"); + + // pop + cb_set_pos(cb, 0); pop(cb, RAX); check_bytes(cb, "58"); + cb_set_pos(cb, 0); pop(cb, RBX); check_bytes(cb, "5B"); + cb_set_pos(cb, 0); pop(cb, RSP); check_bytes(cb, "5C"); + cb_set_pos(cb, 0); pop(cb, RBP); check_bytes(cb, "5D"); + cb_set_pos(cb, 0); pop(cb, R12); check_bytes(cb, "415C"); + cb_set_pos(cb, 0); pop(cb, mem_opnd(64, RAX, 0)); check_bytes(cb, "8F00"); + cb_set_pos(cb, 0); pop(cb, mem_opnd(64, R8, 0)); check_bytes(cb, "418F00"); + cb_set_pos(cb, 0); pop(cb, mem_opnd(64, R8, 3)); check_bytes(cb, "418F4003"); + cb_set_pos(cb, 0); pop(cb, mem_opnd_sib(64, RAX, RCX, 8, 3)); check_bytes(cb, "8F44C803"); + cb_set_pos(cb, 0); pop(cb, mem_opnd_sib(64, R8, RCX, 8, 3)); check_bytes(cb, "418F44C803"); + + // push + cb_set_pos(cb, 0); push(cb, RAX); check_bytes(cb, "50"); + cb_set_pos(cb, 0); push(cb, RBX); check_bytes(cb, "53"); + cb_set_pos(cb, 0); push(cb, R12); check_bytes(cb, "4154"); + cb_set_pos(cb, 0); push(cb, mem_opnd(64, RAX, 0)); check_bytes(cb, "FF30"); + cb_set_pos(cb, 0); push(cb, mem_opnd(64, R8, 0)); check_bytes(cb, "41FF30"); + cb_set_pos(cb, 0); push(cb, mem_opnd(64, R8, 3)); check_bytes(cb, "41FF7003"); + cb_set_pos(cb, 0); push(cb, mem_opnd_sib(64, RAX, RCX, 8, 3)); check_bytes(cb, "FF74C803"); + cb_set_pos(cb, 0); push(cb, mem_opnd_sib(64, R8, RCX, 8, 3)); check_bytes(cb, "41FF74C803"); + + // ret + cb_set_pos(cb, 0); ret(cb); check_bytes(cb, "C3"); + + // sal + cb_set_pos(cb, 0); sal(cb, CX, imm_opnd(1)); check_bytes(cb, "66D1E1"); + cb_set_pos(cb, 0); sal(cb, ECX, imm_opnd(1)); check_bytes(cb, "D1E1"); + cb_set_pos(cb, 0); sal(cb, EBP, imm_opnd(5)); check_bytes(cb, "C1E505"); + cb_set_pos(cb, 0); sal(cb, mem_opnd(32, RSP, 68), imm_opnd(1)); check_bytes(cb, "D1642444"); + + // sar + cb_set_pos(cb, 0); sar(cb, EDX, imm_opnd(1)); check_bytes(cb, "D1FA"); + + // shr + cb_set_pos(cb, 0); shr(cb, R14, imm_opnd(7)); check_bytes(cb, "49C1EE07"); + + /* + // sqrtsd + test( + delegate void (CodeBlock cb) { cb.sqrtsd(X86Opnd(XMM2), X86Opnd(XMM6)); }, + "F20F51D6" + ); + */ + + // sub + cb_set_pos(cb, 0); sub(cb, EAX, imm_opnd(1)); check_bytes(cb, "83E801"); + cb_set_pos(cb, 0); sub(cb, RAX, imm_opnd(2)); check_bytes(cb, "4883E802"); + + // test + cb_set_pos(cb, 0); test(cb, AL, AL); check_bytes(cb, "84C0"); + cb_set_pos(cb, 0); test(cb, AX, AX); check_bytes(cb, "6685C0"); + cb_set_pos(cb, 0); test(cb, CL, imm_opnd(8)); check_bytes(cb, "F6C108"); + cb_set_pos(cb, 0); test(cb, DL, imm_opnd(7)); check_bytes(cb, "F6C207"); + cb_set_pos(cb, 0); test(cb, RCX, imm_opnd(8)); check_bytes(cb, "F6C108"); + cb_set_pos(cb, 0); test(cb, mem_opnd(8, RDX, 8), imm_opnd(8)); check_bytes(cb, "F6420808"); + cb_set_pos(cb, 0); test(cb, mem_opnd(8, RDX, 8), imm_opnd(255)); check_bytes(cb, "F64208FF"); + cb_set_pos(cb, 0); test(cb, DX, imm_opnd(0xFFFF)); check_bytes(cb, "66F7C2FFFF"); + cb_set_pos(cb, 0); test(cb, mem_opnd(16, RDX, 8), imm_opnd(0xFFFF)); check_bytes(cb, "66F74208FFFF"); + cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, 0), imm_opnd(1)); check_bytes(cb, "F60601"); + cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, 16), imm_opnd(1)); check_bytes(cb, "F6461001"); + cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, -16), imm_opnd(1)); check_bytes(cb, "F646F001"); + cb_set_pos(cb, 0); test(cb, mem_opnd(32, RSI, 64), EAX); check_bytes(cb, "854640"); + cb_set_pos(cb, 0); test(cb, mem_opnd(64, RDI, 42), RAX); check_bytes(cb, "4885472A"); + cb_set_pos(cb, 0); test(cb, RAX, RAX); check_bytes(cb, "4885C0"); + cb_set_pos(cb, 0); test(cb, RAX, RSI); check_bytes(cb, "4885F0"); + cb_set_pos(cb, 0); test(cb, mem_opnd(64, RSI, 64), imm_opnd(~0x08)); check_bytes(cb, "48F74640F7FFFFFF"); + + // xchg + cb_set_pos(cb, 0); xchg(cb, RAX, RCX); check_bytes(cb, "4891"); + cb_set_pos(cb, 0); xchg(cb, RAX, R13); check_bytes(cb, "4995"); + cb_set_pos(cb, 0); xchg(cb, RCX, RBX); check_bytes(cb, "4887D9"); + cb_set_pos(cb, 0); xchg(cb, R9, R15); check_bytes(cb, "4D87F9"); + + // xor + cb_set_pos(cb, 0); xor(cb, EAX, EAX); check_bytes(cb, "31C0"); + + printf("Assembler tests done\n"); +} + +void assert_equal(expected, actual) +{ + if (expected != actual) { + fprintf(stderr, "expected %d, got %d\n", expected, actual); + exit(-1); + } +} + +void run_runtime_tests() +{ + printf("Running runtime tests\n"); + + codeblock_t codeblock; + codeblock_t* cb = &codeblock; + + uint8_t* mem_block = alloc_exec_mem(4096); + cb_init(cb, mem_block, 4096); + + int (*function)(void); + function = (int (*)(void))mem_block; + + #define TEST(BODY) cb_set_pos(cb, 0); BODY ret(cb); assert_equal(7, function()); + + // add + TEST({ mov(cb, RAX, imm_opnd(0)); add(cb, RAX, imm_opnd(7)); }) + TEST({ mov(cb, RAX, imm_opnd(0)); mov(cb, RCX, imm_opnd(7)); add(cb, RAX, RCX); }) + + // and + TEST({ mov(cb, RAX, imm_opnd(31)); and(cb, RAX, imm_opnd(7)); }) + TEST({ mov(cb, RAX, imm_opnd(31)); mov(cb, RCX, imm_opnd(7)); and(cb, RAX, RCX); }) + + // or + TEST({ mov(cb, RAX, imm_opnd(3)); or(cb, RAX, imm_opnd(4)); }) + TEST({ mov(cb, RAX, imm_opnd(3)); mov(cb, RCX, imm_opnd(4)); or(cb, RAX, RCX); }) + + // push/pop + TEST({ mov(cb, RCX, imm_opnd(7)); push(cb, RCX); pop(cb, RAX); }) + + // shr + TEST({ mov(cb, RAX, imm_opnd(31)); shr(cb, RAX, imm_opnd(2)); }) + + // sub + TEST({ mov(cb, RAX, imm_opnd(12)); sub(cb, RAX, imm_opnd(5)); }) + TEST({ mov(cb, RAX, imm_opnd(12)); mov(cb, RCX, imm_opnd(5)); sub(cb, RAX, RCX); }) + + // xor + TEST({ mov(cb, RAX, imm_opnd(13)); xor(cb, RAX, imm_opnd(10)); }) + TEST({ mov(cb, RAX, imm_opnd(13)); mov(cb, RCX, imm_opnd(10)); xor(cb, RAX, RCX); }) + + #undef TEST + + printf("Runtime tests done\n"); +} + +int main(int argc, char** argv) +{ + // suppress -Wunused-function + (void)alloc_code_page; + (void)free_code_page; + + run_assembler_tests(); + run_runtime_tests(); + + return 0; +} diff --git a/yjit_asm_tests.c b/yjit_asm_tests.c deleted file mode 100644 index 7650505fb3..0000000000 --- a/yjit_asm_tests.c +++ /dev/null @@ -1,433 +0,0 @@ -// For MAP_ANONYMOUS on GNU/Linux -#define _GNU_SOURCE 1 - -#include -#include -#include -#include - -// This test executable doesn't compile with the rest of Ruby -// so we need to define a rb_bug(). -_Noreturn -static void rb_bug(char *message) -{ - fprintf(stderr, "%s\n", message); - abort(); -} - -#include "yjit_asm.c" - -// Print the bytes in a code block -void print_bytes(codeblock_t* cb) -{ - for (uint32_t i = 0; i < cb->write_pos; ++i) - { - printf("%02X", (int)cb->mem_block[i]); - } - - printf("\n"); -} - -// Check that the code block contains the given sequence of bytes -void check_bytes(codeblock_t* cb, const char* bytes) -{ - printf("checking encoding: %s\n", bytes); - - size_t len = strlen(bytes); - assert (len % 2 == 0); - size_t num_bytes = len / 2; - - if (cb->write_pos != num_bytes) - { - fprintf(stderr, "incorrect encoding length, expected %ld, got %d\n", - num_bytes, - cb->write_pos - ); - printf("%s\n", bytes); - print_bytes(cb); - exit(-1); - } - - for (uint32_t i = 0; i < num_bytes; ++i) - { - char byte_str[] = {0, 0, 0, 0}; - strncpy(byte_str, bytes + (2 * i), 2); - char* endptr; - long int byte = strtol(byte_str, &endptr, 16); - - uint8_t cb_byte = cb->mem_block[i]; - - if (cb_byte != byte) - { - fprintf(stderr, "incorrect encoding at position %d, expected %02X, got %02X\n", - i, - (int)byte, - (int)cb_byte - ); - printf("%s\n", bytes); - print_bytes(cb); - exit(-1); - } - } -} - -void run_assembler_tests() -{ - printf("Running assembler tests\n"); - - codeblock_t cb_obj; - codeblock_t* cb = &cb_obj; - uint8_t* mem_block = alloc_exec_mem(4096); - cb_init(cb, mem_block, 4096); - - // add - cb_set_pos(cb, 0); add(cb, CL, imm_opnd(3)); check_bytes(cb, "80C103"); - cb_set_pos(cb, 0); add(cb, CL, BL); check_bytes(cb, "00D9"); - cb_set_pos(cb, 0); add(cb, CL, SPL); check_bytes(cb, "4000E1"); - cb_set_pos(cb, 0); add(cb, CX, BX); check_bytes(cb, "6601D9"); - cb_set_pos(cb, 0); add(cb, RAX, RBX); check_bytes(cb, "4801D8"); - cb_set_pos(cb, 0); add(cb, ECX, EDX); check_bytes(cb, "01D1"); - cb_set_pos(cb, 0); add(cb, RDX, R14); check_bytes(cb, "4C01F2"); - cb_set_pos(cb, 0); add(cb, mem_opnd(64, RAX, 0), RDX); check_bytes(cb, "480110"); - cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 0)); check_bytes(cb, "480310"); - cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 8)); check_bytes(cb, "48035008"); - cb_set_pos(cb, 0); add(cb, RDX, mem_opnd(64, RAX, 255)); check_bytes(cb, "480390FF000000"); - cb_set_pos(cb, 0); add(cb, mem_opnd(64, RAX, 127), imm_opnd(255)); check_bytes(cb, "4881407FFF000000"); - cb_set_pos(cb, 0); add(cb, mem_opnd(32, RAX, 0), EDX); check_bytes(cb, "0110"); - cb_set_pos(cb, 0); add(cb, RSP, imm_opnd(8)); check_bytes(cb, "4883C408"); - cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(8)); check_bytes(cb, "83C108"); - cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(255)); check_bytes(cb, "81C1FF000000"); - - // and - cb_set_pos(cb, 0); and(cb, EBP, R12D); check_bytes(cb, "4421E5"); - cb_set_pos(cb, 0); and(cb, mem_opnd(64, RAX, 0), imm_opnd(0x08)); check_bytes(cb, "48832008"); - - // call - { - cb_set_pos(cb, 0); - uint32_t fn_label = cb_new_label(cb, "foo"); - call_label(cb, fn_label); - cb_link_labels(cb); - check_bytes(cb, "E8FBFFFFFF"); - } - cb_set_pos(cb, 0); call(cb, RAX); check_bytes(cb, "FFD0"); - cb_set_pos(cb, 0); call(cb, mem_opnd(64, RSP, 8)); check_bytes(cb, "FF542408"); - - // cmovcc - cb_set_pos(cb, 0); cmovg(cb, ESI, EDI); check_bytes(cb, "0F4FF7"); - cb_set_pos(cb, 0); cmovg(cb, ESI, mem_opnd(32, RBP, 12)); check_bytes(cb, "0F4F750C"); - cb_set_pos(cb, 0); cmovl(cb, EAX, ECX); check_bytes(cb, "0F4CC1"); - cb_set_pos(cb, 0); cmovl(cb, RBX, RBP); check_bytes(cb, "480F4CDD"); - cb_set_pos(cb, 0); cmovle(cb, ESI, mem_opnd(32, RSP, 4)); check_bytes(cb, "0F4E742404"); - - // cmp - cb_set_pos(cb, 0); cmp(cb, CL, DL); check_bytes(cb, "38D1"); - cb_set_pos(cb, 0); cmp(cb, ECX, EDI); check_bytes(cb, "39F9"); - cb_set_pos(cb, 0); cmp(cb, RDX, mem_opnd(64, R12, 0)); check_bytes(cb, "493B1424"); - cb_set_pos(cb, 0); cmp(cb, RAX, imm_opnd(2)); check_bytes(cb, "4883F802"); - - // cqo - cb_set_pos(cb, 0); cqo(cb); check_bytes(cb, "4899"); - - // div - /* - test( - delegate void (CodeBlock cb) { cb.div(X86Opnd(EDX)); }, - "F7F2" - ); - test( - delegate void (CodeBlock cb) { cb.div(X86Opnd(32, RSP, -12)); }, - "F77424F4" - ); - */ - - // jcc to label - { - cb_set_pos(cb, 0); - uint32_t loop_label = cb_new_label(cb, "loop"); - jge_label(cb, loop_label); - cb_link_labels(cb); - check_bytes(cb, "0F8DFAFFFFFF"); - } - { - cb_set_pos(cb, 0); - uint32_t loop_label = cb_new_label(cb, "loop"); - jo_label(cb, loop_label); - cb_link_labels(cb); - check_bytes(cb, "0F80FAFFFFFF"); - } - - // jmp to label - { - cb_set_pos(cb, 0); - uint32_t loop_label = cb_new_label(cb, "loop"); - jmp_label(cb, loop_label); - cb_link_labels(cb); - check_bytes(cb, "E9FBFFFFFF"); - } - - // jmp with RM operand - cb_set_pos(cb, 0); jmp_rm(cb, R12); check_bytes(cb, "41FFE4"); - - // lea - cb_set_pos(cb, 0); lea(cb, RDX, mem_opnd(64, RCX, 8)); check_bytes(cb, "488D5108"); - cb_set_pos(cb, 0); lea(cb, RAX, mem_opnd(8, RIP, 0)); check_bytes(cb, "488D0500000000"); - cb_set_pos(cb, 0); lea(cb, RAX, mem_opnd(8, RIP, 5)); check_bytes(cb, "488D0505000000"); - cb_set_pos(cb, 0); lea(cb, RDI, mem_opnd(8, RIP, 5)); check_bytes(cb, "488D3D05000000"); - - // mov - cb_set_pos(cb, 0); mov(cb, EAX, imm_opnd(7)); check_bytes(cb, "B807000000"); - cb_set_pos(cb, 0); mov(cb, EAX, imm_opnd(-3)); check_bytes(cb, "B8FDFFFFFF"); - cb_set_pos(cb, 0); mov(cb, R15, imm_opnd(3)); check_bytes(cb, "49BF0300000000000000"); - cb_set_pos(cb, 0); mov(cb, EAX, EBX); check_bytes(cb, "89D8"); - cb_set_pos(cb, 0); mov(cb, EAX, ECX); check_bytes(cb, "89C8"); - cb_set_pos(cb, 0); mov(cb, EDX, mem_opnd(32, RBX, 128)); check_bytes(cb, "8B9380000000"); - /* - test( - delegate void (CodeBlock cb) { cb.mov(X86Opnd(AL), X86Opnd(8, RCX, 0, 1, RDX)); }, - "8A0411" - ); - */ - cb_set_pos(cb, 0); mov(cb, CL, R9B); check_bytes(cb, "4488C9"); - cb_set_pos(cb, 0); mov(cb, RBX, RAX); check_bytes(cb, "4889C3"); - cb_set_pos(cb, 0); mov(cb, RDI, RBX); check_bytes(cb, "4889DF"); - cb_set_pos(cb, 0); mov(cb, SIL, imm_opnd(11)); check_bytes(cb, "40B60B"); - cb_set_pos(cb, 0); mov(cb, mem_opnd(8, RSP, 0), imm_opnd(-3)); check_bytes(cb, "C60424FD"); - cb_set_pos(cb, 0); mov(cb, mem_opnd(64, RDI, 8), imm_opnd(1)); check_bytes(cb, "48C7470801000000"); - - // movsx - cb_set_pos(cb, 0); movsx(cb, AX, AL); check_bytes(cb, "660FBEC0"); - cb_set_pos(cb, 0); movsx(cb, EDX, AL); check_bytes(cb, "0FBED0"); - cb_set_pos(cb, 0); movsx(cb, RAX, BL); check_bytes(cb, "480FBEC3"); - cb_set_pos(cb, 0); movsx(cb, ECX, AX); check_bytes(cb, "0FBFC8"); - cb_set_pos(cb, 0); movsx(cb, R11, CL); check_bytes(cb, "4C0FBED9"); - cb_set_pos(cb, 0); movsx(cb, R10, mem_opnd(32, RSP, 12)); check_bytes(cb, "4C6354240C"); - cb_set_pos(cb, 0); movsx(cb, RAX, mem_opnd(8, RSP, 0)); check_bytes(cb, "480FBE0424"); - - // neg - cb_set_pos(cb, 0); neg(cb, RAX); check_bytes(cb, "48F7D8"); - - // nop - cb_set_pos(cb, 0); nop(cb, 1); check_bytes(cb, "90"); - - // not - cb_set_pos(cb, 0); not(cb, AX); check_bytes(cb, "66F7D0"); - cb_set_pos(cb, 0); not(cb, EAX); check_bytes(cb, "F7D0"); - cb_set_pos(cb, 0); not(cb, mem_opnd(64, R12, 0)); check_bytes(cb, "49F71424"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 301)); check_bytes(cb, "F794242D010000"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 0)); check_bytes(cb, "F71424"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSP, 3)); check_bytes(cb, "F7542403"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RBP, 0)); check_bytes(cb, "F75500"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RBP, 13)); check_bytes(cb, "F7550D"); - cb_set_pos(cb, 0); not(cb, RAX); check_bytes(cb, "48F7D0"); - cb_set_pos(cb, 0); not(cb, R11); check_bytes(cb, "49F7D3"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RAX, 0)); check_bytes(cb, "F710"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RSI, 0)); check_bytes(cb, "F716"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDI, 0)); check_bytes(cb, "F717"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, 55)); check_bytes(cb, "F75237"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, 1337)); check_bytes(cb, "F79239050000"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, -55)); check_bytes(cb, "F752C9"); - cb_set_pos(cb, 0); not(cb, mem_opnd(32, RDX, -555)); check_bytes(cb, "F792D5FDFFFF"); - /* - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 0, 1, RBX)); }, - "F71418" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 0, 1, R12)); }, - "42F71420" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 0, 1, R12)); }, - "43F71427" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 1, R12)); }, - "43F7542705" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 8, R12)); }, - "43F754E705" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R15, 5, 8, R13)); }, - "43F754EF05" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R12, 5, 4, R9)); }, - "43F7548C05" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, R12, 301, 4, R9)); }, - "43F7948C2D010000" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RAX, 5, 4, RDX)); }, - "F7549005" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(64, RAX, 0, 2, RDX)); }, - "48F71450" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RSP, 0, 1, RBX)); }, - "F7141C" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RSP, 3, 1, RBX)); }, - "F7541C03" - ); - test( - delegate void (CodeBlock cb) { cb.not(X86Opnd(32, RBP, 13, 1, RDX)); }, - "F754150D" - ); - */ - - // or - cb_set_pos(cb, 0); or(cb, EDX, ESI); check_bytes(cb, "09F2"); - - // pop - cb_set_pos(cb, 0); pop(cb, RAX); check_bytes(cb, "58"); - cb_set_pos(cb, 0); pop(cb, RBX); check_bytes(cb, "5B"); - cb_set_pos(cb, 0); pop(cb, RSP); check_bytes(cb, "5C"); - cb_set_pos(cb, 0); pop(cb, RBP); check_bytes(cb, "5D"); - cb_set_pos(cb, 0); pop(cb, R12); check_bytes(cb, "415C"); - cb_set_pos(cb, 0); pop(cb, mem_opnd(64, RAX, 0)); check_bytes(cb, "8F00"); - cb_set_pos(cb, 0); pop(cb, mem_opnd(64, R8, 0)); check_bytes(cb, "418F00"); - cb_set_pos(cb, 0); pop(cb, mem_opnd(64, R8, 3)); check_bytes(cb, "418F4003"); - cb_set_pos(cb, 0); pop(cb, mem_opnd_sib(64, RAX, RCX, 8, 3)); check_bytes(cb, "8F44C803"); - cb_set_pos(cb, 0); pop(cb, mem_opnd_sib(64, R8, RCX, 8, 3)); check_bytes(cb, "418F44C803"); - - // push - cb_set_pos(cb, 0); push(cb, RAX); check_bytes(cb, "50"); - cb_set_pos(cb, 0); push(cb, RBX); check_bytes(cb, "53"); - cb_set_pos(cb, 0); push(cb, R12); check_bytes(cb, "4154"); - cb_set_pos(cb, 0); push(cb, mem_opnd(64, RAX, 0)); check_bytes(cb, "FF30"); - cb_set_pos(cb, 0); push(cb, mem_opnd(64, R8, 0)); check_bytes(cb, "41FF30"); - cb_set_pos(cb, 0); push(cb, mem_opnd(64, R8, 3)); check_bytes(cb, "41FF7003"); - cb_set_pos(cb, 0); push(cb, mem_opnd_sib(64, RAX, RCX, 8, 3)); check_bytes(cb, "FF74C803"); - cb_set_pos(cb, 0); push(cb, mem_opnd_sib(64, R8, RCX, 8, 3)); check_bytes(cb, "41FF74C803"); - - // ret - cb_set_pos(cb, 0); ret(cb); check_bytes(cb, "C3"); - - // sal - cb_set_pos(cb, 0); sal(cb, CX, imm_opnd(1)); check_bytes(cb, "66D1E1"); - cb_set_pos(cb, 0); sal(cb, ECX, imm_opnd(1)); check_bytes(cb, "D1E1"); - cb_set_pos(cb, 0); sal(cb, EBP, imm_opnd(5)); check_bytes(cb, "C1E505"); - cb_set_pos(cb, 0); sal(cb, mem_opnd(32, RSP, 68), imm_opnd(1)); check_bytes(cb, "D1642444"); - - // sar - cb_set_pos(cb, 0); sar(cb, EDX, imm_opnd(1)); check_bytes(cb, "D1FA"); - - // shr - cb_set_pos(cb, 0); shr(cb, R14, imm_opnd(7)); check_bytes(cb, "49C1EE07"); - - /* - // sqrtsd - test( - delegate void (CodeBlock cb) { cb.sqrtsd(X86Opnd(XMM2), X86Opnd(XMM6)); }, - "F20F51D6" - ); - */ - - // sub - cb_set_pos(cb, 0); sub(cb, EAX, imm_opnd(1)); check_bytes(cb, "83E801"); - cb_set_pos(cb, 0); sub(cb, RAX, imm_opnd(2)); check_bytes(cb, "4883E802"); - - // test - cb_set_pos(cb, 0); test(cb, AL, AL); check_bytes(cb, "84C0"); - cb_set_pos(cb, 0); test(cb, AX, AX); check_bytes(cb, "6685C0"); - cb_set_pos(cb, 0); test(cb, CL, imm_opnd(8)); check_bytes(cb, "F6C108"); - cb_set_pos(cb, 0); test(cb, DL, imm_opnd(7)); check_bytes(cb, "F6C207"); - cb_set_pos(cb, 0); test(cb, RCX, imm_opnd(8)); check_bytes(cb, "F6C108"); - cb_set_pos(cb, 0); test(cb, mem_opnd(8, RDX, 8), imm_opnd(8)); check_bytes(cb, "F6420808"); - cb_set_pos(cb, 0); test(cb, mem_opnd(8, RDX, 8), imm_opnd(255)); check_bytes(cb, "F64208FF"); - cb_set_pos(cb, 0); test(cb, DX, imm_opnd(0xFFFF)); check_bytes(cb, "66F7C2FFFF"); - cb_set_pos(cb, 0); test(cb, mem_opnd(16, RDX, 8), imm_opnd(0xFFFF)); check_bytes(cb, "66F74208FFFF"); - cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, 0), imm_opnd(1)); check_bytes(cb, "F60601"); - cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, 16), imm_opnd(1)); check_bytes(cb, "F6461001"); - cb_set_pos(cb, 0); test(cb, mem_opnd(8, RSI, -16), imm_opnd(1)); check_bytes(cb, "F646F001"); - cb_set_pos(cb, 0); test(cb, mem_opnd(32, RSI, 64), EAX); check_bytes(cb, "854640"); - cb_set_pos(cb, 0); test(cb, mem_opnd(64, RDI, 42), RAX); check_bytes(cb, "4885472A"); - cb_set_pos(cb, 0); test(cb, RAX, RAX); check_bytes(cb, "4885C0"); - cb_set_pos(cb, 0); test(cb, RAX, RSI); check_bytes(cb, "4885F0"); - cb_set_pos(cb, 0); test(cb, mem_opnd(64, RSI, 64), imm_opnd(~0x08)); check_bytes(cb, "48F74640F7FFFFFF"); - - // xchg - cb_set_pos(cb, 0); xchg(cb, RAX, RCX); check_bytes(cb, "4891"); - cb_set_pos(cb, 0); xchg(cb, RAX, R13); check_bytes(cb, "4995"); - cb_set_pos(cb, 0); xchg(cb, RCX, RBX); check_bytes(cb, "4887D9"); - cb_set_pos(cb, 0); xchg(cb, R9, R15); check_bytes(cb, "4D87F9"); - - // xor - cb_set_pos(cb, 0); xor(cb, EAX, EAX); check_bytes(cb, "31C0"); - - printf("Assembler tests done\n"); -} - -void assert_equal(expected, actual) -{ - if (expected != actual) { - fprintf(stderr, "expected %d, got %d\n", expected, actual); - exit(-1); - } -} - -void run_runtime_tests() -{ - printf("Running runtime tests\n"); - - codeblock_t codeblock; - codeblock_t* cb = &codeblock; - - uint8_t* mem_block = alloc_exec_mem(4096); - cb_init(cb, mem_block, 4096); - - int (*function)(void); - function = (int (*)(void))mem_block; - - #define TEST(BODY) cb_set_pos(cb, 0); BODY ret(cb); assert_equal(7, function()); - - // add - TEST({ mov(cb, RAX, imm_opnd(0)); add(cb, RAX, imm_opnd(7)); }) - TEST({ mov(cb, RAX, imm_opnd(0)); mov(cb, RCX, imm_opnd(7)); add(cb, RAX, RCX); }) - - // and - TEST({ mov(cb, RAX, imm_opnd(31)); and(cb, RAX, imm_opnd(7)); }) - TEST({ mov(cb, RAX, imm_opnd(31)); mov(cb, RCX, imm_opnd(7)); and(cb, RAX, RCX); }) - - // or - TEST({ mov(cb, RAX, imm_opnd(3)); or(cb, RAX, imm_opnd(4)); }) - TEST({ mov(cb, RAX, imm_opnd(3)); mov(cb, RCX, imm_opnd(4)); or(cb, RAX, RCX); }) - - // push/pop - TEST({ mov(cb, RCX, imm_opnd(7)); push(cb, RCX); pop(cb, RAX); }) - - // shr - TEST({ mov(cb, RAX, imm_opnd(31)); shr(cb, RAX, imm_opnd(2)); }) - - // sub - TEST({ mov(cb, RAX, imm_opnd(12)); sub(cb, RAX, imm_opnd(5)); }) - TEST({ mov(cb, RAX, imm_opnd(12)); mov(cb, RCX, imm_opnd(5)); sub(cb, RAX, RCX); }) - - // xor - TEST({ mov(cb, RAX, imm_opnd(13)); xor(cb, RAX, imm_opnd(10)); }) - TEST({ mov(cb, RAX, imm_opnd(13)); mov(cb, RCX, imm_opnd(10)); xor(cb, RAX, RCX); }) - - #undef TEST - - printf("Runtime tests done\n"); -} - -int main(int argc, char** argv) -{ - // suppress -Wunused-function - (void)alloc_code_page; - (void)free_code_page; - - run_assembler_tests(); - run_runtime_tests(); - - return 0; -} -- cgit v1.2.3