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 @@ -210,6 +210,7 @@ // guarantee that correct order. enum ParserState { FileStart, + FunctionLabel, FunctionStart, FunctionLocals, Instructions, @@ -287,8 +288,8 @@ return Parser.Error(Tok.getLoc(), Msg + Tok.getString()); } - bool error(const Twine &Msg) { - return Parser.Error(Lexer.getTok().getLoc(), Msg); + bool error(const Twine &Msg, SMLoc Loc = SMLoc()) { + return Parser.Error(Loc.isValid() ? Loc : Lexer.getTok().getLoc(), Msg); } void addSignature(std::unique_ptr &&Sig) { @@ -336,11 +337,12 @@ return false; } - bool ensureEmptyNestingStack() { + bool ensureEmptyNestingStack(SMLoc Loc = SMLoc()) { auto Err = !NestingStack.empty(); while (!NestingStack.empty()) { error(Twine("Unmatched block construct(s) at function end: ") + - nestingString(NestingStack.back().NT).first); + nestingString(NestingStack.back().NT).first, + Loc); NestingStack.pop_back(); } return Err; @@ -870,7 +872,18 @@ return true; CurrentState = FunctionStart; LastFunctionLabel = WasmSym; - push(Function); + // We push 'Function' either when a label is parsed or a .functype + // directive is parsed. The reason it is not easy to do this uniformly + // in a single place is, + // 1. We can't do this at label parsing time only because there are + // cases we don't have .functype directive before a function label, + // in which case we don't know if the label is a function at the time + // of parsing. + // 2. We can't do this at .functype parsing time only because we want to + // detect a function started with a label and not ended correctly + // without encountering a .functype directive after the label. + if (CurrentState != FunctionLabel) + push(Function); } auto Signature = std::make_unique(); if (parseSignature(Signature.get())) @@ -1100,6 +1113,22 @@ // Also generate DWARF for this section if requested. if (getContext().getGenDwarfForAssembly()) getContext().addGenDwarfSection(WS); + + if (WasmSym->isFunction()) { + // We give the location of the label (IDLoc) here, because otherwise the + // lexer's next location will be used, which can be confusing. For + // example: + // + // test0: ; This function does not end properly + // ... + // + // test1: ; We would like to point to this line for error + // ... . Not this line, which can contain any instruction + ensureEmptyNestingStack(IDLoc); + CurrentState = FunctionLabel; + LastFunctionLabel = Symbol; + push(Function); + } } void onEndOfFunction(SMLoc ErrorLoc) { diff --git a/llvm/test/MC/WebAssembly/func-end-errors.s b/llvm/test/MC/WebAssembly/func-end-errors.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/func-end-errors.s @@ -0,0 +1,17 @@ +# RUN: not llvm-mc -triple=wasm32-unknown-unknown %s 2>&1 | FileCheck %s + +# A Wasm function should always end with a 'end_function' pseudo instruction in +# assembly. This causes the parser to properly wrap up function info when there +# is no other instructions present. + +.functype test0 () -> () +test0: + +.functype test1 () -> () +# CHECK: [[@LINE+1]]:1: error: Unmatched block construct(s) at function end: function +test1: + end_function + +.functype test2 () -> () +test2: +# CHECK: [[@LINE+1]]:1: error: Unmatched block construct(s) at function end: function