2026-05-21 00:31:25 +02:00

189 lines
3.7 KiB
C

#include "jit_x86.h"
#include "codegen_x86.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
static uint8_t reg_enc(PhysReg r)
{
switch (r) {
case RAX:
return 0;
case RCX:
return 1;
case RDX:
return 2;
case RBX:
return 3;
case RSI:
return 6;
case RDI:
return 7;
case R8:
return 8;
case R9:
return 9;
default:
return 0;
}
}
typedef struct {
uint8_t* buf;
size_t cap;
size_t len;
} AsmBuf;
static void emit8(AsmBuf* a, uint8_t v)
{
if (a->len >= a->cap) {
fprintf(stderr, "JIT buffer overflow\n");
abort();
}
a->buf[a->len++] = v;
}
[[maybe_unused]]
static void emit32(AsmBuf* a, uint32_t v)
{
if (a->len >= a->cap) {
fprintf(stderr, "JIT buffer overflow\n");
abort();
}
memcpy(&a->buf[a->len], &v, 4);
a->len += 4;
}
static void emit64(AsmBuf* a, uint64_t v)
{
if (a->len >= a->cap) {
fprintf(stderr, "JIT buffer overflow\n");
abort();
}
memcpy(&a->buf[a->len], &v, 8);
a->len += 8;
}
static void emit_mov_imm64(AsmBuf* a, PhysReg dst, uint64_t imm)
{
uint8_t r = reg_enc(dst);
// mov r64, imm64 = 48 B8+rd imm64
emit8(a, 0x48);
emit8(a, 0xB8 + r);
emit64(a, imm);
}
static uint8_t rex_enc(uint8_t dst, uint8_t src)
{
return 0x40 | ((dst & 8) ? 0x01 : 0) | // B
((src & 8) ? 0x04 : 0); // R
}
static void emit_alu_rr(
AsmBuf* a, uint8_t rex, uint8_t op, PhysReg dst, PhysReg src)
{
(void)rex;
uint8_t d = reg_enc(dst);
uint8_t s = reg_enc(src);
emit8(a, rex_enc(d, s));
emit8(a, op);
uint8_t modrm = 0xC0 | ((s & 7) << 3) | (d & 7);
emit8(a, modrm);
}
static void emit_add(AsmBuf* a, PhysReg d, PhysReg s)
{
emit_alu_rr(a, 0x48, 0x01, d, s);
}
static void emit_sub(AsmBuf* a, PhysReg d, PhysReg s)
{
emit_alu_rr(a, 0x48, 0x29, d, s);
}
static void emit_mul(AsmBuf* a, PhysReg d, PhysReg s)
{
emit8(a, 0x48);
emit8(a, 0x0F);
emit8(a, 0xAF);
emit8(a, 0xC0 | (reg_enc(s) << 3) | reg_enc(d));
}
static void emit_ret(AsmBuf* a)
{
emit8(a, 0xC3);
}
static size_t align_page(size_t n)
{
size_t page = sysconf(_SC_PAGESIZE);
return (n + page - 1) & ~(page - 1);
}
JitFn cg_block_emit_x86_machine_code(CgBlock* block)
{
AsmBuf a = { 0 };
a.cap = align_page(1024);
a.len = 0;
a.buf = mmap(NULL,
a.cap,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if (a.buf == MAP_FAILED) {
return NULL;
}
for (size_t i = 0; i < block->count; i++) {
CgInst* inst = block->insts[i];
switch (inst->op) {
case CG_IMM64:
emit_mov_imm64(&a, inst->dst, inst->imm);
break;
case CG_ADD8:
emit_add(&a, inst->dst, inst->binop.rhs);
break;
case CG_SUB8:
emit_sub(&a, inst->dst, inst->binop.rhs);
break;
case CG_MUL8:
emit_mul(&a, inst->dst, inst->binop.rhs);
break;
default:
break;
}
}
// ensure result is in rax (ABI return register)
emit_ret(&a);
// make executable
mprotect(a.buf, align_page(a.len), PROT_READ | PROT_EXEC);
for (size_t i = 0; i < a.len; i++) {
printf("%02x ", a.buf[i]);
}
printf("\n");
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
return (JitFn)a.buf;
#pragma GCC diagnostic pop
}