(TEMP) some more insns, cleanup; idea for app using jit
This commit is contained in:
parent
7750618796
commit
2fa4e98197
7 changed files with 151 additions and 67 deletions
1
Makefile
1
Makefile
|
|
@ -62,6 +62,7 @@ KERNEL_SOURCES := \
|
|||
src/app_snake.c \
|
||||
src/app_tron.c \
|
||||
src/app_paint.c \
|
||||
src/app_jitpaint.c \
|
||||
src/font.c \
|
||||
src/karlos-logo.c \
|
||||
src/tlsf.c \
|
||||
|
|
|
|||
|
|
@ -13,12 +13,17 @@ enum op {
|
|||
ADD, // ra += rb
|
||||
MOV, // ra = rb
|
||||
MOVI, // ra = imm
|
||||
SHLI,
|
||||
AND,
|
||||
ANDI,
|
||||
OR,
|
||||
RET, // return
|
||||
LD, // ra = mem[rb + imm]
|
||||
SD, // mem[rb + imm] = ra
|
||||
LW,
|
||||
LWU,
|
||||
SW,
|
||||
// TODO other load types
|
||||
};
|
||||
|
||||
enum reg {
|
||||
|
|
@ -29,14 +34,20 @@ enum reg {
|
|||
R4,
|
||||
R5,
|
||||
R6,
|
||||
R7
|
||||
R7,
|
||||
// used internally, do not use
|
||||
RSCRATCH
|
||||
};
|
||||
|
||||
#define GEN_ADDI(my_ra, i) ((struct insn) { .op = ADDI, .ra = my_ra, .imm = i })
|
||||
#define GEN_ADDI(my_ra, i) ((struct insn) { .op = ADDI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_ADD(my_ra, my_rb) ((struct insn) { .op = ADD, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_ANDI(my_ra, i) ((struct insn) { .op = ANDI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_AND(my_ra, my_rb) ((struct insn) { .op = AND, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_MOV(my_ra, my_rb) ((struct insn) { .op = MOV, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_MOVI(my_ra, i) ((struct insn) { .op = MOVI, .ra = my_ra, .imm = i })
|
||||
#define GEN_RET() ((struct insn) { .op = RET })
|
||||
#define GEN_MOVI(my_ra, i) ((struct insn) { .op = MOVI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_SHLI(my_ra, i) ((struct insn) { .op = SHLI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_OR(my_ra, my_rb) ((struct insn) { .op = OR, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_RET() ((struct insn) { .op = RET, .ra = 0, .rb = 0 })
|
||||
#define GEN_LD(reg, addr, i) ((struct insn) { .op = LD, .ra = reg, .rb = addr, .imm = i })
|
||||
#define GEN_LW(reg, addr, i) ((struct insn) { .op = LW, .ra = reg, .rb = addr, .imm = i })
|
||||
#define GEN_LWU(reg, addr, i) ((struct insn) { .op = LWU, .ra = reg, .rb = addr, .imm = i })
|
||||
|
|
|
|||
|
|
@ -556,6 +556,7 @@ extern struct app_template app_tron_template;
|
|||
extern struct app_template app_paint_template;
|
||||
extern struct app_template app_diskviewer_template;
|
||||
extern struct app_template app_hexer_template;
|
||||
extern struct app_template app_jitpaint_template;
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
|
|
@ -588,6 +589,10 @@ struct {
|
|||
{
|
||||
"hexer",
|
||||
&app_hexer_template,
|
||||
},
|
||||
{
|
||||
"jitpaint",
|
||||
&app_jitpaint_template,
|
||||
}
|
||||
};
|
||||
#define NUM_KNOWN_APPS (sizeof(known_apps)/sizeof(known_apps[0]))
|
||||
|
|
|
|||
48
src/app_jitpaint.c
Normal file
48
src/app_jitpaint.c
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#include "color.h"
|
||||
#include "keyboard.h"
|
||||
#include "std.h"
|
||||
#include "app.h"
|
||||
#include "tlsf.h"
|
||||
#include "jit.h"
|
||||
#include "widget.h"
|
||||
|
||||
#define CANVAS_WIDTH 256
|
||||
#define CANVAS_HEIGHT 256
|
||||
|
||||
struct app_jitpaint {
|
||||
struct widget *canvas;
|
||||
};
|
||||
|
||||
struct app_creation app_jitpaint_create() {
|
||||
struct app_jitpaint *app = kern_alloc(sizeof *app);
|
||||
struct widget *canvas = widget_new_canvas(CANVAS_WIDTH, CANVAS_HEIGHT, NULL, NULL);
|
||||
app->canvas = canvas;
|
||||
return (struct app_creation){ .ptr = app, .root = canvas };
|
||||
}
|
||||
|
||||
static void app_jitpaint_init(void *self) {
|
||||
struct app_jitpaint *app = self;
|
||||
// color format: pack(r, g, b) = (r<<16) | (g<<8) | b
|
||||
struct insn insns[] = {
|
||||
GEN_SHLI(R0, 16),
|
||||
GEN_ANDI(R1, 0xf),
|
||||
GEN_SHLI(R1, 8),
|
||||
GEN_OR(R0, R1),
|
||||
GEN_RET(),
|
||||
};
|
||||
uint8_t mem[100];
|
||||
ASSERT(jit_compile(insns, sizeof(insns)/sizeof(insns[0]), mem));
|
||||
for (unsigned y = 0; y < CANVAS_HEIGHT; y++) {
|
||||
for (unsigned x = 0; x < CANVAS_WIDTH; x++) {
|
||||
uint64_t ret = jit_call(mem, x, y);
|
||||
struct color col = COLOR_RGB((ret>>16)&0xff, (ret>>8)&0xff, ret&0xff);
|
||||
widget_canvas_draw_pixel(app->canvas, col, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct app_template app_jitpaint_template = {
|
||||
.create = app_jitpaint_create,
|
||||
.init = app_jitpaint_init,
|
||||
.destroy = kern_free,
|
||||
};
|
||||
|
|
@ -101,8 +101,6 @@ void main(void) {
|
|||
|
||||
__asm__ ("sti");
|
||||
|
||||
jit_test();
|
||||
|
||||
while (1) {
|
||||
fb_refresh();
|
||||
__asm__ ("hlt");
|
||||
|
|
|
|||
126
src/x86_64/jit.c
126
src/x86_64/jit.c
|
|
@ -9,6 +9,8 @@
|
|||
#define R8 8
|
||||
#define R9 9
|
||||
#define R10 10
|
||||
// used as scratch
|
||||
#define R11 11
|
||||
|
||||
int x86reg(enum reg reg) {
|
||||
switch (reg) {
|
||||
|
|
@ -20,63 +22,92 @@ int x86reg(enum reg reg) {
|
|||
case R5: return R8;
|
||||
case R6: return R9;
|
||||
case R7: return R10;
|
||||
case RSCRATCH: return R11;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#define BIGREG(i) ((i) >= 8)
|
||||
|
||||
static inline uint8_t byte48(enum reg reg1, enum reg reg2) {
|
||||
return 0x48 | ((x86reg(reg1) >> 3) << 2) | (x86reg(reg2) >> 3);
|
||||
}
|
||||
#define OPBYTE(base, reg1, reg2) ((base) | (((reg1) >> 3) << 2) | ((reg2) >> 3))
|
||||
#define MODRM(base, reg1, reg2) ((base) | (((reg1) & 7) << 3)| (x86reg(reg2) & 7))
|
||||
|
||||
static inline uint8_t bytec0(enum reg reg1, enum reg reg2) {
|
||||
return 0xc0 | ((x86reg(reg1) & 7) << 3) | (x86reg(reg2) & 7);
|
||||
uint8_t x86code(enum op op) {
|
||||
switch (op) {
|
||||
case MOV: return 0x89;
|
||||
case ADD: return 0x01;
|
||||
case AND: return 0x21;
|
||||
case OR: return 0x09;
|
||||
case LD: case LWU: return 0x8b;
|
||||
case SD: case SW: return 0x89;
|
||||
case LW: return 0x63;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
uint8_t *jit_compile_single(struct insn *insn, uint8_t *mem) {
|
||||
unsigned x86a, x86b;
|
||||
unsigned x86a = x86reg(insn->ra), x86b = x86reg(insn->rb);
|
||||
struct insn help;
|
||||
switch (insn->op) {
|
||||
case MOV:
|
||||
*mem++ = byte48(insn->rb, insn->ra);
|
||||
*mem++ = 0x89;
|
||||
*mem++ = bytec0(insn->rb, insn->ra);
|
||||
break;
|
||||
case ADD:
|
||||
*mem++ = byte48(insn->rb, insn->ra);
|
||||
*mem++ = 0x01;
|
||||
*mem++ = bytec0(insn->rb, insn->ra);
|
||||
case AND:
|
||||
case OR:
|
||||
*mem++ = OPBYTE(0x48, x86b, x86a);
|
||||
*mem++ = x86code(insn->op);
|
||||
*mem++ = MODRM(0xc0, x86b, x86a);
|
||||
break;
|
||||
case ADDI:
|
||||
// use add insn for now; lea?
|
||||
// there is also a smaller encoding for byte adds
|
||||
if (insn->ra == R0) { // rax has special encoding for some reason
|
||||
*mem++ = 0x48;
|
||||
*mem++ = 0x05;
|
||||
if ((insn->imm >> 32) == 0) {
|
||||
// there is also a smaller encoding for byte adds
|
||||
if (x86a == RAX) { // rax has special encoding for some reason
|
||||
*mem++ = 0x48;
|
||||
*mem++ = 0x05;
|
||||
} else {
|
||||
*mem++ = OPBYTE(0x48, 0, x86a);
|
||||
*mem++ = 0x81;
|
||||
*mem++ = MODRM(0xc0, 0, x86a);
|
||||
}
|
||||
ASSERT(insn->imm <= 0xffffffffull); // x86: 4-byte value
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
} else {
|
||||
*mem++ = 0x48 | (x86reg(insn->ra) >> 3);
|
||||
*mem++ = 0x81;
|
||||
*mem++ = 0xc0 | (x86reg(insn->ra) & 7);
|
||||
}
|
||||
ASSERT(insn->imm <= 0xffffffffull); // x86: 4-byte value
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
help = GEN_MOVI(RSCRATCH, insn->imm);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
help = GEN_ADD(insn->ra, RSCRATCH);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
}
|
||||
break;
|
||||
case ANDI:
|
||||
// This is the worst case, when the operand has to be 8 bytes big. There are way better encodings. the insn also sign-extends.
|
||||
help = GEN_MOVI(RSCRATCH, insn->imm);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
help = GEN_AND(insn->ra, RSCRATCH);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
break;
|
||||
case MOVI:
|
||||
*mem++ = 0x48 | (x86reg(insn->ra) >> 3);
|
||||
*mem++ = 0xb8 | (x86reg(insn->ra) & 7);
|
||||
*mem++ = OPBYTE(0x48, 0, x86a);
|
||||
*mem++ = 0xb8 | (x86a & 7);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
break;
|
||||
case SHLI:
|
||||
*mem++ = OPBYTE(0x48, 0, x86a);
|
||||
*mem++ = 0xc1;
|
||||
*mem++ = MODRM(0xe0, 0, x86a);
|
||||
ASSERT(insn->imm < 64);
|
||||
*mem++ = insn->imm;
|
||||
break;
|
||||
case LD:
|
||||
case SD:
|
||||
*mem++ = byte48(insn->ra, insn->rb);
|
||||
*mem++ = insn->op == LD ? 0x8b : 0x89;
|
||||
*mem++ = 0x80 | ((x86reg(insn->ra) & 7) << 3) | (x86reg(insn->rb) & 7);
|
||||
case LW:
|
||||
*mem++ = OPBYTE(0x48, x86a, x86b);
|
||||
*mem++ = x86code(insn->op);
|
||||
*mem++ = MODRM(0x80, x86a, x86b);
|
||||
ASSERT(insn->imm <= 0xffffffffull); // x86: 4-byte value
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
|
|
@ -84,40 +115,13 @@ uint8_t *jit_compile_single(struct insn *insn, uint8_t *mem) {
|
|||
}
|
||||
break;
|
||||
case LWU:
|
||||
// there are smaller insns for offset=0
|
||||
x86a = x86reg(insn->ra);
|
||||
x86b = x86reg(insn->rb);
|
||||
if (BIGREG(x86a) || BIGREG(x86b)) {
|
||||
*mem++ = 0x40 | ((x86a >> 3) << 2) | (x86b >> 3);
|
||||
}
|
||||
*mem++ = 0x8b;
|
||||
*mem++ = 0x80 | ((x86a & 7) << 3) | (x86b & 7);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
break;
|
||||
case LW:
|
||||
// there are smaller insns for offset=0
|
||||
x86a = x86reg(insn->ra);
|
||||
x86b = x86reg(insn->rb);
|
||||
*mem++ = 0x48 | ((x86a >> 3) << 2) | (x86b >> 3);
|
||||
*mem++ = 0x63;
|
||||
*mem++ = 0x80 | ((x86a & 7) << 3) | (x86b & 7);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
break;
|
||||
case SW:
|
||||
// there are smaller insns for offset=0
|
||||
x86a = x86reg(insn->ra);
|
||||
x86b = x86reg(insn->rb);
|
||||
if (BIGREG(x86a) || BIGREG(x86b)) {
|
||||
*mem++ = 0x40 | ((x86a >> 3) << 2) | (x86b >> 3);
|
||||
*mem++ = OPBYTE(0x40, x86a, x86b);
|
||||
}
|
||||
*mem++ = 0x89;
|
||||
*mem++ = 0x80 | ((x86a & 7) << 3) | (x86b & 7);
|
||||
*mem++ = x86code(insn->op);
|
||||
*mem++ = MODRM(0x80, x86a, x86b);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
|
|
|
|||
|
|
@ -109,12 +109,29 @@ void jit_test_sw(void) {
|
|||
TEST_ASSERT(progdata[0] == 5 && progdata[1] == 10 && progdata[2] == 15 && progdata[3] == 0);
|
||||
}
|
||||
|
||||
void jit_test_insn_addi() {
|
||||
struct insn insns[] = {
|
||||
GEN_ADDI(R0, 20),
|
||||
GEN_RET(),
|
||||
};
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
uint64_t result = jit_call(progmem, 50, 0);
|
||||
TEST_ASSERT(result == 70);
|
||||
|
||||
insns[0] = GEN_ADDI(R0, 0x12345678abcdef00ull);
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
result = jit_call(progmem, 50, 0);
|
||||
TEST_ASSERT(result == 50 + 0x12345678abcdef00ull);
|
||||
}
|
||||
|
||||
int main() {
|
||||
progmem = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
TEST_ASSERT(progmem != MAP_FAILED);
|
||||
int res = mprotect(progmem, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
TEST_ASSERT(res == 0);
|
||||
|
||||
jit_test_insn_addi();
|
||||
|
||||
jit_test1();
|
||||
jit_test2();
|
||||
jit_test_lwu();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue