Progress on porting x86 assembler for MicroJIT

This commit is contained in:
Maxime Chevalier-Boisvert 2020-09-04 15:56:00 -04:00 committed by Alan Wu
parent 07dd5f22a5
commit 0a5dcc056e
5 changed files with 294 additions and 0 deletions

View File

@ -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) \

6
test_asm.sh Executable file
View File

@ -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

183
ujit_asm.c Normal file
View File

@ -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;
}
}

72
ujit_asm.h Normal file
View File

@ -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

31
ujit_asm_tests.c Normal file
View File

@ -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;
}