Skip to content

Commit 3e9881b

Browse files
committed
[WebAssembly] Handle block-like structures consistently in type checker
We disable type check in unreachable code, but when the unreachable code is enclosed within a block-like structure, the block as a whole has a valid type and we should continue type checking after the block. But it looks we currently only do that for blocks and not other block-like structures (`loop`s, `try`s, and `if`s). Also unreachable code within `if`'s true body shouldn't disable type checking in `else` body, and that in `try` body shouldn't disable type checking in `catch/catch_all` body. This also causes the values/types on the stack to be correctly checked when encounterint `catch`, `catch_all`, and `delegate`. Reviewed By: dschuff Differential Revision: https://reviews.llvm.org/D147852
1 parent 8c0798f commit 3e9881b

File tree

3 files changed

+210
-28
lines changed

3 files changed

+210
-28
lines changed

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,26 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
300300
if (popType(ErrorLoc, {}))
301301
return true;
302302
} else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
303-
Name == "else" || Name == "end_try") {
304-
if (checkEnd(ErrorLoc, Name == "else"))
305-
return true;
306-
if (Name == "end_block")
307-
Unreachable = false;
303+
Name == "else" || Name == "end_try" || Name == "catch" ||
304+
Name == "catch_all" || Name == "delegate") {
305+
if (checkEnd(ErrorLoc,
306+
Name == "else" || Name == "catch" || Name == "catch_all"))
307+
return true;
308+
Unreachable = false;
309+
if (Name == "catch") {
310+
const MCSymbolRefExpr *SymRef;
311+
if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
312+
return true;
313+
const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
314+
const auto *Sig = WasmSym->getSignature();
315+
if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG)
316+
return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
317+
WasmSym->getName() +
318+
" missing .tagtype");
319+
// catch instruction pushes values whose types are specified in the tag's
320+
// "params" part
321+
Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end());
322+
}
308323
} else if (Name == "return") {
309324
if (endOfFunction(ErrorLoc))
310325
return true;
@@ -327,19 +342,6 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
327342
if (checkSig(ErrorLoc, *Sig)) return true;
328343
if (Name == "return_call" && endOfFunction(ErrorLoc))
329344
return true;
330-
} else if (Name == "catch") {
331-
const MCSymbolRefExpr *SymRef;
332-
if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
333-
return true;
334-
const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
335-
const auto *Sig = WasmSym->getSignature();
336-
if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG)
337-
return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
338-
WasmSym->getName() +
339-
" missing .tagtype");
340-
// catch instruction pushes values whose types are specified in the tag's
341-
// "params" part
342-
Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end());
343345
} else if (Name == "unreachable") {
344346
Unreachable = true;
345347
} else if (Name == "ref.is_null") {

llvm/test/MC/WebAssembly/type-checker-errors.s

Lines changed: 182 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,36 +258,36 @@ end_loop_type_mismatch:
258258
end_loop
259259
end_function
260260

261-
end_if_insufficient_values_on_stack:
262-
.functype end_if_insufficient_values_on_stack () -> ()
261+
end_if_insufficient_values_on_stack_1:
262+
.functype end_if_insufficient_values_on_stack_1 () -> ()
263263
i32.const 1
264264
if i32
265265
# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack
266266
end_if
267267
end_function
268268

269-
end_if_type_mismatch:
270-
.functype end_if_type_mismatch () -> ()
269+
end_if_type_mismatch_1:
270+
.functype end_if_type_mismatch_1 () -> ()
271271
i32.const 1
272272
if f32
273273
i32.const 1
274274
# CHECK: :[[@LINE+1]]:3: error: end got i32, expected f32
275275
end_if
276276
end_function
277277

278-
else_insufficient_values_on_stack:
279-
.functype else_insufficient_values_on_stack () -> ()
278+
end_if_insufficient_values_on_stack_2:
279+
.functype end_if_insufficient_values_on_stack_2 () -> ()
280280
i32.const 1
281281
if i32
282282
i32.const 2
283283
else
284284
# FIXME: Should complain about insufficient values on the stack.
285285
end_if
286-
# FIXME: Should complain about superflous value on the stack.
286+
drop
287287
end_function
288288

289-
else_type_mismatch:
290-
.functype else_type_mismatch () -> ()
289+
end_if_type_mismatch_2:
290+
.functype end_if_type_mismatch_2 () -> ()
291291
i32.const 1
292292
if i32
293293
i32.const 2
@@ -298,6 +298,31 @@ else_type_mismatch:
298298
drop
299299
end_function
300300

301+
else_insufficient_values_on_stack:
302+
.functype else_insufficient_values_on_stack () -> ()
303+
i32.const 1
304+
if i32
305+
# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack
306+
else
307+
i32.const 0
308+
end_if
309+
drop
310+
end_function
311+
312+
else_type_mismatch:
313+
.functype else_type_mismatch () -> ()
314+
i32.const 1
315+
if i32
316+
f32.const 0.0
317+
# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32
318+
else
319+
i32.const 0
320+
end_if
321+
drop
322+
end_function
323+
324+
.tagtype tag_i32 i32
325+
301326
end_try_insufficient_values_on_stack:
302327
.functype end_try_insufficient_values_on_stack () -> ()
303328
try i32
@@ -313,6 +338,63 @@ end_try_type_mismatch:
313338
end_try
314339
end_function
315340

341+
catch_insufficient_values_on_stack:
342+
.functype catch_insufficient_values_on_stack () -> ()
343+
try i32
344+
# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack
345+
catch tag_i32
346+
end_try
347+
drop
348+
end_function
349+
350+
catch_type_mismatch:
351+
.functype catch_type_mismatch () -> ()
352+
try i32
353+
f32.const 1.0
354+
# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32
355+
catch tag_i32
356+
end_try
357+
drop
358+
end_function
359+
360+
catch_all_insufficient_values_on_stack:
361+
.functype catch_all_insufficient_values_on_stack () -> ()
362+
try i32
363+
# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack
364+
catch_all
365+
i32.const 0
366+
end_try
367+
drop
368+
end_function
369+
370+
catch_all_type_mismatch:
371+
.functype catch_all_type_mismatch () -> ()
372+
try i32
373+
f32.const 1.0
374+
# CHECK: :[[@LINE+1]]:3: error: popped f32, expected i32
375+
catch_all
376+
i32.const 0
377+
end_try
378+
drop
379+
end_function
380+
381+
delegate_insufficient_values_on_stack:
382+
.functype delegate_insufficient_values_on_stack () -> ()
383+
try i32
384+
# CHECK: :[[@LINE+1]]:3: error: end: insufficient values on the type stack
385+
delegate 0
386+
drop
387+
end_function
388+
389+
delegate_type_mismatch:
390+
.functype delegate_type_mismatch () -> ()
391+
try i32
392+
f32.const 1.0
393+
# CHECK: :[[@LINE+1]]:3: error: end got f32, expected i32
394+
delegate 0
395+
drop
396+
end_function
397+
316398
end_function_empty_stack_while_popping:
317399
.functype end_function_empty_stack_while_popping () -> (i32)
318400
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping i32
@@ -467,7 +549,6 @@ catch_missing_tagtype:
467549

468550
catch_superfluous_value_at_end:
469551
.functype catch_superfluous_value_at_end () -> ()
470-
.tagtype tag_i32 i32
471552
try
472553
catch tag_i32
473554
end_try
@@ -520,3 +601,94 @@ other_insn_test_3:
520601
f32.add
521602
# CHECK: :[[@LINE+1]]:3: error: 1 superfluous return values
522603
end_function
604+
605+
# Unreachable code within 'block' does not affect type checking after
606+
# 'end_block'
607+
check_after_unreachable_within_block:
608+
.functype check_after_unreachable_within_block () -> ()
609+
block
610+
unreachable
611+
end_block
612+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
613+
drop
614+
end_function
615+
616+
# Unreachable code within 'loop' does not affect type checking after 'end_loop'
617+
check_after_unreachable_within_loop:
618+
.functype check_after_unreachable_within_loop () -> ()
619+
loop
620+
unreachable
621+
end_loop
622+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
623+
drop
624+
end_function
625+
626+
# Unreachable code within 'if' does not affect type checking after 'end_if'
627+
check_after_unreachable_within_if_1:
628+
.functype check_after_unreachable_within_if_1 () -> ()
629+
i32.const 0
630+
if
631+
unreachable
632+
else
633+
unreachable
634+
end_if
635+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
636+
drop
637+
end_function
638+
639+
# Unreachable code within 'if' does not affect type checking after 'else'
640+
check_after_unreachable_within_if_2:
641+
.functype check_after_unreachable_within_if_2 () -> ()
642+
i32.const 0
643+
if
644+
unreachable
645+
else
646+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
647+
drop
648+
end_if
649+
end_function
650+
651+
# Unreachable code within 'try' does not affect type checking after 'end_try'
652+
check_after_unreachable_within_try_1:
653+
.functype check_after_unreachable_within_try_1 () -> ()
654+
try
655+
unreachable
656+
catch_all
657+
unreachable
658+
end_try
659+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
660+
drop
661+
end_function
662+
663+
# Unreachable code within 'try' does not affect type checking after 'catch'
664+
check_after_unreachable_within_try_2:
665+
.functype check_after_unreachable_within_try_2 () -> ()
666+
try
667+
unreachable
668+
catch tag_i32
669+
drop
670+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
671+
drop
672+
end_try
673+
end_function
674+
675+
# Unreachable code within 'try' does not affect type checking after 'catch_all'
676+
check_after_unreachable_within_try_3:
677+
.functype check_after_unreachable_within_try_3 () -> ()
678+
try
679+
unreachable
680+
catch_all
681+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
682+
drop
683+
end_try
684+
end_function
685+
686+
# Unreachable code within 'try' does not affect type checking after 'delegate'
687+
check_after_unreachable_within_try_4:
688+
.functype check_after_unreachable_within_try_4 () -> ()
689+
try
690+
unreachable
691+
delegate 0
692+
# CHECK: :[[@LINE+1]]:3: error: empty stack while popping value
693+
drop
694+
end_function

llvm/test/MC/WebAssembly/type-checker-return.s

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,11 @@ return_call_superfluous_return_values:
2727
i32.const 2
2828
return_call fn_void_to_void
2929
end_function
30+
31+
# Unreachable code is stack-polymorphic, meaning its input and return types can
32+
# be anything. So the 'drop' after it doesn't cause an error.
33+
no_check_after_unreachable:
34+
.functype no_check_after_unreachable () -> ()
35+
unreachable
36+
drop
37+
end_function

0 commit comments

Comments
 (0)