diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -1114,6 +1114,8 @@ void onEndOfFunction(SMLoc ErrorLoc) { TC.endOfFunction(ErrorLoc); + // Reset the type checker state. + TC.Clear(); // Automatically output a .size directive, so it becomes optional for the // user. diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h @@ -32,15 +32,9 @@ SmallVector ReturnTypes; wasm::WasmSignature LastSig; bool TypeErrorThisFunction = false; + bool Unreachable = false; bool is64; - void Clear() { - Stack.clear(); - LocalTypes.clear(); - ReturnTypes.clear(); - TypeErrorThisFunction = false; - } - void dumpTypeStack(Twine Msg); bool typeError(SMLoc ErrorLoc, const Twine &Msg); bool popType(SMLoc ErrorLoc, Optional EVT); @@ -57,8 +51,16 @@ void funcDecl(const wasm::WasmSignature &Sig); void localDecl(const SmallVector &Locals); void setLastSig(const wasm::WasmSignature &Sig) { LastSig = Sig; } - void endOfFunction(SMLoc ErrorLoc); + bool endOfFunction(SMLoc ErrorLoc); bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst); + + void Clear() { + Stack.clear(); + LocalTypes.clear(); + ReturnTypes.clear(); + TypeErrorThisFunction = false; + Unreachable = false; + } }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp @@ -74,6 +74,9 @@ // which are mostly not helpful. if (TypeErrorThisFunction) return true; + // If we're currently in unreachable code, we surpress errors as well. + if (Unreachable) + return true; TypeErrorThisFunction = true; dumpTypeStack("current stack: "); return Parser.Error(ErrorLoc, Msg); @@ -170,17 +173,18 @@ return false; } -void WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) { +bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) { // Check the return types. for (auto RVT : llvm::reverse(ReturnTypes)) { - popType(ErrorLoc, RVT); + if (popType(ErrorLoc, RVT)) + return true; } if (!Stack.empty()) { - typeError(ErrorLoc, - std::to_string(Stack.size()) + " superfluous return values"); + return typeError(ErrorLoc, std::to_string(Stack.size()) + + " superfluous return values"); } - // Reset the type checker state. - Clear(); + Unreachable = true; + return false; } bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst) { @@ -219,10 +223,17 @@ Name == "else" || Name == "end_try") { if (checkEnd(ErrorLoc)) return true; + if (Name == "end_block") + Unreachable = false; + } else if (Name == "return") { + if (endOfFunction(ErrorLoc)) + return true; } else if (Name == "call_indirect" || Name == "return_call_indirect") { // Function value. if (popType(ErrorLoc, wasm::ValType::I32)) return true; if (checkSig(ErrorLoc, LastSig)) return true; + if (Name == "return_call_indirect" && endOfFunction(ErrorLoc)) + return true; } else if (Name == "call" || Name == "return_call") { const MCSymbolRefExpr *SymRef; if (getSymRef(ErrorLoc, Inst, SymRef)) @@ -233,6 +244,8 @@ return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + " missing .functype"); if (checkSig(ErrorLoc, *Sig)) return true; + if (Name == "return_call" && endOfFunction(ErrorLoc)) + return true; } else if (Name == "catch") { const MCSymbolRefExpr *SymRef; if (getSymRef(ErrorLoc, Inst, SymRef)) @@ -248,6 +261,8 @@ } else if (Name == "ref.null") { auto VT = static_cast(Inst.getOperand(0).getImm()); Stack.push_back(VT); + } else if (Name == "unreachable") { + Unreachable = true; } else { // The current instruction is a stack instruction which doesn't have // explicit operands that indicate push/pop types, so we get those from diff --git a/llvm/test/MC/WebAssembly/basic-assembly.s b/llvm/test/MC/WebAssembly/basic-assembly.s --- a/llvm/test/MC/WebAssembly/basic-assembly.s +++ b/llvm/test/MC/WebAssembly/basic-assembly.s @@ -1,9 +1,10 @@ -# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types,atomics,+simd128,+nontrapping-fptoint,+exception-handling < %s | FileCheck %s +# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+tail-call,+reference-types,atomics,+simd128,+nontrapping-fptoint,+exception-handling < %s | FileCheck %s # Check that it converts to .o without errors, but don't check any output: -# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+reference-types,+atomics,+simd128,+nontrapping-fptoint,+exception-handling -o %t.o < %s +# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+tail-call,+reference-types,+atomics,+simd128,+nontrapping-fptoint,+exception-handling -o %t.o < %s .functype something1 () -> () .functype something2 (i64) -> (i32, f64) +.functype something3 () -> (i32) .globaltype __stack_pointer, i32 empty_func: @@ -86,6 +87,17 @@ else end_if drop + block void + i32.const 2 + return + end_block + block void + return_call something3 + end_block + block void + i32.const 3 + return_call_indirect () -> (i32) + end_block local.get 4 local.get 5 f32x4.add @@ -215,6 +227,17 @@ # CHECK-NEXT: else # CHECK-NEXT: end_if # CHECK-NEXT: drop +# CHECK-NEXT: block +# CHECK-NEXT: i32.const 2 +# CHECK-NEXT: return +# CHECK-NEXT: end_block +# CHECK-NEXT: block +# CHECK-NEXT: return_call something3 +# CHECK-NEXT: end_block +# CHECK-NEXT: block +# CHECK-NEXT: i32.const 3 +# CHECK-NEXT: return_call_indirect __indirect_function_table, () -> (i32) +# CHECK-NEXT: end_block # CHECK-NEXT: local.get 4 # CHECK-NEXT: local.get 5 # CHECK-NEXT: f32x4.add