summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wasm-ast-checker.c124
-rw-r--r--src/wasm-ast-parser-lexer-shared.c5
-rw-r--r--src/wasm-ast-parser-lexer-shared.h7
-rw-r--r--src/wasm-ast-parser.y31
-rw-r--r--src/wasm-binary-reader-interpreter.c146
-rw-r--r--src/wasm-common.h3
-rw-r--r--src/wasm-interpreter.c15
-rw-r--r--src/wasm-interpreter.h5
8 files changed, 194 insertions, 142 deletions
diff --git a/src/wasm-ast-checker.c b/src/wasm-ast-checker.c
index e6f25c9d..658c2010 100644
--- a/src/wasm-ast-checker.c
+++ b/src/wasm-ast-checker.c
@@ -179,7 +179,8 @@ static WasmResult check_func_var(Context* ctx,
static WasmResult check_global_var(Context* ctx,
const WasmVar* var,
- const WasmGlobal** out_global) {
+ const WasmGlobal** out_global,
+ int* out_global_index) {
int index;
if (WASM_FAILED(check_var(ctx, &ctx->current_module->global_bindings,
ctx->current_module->globals.size, var, "global",
@@ -189,6 +190,8 @@ static WasmResult check_global_var(Context* ctx,
if (out_global)
*out_global = ctx->current_module->globals.data[index];
+ if (out_global_index)
+ *out_global_index = index;
return WASM_OK;
}
@@ -683,7 +686,7 @@ static void check_expr(Context* ctx, const WasmExpr* expr) {
}
case WASM_EXPR_TYPE_DROP: {
- WasmType type = top_type(ctx);
+ WasmCheckType type = top_type(ctx);
transform_stack(ctx, &expr->loc, "drop", 1, 0, type);
break;
}
@@ -691,7 +694,7 @@ static void check_expr(Context* ctx, const WasmExpr* expr) {
case WASM_EXPR_TYPE_GET_GLOBAL: {
const WasmGlobal* global;
if (WASM_SUCCEEDED(
- check_global_var(ctx, &expr->get_global.var, &global))) {
+ check_global_var(ctx, &expr->get_global.var, &global, NULL))) {
push_type(ctx, global->type);
}
break;
@@ -804,7 +807,7 @@ static void check_expr(Context* ctx, const WasmExpr* expr) {
WasmCheckType type = WASM_TYPE_I32;
const WasmGlobal* global;
if (WASM_SUCCEEDED(
- check_global_var(ctx, &expr->set_global.var, &global))) {
+ check_global_var(ctx, &expr->set_global.var, &global, NULL))) {
type = global->type;
}
transform_stack(ctx, &expr->loc, "set_global", 1, 0, type);
@@ -885,6 +888,10 @@ static void check_func(Context* ctx,
const WasmLocation* loc,
const WasmFunc* func) {
ctx->current_func = func;
+ if (wasm_get_result_type(ctx->current_module, func) == WASM_TYPE_MULTIPLE) {
+ print_error(ctx, CHECK_STYLE_FULL, loc,
+ "multiple result values not currently supported.");
+ }
if (wasm_decl_has_func_type(&func->decl)) {
const WasmFuncType* func_type;
if (WASM_SUCCEEDED(
@@ -909,44 +916,15 @@ static void check_func(Context* ctx,
ctx->current_func = NULL;
}
-static WasmResult check_const_expr(Context* ctx,
- const WasmExpr* expr,
- const char* desc,
- WasmCheckType* out_type) {
- if (expr->next == NULL) {
- switch (expr->type) {
- case WASM_EXPR_TYPE_CONST:
- if (out_type)
- *out_type = expr->const_.type;
- return WASM_OK;
-
- case WASM_EXPR_TYPE_GET_GLOBAL: {
- const WasmGlobal* ref_global = NULL;
- if (WASM_SUCCEEDED(
- check_global_var(ctx, &expr->get_global.var, &ref_global))) {
- if (out_type)
- *out_type = ref_global->type;
- return WASM_OK;
- }
- return WASM_ERROR;
- }
-
- default:
- break;
- }
- }
-
- print_error(ctx, CHECK_STYLE_FULL, &expr->loc,
- "invalid %s, must be a constant expression; either *.const or "
- "get_global.",
- desc);
- return WASM_ERROR;
-}
-
static WasmResult eval_const_expr_i32(Context* ctx,
const WasmExpr* expr,
const char* desc,
uint32_t* out_value) {
+ if (expr->next != NULL) {
+ expr = expr->next;
+ goto invalid_expr;
+ }
+
switch (expr->type) {
case WASM_EXPR_TYPE_CONST:
if (expr->const_.type != WASM_TYPE_I32) {
@@ -960,7 +938,8 @@ static WasmResult eval_const_expr_i32(Context* ctx,
case WASM_EXPR_TYPE_GET_GLOBAL: {
const WasmGlobal* global = NULL;
- if (WASM_FAILED(check_global_var(ctx, &expr->get_global.var, &global)))
+ if (WASM_FAILED(
+ check_global_var(ctx, &expr->get_global.var, &global, NULL)))
return WASM_ERROR;
if (global->type != WASM_TYPE_I32) {
@@ -974,6 +953,7 @@ static WasmResult eval_const_expr_i32(Context* ctx,
return eval_const_expr_i32(ctx, global->init_expr, desc, out_value);
}
+ invalid_expr:
default:
print_error(
ctx, CHECK_STYLE_FULL, &expr->loc,
@@ -986,11 +966,48 @@ static WasmResult eval_const_expr_i32(Context* ctx,
static void check_global(Context* ctx,
const WasmLocation* loc,
- const WasmGlobal* global) {
- WasmCheckType type;
- if (WASM_SUCCEEDED(check_const_expr(
- ctx, global->init_expr, "global initializer expression", &type))) {
- check_type(ctx, loc, type, global->type, "global initializer expression");
+ const WasmGlobal* global,
+ int global_index) {
+ WasmCheckType type = WASM_CHECK_TYPE_VOID;
+ WasmBool invalid_expr = WASM_TRUE;
+ WasmExpr* expr = global->init_expr;
+
+ if (expr->next == NULL) {
+ switch (expr->type) {
+ case WASM_EXPR_TYPE_CONST:
+ type = expr->const_.type;
+ invalid_expr = WASM_FALSE;
+ break;
+
+ case WASM_EXPR_TYPE_GET_GLOBAL: {
+ const WasmGlobal* ref_global = NULL;
+ int ref_global_index;
+ if (WASM_SUCCEEDED(check_global_var(ctx, &expr->get_global.var,
+ &ref_global, &ref_global_index))) {
+ type = ref_global->type;
+ /* globals can only reference previously defined globals */
+ if (ref_global_index >= global_index) {
+ print_error(ctx, CHECK_STYLE_FULL, loc,
+ "global can only be defined in terms of a previously "
+ "defined global.");
+ }
+ }
+ invalid_expr = WASM_FALSE;
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (invalid_expr) {
+ print_error(ctx, CHECK_STYLE_FULL, loc,
+ "invalid global initializer expression. Must be *.const or "
+ "get_global.");
+ } else {
+ check_type(ctx, &expr->loc, type, global->type,
+ "global initializer expression");
}
}
@@ -1046,6 +1063,12 @@ static void check_elem_segments(Context* ctx, const WasmModule* module) {
if (field->type != WASM_MODULE_FIELD_TYPE_ELEM_SEGMENT)
continue;
+ if (!table) {
+ print_error(ctx, CHECK_STYLE_FULL, &field->loc,
+ "elem segment cannot be specified without table section");
+ break;
+ }
+
WasmElemSegment* elem_segment = &field->elem_segment;
uint32_t offset = 0;
if (WASM_SUCCEEDED(eval_const_expr_i32(
@@ -1058,7 +1081,7 @@ static void check_elem_segments(Context* ctx, const WasmModule* module) {
}
uint64_t max =
- table->elem_limits.has_max ? table->elem_limits.max : UINT32_MAX;
+ table->elem_limits.has_max ? table->elem_limits.initial : UINT32_MAX;
if (offset + elem_segment->vars.size > max) {
print_error(ctx, CHECK_STYLE_FULL, &field->loc,
@@ -1086,6 +1109,12 @@ static void check_data_segments(Context* ctx, const WasmModule* module) {
if (field->type != WASM_MODULE_FIELD_TYPE_DATA_SEGMENT)
continue;
+ if (!memory) {
+ print_error(ctx, CHECK_STYLE_FULL, &field->loc,
+ "data segment cannot be specified without memory section");
+ break;
+ }
+
WasmDataSegment* data_segment = &field->data_segment;
uint32_t offset = 0;
if (WASM_SUCCEEDED(eval_const_expr_i32(
@@ -1097,7 +1126,7 @@ static void check_data_segments(Context* ctx, const WasmModule* module) {
offset, last_end);
}
- uint32_t max = memory->page_limits.has_max ? memory->page_limits.max
+ uint32_t max = memory->page_limits.has_max ? memory->page_limits.initial
: WASM_MAX_PAGES;
const uint64_t memory_max_size = max * WASM_PAGE_SIZE;
if (offset + data_segment->size > memory_max_size) {
@@ -1118,6 +1147,7 @@ static void check_module(Context* ctx, const WasmModule* module) {
WasmBool seen_export_memory = WASM_FALSE;
WasmBool seen_table = WASM_FALSE;
WasmBool seen_start = WASM_FALSE;
+ int global_index = 0;
ctx->current_module = module;
@@ -1129,7 +1159,7 @@ static void check_module(Context* ctx, const WasmModule* module) {
break;
case WASM_MODULE_FIELD_TYPE_GLOBAL:
- check_global(ctx, &field->loc, &field->global);
+ check_global(ctx, &field->loc, &field->global, global_index++);
break;
case WASM_MODULE_FIELD_TYPE_IMPORT:
diff --git a/src/wasm-ast-parser-lexer-shared.c b/src/wasm-ast-parser-lexer-shared.c
index 2093624b..2ff81fd3 100644
--- a/src/wasm-ast-parser-lexer-shared.c
+++ b/src/wasm-ast-parser-lexer-shared.c
@@ -100,6 +100,7 @@ void wasm_destroy_func_fields(struct WasmAllocator* allocator,
case WASM_FUNC_FIELD_TYPE_PARAM_TYPES:
case WASM_FUNC_FIELD_TYPE_LOCAL_TYPES:
+ case WASM_FUNC_FIELD_TYPE_RESULT_TYPES:
wasm_destroy_type_vector(allocator, &func_field->types);
break;
@@ -107,10 +108,6 @@ void wasm_destroy_func_fields(struct WasmAllocator* allocator,
case WASM_FUNC_FIELD_TYPE_BOUND_LOCAL:
wasm_destroy_string_slice(allocator, &func_field->bound_type.name);
break;
-
- case WASM_FUNC_FIELD_TYPE_RESULT_TYPE:
- /* nothing to free */
- break;
}
wasm_free(allocator, func_field);
diff --git a/src/wasm-ast-parser-lexer-shared.h b/src/wasm-ast-parser-lexer-shared.h
index bba39a54..f516d8fd 100644
--- a/src/wasm-ast-parser-lexer-shared.h
+++ b/src/wasm-ast-parser-lexer-shared.h
@@ -69,7 +69,7 @@ typedef enum WasmFuncFieldType {
WASM_FUNC_FIELD_TYPE_EXPRS,
WASM_FUNC_FIELD_TYPE_PARAM_TYPES,
WASM_FUNC_FIELD_TYPE_BOUND_PARAM,
- WASM_FUNC_FIELD_TYPE_RESULT_TYPE,
+ WASM_FUNC_FIELD_TYPE_RESULT_TYPES,
WASM_FUNC_FIELD_TYPE_LOCAL_TYPES,
WASM_FUNC_FIELD_TYPE_BOUND_LOCAL,
} WasmFuncFieldType;
@@ -83,10 +83,9 @@ typedef struct WasmBoundType {
typedef struct WasmFuncField {
WasmFuncFieldType type;
union {
- WasmExpr* first_expr;
- WasmTypeVector types; /* WASM_FUNC_FIELD_TYPE_{PARAM,LOCAL}_TYPES */
+ WasmExpr* first_expr; /* WASM_FUNC_FIELD_TYPE_EXPRS */
+ WasmTypeVector types; /* WASM_FUNC_FIELD_TYPE_*_TYPES */
WasmBoundType bound_type; /* WASM_FUNC_FIELD_TYPE_BOUND_{LOCAL, PARAM} */
- WasmType result_type;
};
struct WasmFuncField* next;
} WasmFuncField;
diff --git a/src/wasm-ast-parser.y b/src/wasm-ast-parser.y
index 46d6bd24..a97ae6ba 100644
--- a/src/wasm-ast-parser.y
+++ b/src/wasm-ast-parser.y
@@ -116,6 +116,8 @@ static WasmBool is_empty_signature(WasmFuncSignature* sig);
static void append_implicit_func_declaration(WasmAllocator*, WasmLocation*,
WasmModule*, WasmFuncDeclaration*);
+static WasmType get_result_type_from_type_vector(WasmTypeVector* types);
+
typedef struct BinaryErrorCallbackData {
WasmLocation* loc;
WasmAstLexer* lexer;
@@ -263,11 +265,14 @@ func_type :
$$.result_type = WASM_TYPE_VOID;
$$.param_types = $3;
}
- | LPAR PARAM value_type_list RPAR LPAR RESULT VALUE_TYPE RPAR {
- $$.result_type = $7;
+ | LPAR PARAM value_type_list RPAR LPAR RESULT value_type_list RPAR {
$$.param_types = $3;
+ $$.result_type = get_result_type_from_type_vector(&$7);
+ }
+ | LPAR RESULT value_type_list RPAR {
+ WASM_ZERO_MEMORY($$);
+ $$.result_type = get_result_type_from_type_vector(&$3);
}
- | LPAR RESULT VALUE_TYPE RPAR { WASM_ZERO_MEMORY($$); $$.result_type = $3; }
;
@@ -748,10 +753,10 @@ const_expr :
/* Functions */
func_fields :
func_body
- | LPAR RESULT VALUE_TYPE RPAR func_body {
+ | LPAR RESULT value_type_list RPAR func_body {
$$ = new_func_field(parser->allocator);
- $$->type = WASM_FUNC_FIELD_TYPE_RESULT_TYPE;
- $$->result_type = $3;
+ $$->type = WASM_FUNC_FIELD_TYPE_RESULT_TYPES;
+ $$->types = $3;
$$->next = $5;
}
| LPAR PARAM value_type_list RPAR func_fields {
@@ -838,8 +843,9 @@ func_info :
break;
}
- case WASM_FUNC_FIELD_TYPE_RESULT_TYPE:
- $$->decl.sig.result_type = field->result_type;
+ case WASM_FUNC_FIELD_TYPE_RESULT_TYPES:
+ $$->decl.sig.result_type =
+ get_result_type_from_type_vector(&field->types);
break;
}
@@ -1607,6 +1613,15 @@ static void append_implicit_func_declaration(WasmAllocator* allocator,
decl->flags |= WASM_FUNC_DECLARATION_FLAG_SHARED_SIGNATURE;
}
+static WasmType get_result_type_from_type_vector(WasmTypeVector* types) {
+ /* TODO(binji): handle multiple result types more cleanly */
+ switch (types->size) {
+ case 0: return WASM_TYPE_VOID; break;
+ case 1: return types->data[0]; break;
+ default: return WASM_TYPE_MULTIPLE; break;
+ }
+}
+
WasmResult wasm_parse_ast(WasmAstLexer* lexer,
struct WasmScript* out_script,
WasmSourceErrorHandler* error_handler) {
diff --git a/src/wasm-binary-reader-interpreter.c b/src/wasm-binary-reader-interpreter.c
index 661caa58..ce0e27c0 100644
--- a/src/wasm-binary-reader-interpreter.c
+++ b/src/wasm-binary-reader-interpreter.c
@@ -276,13 +276,12 @@ static WasmResult emit_drop_keep(Context* ctx, uint32_t drop, uint8_t keep) {
return WASM_OK;
}
-static WasmResult emit_return(Context* ctx, WasmType result_type) {
+static WasmResult drop_exprs_for_return(Context* ctx, WasmType result_type) {
/* drop the locals and params, but keep the return value, if any */
uint32_t keep_count = get_value_count(result_type);
assert(ctx->expr_stack.size >= keep_count);
uint32_t drop_count = ctx->expr_stack.size - keep_count;
CHECK_RESULT(emit_drop_keep(ctx, drop_count, keep_count));
- CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_RETURN));
return WASM_OK;
}
@@ -455,6 +454,7 @@ static WasmResult on_memory_limits(WasmBool has_max,
memory->allocator = ctx->memory_allocator;
memory->page_size = initial_pages;
memory->byte_size = initial_pages * WASM_PAGE_SIZE;
+ memory->max_page_size = has_max ? max_pages : WASM_MAX_PAGES;
memory->data = wasm_alloc_zero(ctx->memory_allocator, memory->byte_size,
WASM_DEFAULT_ALIGN);
return WASM_OK;
@@ -677,32 +677,28 @@ static void pop_label(Context* ctx) {
}
}
-static void push_expr(Context* ctx, WasmType type, WasmOpcode opcode) {
- /* TODO: refactor to remove opcode param */
- if (type != WASM_TYPE_VOID) {
- Label* label = top_label(ctx);
- if (ctx->expr_stack.size > label->expr_stack_size) {
- WasmType top_type = ctx->expr_stack.data[ctx->expr_stack.size - 1];
- if (top_type == WASM_TYPE_ANY)
- return;
- }
-
- LOGF("%3" PRIzd ": push %s:%s\n", ctx->expr_stack.size,
- s_opcode_name[opcode], s_type_names[type]);
- wasm_append_type_value(ctx->allocator, &ctx->expr_stack, &type);
- }
-}
-
static WasmType top_expr(Context* ctx) {
Label* label = top_label(ctx);
assert(ctx->expr_stack.size > label->expr_stack_size);
return ctx->expr_stack.data[ctx->expr_stack.size - 1];
}
+static WasmBool top_expr_is_any(Context* ctx) {
+ Label* label = top_label(ctx);
+ if (ctx->expr_stack.size > label->expr_stack_size) {
+ WasmType top_type = ctx->expr_stack.data[ctx->expr_stack.size - 1];
+ if (top_type == WASM_TYPE_ANY)
+ return WASM_TRUE;
+ }
+ return WASM_FALSE;
+}
+
static WasmType pop_expr(Context* ctx) {
WasmType type = top_expr(ctx);
- LOGF("%3" PRIzd ": pop %s\n", ctx->expr_stack.size, s_type_names[type]);
- ctx->expr_stack.size--;
+ if (type != WASM_TYPE_ANY) {
+ LOGF("%3" PRIzd ": pop %s\n", ctx->expr_stack.size, s_type_names[type]);
+ ctx->expr_stack.size--;
+ }
return type;
}
@@ -712,10 +708,29 @@ static WasmType top_expr_if(Context* ctx, WasmBool cond) {
return WASM_TYPE_VOID;
}
-static WasmResult check_end(Context* ctx, WasmType* top_type) {
+static void push_expr(Context* ctx, WasmType type, WasmOpcode opcode) {
+ /* TODO: refactor to remove opcode param */
+ if (type != WASM_TYPE_VOID) {
+ if (top_expr_is_any(ctx))
+ return;
+
+ LOGF("%3" PRIzd ": push %s:%s\n", ctx->expr_stack.size,
+ s_opcode_name[opcode], s_type_names[type]);
+ wasm_append_type_value(ctx->allocator, &ctx->expr_stack, &type);
+ }
+}
+
+static WasmResult check_end(Context* ctx,
+ WasmType* top_type,
+ size_t stack_size_adjustment) {
Label* label = top_label(ctx);
- assert(ctx->expr_stack.size >= label->expr_stack_size);
- size_t stack_size = ctx->expr_stack.size - label->expr_stack_size;
+ /* the stack_size_adjustment is typically 0; it is non-zero when checking the
+ * implicit function label. The function label stack size does not include
+ * the parameters, so it will appear as though there are too many values on
+ * the stack if the stack size is not adjusted. */
+ size_t label_stack_size = label->expr_stack_size + stack_size_adjustment;
+ assert(ctx->expr_stack.size >= label_stack_size);
+ size_t stack_size = ctx->expr_stack.size - label_stack_size;
if (stack_size == 0) {
*top_type = WASM_TYPE_VOID;
return WASM_OK;
@@ -741,10 +756,14 @@ static WasmResult begin_function_body(uint32_t index, void* user_data) {
ctx->current_func = func;
ctx->depth_fixups.size = 0;
- ctx->expr_stack.size = sig->param_types.size;
+ ctx->expr_stack.size = 0;
ctx->label_stack.size = 0;
ctx->depth = 0;
+ /* push implicit func label (equivalent to return) */
+ push_label(ctx, LABEL_TYPE_FUNC, sig->result_type, WASM_INVALID_OFFSET,
+ WASM_INVALID_OFFSET);
+
/* fixup function references */
uint32_t i;
Uint32Vector* fixups = &ctx->func_fixups.data[index];
@@ -753,25 +772,33 @@ static WasmResult begin_function_body(uint32_t index, void* user_data) {
/* append param types */
for (i = 0; i < sig->param_types.size; ++i) {
- wasm_append_type_value(ctx->allocator, &func->param_and_local_types,
- &sig->param_types.data[i]);
+ WasmType type = sig->param_types.data[i];
+ wasm_append_type_value(ctx->allocator, &func->param_and_local_types, &type);
+ wasm_append_type_value(ctx->allocator, &ctx->expr_stack, &type);
}
- /* push implicit func label (equivalent to return) */
- push_label(ctx, LABEL_TYPE_FUNC, sig->result_type, WASM_INVALID_OFFSET,
- WASM_INVALID_OFFSET);
return WASM_OK;
}
static WasmResult end_function_body(uint32_t index, void* user_data) {
Context* ctx = user_data;
+ Label* label = top_label(ctx);
+ if (!label || label->label_type != LABEL_TYPE_FUNC) {
+ print_error(ctx, "unexpected function end");
+ return WASM_ERROR;
+ }
+
WasmInterpreterFuncSignature* sig =
get_func_signature(ctx, ctx->current_func);
WasmType top_type;
- CHECK_RESULT(check_end(ctx, &top_type));
- CHECK_RESULT(check_type(ctx, sig->result_type, top_type, "implicit return"));
- CHECK_RESULT(emit_return(ctx, sig->result_type));
+ CHECK_RESULT(check_end(ctx, &top_type, sig->param_types.size));
+ unify_type(&label->type, top_type);
+ CHECK_RESULT(
+ check_type(ctx, sig->result_type, label->type, "implicit return"));
+ CHECK_RESULT(drop_exprs_for_return(ctx, sig->result_type));
+ fixup_top_label(ctx, get_istream_offset(ctx));
+ CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_RETURN));
pop_label(ctx);
ctx->current_func = NULL;
ctx->expr_stack.size = 0;
@@ -815,7 +842,6 @@ static WasmResult on_local_decl(uint32_t decl_index,
static WasmResult on_unary_expr(WasmOpcode opcode, void* user_data) {
Context* ctx = user_data;
const char* opcode_name = s_opcode_name[opcode];
- LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, opcode_name);
WasmType value = pop_expr(ctx);
CHECK_RESULT(check_type(ctx, s_opcode_type1[opcode], value, opcode_name));
CHECK_RESULT(emit_opcode(ctx, opcode));
@@ -826,7 +852,6 @@ static WasmResult on_unary_expr(WasmOpcode opcode, void* user_data) {
static WasmResult on_binary_expr(WasmOpcode opcode, void* user_data) {
Context* ctx = user_data;
const char* opcode_name = s_opcode_name[opcode];
- LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, opcode_name);
WasmType right = pop_expr(ctx);
WasmType left = pop_expr(ctx);
CHECK_RESULT(check_type(ctx, s_opcode_type1[opcode], left, opcode_name));
@@ -838,7 +863,6 @@ static WasmResult on_binary_expr(WasmOpcode opcode, void* user_data) {
static WasmResult on_block_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": block\n", ctx->expr_stack.size);
push_label(ctx, LABEL_TYPE_BLOCK, WASM_TYPE_ANY, WASM_INVALID_OFFSET,
WASM_INVALID_OFFSET);
return WASM_OK;
@@ -846,7 +870,6 @@ static WasmResult on_block_expr(void* user_data) {
static WasmResult on_loop_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": loop\n", ctx->expr_stack.size);
push_label(ctx, LABEL_TYPE_LOOP, WASM_TYPE_VOID, get_istream_offset(ctx),
WASM_INVALID_OFFSET);
return WASM_OK;
@@ -854,7 +877,6 @@ static WasmResult on_loop_expr(void* user_data) {
static WasmResult on_if_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": if\n", ctx->expr_stack.size);
WasmType cond = pop_expr(ctx);
CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, cond, "if"));
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_UNLESS));
@@ -867,7 +889,6 @@ static WasmResult on_if_expr(void* user_data) {
static WasmResult on_else_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": else\n", ctx->expr_stack.size);
Label* label = top_label(ctx);
if (!label || label->label_type != LABEL_TYPE_IF) {
print_error(ctx, "unexpected else operator");
@@ -875,7 +896,7 @@ static WasmResult on_else_expr(void* user_data) {
}
WasmType top_type;
- CHECK_RESULT(check_end(ctx, &top_type));
+ CHECK_RESULT(check_end(ctx, &top_type, 0));
CHECK_RESULT(
unify_and_check_type(ctx, &label->type, top_type, "if true branch"));
@@ -892,7 +913,6 @@ static WasmResult on_else_expr(void* user_data) {
static WasmResult on_end_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": end\n", ctx->expr_stack.size);
Label* label = top_label(ctx);
if (!label || label->label_type == LABEL_TYPE_FUNC) {
print_error(ctx, "unexpected end operator");
@@ -903,9 +923,12 @@ static WasmResult on_end_expr(void* user_data) {
label->type = WASM_TYPE_VOID;
WasmType top_type;
- CHECK_RESULT(check_end(ctx, &top_type));
- CHECK_RESULT(unify_and_check_type(ctx, &label->type, top_type,
- s_label_type_name[label->label_type]));
+ CHECK_RESULT(check_end(ctx, &top_type, 0));
+ if (label->label_type != LABEL_TYPE_LOOP) {
+ CHECK_RESULT(unify_and_check_type(ctx, &label->type, top_type,
+ s_label_type_name[label->label_type]));
+ top_type = label->type;
+ }
if (label->label_type == LABEL_TYPE_IF ||
label->label_type == LABEL_TYPE_ELSE) {
@@ -916,13 +939,12 @@ static WasmResult on_end_expr(void* user_data) {
fixup_top_label(ctx, get_istream_offset(ctx));
ctx->expr_stack.size = label->expr_stack_size;
pop_label(ctx);
- push_expr(ctx, label->type, WASM_OPCODE_END);
+ push_expr(ctx, top_type, WASM_OPCODE_END);
return WASM_OK;
}
static WasmResult on_br_expr(uint8_t arity, uint32_t depth, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": br\n", ctx->expr_stack.size);
WasmType value = top_expr_if(ctx, arity > 0);
CHECK_DEPTH(ctx, depth);
depth = translate_depth(ctx, depth);
@@ -937,7 +959,6 @@ static WasmResult on_br_if_expr(uint8_t arity,
uint32_t depth,
void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": br_if\n", ctx->expr_stack.size);
WasmType cond = pop_expr(ctx);
WasmType value = top_expr_if(ctx, arity > 0);
CHECK_DEPTH(ctx, depth);
@@ -953,8 +974,10 @@ static WasmResult on_br_if_expr(uint8_t arity,
CHECK_RESULT(emit_i32_at(ctx, fixup_br_offset, get_istream_offset(ctx)));
/* clean up the value (if any), when the branch isn't taken */
CHECK_RESULT(emit_drop_keep(ctx, arity, 0));
- ctx->expr_stack.size -= arity;
- push_expr(ctx, WASM_TYPE_VOID, WASM_OPCODE_BR_IF);
+ /* don't pop the top expr from the stack if it is ANY; we want that to
+ * propagate through */
+ if (!top_expr_is_any(ctx))
+ ctx->expr_stack.size -= arity;
return WASM_OK;
}
@@ -964,7 +987,6 @@ static WasmResult on_br_table_expr(uint8_t arity,
uint32_t default_target_depth,
void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": br_table\n", ctx->expr_stack.size);
WasmType key = pop_expr(ctx);
WasmType value = top_expr_if(ctx, arity > 0);
CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, key, "br_table"));
@@ -994,7 +1016,6 @@ static WasmResult on_br_table_expr(uint8_t arity,
static WasmResult on_call_expr(uint32_t func_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": call\n", ctx->expr_stack.size);
assert(func_index < ctx->funcs.size);
InterpreterFunc* func = get_func(ctx, func_index);
WasmInterpreterFuncSignature* sig = get_func_signature(ctx, func);
@@ -1013,7 +1034,6 @@ static WasmResult on_call_expr(uint32_t func_index, void* user_data) {
static WasmResult on_call_import_expr(uint32_t import_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": call_import\n", ctx->expr_stack.size);
assert(import_index < ctx->module->imports.size);
WasmInterpreterImport* import = get_import(ctx, import_index);
WasmInterpreterFuncSignature* sig = get_signature(ctx, import->sig_index);
@@ -1033,7 +1053,6 @@ static WasmResult on_call_import_expr(uint32_t import_index, void* user_data) {
static WasmResult on_call_indirect_expr(uint32_t sig_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": call_indirect\n", ctx->expr_stack.size);
WasmInterpreterFuncSignature* sig = get_signature(ctx, sig_index);
WasmType entry_index = pop_expr(ctx);
CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, entry_index, "call_indirect"));
@@ -1053,7 +1072,6 @@ static WasmResult on_call_indirect_expr(uint32_t sig_index, void* user_data) {
static WasmResult on_drop_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": drop\n", ctx->expr_stack.size);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DROP));
pop_expr(ctx);
return WASM_OK;
@@ -1061,7 +1079,6 @@ static WasmResult on_drop_expr(void* user_data) {
static WasmResult on_i32_const_expr(uint32_t value, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": i32.const\n", ctx->expr_stack.size);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I32_CONST));
CHECK_RESULT(emit_i32(ctx, value));
push_expr(ctx, WASM_TYPE_I32, WASM_OPCODE_I32_CONST);
@@ -1070,7 +1087,6 @@ static WasmResult on_i32_const_expr(uint32_t value, void* user_data) {
static WasmResult on_i64_const_expr(uint64_t value, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": i64.const\n", ctx->expr_stack.size);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I64_CONST));
CHECK_RESULT(emit_i64(ctx, value));
push_expr(ctx, WASM_TYPE_I64, WASM_OPCODE_I64_CONST);
@@ -1079,7 +1095,6 @@ static WasmResult on_i64_const_expr(uint64_t value, void* user_data) {
static WasmResult on_f32_const_expr(uint32_t value_bits, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": f32.const\n", ctx->expr_stack.size);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F32_CONST));
CHECK_RESULT(emit_i32(ctx, value_bits));
push_expr(ctx, WASM_TYPE_F32, WASM_OPCODE_F32_CONST);
@@ -1088,7 +1103,6 @@ static WasmResult on_f32_const_expr(uint32_t value_bits, void* user_data) {
static WasmResult on_f64_const_expr(uint64_t value_bits, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": f64.const\n", ctx->expr_stack.size);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F64_CONST));
CHECK_RESULT(emit_i64(ctx, value_bits));
push_expr(ctx, WASM_TYPE_F64, WASM_OPCODE_F64_CONST);
@@ -1097,7 +1111,6 @@ static WasmResult on_f64_const_expr(uint64_t value_bits, void* user_data) {
static WasmResult on_get_global_expr(uint32_t global_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": get_global\n", ctx->expr_stack.size);
CHECK_GLOBAL(ctx, global_index);
WasmType type = get_global_index_type(ctx, global_index);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_GLOBAL));
@@ -1108,7 +1121,6 @@ static WasmResult on_get_global_expr(uint32_t global_index, void* user_data) {
static WasmResult on_set_global_expr(uint32_t global_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": set_global\n", ctx->expr_stack.size);
CHECK_GLOBAL(ctx, global_index);
WasmType type = get_global_index_type(ctx, global_index);
WasmType value = pop_expr(ctx);
@@ -1120,7 +1132,6 @@ static WasmResult on_set_global_expr(uint32_t global_index, void* user_data) {
static WasmResult on_get_local_expr(uint32_t local_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": get_local\n", ctx->expr_stack.size);
CHECK_LOCAL(ctx, local_index);
WasmType type = get_local_index_type(ctx->current_func, local_index);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_LOCAL));
@@ -1131,7 +1142,6 @@ static WasmResult on_get_local_expr(uint32_t local_index, void* user_data) {
static WasmResult on_set_local_expr(uint32_t local_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": set_local\n", ctx->expr_stack.size);
CHECK_LOCAL(ctx, local_index);
WasmType type = get_local_index_type(ctx->current_func, local_index);
WasmType value = pop_expr(ctx);
@@ -1143,10 +1153,9 @@ static WasmResult on_set_local_expr(uint32_t local_index, void* user_data) {
static WasmResult on_tee_local_expr(uint32_t local_index, void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": tee_local\n", ctx->expr_stack.size);
CHECK_LOCAL(ctx, local_index);
WasmType type = get_local_index_type(ctx->current_func, local_index);
- WasmType value = pop_expr(ctx);
+ WasmType value = top_expr(ctx);
CHECK_RESULT(check_type(ctx, type, value, "tee_local"));
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_TEE_LOCAL));
CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index)));
@@ -1155,7 +1164,6 @@ static WasmResult on_tee_local_expr(uint32_t local_index, void* user_data) {
static WasmResult on_grow_memory_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": grow_memory\n", ctx->expr_stack.size);
WasmType value = pop_expr(ctx);
CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, value, "grow_memory"));
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GROW_MEMORY));
@@ -1168,7 +1176,6 @@ static WasmResult on_load_expr(WasmOpcode opcode,
uint32_t offset,
void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, s_opcode_name[opcode]);
WasmType addr = pop_expr(ctx);
CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, addr, s_opcode_name[opcode]));
CHECK_RESULT(emit_opcode(ctx, opcode));
@@ -1182,7 +1189,6 @@ static WasmResult on_store_expr(WasmOpcode opcode,
uint32_t offset,
void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, s_opcode_name[opcode]);
WasmType value = pop_expr(ctx);
WasmType addr = pop_expr(ctx);
WasmType type = s_opcode_rtype[opcode];
@@ -1196,7 +1202,6 @@ static WasmResult on_store_expr(WasmOpcode opcode,
static WasmResult on_current_memory_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": current_memory\n", ctx->expr_stack.size);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CURRENT_MEMORY));
push_expr(ctx, WASM_TYPE_I32, WASM_OPCODE_CURRENT_MEMORY);
return WASM_OK;
@@ -1205,27 +1210,25 @@ static WasmResult on_current_memory_expr(void* user_data) {
static WasmResult on_nop_expr(void* user_data) {
Context* ctx = user_data;
WASM_USE(ctx);
- LOGF("%3" PRIzd ": nop\n", ctx->expr_stack.size);
return WASM_OK;
}
static WasmResult on_return_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": return\n", ctx->expr_stack.size);
WasmInterpreterFuncSignature* sig =
get_func_signature(ctx, ctx->current_func);
if (get_value_count(sig->result_type)) {
WasmType value = top_expr(ctx);
CHECK_RESULT(check_type(ctx, sig->result_type, value, "return"));
}
- CHECK_RESULT(emit_return(ctx, sig->result_type));
+ CHECK_RESULT(drop_exprs_for_return(ctx, sig->result_type));
+ CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_RETURN));
push_expr(ctx, WASM_TYPE_ANY, WASM_OPCODE_RETURN);
return WASM_OK;
}
static WasmResult on_select_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": select\n", ctx->expr_stack.size);
WasmType cond = pop_expr(ctx);
WasmType right = pop_expr(ctx);
WasmType left = pop_expr(ctx);
@@ -1240,7 +1243,6 @@ static WasmResult on_select_expr(void* user_data) {
static WasmResult on_unreachable_expr(void* user_data) {
Context* ctx = user_data;
- LOGF("%3" PRIzd ": unreachable\n", ctx->expr_stack.size);
CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_UNREACHABLE));
push_expr(ctx, WASM_TYPE_ANY, WASM_OPCODE_UNREACHABLE);
return WASM_OK;
diff --git a/src/wasm-common.h b/src/wasm-common.h
index 5807834e..744c8f6e 100644
--- a/src/wasm-common.h
+++ b/src/wasm-common.h
@@ -132,6 +132,9 @@ enum {
WASM_TYPE_F64,
WASM_NUM_TYPES,
WASM_TYPE____ = WASM_TYPE_VOID, /* convenient for the opcode table below */
+ /* used when parsing multiple return types to signify an error */
+ /* TODO(binji): remove and support properly */
+ WASM_TYPE_MULTIPLE = WASM_NUM_TYPES,
};
typedef unsigned char WasmType;
diff --git a/src/wasm-interpreter.c b/src/wasm-interpreter.c
index 393a37a8..f6eab79e 100644
--- a/src/wasm-interpreter.c
+++ b/src/wasm-interpreter.c
@@ -347,6 +347,12 @@ DEFINE_BITCAST(bitcast_u64_to_f64, uint64_t, double)
#define CHECK_STACK() TRAP_IF(vs_top >= vs_end, VALUE_STACK_EXHAUSTED)
+#define PUSH_NEG_1_AND_BREAK_IF(cond) \
+ if (WASM_UNLIKELY(cond)) { \
+ PUSH_I32(-1); \
+ break; \
+ }
+
#define PUSH(v) \
do { \
CHECK_STACK(); \
@@ -874,13 +880,14 @@ WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module,
uint32_t old_byte_size = module->memory.byte_size;
VALUE_TYPE_I32 grow_pages = POP_I32();
uint32_t new_page_size = old_page_size + grow_pages;
- TRAP_IF((uint64_t)new_page_size * WASM_PAGE_SIZE > UINT32_MAX,
- MEMORY_SIZE_OVERFLOW);
+ PUSH_NEG_1_AND_BREAK_IF(new_page_size > module->memory.max_page_size);
+ PUSH_NEG_1_AND_BREAK_IF((uint64_t)new_page_size * WASM_PAGE_SIZE >
+ UINT32_MAX);
uint32_t new_byte_size = new_page_size * WASM_PAGE_SIZE;
WasmAllocator* allocator = module->memory.allocator;
void* new_data = wasm_realloc(allocator, module->memory.data,
new_byte_size, WASM_DEFAULT_ALIGN);
- TRAP_IF(new_data == NULL, OUT_OF_MEMORY);
+ PUSH_NEG_1_AND_BREAK_IF(new_data == NULL);
memset((void*)((intptr_t)new_data + old_byte_size), 0,
new_byte_size - old_byte_size);
module->memory.data = new_data;
@@ -1601,6 +1608,7 @@ void wasm_trace_pc(WasmInterpreterModule* module,
break;
case WASM_OPCODE_SET_LOCAL:
+ case WASM_OPCODE_TEE_LOCAL:
wasm_writef(stream, "%s $%u, %u\n", s_opcode_name[opcode],
read_u32_at(pc), TOP().i32);
break;
@@ -1931,6 +1939,7 @@ void wasm_disassemble_module(WasmInterpreterModule* module,
break;
case WASM_OPCODE_SET_LOCAL:
+ case WASM_OPCODE_TEE_LOCAL:
wasm_writef(stream, "%s $%u, %%[-1]\n", s_opcode_name[opcode],
read_u32(&pc));
break;
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index b0b733d5..8fc5f5c4 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -44,10 +44,6 @@ struct WasmStream;
V(TRAP_UNREACHABLE, "unreachable executed") \
/* call indirect signature doesn't match function table signature */ \
V(TRAP_INDIRECT_CALL_SIGNATURE_MISMATCH, "indirect call signature mismatch") \
- /* growing the memory would overflow the memory size type */ \
- V(TRAP_MEMORY_SIZE_OVERFLOW, "memory size overflow") \
- /* out of memory */ \
- V(TRAP_OUT_OF_MEMORY, "memory size exceeds implementation limit") \
/* ran out of call stack frames (probably infinite recursion) */ \
V(TRAP_CALL_STACK_EXHAUSTED, "call stack exhausted") \
/* ran out of value stack space */ \
@@ -95,6 +91,7 @@ typedef struct WasmInterpreterMemory {
void* data;
uint32_t page_size;
uint32_t byte_size;
+ uint32_t max_page_size;
} WasmInterpreterMemory;
typedef struct WasmInterpreterFuncTableEntry {