refactor: have instruction as macro to generate enum, functions and array

This commit is contained in:
Orangerot 2025-05-22 05:50:35 +02:00
parent d8c0b2bc2c
commit 9d0692a391

169
wai.c
View file

@ -64,6 +64,27 @@ enum TYPE {
TYPE_EXTERNREF = 0x6F
};
static const int TYPE_SIZE[] = {
[TYPE_I32] = 4,
[TYPE_I64] = 8,
[TYPE_F32] = 4,
[TYPE_F64] = 8,
[TYPE_V128] = 16,
[TYPE_FUNCREF] = 16,
[TYPE_EXTERNREF] = 16
};
static const char *TYPE_NAME[] = {
[TYPE_I32] = "I32",
[TYPE_I64] = "I64",
[TYPE_F32] = "F32",
[TYPE_F64] = "F64",
[TYPE_V128] = "V128",
[TYPE_FUNCREF] = "FREF",
[TYPE_EXTERNREF] = "EXTR",
[TYPE_ANY] = "ANY",
};
struct stack {
u_char items[STACK_CAPACITY];
size_t bytes;
@ -112,39 +133,6 @@ struct module {
size_t num_exports;
};
enum OP_CODES {
INSTR_CALL = 0x10,
INSTR_ELSE = 0x05,
INSTR_END = 0x0b,
INSTR_F64_CONST = 0x44,
INSTR_F64_LT = 0x63,
INSTR_F64_MUL = 0xa2,
INSTR_F64_SUB = 0xa1,
INSTR_IF = 0x04,
INSTR_LOCAL_GET = 0x20,
};
static const int TYPE_SIZE[] = {
[TYPE_I32] = 4,
[TYPE_I64] = 8,
[TYPE_F32] = 4,
[TYPE_F64] = 8,
[TYPE_V128] = 16,
[TYPE_FUNCREF] = 16,
[TYPE_EXTERNREF] = 16
};
static const char *TYPE_NAME[] = {
[TYPE_I32] = "I32",
[TYPE_I64] = "I64",
[TYPE_F32] = "F32",
[TYPE_F64] = "F64",
[TYPE_V128] = "V128",
[TYPE_FUNCREF] = "FREF",
[TYPE_EXTERNREF] = "EXTR",
[TYPE_ANY] = "ANY",
};
struct value_t {
enum TYPE type;
union {
@ -173,6 +161,7 @@ void print_value(struct value_t *value) {
printf("%d", *(int32_t*)number);
break;
case TYPE_I64:
case TYPE_ANY:
printf("%ld", *(int64_t*)number);
break;
case TYPE_F32:
@ -232,46 +221,72 @@ void stack_pop(struct stack *s, struct value_t *value) {
int parse_function(struct module *module, size_t func_i, size_t len);
int parse_instruction(struct context context, u_char *binary, size_t len);
#define OP(name, body) int name(struct context context, struct value_t *a, struct value_t *b, void *immidiate, struct value_t *result, u_char *binary, size_t len) { body; return 0;}
OP(instr_f64_mul_exec, result->type = TYPE_F64; result->value.f64 = a->value.f64 * b->value.f64)
OP(instr_f64_sub_exec, result->type = TYPE_F64; result->value.f64 = b->value.f64 - a->value.f64)
OP(instr_f64_lt_exec, result->type = TYPE_F64; result->value.f64 = b->value.f64 < a->value.f64)
OP(instr_f64_const_exec, result->type = TYPE_F64; result->value.f64 = *(double*)immidiate)
OP(instr_local_get_exec,
size_t func_type_i = context.module->func[context.func_i].func_type_index;
struct func_type_t *func_type = &context.module->func_types[func_type_i];
int num_locals = func_type->num_params + context.module->func[context.func_i].num_local_vars;
#define PARAMS(...) __VA_ARGS__
printf("num locals %d, %d\n", num_locals, num_locals - 1 - *(u_char*)immidiate);
stack_peak(&context.module->stack, result, num_locals - 1 - *(u_char*)immidiate, context.func_stack_begin);
// https://webassembly.github.io/spec/core/appendix/index-instructions.html
// OP(NAME, CODE, PARAM, NUM_RESULTS, LEN_IMMIDIATE, BODY)
#define DEFINE_OPERATIONS(OP) \
OP(INSTR_F64_MUL, 0xa2, PARAMS(TYPE_F64, TYPE_F64), 1, 0, result->type = TYPE_F64; result->value.f64 = a->value.f64 * b->value.f64) \
OP(INSTR_F64_SUB, 0xa1, PARAMS(TYPE_F64, TYPE_F64), 1, 0, result->type = TYPE_F64; result->value.f64 = b->value.f64 - a->value.f64) \
OP(INSTR_F64_LT, 0x63, PARAMS(TYPE_F64, TYPE_F64), 1, 0, result->type = TYPE_F64; result->value.f64 = b->value.f64 < a->value.f64) \
OP(INSTR_F64_CONST, 0x44, PARAMS(), 1, 8, result->type = TYPE_F64; result->value.f64 = *(double*)immidiate) \
OP(INSTR_LOCAL_GET, 0x20, PARAMS(), 1, 1, \
size_t func_type_i = context.module->func[context.func_i].func_type_index; \
struct func_type_t *func_type = &context.module->func_types[func_type_i]; \
int num_locals = func_type->num_params + context.module->func[context.func_i].num_local_vars; \
\
printf("num locals %d, %d\n", num_locals, num_locals - 1 - *(u_char*)immidiate); \
stack_peak(&context.module->stack, result, num_locals - 1 - *(u_char*)immidiate, context.func_stack_begin); \
) \
OP(INSTR_CALL, 0x10, PARAMS(), 0, 1, parse_function(context.module, *(u_char*)immidiate, len)) \
OP(INSTR_IF, 0x04, PARAMS(TYPE_ANY), 0, 1, \
size_t i = 0; \
enum TYPE condition_type = *(u_char*) immidiate; \
if (a->type != condition_type) \
printf("Wrong types!\n"); \
\
while (binary[i] != INSTR_ELSE) { \
if (a->value.i64) { \
i += parse_instruction(context, &binary[i], len); \
} else { \
incr(i, len); \
} \
} \
incr(i, len); \
while (binary[i] != INSTR_END) { \
if (a->value.i64) { \
incr(i, len); \
} else { \
i += parse_instruction(context, &binary[i], len); \
} \
} \
incr(i, len); \
return i; \
)
OP(instr_call_exec, parse_function(context.module, *(u_char*)immidiate, len))
OP(instr_if_exec,
size_t i = 0;
enum TYPE condition_type = *(u_char*) immidiate;
if (a->type != condition_type)
printf("Wrong types!\n");
while (binary[i] != INSTR_ELSE) {
// TODO test condition with correct type.
// This might not matter since all types are false with 0x0
if (a->value.i64) {
i += parse_instruction(context, &binary[i], len);
} else {
incr(i, len);
}
}
incr(i, len);
while (binary[i] != INSTR_END) {
if (a->value.i64) {
incr(i, len);
} else {
i += parse_instruction(context, &binary[i], len);
}
enum OP_CODES {
#define AS_ENUM(NAME, CODE, PARAM, NUM_RESULTS, LEN_IMMIDIATE, BODY) NAME = CODE,
DEFINE_OPERATIONS(AS_ENUM)
INSTR_END = 0x0B,
INSTR_ELSE = 0x05
};
#define AS_FUNCTION(NAME, CODE, PARAM, NUM_RESULTS, LEN_IMMIDIATE, BODY) \
int exec_##NAME(struct context context, struct value_t *a, \
struct value_t *b, void *immidiate, struct value_t *result, \
u_char *binary, size_t len) { \
(void) context; \
(void) a; \
(void) b; \
(void) immidiate; \
(void) result; \
(void) binary; \
(void) len; \
BODY; \
return 0; \
}
incr(i, len);
return i;
)
DEFINE_OPERATIONS(AS_FUNCTION)
struct instruction {
size_t num_param;
@ -282,13 +297,15 @@ struct instruction {
};
struct instruction INSTRUCTIONS[] = {
[INSTR_F64_MUL] = {.num_param = 2, .params = {TYPE_F64, TYPE_F64}, .num_results = 1, .len_immidiate = 0, .exec = &instr_f64_mul_exec},
[INSTR_F64_SUB] = {.num_param = 2, .params = {TYPE_F64, TYPE_F64}, .num_results = 1, .len_immidiate = 0, .exec = &instr_f64_sub_exec},
[INSTR_F64_LT] = {.num_param = 2, .params = {TYPE_F64, TYPE_F64}, .num_results = 1, .len_immidiate = 0, .exec = &instr_f64_lt_exec},
[INSTR_F64_CONST] = {.num_param = 0, .params = {}, .num_results = 1, .len_immidiate = 8, .exec = &instr_f64_const_exec},
[INSTR_LOCAL_GET] = {.num_param = 0, .params = {}, .num_results = 1, .len_immidiate = 1, .exec = &instr_local_get_exec},
[INSTR_CALL] = {.num_param = 0, .params = {}, .num_results = 0, .len_immidiate = 1, .exec = &instr_call_exec},
[INSTR_IF] = {.num_param = 1, .params = {TYPE_ANY}, .num_results = 0, .len_immidiate = 1, .exec = &instr_if_exec},
#define AS_INSTRUCTION(NAME, CODE, PARAM, NUM_RESULTS, LEN_IMMIDIATE, BODY) \
[NAME] = { \
.num_param = sizeof((enum TYPE[]) {PARAM}) / sizeof(enum TYPE), \
.params = {PARAM}, \
.num_results = NUM_RESULTS, \
.len_immidiate = LEN_IMMIDIATE, \
.exec = &exec_##NAME \
},
DEFINE_OPERATIONS(AS_INSTRUCTION)
};
int parse_instruction(struct context context, u_char *binary, size_t len) {