(TEMP) some more insns, cleanup; idea for app using jit

This commit is contained in:
uosfz 2026-06-17 22:12:23 +02:00
parent 7750618796
commit 2fa4e98197
Signed by: uosfz
SSH key fingerprint: SHA256:FlktuluyhTQg3jHZNLKwxOOC5hbfrUXM0tz3IA3lGJo
7 changed files with 151 additions and 67 deletions

View file

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

View file

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

View file

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

View file

@ -101,8 +101,6 @@ void main(void) {
__asm__ ("sti");
jit_test();
while (1) {
fb_refresh();
__asm__ ("hlt");

View file

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

View file

@ -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();