diff options
author | Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> | 2020-09-04 15:56:00 -0400 |
---|---|---|
committer | Alan Wu <XrXr@users.noreply.github.com> | 2021-10-20 18:19:22 -0400 |
commit | 0a5dcc056e22bab849d8d7877928d300201af823 (patch) | |
tree | b633cb97d8bacda6f8b5a5df533404c260a2e565 | |
parent | 07dd5f22a5d3127981eea7602bd3d6c221f5d12e (diff) | |
download | ruby-0a5dcc056e22bab849d8d7877928d300201af823.tar.gz |
Progress on porting x86 assembler for MicroJIT
-rw-r--r-- | common.mk | 2 | ||||
-rwxr-xr-x | test_asm.sh | 6 | ||||
-rw-r--r-- | ujit_asm.c | 183 | ||||
-rw-r--r-- | ujit_asm.h | 72 | ||||
-rw-r--r-- | ujit_asm_tests.c | 31 |
5 files changed, 294 insertions, 0 deletions
@@ -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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <assert.h> + +// For mmapp() +#include <sys/mman.h> + +#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 <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +// 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 <stdio.h> +#include <stdlib.h> +#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; +} |