diff --git a/common.mk b/common.mk index 45ebaab86a..59d5dcea5d 100644 --- a/common.mk +++ b/common.mk @@ -150,6 +150,8 @@ COMMONOBJS = array.$(OBJEXT) \ vm_dump.$(OBJEXT) \ vm_sync.$(OBJEXT) \ vm_trace.$(OBJEXT) \ + ujit_asm.$(OBJEXT) \ + ujit_asm_tests.$(OBJEXT) \ $(COROUTINE_OBJ) \ $(DTRACE_OBJ) \ $(BUILTIN_ENCOBJS) \ diff --git a/test_asm.sh b/test_asm.sh new file mode 100755 index 0000000000..446dd36d65 --- /dev/null +++ b/test_asm.sh @@ -0,0 +1,6 @@ +# NOTE: I did not know what would be the sensible way to compile +# and run these tests from the Ruby makefile + +clang -std=c99 -Wall ujit_asm.c ujit_asm_tests.c -o asm_test + +./asm_test diff --git a/ujit_asm.c b/ujit_asm.c new file mode 100644 index 0000000000..7ff8792059 --- /dev/null +++ b/ujit_asm.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +// For mmapp() +#include + +#include "ujit_asm.h" + +// TODO: give ujit_examples.h some more meaningful file name +#include "ujit_examples.h" + +void cb_init(codeblock_t* cb, size_t mem_size) +{ + // Map the memory as executable + cb->mem_block = (uint8_t*)mmap( + NULL, + mem_size, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0 + ); + + // Check that the memory mapping was successful + if (cb->mem_block == MAP_FAILED) + { + fprintf(stderr, "mmap call failed\n"); + exit(-1); + } + + cb->mem_size = mem_size; + cb->write_pos = 0; + cb->num_labels = 0; + cb->num_refs = 0; +} + +// Get a direct pointer into the executable memory block +uint8_t* cb_get_ptr(codeblock_t* cb, size_t index) +{ + assert (index < cb->mem_size); + return &cb->mem_block[index]; +} + +// Write a byte at the current position +void cb_write_byte(codeblock_t* cb, uint8_t byte) +{ + assert (cb->mem_block); + assert (cb->write_pos + 1 <= cb->mem_size); + cb->mem_block[cb->write_pos++] = byte; +} + +// Write multiple bytes starting from the current position +void cb_write_bytes(codeblock_t* cb, size_t num_bytes, ...) +{ + va_list va; + va_start(va, num_bytes); + + for (size_t i = 0; i < num_bytes; ++i) + { + uint8_t byte = va_arg(va, int); + cb_write_byte(cb, byte); + } + + va_end(va); +} + +// Write a signed integer over a given number of bits at the current position +void cb_write_int(codeblock_t* cb, uint64_t val, size_t num_bits) +{ + assert (num_bits > 0); + assert (num_bits % 8 == 0); + + // Switch on the number of bits + switch (num_bits) + { + case 8: + cb_write_byte(cb, (uint8_t)val); + break; + + case 16: + cb_write_bytes( + cb, + 2, + (uint8_t)((val >> 0) & 0xFF), + (uint8_t)((val >> 8) & 0xFF) + ); + break; + + case 32: + cb_write_bytes( + cb, + 4, + (uint8_t)((val >> 0) & 0xFF), + (uint8_t)((val >> 8) & 0xFF), + (uint8_t)((val >> 16) & 0xFF), + (uint8_t)((val >> 24) & 0xFF) + ); + break; + + default: + { + // Compute the size in bytes + size_t num_bytes = num_bits / 8; + + // Write out the bytes + for (size_t i = 0; i < num_bytes; ++i) + { + uint8_t byte_val = (uint8_t)(val & 0xFF); + cb_write_byte(cb, byte_val); + val >>= 8; + } + } + } +} + +// nop - Noop, one or multiple bytes long +void nop(codeblock_t* cb, size_t length) +{ + switch (length) + { + case 0: + break; + + case 1: + //cb.writeASM("nop1"); + cb_write_byte(cb, 0x90); + break; + + case 2: + //cb.writeASM("nop2"); + cb_write_bytes(cb, 2, 0x66,0x90); + break; + + case 3: + //cb.writeASM("nop3"); + cb_write_bytes(cb, 3, 0x0F,0x1F,0x00); + break; + + case 4: + //cb.writeASM("nop4"); + cb_write_bytes(cb, 4, 0x0F,0x1F,0x40,0x00); + break; + + case 5: + //cb.writeASM("nop5"); + cb_write_bytes(cb, 5, 0x0F,0x1F,0x44,0x00,0x00); + break; + + case 6: + //cb.writeASM("nop6"); + cb_write_bytes(cb, 6, 0x66,0x0F,0x1F,0x44,0x00,0x00); + break; + + case 7: + //cb.writeASM("nop7"); + cb_write_bytes(cb, 7, 0x0F,0x1F,0x80,0x00,0x00,0x00,0x00); + break; + + case 8: + //cb.writeASM("nop8"); + cb_write_bytes(cb, 8, 0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00); + break; + + case 9: + //cb.writeASM("nop9"); + cb_write_bytes(cb, 9, 0x66,0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00); + break; + + default: + { + size_t written = 0; + while (written + 9 <= length) + { + nop(cb, 9); + written += 9; + } + nop(cb, length - written); + } + break; + } +} diff --git a/ujit_asm.h b/ujit_asm.h new file mode 100644 index 0000000000..998c171d5d --- /dev/null +++ b/ujit_asm.h @@ -0,0 +1,72 @@ +#ifndef UJIT_ASM_H +#define UJIT_ASM_H 1 + +#include +#include +#include + +// Maximum number of labels to link +#define MAX_LABELS 32 + +// Maximum number of label references +#define MAX_LABEL_REFS 32 + +typedef struct LabelRef +{ + // Position where the label reference is in the code block + size_t pos; + + // Label which this refers to + size_t label_idx; + +} labelref_t; + +typedef struct CodeBlock +{ + // Memory block + uint8_t* mem_block; + + // Memory block size + size_t mem_size; + + /// Current writing position + size_t write_pos; + + // Table of registered label addresses + size_t label_addrs[MAX_LABELS]; + + // References to labels + labelref_t label_refs[MAX_LABEL_REFS]; + + // Number of labels registeered + size_t num_labels; + + // Number of references to labels + size_t num_refs; + + // TODO: system for disassembly/comment strings, indexed by position + + // Flag to enable or disable comments + bool has_asm; + +} codeblock_t; + +void cb_init(codeblock_t* cb, size_t mem_size); +uint8_t* cb_get_ptr(codeblock_t* cb, size_t index); +void cb_write_byte(codeblock_t* cb, uint8_t byte); +void cb_write_bytes(codeblock_t* cb, size_t num_bytes, ...); +void cb_write_int(codeblock_t* cb, uint64_t val, size_t num_bits); + +// TODO: +// prologue and epilogue functions +// cb_write_prologue() +// cb_write_epilogue +// Test those out + +void nop(codeblock_t* cb, size_t length); + + + + + +#endif diff --git a/ujit_asm_tests.c b/ujit_asm_tests.c new file mode 100644 index 0000000000..0c94fb8b04 --- /dev/null +++ b/ujit_asm_tests.c @@ -0,0 +1,31 @@ +#include +#include +#include "ujit_asm.h" + +//fprintf(stderr, format); +//exit(-1) + +void run_tests() +{ + printf("Running assembler tests\n"); + + codeblock_t cb; + cb_init(&cb, 4096); + + + + + + + + + + printf("Assembler tests done\n"); +} + +int main(int argc, char** argv) +{ + run_tests(); + + return 0; +}