Index: include/llvm/MC/MCParser/MCTargetAsmParser.h =================================================================== --- include/llvm/MC/MCParser/MCTargetAsmParser.h +++ include/llvm/MC/MCParser/MCTargetAsmParser.h @@ -490,6 +490,9 @@ MCContext &Ctx) { return nullptr; } + + // For any checks or cleanups at the end of parsing. + virtual void onEndOfFile() {} }; } // end namespace llvm Index: lib/MC/MCParser/AsmParser.cpp =================================================================== --- lib/MC/MCParser/AsmParser.cpp +++ lib/MC/MCParser/AsmParser.cpp @@ -898,6 +898,9 @@ eatToEndOfStatement(); } + getTargetParser().onEndOfFile(); + printPendingErrors(); + // All errors should have been emitted. assert(!hasPendingError() && "unexpected error from parseStatement"); Index: lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp =================================================================== --- lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -172,6 +172,17 @@ Instructions, } CurrentState = FileStart; + // For ensuring blocks are properly nested. + enum NestingType { + Function, + Block, + Loop, + If, + Else, + Undefined, + }; + std::vector NestingStack; + // We track this to see if a .functype following a label is the same, // as this is how we recognize the start of a function. MCSymbol *LastLabel = nullptr; @@ -184,10 +195,6 @@ setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); } - void addSignature(std::unique_ptr &&Sig) { - Signatures.push_back(std::move(Sig)); - } - #define GET_ASSEMBLER_HEADER #include "WebAssemblyGenAsmMatcher.inc" @@ -197,10 +204,56 @@ llvm_unreachable("ParseRegister is not implemented."); } - bool error(const StringRef &Msg, const AsmToken &Tok) { + bool error(const Twine &Msg, const AsmToken &Tok) { return Parser.Error(Tok.getLoc(), Msg + Tok.getString()); } + bool error(const Twine &Msg) { + return Parser.Error(Lexer.getTok().getLoc(), Msg); + } + + void addSignature(std::unique_ptr &&Sig) { + Signatures.push_back(std::move(Sig)); + } + + StringRef NestingString(NestingType NT) { + switch (NT) { + case Function: + return " function"; + case Block: + return " block"; + case Loop: + return " loop"; + case If: + return " if"; + case Else: + return " else"; + default: + return ""; + } + } + + void Push(NestingType NT) { NestingStack.push_back(NT); } + + bool Pop(NestingType NT1, NestingType NT2 = Undefined) { + if (NestingStack.empty()) + return error(Twine("End of block construct with no start:") + + NestingString(NT1) + NestingString(NT2)); + auto Top = NestingStack.back(); + NestingStack.pop_back(); + if (Top != NT1 && Top != NT2) + return error(Twine("Block construct type mismatch, expected:") + + NestingString(NT1) + NestingString(NT2) + + ", instead got:" + NestingString(Top)); + return false; + } + + bool CheckFunctionEnd() { + return !NestingStack.empty() && + error(Twine("Unmatched block construct(s) at function end:") + + NestingString(NestingStack.back())); + } + bool isNext(AsmToken::TokenKind Kind) { auto Ok = Lexer.is(Kind); if (Ok) @@ -327,6 +380,32 @@ // If no '.', there is no type prefix. auto BaseName = NamePair.second.empty() ? NamePair.first : NamePair.second; + // If this instruction is part of a control flow structure, ensure + // proper nesting. + if (BaseName == "block") { + Push(Block); + } else if (BaseName == "loop") { + Push(Loop); + } else if (BaseName == "if") { + Push(If); + } else if (BaseName == "else") { + if (Pop(If)) + return true; + Push(Else); + } else if (BaseName == "end_if") { + if (Pop(If, Else)) + return true; + } else if (BaseName == "end_loop") { + if (Pop(Loop)) + return true; + } else if (BaseName == "end_block") { + if (Pop(Block)) + return true; + } else if (BaseName == "end_function") { + if (Pop(Function) || CheckFunctionEnd()) + return true; + } + while (Lexer.isNot(AsmToken::EndOfStatement)) { auto &Tok = Lexer.getTok(); switch (Tok.getKind()) { @@ -476,7 +555,10 @@ TOut.getStreamer().getContext().getOrCreateSymbol(SymName)); if (CurrentState == Label && WasmSym == LastLabel) { // This .functype indicates a start of a function. + if (CheckFunctionEnd()) + return true; CurrentState = FunctionStart; + Push(Function); } auto Signature = make_unique(); if (parseSignature(Signature.get())) @@ -565,6 +647,8 @@ } llvm_unreachable("Implement any new match types added!"); } + + void onEndOfFile() override { CheckFunctionEnd(); } }; } // end anonymous namespace Index: test/MC/WebAssembly/basic-assembly.s =================================================================== --- test/MC/WebAssembly/basic-assembly.s +++ test/MC/WebAssembly/basic-assembly.s @@ -59,6 +59,11 @@ end_block # default jumps here. i32.const 3 end_block # "switch" exit. + #if # These are not in tablegen defs yet.. + #if + #end_if + #else + #end_if f32x4.add # Test correct parsing of instructions with / and : in them: # TODO: enable once instruction has been added. @@ -129,6 +134,11 @@ # CHECK-NEXT: end_block # label3: # CHECK-NEXT: i32.const 3 # CHECK-NEXT: end_block # label2: +# DONT-CHECK-NEXT: if +# DONT-CHECK-NEXT: if +# DONT-CHECK-NEXT: end_if +# DONT-CHECK-NEXT: else +# DONT-CHECK-NEXT: end_if # CHECK-NEXT: f32x4.add # CHECK-NEXT: i32.trunc_s/f32 # CHECK-NEXT: try Index: test/MC/WebAssembly/simd-encodings.s =================================================================== --- test/MC/WebAssembly/simd-encodings.s +++ test/MC/WebAssembly/simd-encodings.s @@ -1,5 +1,8 @@ # RUN: llvm-mc -show-encoding -triple=wasm32-unkown-unknown -mattr=+sign-ext,+simd128 < %s | FileCheck %s +main: + .functype main () -> () + # CHECK: v128.load 48:p2align=0 # encoding: [0xfd,0x00,0x00,0x30] v128.load 48