refactor: have instructions in array instead of switch-case
This commit is contained in:
parent
311010f4e6
commit
d8c0b2bc2c
192
wai.c
192
wai.c
|
@ -54,6 +54,7 @@ enum section {
|
|||
};
|
||||
|
||||
enum TYPE {
|
||||
TYPE_ANY = 0,
|
||||
TYPE_I32 = 0x7F,
|
||||
TYPE_I64 = 0x7E,
|
||||
TYPE_F32 = 0x7D,
|
||||
|
@ -111,7 +112,7 @@ struct module {
|
|||
size_t num_exports;
|
||||
};
|
||||
|
||||
enum INSTRUCTION {
|
||||
enum OP_CODES {
|
||||
INSTR_CALL = 0x10,
|
||||
INSTR_ELSE = 0x05,
|
||||
INSTR_END = 0x0b,
|
||||
|
@ -140,7 +141,8 @@ static const char *TYPE_NAME[] = {
|
|||
[TYPE_F64] = "F64",
|
||||
[TYPE_V128] = "V128",
|
||||
[TYPE_FUNCREF] = "FREF",
|
||||
[TYPE_EXTERNREF] = "EXTR"
|
||||
[TYPE_EXTERNREF] = "EXTR",
|
||||
[TYPE_ANY] = "ANY",
|
||||
};
|
||||
|
||||
struct value_t {
|
||||
|
@ -156,6 +158,12 @@ struct value_t {
|
|||
} value;
|
||||
};
|
||||
|
||||
struct context {
|
||||
struct module *module;
|
||||
size_t func_i;
|
||||
size_t func_stack_begin;
|
||||
};
|
||||
|
||||
#define incr(i, len) i++; if (i >= len) {return 0;}
|
||||
|
||||
void print_value(struct value_t *value) {
|
||||
|
@ -222,107 +230,94 @@ 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 module *module, u_char *binary, size_t func_i, size_t func_stack_begin, 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;
|
||||
|
||||
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_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);
|
||||
}
|
||||
}
|
||||
incr(i, len);
|
||||
return i;
|
||||
)
|
||||
|
||||
struct instruction {
|
||||
size_t num_param;
|
||||
enum TYPE params[2];
|
||||
size_t len_immidiate;
|
||||
size_t num_results;
|
||||
int (*exec) (struct context context, struct value_t *a, struct value_t *b, void *immidiate, struct value_t *result, u_char *binary, size_t len);
|
||||
};
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
int parse_instruction(struct context context, u_char *binary, size_t len) {
|
||||
size_t i = 0;
|
||||
enum INSTRUCTION instr = binary[i];
|
||||
enum OP_CODES op_code = binary[i];
|
||||
u_char *instr_addr = &binary[i];
|
||||
struct value_t a = {0};
|
||||
struct value_t b = {0};
|
||||
struct value_t result = {0};
|
||||
struct value_t arguments[2];
|
||||
|
||||
incr(i, len);
|
||||
|
||||
switch (instr) {
|
||||
case INSTR_CALL: {
|
||||
int func_index = binary[i];
|
||||
incr(i, len);
|
||||
// stack_pop(&module->stack, &a);
|
||||
parse_function(module, func_index, len);
|
||||
break;
|
||||
}
|
||||
case INSTR_ELSE:
|
||||
printf("reached else instruction: impossible!\n");
|
||||
case INSTR_END:
|
||||
break;
|
||||
case INSTR_F64_CONST:
|
||||
result.type = TYPE_F64;
|
||||
result.value.f64 = *(double*)&binary[i];
|
||||
i += 8;
|
||||
stack_push(&module->stack, &result);
|
||||
break;
|
||||
case INSTR_F64_LT: {
|
||||
stack_pop(&module->stack, &a);
|
||||
stack_pop(&module->stack, &b);
|
||||
if (a.type != TYPE_F64 || b.type != TYPE_F64)
|
||||
printf("Wrong types!\n");
|
||||
result.type = TYPE_F64;
|
||||
result.value.f64 = b.value.f64 < a.value.f64;
|
||||
stack_push(&module->stack, &result);
|
||||
break;
|
||||
}
|
||||
case INSTR_F64_MUL: {
|
||||
stack_pop(&module->stack, &a);
|
||||
stack_pop(&module->stack, &b);
|
||||
if (a.type != TYPE_F64 || b.type != TYPE_F64)
|
||||
printf("Wrong types!\n");
|
||||
result.type = TYPE_F64;
|
||||
result.value.f64 = a.value.f64 * b.value.f64;
|
||||
stack_push(&module->stack, &result);
|
||||
break;
|
||||
}
|
||||
case INSTR_F64_SUB: {
|
||||
stack_pop(&module->stack, &a);
|
||||
stack_pop(&module->stack, &b);
|
||||
if (a.type != TYPE_F64 || b.type != TYPE_F64)
|
||||
printf("Wrong types!\n");
|
||||
result.type = TYPE_F64;
|
||||
result.value.f64 = b.value.f64 - a.value.f64;
|
||||
stack_push(&module->stack, &result);
|
||||
break;
|
||||
}
|
||||
case INSTR_IF: {
|
||||
stack_pop(&module->stack, &a);
|
||||
enum TYPE condition_type = binary[i];
|
||||
incr(i, len);
|
||||
if (a.type != condition_type)
|
||||
printf("Wrong types!\n");
|
||||
struct instruction *instr = &INSTRUCTIONS[op_code];
|
||||
if (instr->exec == NULL) {
|
||||
printf("not implemented/illegal instruction %x at %lx\n", op_code, instr_addr - context.module->binary);
|
||||
exit(1);
|
||||
};
|
||||
|
||||
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(module, &binary[i], func_i, func_stack_begin, len);
|
||||
} else {
|
||||
incr(i, len);
|
||||
}
|
||||
}
|
||||
incr(i, len);
|
||||
while (binary[i] != INSTR_END) {
|
||||
if (a.value.i64) {
|
||||
incr(i, len);
|
||||
} else {
|
||||
i += parse_instruction(module, &binary[i], func_i, func_stack_begin, len);
|
||||
}
|
||||
}
|
||||
incr(i, len);
|
||||
break;
|
||||
}
|
||||
case INSTR_LOCAL_GET: {
|
||||
int local_index = binary[i];
|
||||
size_t func_type_i = module->func[func_i].func_type_index;
|
||||
struct func_type_t *func_type = &module->func_types[func_type_i];
|
||||
int num_locals = func_type->num_params + module->func[func_i].num_local_vars;
|
||||
|
||||
printf("num locals %d, %d\n", num_locals, num_locals - 1 - local_index);
|
||||
stack_peak(&module->stack, &result, num_locals - 1 - local_index, func_stack_begin);
|
||||
incr(i, len);
|
||||
stack_push(&module->stack, &result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("unknown instruction! %x at %lx\n", instr, instr_addr - module->binary);
|
||||
exit(1);
|
||||
for (size_t param_i = 0; param_i < instr->num_param; param_i++) {
|
||||
stack_pop(&context.module->stack, &arguments[param_i]);
|
||||
if (instr->params[param_i] != TYPE_ANY && arguments[param_i].type != instr->params[param_i]) {
|
||||
printf("wrong type! %x\n", op_code);
|
||||
}
|
||||
}
|
||||
i += instr->exec(context, &arguments[0], &arguments[1], &binary[i], &result, &binary[i + instr->len_immidiate], len);
|
||||
i += instr->len_immidiate;
|
||||
if (instr->num_results) {
|
||||
stack_push(&context.module->stack, &result);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
@ -336,6 +331,11 @@ int parse_function(struct module *module, size_t func_i, size_t len) {
|
|||
size_t func_stack_begin = module->stack.bytes;
|
||||
size_t func_stack_end;
|
||||
struct value_t result = {0};
|
||||
struct context context = {
|
||||
.module = module,
|
||||
.func_i = func_i,
|
||||
.func_stack_begin = func_stack_begin
|
||||
};
|
||||
|
||||
incr(i, len);
|
||||
func->num_local_vars = binary[i];
|
||||
|
@ -345,7 +345,7 @@ int parse_function(struct module *module, size_t func_i, size_t len) {
|
|||
incr(i, len);
|
||||
}
|
||||
while (binary[i] != INSTR_END) {
|
||||
i += parse_instruction(module, &binary[i], func_i, func_stack_begin, len);
|
||||
i += parse_instruction(context, &binary[i], len);
|
||||
}
|
||||
incr(i, len);
|
||||
|
||||
|
|
Loading…
Reference in a new issue