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 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 { struct stack {
u_char items[STACK_CAPACITY]; u_char items[STACK_CAPACITY];
size_t bytes; size_t bytes;
@ -112,39 +133,6 @@ struct module {
size_t num_exports; 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 { struct value_t {
enum TYPE type; enum TYPE type;
union { union {
@ -173,6 +161,7 @@ void print_value(struct value_t *value) {
printf("%d", *(int32_t*)number); printf("%d", *(int32_t*)number);
break; break;
case TYPE_I64: case TYPE_I64:
case TYPE_ANY:
printf("%ld", *(int64_t*)number); printf("%ld", *(int64_t*)number);
break; break;
case TYPE_F32: 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_function(struct module *module, size_t func_i, size_t len);
int parse_instruction(struct context context, u_char *binary, 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;} #define PARAMS(...) __VA_ARGS__
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;
printf("num locals %d, %d\n", num_locals, num_locals - 1 - *(u_char*)immidiate); // https://webassembly.github.io/spec/core/appendix/index-instructions.html
stack_peak(&context.module->stack, result, num_locals - 1 - *(u_char*)immidiate, context.func_stack_begin); // 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) { enum OP_CODES {
// TODO test condition with correct type. #define AS_ENUM(NAME, CODE, PARAM, NUM_RESULTS, LEN_IMMIDIATE, BODY) NAME = CODE,
// This might not matter since all types are false with 0x0 DEFINE_OPERATIONS(AS_ENUM)
if (a->value.i64) { INSTR_END = 0x0B,
i += parse_instruction(context, &binary[i], len); INSTR_ELSE = 0x05
} else { };
incr(i, len);
}
} #define AS_FUNCTION(NAME, CODE, PARAM, NUM_RESULTS, LEN_IMMIDIATE, BODY) \
incr(i, len); int exec_##NAME(struct context context, struct value_t *a, \
while (binary[i] != INSTR_END) { struct value_t *b, void *immidiate, struct value_t *result, \
if (a->value.i64) { u_char *binary, size_t len) { \
incr(i, len); (void) context; \
} else { (void) a; \
i += parse_instruction(context, &binary[i], len); (void) b; \
} (void) immidiate; \
(void) result; \
(void) binary; \
(void) len; \
BODY; \
return 0; \
} }
incr(i, len); DEFINE_OPERATIONS(AS_FUNCTION)
return i;
)
struct instruction { struct instruction {
size_t num_param; size_t num_param;
@ -282,13 +297,15 @@ struct instruction {
}; };
struct instruction INSTRUCTIONS[] = { 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}, #define AS_INSTRUCTION(NAME, CODE, PARAM, NUM_RESULTS, LEN_IMMIDIATE, BODY) \
[INSTR_F64_SUB] = {.num_param = 2, .params = {TYPE_F64, TYPE_F64}, .num_results = 1, .len_immidiate = 0, .exec = &instr_f64_sub_exec}, [NAME] = { \
[INSTR_F64_LT] = {.num_param = 2, .params = {TYPE_F64, TYPE_F64}, .num_results = 1, .len_immidiate = 0, .exec = &instr_f64_lt_exec}, .num_param = sizeof((enum TYPE[]) {PARAM}) / sizeof(enum TYPE), \
[INSTR_F64_CONST] = {.num_param = 0, .params = {}, .num_results = 1, .len_immidiate = 8, .exec = &instr_f64_const_exec}, .params = {PARAM}, \
[INSTR_LOCAL_GET] = {.num_param = 0, .params = {}, .num_results = 1, .len_immidiate = 1, .exec = &instr_local_get_exec}, .num_results = NUM_RESULTS, \
[INSTR_CALL] = {.num_param = 0, .params = {}, .num_results = 0, .len_immidiate = 1, .exec = &instr_call_exec}, .len_immidiate = LEN_IMMIDIATE, \
[INSTR_IF] = {.num_param = 1, .params = {TYPE_ANY}, .num_results = 0, .len_immidiate = 1, .exec = &instr_if_exec}, .exec = &exec_##NAME \
},
DEFINE_OPERATIONS(AS_INSTRUCTION)
}; };
int parse_instruction(struct context context, u_char *binary, size_t len) { int parse_instruction(struct context context, u_char *binary, size_t len) {