Compare commits
3 commits
typed-valu
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
bce3e3bf25 | ||
|
64f9409f9c | ||
|
2b3c39d18c |
3
Makefile
3
Makefile
|
@ -3,10 +3,9 @@
|
|||
# SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
wai: wai.c
|
||||
gcc -Wall -Wextra -o wai wai.c
|
||||
gcc -ggdb -Wall -Wextra -o wai wai.c
|
||||
|
||||
# tests/factorial.wasm: tests/factorial.wat
|
||||
.PHONY: tests
|
||||
tests:
|
||||
wat2wasm -o tests/factorial.wasm tests/factorial.wat
|
||||
|
||||
|
|
202
wai.c
202
wai.c
|
@ -27,7 +27,9 @@
|
|||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
enum section {
|
||||
Section_Custom,
|
||||
|
@ -47,14 +49,28 @@ enum section {
|
|||
|
||||
#define STACK_CAPACITY 1024
|
||||
struct stack {
|
||||
double items[STACK_CAPACITY];
|
||||
size_t count;
|
||||
u_char items[STACK_CAPACITY];
|
||||
size_t bytes;
|
||||
};
|
||||
|
||||
#define MAX_FUNCTIONS 128
|
||||
enum export_desc {
|
||||
Export_Func,
|
||||
Export_Table,
|
||||
Export_Mem,
|
||||
Export_Global,
|
||||
};
|
||||
|
||||
struct export_t {
|
||||
u_char name[128];
|
||||
size_t name_length;
|
||||
uint32_t index;
|
||||
enum export_desc description;
|
||||
};
|
||||
|
||||
struct module {
|
||||
struct type_t *types;
|
||||
u_char *funcs[128];
|
||||
u_char *funcs[MAX_FUNCTIONS];
|
||||
struct table_t *tables;
|
||||
struct mem_t *mems;
|
||||
struct global_t *globals;
|
||||
|
@ -62,7 +78,7 @@ struct module {
|
|||
struct data_t *datas;
|
||||
struct start_t *start;
|
||||
struct import_t *imports;
|
||||
struct export_t *exports;
|
||||
struct export_t exports[MAX_FUNCTIONS];
|
||||
u_char *binary;
|
||||
struct stack stack;
|
||||
int scope;
|
||||
|
@ -72,7 +88,6 @@ enum INSTRUCTION {
|
|||
INSTR_CALL = 0x10,
|
||||
INSTR_ELSE = 0x05,
|
||||
INSTR_END = 0x0b,
|
||||
INSTR_F64 = 0x7C,
|
||||
INSTR_F64_CONST = 0x44,
|
||||
INSTR_F64_LT = 0x63,
|
||||
INSTR_F64_MUL = 0xa2,
|
||||
|
@ -91,26 +106,81 @@ 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
|
||||
};
|
||||
|
||||
struct value_t {
|
||||
enum TYPE type;
|
||||
union {
|
||||
int32_t i32;
|
||||
int64_t i64;
|
||||
float f32;
|
||||
double f64;
|
||||
__int128 v128;
|
||||
int64_t funcref;
|
||||
int64_t extref;
|
||||
} value;
|
||||
};
|
||||
|
||||
#define incr(i, len) i++; if (i >= len) {return -1;}
|
||||
|
||||
void stack_push(struct stack *s, double a) {
|
||||
s->items[s->count++] = a;
|
||||
void stack_push(struct stack *s, const struct value_t *value) {
|
||||
size_t type_size = TYPE_SIZE[value->type];
|
||||
memcpy(&(s->items[s->bytes]), &value->value, type_size);
|
||||
s->items[s->bytes + type_size] = value->type;
|
||||
s->bytes += type_size + 1;
|
||||
|
||||
printf("stack: ");
|
||||
for (int i = 0; i < s->count; i++) {
|
||||
printf("%f, ", s->items[i]);
|
||||
for (int i = s->bytes - 1; i > 0; i -= TYPE_SIZE[s->items[i]] + 1) {
|
||||
enum TYPE t = s->items[i];
|
||||
size_t type_size = TYPE_SIZE[t];
|
||||
void *value = &s->items[i - type_size];
|
||||
|
||||
switch (t) {
|
||||
case TYPE_I32:
|
||||
printf("%d (I32)", *(int32_t*)value);
|
||||
break;
|
||||
case TYPE_I64:
|
||||
printf("%ld (I32)", *(int64_t*)value);
|
||||
break;
|
||||
case TYPE_F32:
|
||||
printf("%f (F32)", *(float*)value);
|
||||
break;
|
||||
case TYPE_F64:
|
||||
printf("%f (F64)", *(double*)value);
|
||||
break;
|
||||
case TYPE_V128:
|
||||
printf("%ld (V128)", *(__int128*)value);
|
||||
break;
|
||||
case TYPE_FUNCREF:
|
||||
printf("%ld (EREF)", *(int64_t*)value);
|
||||
break;
|
||||
case TYPE_EXTERNREF:
|
||||
printf("%ld (EREF)", *(int64_t*)value);
|
||||
break;
|
||||
}
|
||||
printf(", ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
double stack_pop(struct stack *s) {
|
||||
s->count--;
|
||||
return s->items[s->count];
|
||||
}
|
||||
|
||||
double stack_top(struct stack *s) {
|
||||
return s->items[s->count-1];
|
||||
void stack_top(struct stack *s, struct value_t *value) {
|
||||
value->type = s->items[s->bytes-1];
|
||||
memcpy(&value->value, &(s->items[s->bytes - 1 - TYPE_SIZE[value->type]]), TYPE_SIZE[value->type]);
|
||||
}
|
||||
|
||||
void stack_pop(struct stack *s, struct value_t *value) {
|
||||
stack_top(s, value);
|
||||
s->bytes -= TYPE_SIZE[value->type] + 1;
|
||||
}
|
||||
|
||||
int parse_type(u_char *binary, int len) {
|
||||
int i = 0;
|
||||
enum TYPE param = binary[i];
|
||||
|
@ -136,47 +206,71 @@ int parse_instruction(struct module *module, u_char *binary, double param, int l
|
|||
int i = 0;
|
||||
enum INSTRUCTION instr = (u_char) binary[i];
|
||||
u_char *instr_addr = &binary[i];
|
||||
struct value_t a = {0};
|
||||
struct value_t b = {0};
|
||||
struct value_t result = {0};
|
||||
|
||||
incr(i, len);
|
||||
|
||||
switch (instr) {
|
||||
case INSTR_CALL:
|
||||
int a = binary[i];
|
||||
case INSTR_CALL: {
|
||||
int func_index = binary[i];
|
||||
incr(i, len);
|
||||
parse_function(module, module->funcs[a], stack_pop(&module->stack), len);
|
||||
stack_pop(&module->stack, &a);
|
||||
parse_function(module, module->funcs[func_index], a.value.f64, len);
|
||||
break;
|
||||
}
|
||||
case INSTR_ELSE:
|
||||
printf("reached else instruction: impossible!\n");
|
||||
case INSTR_END:
|
||||
break;
|
||||
case INSTR_F64:
|
||||
break;
|
||||
case INSTR_F64_CONST:
|
||||
double k = *(double*)&binary[i];
|
||||
stack_push(&module->stack, k);
|
||||
result.type = TYPE_F64;
|
||||
result.value.f64 = *(double*)&binary[i];
|
||||
i += 8;
|
||||
stack_push(&module->stack, &result);
|
||||
break;
|
||||
case INSTR_F64_LT: {
|
||||
double b = stack_pop(&module->stack);
|
||||
double a = stack_pop(&module->stack);
|
||||
stack_push(&module->stack, a < b);
|
||||
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: {
|
||||
double b = stack_pop(&module->stack);
|
||||
double a = stack_pop(&module->stack);
|
||||
stack_push(&module->stack, a * b);
|
||||
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: {
|
||||
double b = stack_pop(&module->stack);
|
||||
double a = stack_pop(&module->stack);
|
||||
stack_push(&module->stack, a - b);
|
||||
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: {
|
||||
double a = stack_pop(&module->stack);
|
||||
stack_pop(&module->stack, &a);
|
||||
enum TYPE condition_type = binary[i];
|
||||
incr(i, len);
|
||||
if (a.type != condition_type)
|
||||
printf("Wrong types!\n");
|
||||
|
||||
while (binary[i] != INSTR_ELSE) {
|
||||
if (a) {
|
||||
// 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], param, len);
|
||||
} else {
|
||||
incr(i, len);
|
||||
|
@ -184,7 +278,7 @@ int parse_instruction(struct module *module, u_char *binary, double param, int l
|
|||
}
|
||||
incr(i, len);
|
||||
while (binary[i] != INSTR_END) {
|
||||
if (a) {
|
||||
if (a.value.i64) {
|
||||
incr(i, len);
|
||||
} else {
|
||||
i += parse_instruction(module, &binary[i], param, len);
|
||||
|
@ -193,11 +287,12 @@ int parse_instruction(struct module *module, u_char *binary, double param, int l
|
|||
incr(i, len);
|
||||
break;
|
||||
}
|
||||
case INSTR_LOCAL_GET:
|
||||
case INSTR_LOCAL_GET: {
|
||||
int local_index = binary[i];
|
||||
incr(i, len);
|
||||
stack_push(&module->stack, param);
|
||||
stack_push(&module->stack, &(struct value_t) {.value.f64 = param, .type = TYPE_F64});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("unknown instruction! %x at %lx\n", instr, instr_addr - module->binary);
|
||||
exit(1);
|
||||
|
@ -271,19 +366,27 @@ int parse_section(struct module *module, u_char *binary, int len) {
|
|||
case Section_Export:
|
||||
printf("section: exports\n");
|
||||
int num_exports = binary[i];
|
||||
|
||||
if(num_exports > MAX_FUNCTIONS) {
|
||||
printf("Number of exports exceeds maximum number of functions in a module (%d)", MAX_FUNCTIONS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
incr(i, len);
|
||||
for (int exports_i = 0; exports_i < num_exports; exports_i++) {
|
||||
int string_len = binary[i];
|
||||
struct export_t *export = &module->exports[i];
|
||||
|
||||
export->name_length = binary[i];
|
||||
incr(i, len);
|
||||
printf("export name: ");
|
||||
for (int si = 0; si < string_len; si++) {
|
||||
putchar(binary[i]);
|
||||
|
||||
for (int si = 0; si < export->name_length; si++) {
|
||||
export->name[si] = binary[i];
|
||||
incr(i, len);
|
||||
}
|
||||
putchar('\n');
|
||||
// export kind
|
||||
export->description = (int) binary[i];
|
||||
incr(i, len);
|
||||
// export func index
|
||||
export->index = (uint32_t) binary[i];
|
||||
printf("export name: %s of type %d\n", export->name, export->description);
|
||||
incr(i, len);
|
||||
}
|
||||
break;
|
||||
|
@ -299,7 +402,7 @@ int parse_section(struct module *module, u_char *binary, int len) {
|
|||
module->funcs[function_i] = &binary[i];
|
||||
i += parse_function(module, &binary[i], 4, len);
|
||||
}
|
||||
printf("result: %f\n", module->stack.items[0]);
|
||||
// printf("result: %f\n", module->stack.items[0]);
|
||||
break;
|
||||
case Section_Data:
|
||||
break;
|
||||
|
@ -316,9 +419,9 @@ int parse_section(struct module *module, u_char *binary, int len) {
|
|||
|
||||
int parse_module(u_char *binary, size_t len) {
|
||||
int i = 0;
|
||||
u_char *magic = "\0asm";
|
||||
char *magic = "\0asm";
|
||||
while (i < 4) {
|
||||
if (binary[i] != magic[i]) {
|
||||
if ((char) binary[i] != magic[i]) {
|
||||
fprintf(stderr, "no wasm magic\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -338,7 +441,6 @@ int parse_module(u_char *binary, size_t len) {
|
|||
return i;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc != 2) {
|
||||
|
@ -355,8 +457,8 @@ int main(int argc, char **argv) {
|
|||
stat(argv[1], &st);
|
||||
printf("size: %ld\n", st.st_size);
|
||||
|
||||
unsigned char *binary = malloc(st.st_size);
|
||||
fread(binary, st.st_size, st.st_size, file);
|
||||
u_char *binary = malloc(st.st_size);
|
||||
fread(binary, st.st_size, 1, file);
|
||||
|
||||
if (parse_module(binary, st.st_size) == -1) {
|
||||
printf("error :(\n");
|
||||
|
|
Loading…
Reference in a new issue