Index: llvm/test/MC/WebAssembly/type-checker-errors.s =================================================================== --- /dev/null +++ llvm/test/MC/WebAssembly/type-checker-errors.s @@ -0,0 +1,513 @@ +# RUN: not llvm-mc -triple=wasm32 -mattr=+exception-handling,+reference-types,+tail-call %s 2>&1 | FileCheck %s + +# These tests are intended to act as a litmus test for the WebAssembly ASM +# type-checker - both in terms of errors it can catch and in terms of the +# location information used in the error messages. + +local_get_no_local_type: + .functype local_get_no_local_type () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: no local type specified for index 0 + local.get 0 + end_function + +local_set_no_local_type: + .functype local_set_no_local_type () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: no local type specified for index 0 + local.set 0 + end_function + +local_set_empty_stack_while_popping: + .functype local_set_empty_stack_while_popping () -> () + .local i32 +# CHECK: [[@LINE+1]]:3: error: empty stack while popping i32 + local.set 0 + end_function + +local_set_type_mismatch: + .functype local_set_type_mismatch () -> () + .local i32 + f32.const 1.0 +# CHECK: [[@LINE+1]]:3: error: popped f32, expected i32 + local.set 0 + end_function + +local_tee_no_local_type: + .functype local_tee_no_local_type () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: no local type specified for index 0 + local.tee 0 + end_function + +local_tee_empty_stack_while_popping: + .functype local_tee_empty_stack_while_popping () -> () + .local f32 +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping f32 + local.tee 0 + end_function + +local_tee_type_mismatch: + .functype local_tee_type_mismatch () -> () + .local f32 + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: popped i32, expected f32 + local.tee 0 + end_function + +global_get_missing_globaltype: + .functype global_get_missing_globaltype () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: symbol foo missing .globaltype + global.get foo + end_function + +global_get_expected_expression_operand: + .functype global_get_expected_expression_operand () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + global.get 1 + end_function + +global_set_missing_globaltype: + .functype global_set_missing_globaltype () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: symbol foo missing .globaltype + global.set foo + end_function + +global_set_expected_expression_operand: + .functype global_set_expected_expression_operand () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + global.set 1 + end_function + +global_set_empty_stack_while_popping: + .functype global_set_empty_stack_while_popping () -> () + .globaltype valid_global, i64 +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i64 + global.set valid_global + end_function + +global_set_type_mismatch: + .functype global_set_type_mismatch () -> () + .globaltype valid_global, i64 + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: popped i32, expected i64 + global.set valid_global + end_function + +table_get_expected_expression_operand: + .functype table_get_expected_expression_operand () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + table.get 1 + end_function + +table_get_missing_tabletype: + .functype table_get_missing_tabletype () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: symbol foo missing .tabletype + table.get foo + end_function + +.tabletype valid_table, externref + +table_get_empty_stack_while_popping: + .functype table_get_empty_stack_while_popping () -> () +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + table.get valid_table + end_function + +table_get_type_mismatch: + .functype table_get_type_mismatch () -> () + f32.const 1.0 +# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32 + table.get valid_table + end_function + +table_set_expected_expression_operand: + .functype table_set_expected_expression_operand () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + table.set 1 + end_function + +table_set_missing_tabletype: + .functype table_set_missing_tabletype () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: symbol foo missing .tabletype + table.set foo + end_function + +table_set_empty_stack_while_popping_1: + .functype table_set_empty_stack_while_popping_1 () -> () +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping externref + table.set valid_table + end_function + +table_set_empty_stack_while_popping_2: + .functype table_set_empty_stack_while_popping_2 (externref) -> () + local.get 0 +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + table.set valid_table + end_function + +table_set_type_mismatch_1: + .functype table_set_type_mismatch_1 () -> () + ref.null_func +# CHECK: :[[@LINE+1]]:3: error: popped funcref, expected externref + table.set valid_table + end_function + +table_set_type_mismatch_2: + .functype table_set_type_mismatch_2 () -> () + f32.const 1.0 + ref.null_extern +# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32 + table.set valid_table + end_function + +table_fill_expected_expression_operand: + .functype table_fill_expected_expression_operand () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + table.fill 1 + end_function + +table_fill_missing_tabletype: + .functype table_fill_missing_tabletype () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: symbol foo missing .tabletype + table.fill foo + end_function + +table_fill_empty_stack_while_popping_1: + .functype table_fill_empty_stack_while_popping_1 () -> () +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + table.fill valid_table + end_function + +table_fill_empty_stack_while_popping_2: + .functype table_fill_empty_stack_while_popping_2 (i32) -> () + local.get 0 +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping externref + table.fill valid_table + end_function + +table_fill_empty_stack_while_popping_3: + .functype table_fill_empty_stack_while_popping_3 (i32, externref) -> () + local.get 1 + local.get 0 +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + table.fill valid_table + end_function + +table_fill_type_mismatch_1: + .functype table_fill_type_mismatch_1 () -> () + ref.null_func +# CHECK: :[[@LINE+1]]:3: error: popped funcref, expected i32 + table.fill valid_table + end_function + +table_fill_type_mismatch_2: + .functype table_fill_type_mismatch_2 () -> () + ref.null_func + i32.const 1 +# CHECK: [[@LINE+1]]:3: error: popped funcref, expected externref + table.fill valid_table + end_function + +table_fill_type_mismatch_3: + .functype table_fill_type_mismatch_3 () -> () + f32.const 2.0 + ref.null_extern + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32 + table.fill valid_table + end_function + +drop_empty_stack_while_popping: + .functype drop_empty_stack_while_popping () -> () +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value + drop + end_function + +end_block_insufficient_values_on_stack: + .functype end_block_insufficient_values_on_stack () -> () + block i32 +# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack + end_block + end_function + +end_block_type_mismatch: + .functype end_block_type_mismatch () -> () + block i32 + f32.const 1.0 +# CHECK: :[[@LINE+1]]:3: error: end got f32, expected i32 + end_block + end_function + +end_loop_insufficient_values_on_stack: + .functype end_loop_insufficient_values_on_stack () -> () + loop i32 +# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack + end_loop + end_function + +end_loop_type_mismatch: + .functype end_loop_type_mismatch () -> () + loop f32 + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: end got i32, expected f32 + end_loop + end_function + +end_if_insufficient_values_on_stack: + .functype end_if_insufficient_values_on_stack () -> () + i32.const 1 + if i32 +# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack + end_if + end_function + +end_if_type_mismatch: + .functype end_if_type_mismatch () -> () + i32.const 1 + if f32 + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: end got i32, expected f32 + end_if + end_function + +else_insufficient_values_on_stack: + .functype else_insufficient_values_on_stack () -> () + i32.const 1 + if i32 + i32.const 2 + else +# FIXME: Should complain about insufficient values on the stack. + end_if +# FIXME: Should complain about superflous value on the stack. + end_function + +else_type_mismatch: + .functype else_type_mismatch () -> () + i32.const 1 + if i32 + i32.const 2 + else +# FIXME: Should complain about a type mismatch. + f32.const 3.0 + end_if + drop + end_function + +end_try_insufficient_values_on_stack: + .functype end_try_insufficient_values_on_stack () -> () + try i32 +# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack + end_try + end_function + +end_try_type_mismatch: + .functype end_try_type_mismatch () -> () + try i32 + f32.const 1.0 +# CHECK: :[[@LINE+1]]:3: error: end got f32, expected i32 + end_try + end_function + +end_function_empty_stack_while_popping: + .functype end_function_empty_stack_while_popping () -> (i32) +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + end_function + +end_function_type_mismatch: + .functype end_function_type_mismatch () -> (f32) + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: popped i32, expected f32 + end_function + +end_function_superfluous_end_function_values: + .functype end_function_superfluous_end_function_values () -> () + i32.const 1 + f32.const 2.0 +# CHECK: :[[@LINE+1]]:3: error: 2 superfluous return values + end_function + +return_empty_stack_while_popping: + .functype return_empty_stack_while_popping () -> (i32) +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + return + end_function + +return_type_mismatch: + .functype return_type_mismatch () -> (f32) + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: popped i32, expected f32 + return + end_function + +# Missing index for call_indirect. +call_indirect_empty_stack_while_popping_1: + .functype call_indirect_empty_stack_while_popping_1 () -> () +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + call_indirect () -> () + end_function + +# Missing arguments for target of call_indirect. +call_indirect_empty_stack_while_popping_2: + .functype call_indirect_empty_stack_while_popping_1 (f32) -> () + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping f32 + call_indirect (f32) -> () + end_function + +call_indirect_type_mismatch_for_argument: + .functype call_indirect_type_mismatch_for_argument () -> () + i32.const 1 + i32.const 2 +# CHECK: :[[@LINE+1]]:3: error: popped i32, expected f32 + call_indirect (f32) -> () + end_function + +call_indirect_superfluous_value_at_end: + .functype call_indirect_superfluous_value_at_end () -> () + i32.const 1 + call_indirect () -> (i64) +# CHECK: :[[@LINE+1]]:3: error: 1 superfluous return values + end_function + +# Missing index for return_call_indirect. +return_call_indirect_empty_stack_while_popping_1: + .functype return_call_indirect_empty_stack_while_popping_1 () -> () +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32 + return_call_indirect () -> () + end_function + +# Missing arguments for target of return_call_indirect. +return_call_indirect_empty_stack_while_popping_2: + .functype return_call_indirect_empty_stack_while_popping_2 () -> () + i32.const 1 +# CHECK: :[[@LINE+1]]:3: error: empty stack while popping f32 + return_call_indirect (f32) -> () + end_function + +call_expected_expression_operand: + .functype call_expected_expression_operand () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + call 1 + end_function + +.functype fn_i32_to_void (i32) -> () + +call_empty_stack_while_popping: + .functype call_empty_stack_while_popping () -> () +# CHECK: [[@LINE+1]]:3: error: empty stack while popping i32 + call fn_i32_to_void + end_function + +call_type_mismatch: + .functype call_type_mismatch () -> () + f32.const 1.0 +# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32 + call fn_i32_to_void + end_function + +.functype fn_void_to_i32 () -> (i32) + +call_superfluous_value_at_end: + .functype call_superfluous_value_at_end () -> () + call fn_void_to_i32 +# CHECK: :[[@LINE+1]]:3: error: 1 superfluous return values + end_function + +call_missing_functype: + .functype call_missing_functype () -> () +# CHECK: :[[@LINE+1]]:3: error: symbol no_functype missing .functype + call no_functype + end_function + +return_call_expected_expression_operand: + .functype return_call_expected_expression_operand () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + return_call 1 + end_function + +return_call_empty_stack_while_popping: + .functype return_call_empty_stack_while_popping () -> () +# CHECK: [[@LINE+1]]:3: error: empty stack while popping i32 + return_call fn_i32_to_void + end_function + +return_call_type_mismatch: + .functype return_call_type_mismatch () -> () + f32.const 1.0 +# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32 + return_call fn_i32_to_void + end_function + +return_call_missing_functype: + .functype return_call_missing_functype () -> () +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: symbol no_functype missing .functype + return_call no_functype + end_function + +catch_expected_expression_operand: + .functype catch_expected_expression_operand () -> () + try +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: expected expression operand + catch 1 + end_try + end_function + +catch_missing_tagtype: + .functype catch_missing_tagtype () -> () + try +# FIXME: Error location should be at operand. +# CHECK: :[[@LINE+1]]:3: error: symbol no_tagtype missing .tagtype + catch no_tagtype + end_try + end_function + +catch_superfluous_value_at_end: + .functype catch_superfluous_value_at_end () -> () + .tagtype tag_i32 i32 + try + catch tag_i32 + end_try +# FIXME: Superfluous value should be caught at end_try? +# CHECK: :[[@LINE+1]]:3: error: 1 superfluous return values + end_function + +# For the other instructions, the type checker checks vs the operands in the +# instruction definition. Perform some simple checks for these rather than +# exhaustively testing all instructions. + +other_insn_test_1: + .functype other_insn_test_1 () -> () +# CHECK: [[@LINE+1]]:3: error: empty stack while popping i32 + i32.add + end_function + +other_insn_test_2: + .functype other_insn_test_2 () -> () + i32.const 1 + ref.null_func +# CHECK: :[[@LINE+1]]:3: error: popped funcref, expected i32 + i32.add + end_function + +other_insn_test_3: + .functype other_insn_test_3 () -> () + f32.const 1.0 + f32.const 2.0 + f32.add +# CHECK: :[[@LINE+1]]:3: error: 1 superfluous return values + end_function Index: llvm/test/MC/WebAssembly/type-checker-return.s =================================================================== --- /dev/null +++ llvm/test/MC/WebAssembly/type-checker-return.s @@ -0,0 +1,29 @@ +# RUN: llvm-mc -triple=wasm32 -mattr=+tail-call %s 2>&1 + +# XFAIL: * + +# These shouldn't produce an error, as return will implicitly drop any +# superfluous values. + +return_superfluous_return_values: + .functype return_superfluous_return_values () -> () + i32.const 1 + f32.const 2.0 + return + end_function + +return_call_indirect_superfluous_return_values: + .functype return_call_indirect_superfluous_return_values () -> () + f32.const 1.0 + i32.const 2 + return_call_indirect () -> () + end_function + +.functype fn_void_to_void () -> () + +return_call_superfluous_return_values: + .functype return_call_superfluous_return_values () -> () + f32.const 1.0 + i32.const 2 + return_call fn_void_to_void + end_function