diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -66,10 +66,10 @@ void emitEndOfAsmFile(Module &M) override; void EmitProducerInfo(Module &M); void EmitTargetFeatures(Module &M); + void emitSymbolType(const MCSymbolWasm *Sym); void emitGlobalVariable(const GlobalVariable *GV) override; void emitJumpTableInfo() override; void emitConstantPool() override; - void emitLinkage(const GlobalValue *, MCSymbol *) const override; void emitFunctionBodyStart() override; void emitInstruction(const MachineInstr *MI) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -180,26 +180,26 @@ MCSymbolWasm *Sym = cast(getSymbol(GV)); if (!Sym->getType()) { - const WebAssemblyTargetLowering &TLI = *Subtarget->getTargetLowering(); SmallVector VTs; Type *GlobalVT = GV->getValueType(); - computeLegalValueVTs(TLI, GV->getParent()->getContext(), - GV->getParent()->getDataLayout(), GlobalVT, VTs); + if (Subtarget) { + // Subtarget is only set when a function is defined, because + // each function can declare a different subtarget. For example, + // on ARM a compilation unit might have a function on ARM and + // another on Thumb. Therefore only if Subtarget is non-null we + // can actually calculate the legal VTs. + const WebAssemblyTargetLowering &TLI = *Subtarget->getTargetLowering(); + computeLegalValueVTs(TLI, GV->getParent()->getContext(), + GV->getParent()->getDataLayout(), GlobalVT, VTs); + } WebAssembly::wasmSymbolSetType(Sym, GlobalVT, VTs); } - // If the GlobalVariable refers to a table, we handle it here instead of - // in emitExternalDecls - if (Sym->isTable()) { - getTargetStreamer()->emitTableType(Sym); - return; - } - emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration()); + emitSymbolType(Sym); if (GV->hasInitializer()) { assert(getSymbolPreferLocal(*GV) == Sym); emitLinkage(GV, Sym); - getTargetStreamer()->emitGlobalType(Sym); OutStreamer->emitLabel(Sym); // TODO: Actually emit the initializer value. Otherwise the global has the // default value for its type (0, ref.null, etc). @@ -271,31 +271,47 @@ return WasmSym; } +void WebAssemblyAsmPrinter::emitSymbolType(const MCSymbolWasm *Sym) { + Optional WasmTy = Sym->getType(); + if (!WasmTy) + return; + + switch (WasmTy.getValue()) { + case wasm::WASM_SYMBOL_TYPE_GLOBAL: + getTargetStreamer()->emitGlobalType(Sym); + break; + case wasm::WASM_SYMBOL_TYPE_TAG: + getTargetStreamer()->emitTagType(Sym); + break; + case wasm::WASM_SYMBOL_TYPE_TABLE: + getTargetStreamer()->emitTableType(Sym); + break; + default: + break; // We only handle globals, tags and tables here + } +} + void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) { if (signaturesEmitted) return; signaturesEmitted = true; // Normally symbols for globals get discovered as the MI gets lowered, - // but we need to know about them ahead of time. + // but we need to know about them ahead of time. This will however, + // only find symbols that have been used. Unused symbols from globals will + // not be found here. MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo(); for (const auto &Name : MMIW.MachineSymbolsUsed) { getOrCreateWasmSymbol(Name.getKey()); } for (auto &It : OutContext.getSymbols()) { - // Emit .globaltype, .tagtype, or .tabletype declarations. + // Emit .globaltype, .tagtype, or .tabletype declarations for extern + // declarations, i.e. those that have only been declared (but not defined) + // in the current module auto Sym = cast(It.getValue()); - if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL) { - // .globaltype already handled by emitGlobalVariable for defined - // variables; here we make sure the types of external wasm globals get - // written to the file. - if (Sym->isUndefined()) - getTargetStreamer()->emitGlobalType(Sym); - } else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_TAG) - getTargetStreamer()->emitTagType(Sym); - else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_TABLE) - getTargetStreamer()->emitTableType(Sym); + if (!Sym->isDefined()) + emitSymbolType(Sym); } DenseSet InvokeSymbols; @@ -362,8 +378,11 @@ } } } - + void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { + // This is required to emit external declarations (like .functypes) when + // no functions are defined in the compilation unit and therefore, + // emitExternalDecls() is not called until now. emitExternalDecls(M); // When a function's address is taken, a TABLE_INDEX relocation is emitted @@ -532,6 +551,7 @@ } void WebAssemblyAsmPrinter::emitConstantPool() { + emitExternalDecls(*MMI->getModule()); assert(MF->getConstantPool()->getConstants().empty() && "WebAssembly disables constant pools"); } @@ -540,17 +560,6 @@ // Nothing to do; jump tables are incorporated into the instruction stream. } -void WebAssemblyAsmPrinter::emitLinkage(const GlobalValue *GV, MCSymbol *Sym) - const { - AsmPrinter::emitLinkage(GV, Sym); - // This gets called before the function label and type are emitted. - // We use it to emit signatures of external functions. - // FIXME casts! - const_cast(this) - ->emitExternalDecls(*MMI->getModule()); -} - - void WebAssemblyAsmPrinter::emitFunctionBodyStart() { const Function &F = MF->getFunction(); SmallVector ResultVTs; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp @@ -65,6 +65,9 @@ // for all functions before AsmPrinter. If this way of doing things is ever // suboptimal, we could opt to make it a MachineFunctionPass and instead use // something like createBarrierNoopPass() to enforce ordering. +// +// The information stored here is essential for emitExternalDecls in the Wasm +// AsmPrinter bool WebAssemblyMCLowerPrePass::runOnModule(Module &M) { auto *MMIWP = getAnalysisIfAvailable(); if (!MMIWP) diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll --- a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll @@ -73,4 +73,5 @@ ret %externref %ref } -; CHECK: .tabletype externref_table, externref +; CHECK: .tabletype externref_table, externref +; CHECK-LABEL: externref_table: diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll --- a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll +++ b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll @@ -94,4 +94,5 @@ ret void } -; CHECK: .tabletype externref_table, externref +; CHECK: .tabletype externref_table, externref +; CHECK-LABEL: externref_table: diff --git a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll --- a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll @@ -5,16 +5,9 @@ @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef -define void @call_funcref_from_table(i32 %i) { - %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %i - %ref = load %funcref, %funcref addrspace(1)* %p - %fn = bitcast %funcref %ref to %funcptr - call addrspace(20) void %fn() - ret void -} - -; CHECK: .tabletype __funcref_call_table, funcref, 1 +; CHECK: .tabletype __funcref_call_table, funcref, 1 +define void @call_funcref_from_table(i32 %i) { ; CHECK-LABEL: call_funcref_from_table: ; CHECK-NEXT: .functype call_funcref_from_table (i32) -> () ; CHECK-NEXT: i32.const 0 @@ -27,6 +20,13 @@ ; CHECK-NEXT: ref.null_func ; CHECK-NEXT: table.set __funcref_call_table ; CHECK-NEXT: end_function + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %i + %ref = load %funcref, %funcref addrspace(1)* %p + %fn = bitcast %funcref %ref to %funcptr + call addrspace(20) void %fn() + ret void +} -; CHECK: .tabletype funcref_table, funcref +; CHECK: .tabletype funcref_table, funcref +; CHECK-LABEL: funcref_table: diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll --- a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll @@ -72,4 +72,5 @@ ret %funcref %ref } -; CHECK: .tabletype funcref_table, funcref +; CHECK: .tabletype funcref_table, funcref +; CHECK-LABEL: funcref_table: diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll --- a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll @@ -78,4 +78,5 @@ ret void } -; CHECK: .tabletype funcref_table, funcref +; CHECK: .tabletype funcref_table, funcref +; CHECK-LABEL: funcref_table: diff --git a/llvm/test/CodeGen/WebAssembly/global-get.ll b/llvm/test/CodeGen/WebAssembly/global-get.ll --- a/llvm/test/CodeGen/WebAssembly/global-get.ll +++ b/llvm/test/CodeGen/WebAssembly/global-get.ll @@ -54,28 +54,26 @@ } -; CHECK: .globl i32_global ; CHECK: .globaltype i32_global, i32 +; CHECK: .globl i32_global ; CHECK-LABEL: i32_global: -; CHECK: .globl i64_global ; CHECK: .globaltype i64_global, i64 +; CHECK: .globl i64_global ; CHECK-LABEL: i64_global: -; CHECK: .globl f32_global ; CHECK: .globaltype f32_global, f32 +; CHECK: .globl f32_global ; CHECK-LABEL: f32_global: -; CHECK: .globl f64_global ; CHECK: .globaltype f64_global, f64 +; CHECK: .globl f64_global ; CHECK-LABEL: f64_global: -; FIXME: are we still expecting these to be emitted? - +; CHECK: .globaltype i32_external_used, i32 ; CHECK-NOT: .global i32_external_used -; CHECK-NOT: .globaltype i32_external_used, i32 ; CHECK-NOT: i32_external_used: +; CHECK: .globaltype i32_external_unused, i32 ; CHECK-NOT: .global i32_external_unused -; CHECK-NOT: .globaltype i32_external_unused, i32 ; CHECK-NOT: i32_external_unused: diff --git a/llvm/test/CodeGen/WebAssembly/global-set.ll b/llvm/test/CodeGen/WebAssembly/global-set.ll --- a/llvm/test/CodeGen/WebAssembly/global-set.ll +++ b/llvm/test/CodeGen/WebAssembly/global-set.ll @@ -45,18 +45,18 @@ ret void } -; CHECK: .globl i32_global ; CHECK: .globaltype i32_global, i32 +; CHECK: .globl i32_global ; CHECK-LABEL: i32_global: -; CHECK: .globl i64_global ; CHECK: .globaltype i64_global, i64 +; CHECK: .globl i64_global ; CHECK-LABEL: i64_global: -; CHECK: .globl f32_global ; CHECK: .globaltype f32_global, f32 +; CHECK: .globl f32_global ; CHECK-LABEL: f32_global: -; CHECK: .globl f64_global ; CHECK: .globaltype f64_global, f64 +; CHECK: .globl f64_global ; CHECK-LABEL: f64_global: diff --git a/llvm/test/CodeGen/WebAssembly/only-data.ll b/llvm/test/CodeGen/WebAssembly/only-data.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/only-data.ll @@ -0,0 +1,14 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s +; Verify that types are output for external symbols, even in the absence of any defined functions + +; CHECK: .type foo,@object +; CHECK-NEXT: .section .data.foo,"",@ +; CHECK-NEXT: .globl foo +@foo = local_unnamed_addr global i32 (i32)* @bar, align 4 + +; CHECK-LABEL: foo: +; CHECK-NEXT: .int32 bar +; CHECK-NEXT: .size foo, 4 + +; CHECK: .functype bar (i32) -> (i32) +declare i32 @bar(i32 noundef) diff --git a/llvm/test/CodeGen/WebAssembly/table-types.ll b/llvm/test/CodeGen/WebAssembly/table-types.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/table-types.ll @@ -0,0 +1,37 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%extern = type opaque +%externref = type %extern addrspace(10)* ;; addrspace 10 is nonintegral + +%func = type void () +%funcref = type %func addrspace(20)* ;; addrspace 20 is nonintegral + +; CHECK: .tabletype eref_table, externref +; CHECK-NEXT: .globl eref_table +; CHECK-LABEL: eref_table: +@eref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef + +; CHECK-NOT: .globl .Lprivate_eref_table +; CHECK: .tabletype .Lprivate_eref_table, externref +; CHECK-LABEL: .Lprivate_eref_table: +@private_eref_table = private local_unnamed_addr addrspace(1) global [0 x %externref] undef + +; CHECK: .tabletype extern_eref_table, externref +; CHECK-NOT: .globl extern_eref_table +; CHECK-NOT: extern_eref_table: +@extern_eref_table = external addrspace(1) global [0 x %externref] + +; CHECK: .tabletype fref_table, funcref +; CHECK-NEXT: .globl fref_table +; CHECK-LABEL: fref_table: +@fref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef + +; CHECK-NOT: .globl .Lprivate_fref_table +; CHECK: .tabletype .Lprivate_fref_table, funcref +; CHECK-LABEL: .Lprivate_fref_table: +@private_fref_table = private local_unnamed_addr addrspace(1) global [0 x %funcref] undef + +; CHECK: .tabletype extern_fref_table, funcref +; CHECK-NOT: .globl extern_fref_table +; CHECK-NOT: extern_fref_table: +@extern_fref_table = external addrspace(1) global [0 x %funcref] diff --git a/llvm/test/MC/WebAssembly/assembler-binary.ll b/llvm/test/MC/WebAssembly/assembler-binary.ll --- a/llvm/test/MC/WebAssembly/assembler-binary.ll +++ b/llvm/test/MC/WebAssembly/assembler-binary.ll @@ -22,8 +22,8 @@ ; ASM: .text ; ASM: .file "assembler-binary.ll" -; ASM: .globl foo ; ASM: .functype bar () -> () +; ASM: .globl foo ; ASM: foo: ; ASM-NEXT: .functype foo (i32) -> () ; ASM-NEXT: call bar diff --git a/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll b/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll --- a/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll +++ b/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll @@ -8,7 +8,7 @@ } ; Function that uses explict stack, and should generate a reference to -; __stack_pointer, along with the corresponding reloction entry. +; __stack_pointer, along with the corresponding relocation entry. define hidden void @foo() #0 { entry: alloca i32, align 4 @@ -17,10 +17,10 @@ ; CHECK: .text ; CHECK-NEXT: .file "stack-ptr-mclower.ll" +; CHECK-NEXT: .globaltype __stack_pointer, [[PTR]] ; CHECK-NEXT: .section .text.bar,"",@ ; CHECK-NEXT: .hidden bar ; CHECK-NEXT: .globl bar -; CHECK-NEXT: .globaltype __stack_pointer, [[PTR]] ; CHECK-NEXT: .type bar,@function ; CHECK-NEXT: bar: ; CHECK-NEXT: .functype bar () -> ()