diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -1061,6 +1061,9 @@ auto *FuncSection = static_cast(Func.Section); int64_t Size = Layout.getSectionAddressSize(FuncSection); + // Wasm functions can't have zero size, they at least need to + // encode the numer of locals, even if that number is zero. + assert(Size); encodeULEB128(Size, W->OS); FuncSection->setSectionOffset(W->OS.tell() - Section.ContentsOffset); Asm.writeSectionData(W->OS, FuncSection, Layout); 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 @@ -863,7 +863,7 @@ if (SymName.empty()) return true; auto WasmSym = cast(Ctx.getOrCreateSymbol(SymName)); - if (WasmSym->isDefined()) { + if (WasmSym->isDefined() && CurrentState != FunctionStart) { // This .functype indicates a start of a function. if (ensureEmptyNestingStack()) return true; @@ -1060,10 +1060,18 @@ void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override { // Code below only applies to labels in text sections. auto CWS = cast(getStreamer().getCurrentSection().first); - if (!CWS || !CWS->getKind().isText()) + auto WasmSym = cast(Symbol); + if (!CWS || !CWS->getKind().isText()) { + if (CurrentState == Instructions || CurrentState == FunctionStart || + CurrentState == FunctionLocals) { + Parser.Error(IDLoc, + "premature end of function (expected end_function)"); + CurrentState = EndFunction; + pop("end_function", Function); + } return; + } - auto WasmSym = cast(Symbol); // Unlike other targets, we don't allow data in text sections (labels // declared with .type @object). if (WasmSym->getType() == wasm::WASM_SYMBOL_TYPE_DATA) { @@ -1072,6 +1080,19 @@ return; } + if (WasmSym->isFunction()) { + if (CurrentState == FunctionStart && LastFunctionLabel != WasmSym) { + Parser.Error( + IDLoc, "previous function not terminated (expected end_function)"); + pop("end_function", Function); + } + if (ensureEmptyNestingStack()) + return; + CurrentState = FunctionStart; + LastFunctionLabel = WasmSym; + push(Function); + } + // Start a new section for the next function automatically, since our // object writer expects each function to have its own section. This way // The user can't forget this "convention". diff --git a/llvm/test/MC/WebAssembly/empty-function-body.s b/llvm/test/MC/WebAssembly/empty-function-body.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/empty-function-body.s @@ -0,0 +1,7 @@ +# RUN: not llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s -o %t.o 2>&1 | FileCheck %s + +# An empty function should produce an error. +foo: + .functype foo () -> () + +# CHECK: empty-function-body.s:8:1: error: Unmatched block construct(s) at function end: function diff --git a/llvm/test/MC/WebAssembly/empty-function-body2.s b/llvm/test/MC/WebAssembly/empty-function-body2.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/empty-function-body2.s @@ -0,0 +1,8 @@ +# RUN: not llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s -o %t.o 2>&1 | FileCheck %s + +# This is the same as empty-function-body.s but with the .functype declaration +# before the body of the function. +.functype bar () -> () +bar: + +# CHECK: empty-function-body2.s:9:1: error: Unmatched block construct(s) at function end: function diff --git a/llvm/test/MC/WebAssembly/empty-function-body3.s b/llvm/test/MC/WebAssembly/empty-function-body3.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/empty-function-body3.s @@ -0,0 +1,23 @@ +# RUN: not llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s -o %t.o 2>&1 | FileCheck %s + +# An empty function should produce an error as soon as a non-function symbol +# is defined. +foo: + .functype foo () -> () + +.section .data.bar,"",@ +bar: +# CHECK: empty-function-body3.s:9:1: error: premature end of function (expected end_function) + .int32 7 + .size bar, 4 + +.section .text.foo2,"",@ +foo2: + .functype foo2 () -> () + +# .. or when another function body is encountered. +.section .text.bar2,"",@ +.functype bar2 () -> () +bar2: +# CHECK: empty-function-body3.s:21:1: error: previous function not terminated (expected end_function) + end_function