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).
return WasmSym;
}
-void WebAssemblyAsmPrinter::emitSymbolType(const MCSymbolWasm *Sym) {
- Optional<wasm::WasmSymbolType> 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. This will however,
- // only find symbols that have been used. Unused symbols from globals will
- // not be found here.
+ // but we need to know about them ahead of time.
MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo<MachineModuleInfoWasm>();
for (const auto &Name : MMIW.MachineSymbolsUsed) {
getOrCreateWasmSymbol(Name.getKey());
}
for (auto &It : OutContext.getSymbols()) {
- // Emit .globaltype, .tagtype, or .tabletype declarations for extern
- // declarations, i.e. those that have only been declared (but not defined)
- // in the current module
+ // Emit .globaltype, .tagtype, or .tabletype declarations.
auto Sym = cast<MCSymbolWasm>(It.getValue());
- if (!Sym->isDefined())
- emitSymbolType(Sym);
+ 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);
}
DenseSet<MCSymbol *> InvokeSymbols;
}
}
}
-
+
void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
+ emitExternalDecls(M);
+
// When a function's address is taken, a TABLE_INDEX relocation is emitted
// against the function symbol at the use site. However the relocation
// doesn't explicitly refer to the table. In the future we may want to
}
void WebAssemblyAsmPrinter::emitConstantPool() {
- const Module *M = MMI->getModule();
- emitExternalDecls(*M);
assert(MF->getConstantPool()->getConstants().empty() &&
"WebAssembly disables constant pools");
}
// 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<WebAssemblyAsmPrinter *>(this)
+ ->emitExternalDecls(*MMI->getModule());
+}
+
+
void WebAssemblyAsmPrinter::emitFunctionBodyStart() {
const Function &F = MF->getFunction();
SmallVector<MVT, 1> ResultVTs;
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,
// 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<MachineModuleInfoWrapperPass>();
if (!MMIWP)
ret %externref %ref
}
-; CHECK: .tabletype externref_table, externref
-; CHECK-LABEL: externref_table:
+; CHECK: .tabletype externref_table, externref
ret void
}
-; CHECK: .tabletype externref_table, externref
-; CHECK-LABEL: externref_table:
+; CHECK: .tabletype externref_table, externref
@funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef
-; CHECK: .tabletype __funcref_call_table, funcref, 1
-
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-LABEL: call_funcref_from_table:
; CHECK-NEXT: .functype call_funcref_from_table (i32) -> ()
; CHECK-NEXT: i32.const 0
; 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-LABEL: funcref_table:
+; CHECK: .tabletype funcref_table, funcref
ret %funcref %ref
}
-; CHECK: .tabletype funcref_table, funcref
-; CHECK-LABEL: funcref_table:
+; CHECK: .tabletype funcref_table, funcref
ret void
}
-; CHECK: .tabletype funcref_table, funcref
-; CHECK-LABEL: funcref_table:
+; CHECK: .tabletype funcref_table, funcref
}
-; CHECK: .globaltype i32_global, i32
; CHECK: .globl i32_global
+; CHECK: .globaltype i32_global, i32
; CHECK-LABEL: i32_global:
-; CHECK: .globaltype i64_global, i64
; CHECK: .globl i64_global
+; CHECK: .globaltype i64_global, i64
; CHECK-LABEL: i64_global:
-; CHECK: .globaltype f32_global, f32
; CHECK: .globl f32_global
+; CHECK: .globaltype f32_global, f32
; CHECK-LABEL: f32_global:
-; CHECK: .globaltype f64_global, f64
; CHECK: .globl f64_global
+; CHECK: .globaltype f64_global, f64
; CHECK-LABEL: f64_global:
-; CHECK: .globaltype i32_external_used, i32
+; FIXME: are we still expecting these to be emitted?
+
; 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:
ret void
}
-; CHECK: .globaltype i32_global, i32
; CHECK: .globl i32_global
+; CHECK: .globaltype i32_global, i32
; CHECK-LABEL: i32_global:
-; CHECK: .globaltype i64_global, i64
; CHECK: .globl i64_global
+; CHECK: .globaltype i64_global, i64
; CHECK-LABEL: i64_global:
-; CHECK: .globaltype f32_global, f32
; CHECK: .globl f32_global
+; CHECK: .globaltype f32_global, f32
; CHECK-LABEL: f32_global:
-; CHECK: .globaltype f64_global, f64
; CHECK: .globl f64_global
+; CHECK: .globaltype f64_global, f64
; CHECK-LABEL: f64_global:
+++ /dev/null
-; 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]
; ASM: .text
; ASM: .file "assembler-binary.ll"
-; ASM: .functype bar () -> ()
; ASM: .globl foo
+; ASM: .functype bar () -> ()
; ASM: foo:
; ASM-NEXT: .functype foo (i32) -> ()
; ASM-NEXT: call bar
}
; Function that uses explict stack, and should generate a reference to
-; __stack_pointer, along with the corresponding relocation entry.
+; __stack_pointer, along with the corresponding reloction entry.
define hidden void @foo() #0 {
entry:
alloca i32, align 4
; 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 () -> ()