From 0b3cf247c47e0ae718a5ff28195407f35f8523ac Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Mon, 4 Feb 2019 18:03:11 +0000 Subject: [PATCH] [WebAssembly] Make segment/size/type directives optional in asm Summary: These were "boilerplate" that repeated information already present in .functype and end_function, that needed to be repeated to Please the particular way our object writing works, and missing them would generate errors. Instead, we generate the information for these automatically so the user can concern itself with writing more canonical wasm functions that always work as expected. Reviewers: dschuff, sbc100 Subscribers: jgravelle-google, aheejin, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57546 llvm-svn: 353067 --- llvm/lib/MC/MCParser/WasmAsmParser.cpp | 13 ++++--- .../AsmParser/WebAssemblyAsmParser.cpp | 36 ++++++++++++++++++- llvm/test/MC/WebAssembly/assembler-binary.ll | 2 -- .../MC/WebAssembly/basic-assembly-errors.s | 5 --- llvm/test/MC/WebAssembly/basic-assembly.s | 5 --- llvm/test/MC/WebAssembly/objdump.s | 8 ----- 6 files changed, 44 insertions(+), 25 deletions(-) diff --git a/llvm/lib/MC/MCParser/WasmAsmParser.cpp b/llvm/lib/MC/MCParser/WasmAsmParser.cpp index d2099be9c8c7..485be2285b0a 100644 --- a/llvm/lib/MC/MCParser/WasmAsmParser.cpp +++ b/llvm/lib/MC/MCParser/WasmAsmParser.cpp @@ -91,8 +91,11 @@ public: Expect(AsmToken::Comma, ",") || Expect(AsmToken::At, "@") || Expect(AsmToken::EndOfStatement, "eol")) return true; - auto WS = getContext().getWasmSection(Name, SectionKind::getText()); - getStreamer().SwitchSection(WS); + // This is done automatically by the assembler for text sections currently, + // so we don't need to emit that here. This is what it would do (and may + // be needed later for other section types): + // auto WS = getContext().getWasmSection(Name, SectionKind::getText()); + // getStreamer().SwitchSection(WS); return false; } @@ -110,8 +113,10 @@ public: return true; if (Expect(AsmToken::EndOfStatement, "eol")) return true; - // MCWasmStreamer implements this. - getStreamer().emitELFSize(Sym, Expr); + // This is done automatically by the assembler for functions currently, + // so we don't need to emit that here. This is what it would do: + (void)Sym; + // getStreamer().emitELFSize(Sym, Expr); return false; } diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp index 48af89f24aa2..a0455cb98d5f 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -17,10 +17,12 @@ #include "MCTargetDesc/WebAssemblyTargetStreamer.h" #include "WebAssembly.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" @@ -169,6 +171,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser { FunctionStart, FunctionLocals, Instructions, + EndFunction, } CurrentState = FileStart; // For ensuring blocks are properly nested. @@ -186,6 +189,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser { // 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; + MCSymbol *LastFunctionLabel = nullptr; public: WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, @@ -445,6 +449,7 @@ public: if (pop(BaseName, Block)) return true; } else if (BaseName == "end_function") { + CurrentState = EndFunction; if (pop(BaseName, Function) || ensureEmptyNestingStack()) return true; } @@ -604,6 +609,7 @@ public: if (ensureEmptyNestingStack()) return true; CurrentState = FunctionStart; + LastFunctionLabel = LastLabel; push(Function); } auto Signature = make_unique(); @@ -666,8 +672,12 @@ public: *Out.getTargetStreamer()); TOut.emitLocal(SmallVector()); } - CurrentState = Instructions; Out.EmitInstruction(Inst, getSTI()); + if (CurrentState == EndFunction) { + onEndOfFunction(); + } else { + CurrentState = Instructions; + } return false; } case Match_MissingFeature: @@ -693,6 +703,30 @@ public: llvm_unreachable("Implement any new match types added!"); } + void doBeforeLabelEmit(MCSymbol *Symbol) override { + // 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". + auto SymName = Symbol->getName(); + if (SymName.startswith(".L")) + return; // Local Symbol. + auto SecName = ".text." + SymName; + auto WS = getContext().getWasmSection(SecName, SectionKind::getText()); + getStreamer().SwitchSection(WS); + } + + void onEndOfFunction() { + // Automatically output a .size directive, so it becomes optional for the + // user. + auto TempSym = getContext().createLinkerPrivateTempSymbol(); + getStreamer().EmitLabel(TempSym); + auto Start = MCSymbolRefExpr::create(LastLabel, getContext()); + auto End = MCSymbolRefExpr::create(TempSym, getContext()); + auto Expr = + MCBinaryExpr::create(MCBinaryExpr::Sub, End, Start, getContext()); + getStreamer().emitELFSize(LastFunctionLabel, Expr); + } + void onEndOfFile() override { ensureEmptyNestingStack(); } }; } // end anonymous namespace diff --git a/llvm/test/MC/WebAssembly/assembler-binary.ll b/llvm/test/MC/WebAssembly/assembler-binary.ll index d5d594a24e09..35bd3a88646c 100644 --- a/llvm/test/MC/WebAssembly/assembler-binary.ll +++ b/llvm/test/MC/WebAssembly/assembler-binary.ll @@ -28,8 +28,6 @@ entry: ; ASM-NEXT: .functype foo (i32) -> () ; ASM-NEXT: call bar@FUNCTION ; ASM-NEXT: end_function -; ASM-NEXT: .Lfunc_end0: -; ASM-NEXT: .size foo, .Lfunc_end0-foo ; ASM: .functype bar () -> () diff --git a/llvm/test/MC/WebAssembly/basic-assembly-errors.s b/llvm/test/MC/WebAssembly/basic-assembly-errors.s index 06fc4f85e7cb..eead082e93f7 100644 --- a/llvm/test/MC/WebAssembly/basic-assembly-errors.s +++ b/llvm/test/MC/WebAssembly/basic-assembly-errors.s @@ -1,8 +1,5 @@ # RUN: not llvm-mc -triple=wasm32-unknown-unknown -mattr=+simd128,+nontrapping-fptoint,+exception-handling < %s 2>&1 | FileCheck %s - .text - .section .text.main,"",@ - .type test0,@function # CHECK: End of block construct with no start: end_try end_try test0: @@ -20,6 +17,4 @@ test0: # CHECK: error: Unmatched block construct(s) at function end: block # CHECK: error: Unmatched block construct(s) at function end: function end_function -.Lfunc_end0: - .size test0, .Lfunc_end0-test0 diff --git a/llvm/test/MC/WebAssembly/basic-assembly.s b/llvm/test/MC/WebAssembly/basic-assembly.s index e5f2f5f61b01..85da7819f9b9 100644 --- a/llvm/test/MC/WebAssembly/basic-assembly.s +++ b/llvm/test/MC/WebAssembly/basic-assembly.s @@ -2,9 +2,6 @@ # this one is just here to see if it converts to .o without errors, but doesn't check any output: # RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+unimplemented-simd128,+nontrapping-fptoint,+exception-handling < %s - .text - .section .text.main,"",@ - .type test0,@function test0: # Test all types: .functype test0 (i32, i64) -> (i32) @@ -86,8 +83,6 @@ test0: #i32.trunc_sat_f32_s global.get __stack_pointer@GLOBAL end_function -.Lfunc_end0: - .size test0, .Lfunc_end0-test0 .globaltype __stack_pointer, i32 # CHECK: .text diff --git a/llvm/test/MC/WebAssembly/objdump.s b/llvm/test/MC/WebAssembly/objdump.s index fc8717492201..d156ec60eefa 100644 --- a/llvm/test/MC/WebAssembly/objdump.s +++ b/llvm/test/MC/WebAssembly/objdump.s @@ -1,25 +1,17 @@ # RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -o %t.o -mattr=+simd128,+nontrapping-fptoint,+exception-handling < %s # RUN: llvm-objdump -triple=wasm32-unknown-unknown -disassemble %t.o | FileCheck %s - .section .text.main1,"",@ - .type test0,@function test0: .functype test0 (i32, i64) -> (i32) .local f32, f64, v128, v128 local.get 2 end_function -.Lfunc_end0: - .size test0, .Lfunc_end0-test0 - .section .text.main2,"",@ - .type test1,@function test1: .functype test1 (i32, i64) -> (i32) .local i32, i64, except_ref local.get 3 end_function -.Lfunc_end1: - .size test1, .Lfunc_end1-test1 # CHECK-LABEL: CODE: