Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -78,40 +78,54 @@ //===----------------------------------------------------------------------===// void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) { - for (auto &It : OutContext.getSymbols()) { - // Emit a .globaltype and .eventtype declaration. - auto Sym = cast(It.getValue()); - if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL) - getTargetStreamer()->emitGlobalType(Sym); - else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_EVENT) - getTargetStreamer()->emitEventType(Sym); - } - for (const auto &F : M) { - // Emit function type info for all undefined functions - if (F.isDeclarationForLinker() && !F.isIntrinsic()) { + if (F.isDeclarationForLinker() && !F.isIntrinsic()) { SmallVector Results; SmallVector Params; ComputeSignatureVTs(F.getFunctionType(), F, TM, Params, Results); auto *Sym = cast(getSymbol(&F)); - if (!Sym->getSignature()) { + + if (Sym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION) { + dbgs() << "Missing Sig: " << *Sym << "\n"; auto Signature = SignatureFromMVTs(Results, Params); + Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); Sym->setSignature(Signature.get()); addSignature(std::move(Signature)); + //assert(false); } - // FIXME: this was originally intended for post-linking and was only used - // for imports that were only called indirectly (i.e. s2wasm could not - // infer the type from a call). With object files it applies to all - // imports. so fix the names and the tests, or rethink how import - // delcarations work in asm files. - getTargetStreamer()->emitIndirectFunctionType(Sym); - if (TM.getTargetTriple().isOSBinFormatWasm() && - F.hasFnAttribute("wasm-import-module")) { + if (F.hasFnAttribute("wasm-import-module")) { + auto *Sym = cast(getSymbol(&F)); StringRef Name = F.getFnAttribute("wasm-import-module").getValueAsString(); getTargetStreamer()->emitImportModule(Sym, Name); } + } + } + + for (auto &It : OutContext.getSymbols()) { + // Emit a .globaltype/.eventtype/.functype declaration. + auto* Sym = cast(It.getValue()); + //dbgs() << "sym: " << *Sym << "\n"; + if (Sym->isVariable()) + continue; + switch (Sym->getType()) { + case wasm::WASM_SYMBOL_TYPE_GLOBAL: + getTargetStreamer()->emitGlobalType(Sym); + break; + case wasm::WASM_SYMBOL_TYPE_EVENT: + getTargetStreamer()->emitEventType(Sym); + break; + case wasm::WASM_SYMBOL_TYPE_FUNCTION: + // Defined symbols have this function type set at the declartion site + if (Sym->isDefined()) + continue; + assert(Sym->getSignature()); + getTargetStreamer()->emitIndirectFunctionType(Sym); + break; + case wasm::WASM_SYMBOL_TYPE_DATA: + case wasm::WASM_SYMBOL_TYPE_SECTION: + break; } } Index: lib/Target/WebAssembly/WebAssemblyFastISel.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -379,10 +379,36 @@ if (Reg == 0) { Reg = createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass); - unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 - : WebAssembly::CONST_I32; - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg) - .addImm(0); + + const GlobalValue *GV = Addr.getGlobalValue(); + if (TLI.isPositionIndependent() && GV) { + // For PIC code, access all hidden globals via __memory_base. + if (GV->hasHiddenVisibility()) { + auto *MemBaseSymbol = + FuncInfo.MF->createExternalSymbolName("__memory_base"); + unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::GET_GLOBAL_I64 + : WebAssembly::GET_GLOBAL_I32; + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg) + .addExternalSymbol(MemBaseSymbol, + WebAssemblyII::MO_SYMBOL_GLOBAL); + } else { + std::string getterName("g$"); + getterName += GV->getName(); + auto *getterSymName = + FuncInfo.MF->createExternalSymbolName(getterName); + unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CALL_I64 + : WebAssembly::CALL_I32; + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg) + .addExternalSymbol(getterSymName, + WebAssemblyII::MO_SYMBOL_FUNCTION); + } + } else { + unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 + : WebAssembly::CONST_I32; + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg) + .addImm(0); + } + Addr.setReg(Reg); } } @@ -742,9 +768,17 @@ return false; bool IsDirect = Func != nullptr; + if (!IsDirect && isa(Call->getCalledValue())) return false; + if (IsDirect && TLI.isPositionIndependent() && !Func->hasHiddenVisibility()) { + dbgs() << "XXX\n"; + dbgs() << *Func << "\n"; + dbgs() << "YYY\n"; + IsDirect = false; + } + FunctionType *FuncTy = Call->getFunctionType(); unsigned Opc; bool IsVoid = FuncTy->getReturnType()->isVoidTy(); @@ -1183,6 +1217,9 @@ } materializeLoadStoreOperands(Addr); + //dbgs() << "selectLoad\n"; + //if (TLI.isPositionIndependent() + //dbgs() << "INDIRECT\n"; unsigned ResultReg = createResultReg(RC); auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -731,9 +731,10 @@ } InTys.push_back(MVT::Other); SDVTList InTyList = DAG.getVTList(InTys); - SDValue Res = - DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1, - DL, InTyList, Ops); + unsigned Opcode = Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1; + //if (isPositionIndependent() ) + //Opcode = Ins.empty() ? WebAssemblyISD::CALL_INDIRECT1 : WebAssemblyISD::CALL_INDIRECT1; + SDValue Res = DAG.getNode(Opcode, DL, InTyList, Ops); if (Ins.empty()) { Chain = Res; } else { @@ -964,11 +965,10 @@ EVT VT = Op.getValueType(); assert(ES->getTargetFlags() == 0 && "Unexpected target flags on generic ExternalSymbolSDNode"); - // Set the TargetFlags to 0x1 which indicates that this is a "function" - // symbol rather than a data symbol. We do this unconditionally even though - // we don't know anything about the symbol other than its name, because all - // external symbols used in target-independent SelectionDAG code are for - // functions. + // Set TargetFlags to WebAssemblyII::MO_SYMBOL_FUNCTION. We do this + // unconditionally even though we don't know anything about the symbol other + // than its name, because all external symbols used in target-independent + // SelectionDAG code are for functions. return DAG.getNode( WebAssemblyISD::Wrapper, DL, VT, DAG.getTargetExternalSymbol(ES->getSymbol(), VT, Index: lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -79,7 +79,7 @@ // other external symbols used by CodeGen are functions. It's OK to hardcode // knowledge of specific symbols here; this method is precisely there for // fetching the signatures of known Clang-provided symbols. - if (strcmp(Name, "__stack_pointer") == 0) { + if (strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__memory_base") == 0) { WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); WasmSym->setGlobalType(wasm::WasmGlobalType{ uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64 @@ -108,6 +108,10 @@ // functions, exceptions are assumed to have void return type. Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64 : wasm::ValType::I32); + } else if (strncmp(Name, "g$", 2) == 0) { + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); + Returns.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64 + : wasm::ValType::I32); } else { // Function symbols WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); GetLibcallSignature(Subtarget, Name, Returns, Params); Index: test/CodeGen/WebAssembly/global-pic.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/global-pic.ll @@ -0,0 +1,85 @@ +; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefix=NON-PIC +; RUN: llc < %s -asm-verbose=false -relocation-model=pic -fast-isel -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefix=PIC + +; Test that globals assemble as expected with -fPIC + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +@hidden_global = external hidden global i32 + +declare i32 @foo(); + +; For hidden symbols PIC code needs to offset all loads and stores +; by the value of the __memory_base global + +define i32 @load_hidden_global() { +; PIC-LABEL: load_hidden_global: +; PIC: get_global $push0=, __memory_base@GLOBAL +; PIC-NEXT: i32.load $push1=, hidden_global($pop0) +; PIC-NEXT: end_function + +; NON-PIC-LABEL: load_hidden_global: +; NON-PIC: i32.const $push0=, 0 +; NON-PIC-NEXT: i32.load $push1=, hidden_global($pop0) +; NON-PIC-NEXT: end_function + + %1 = load i32, i32* @hidden_global + ret i32 %1 +} + +; Store to a hidden global + +define void @store_hidden_global(i32 %n) { +; PIC-LABEL: store_hidden_global: +; PIC: get_global $push0=, __memory_base@GLOBAL +; PIC-NEXT: i32.store hidden_global($pop0), $0 +; PIC-NEXT: end_function + +; NON-PIC-LABEL: store_hidden_global: +; NON-PIC: i32.const $push0=, 0 +; NON-PIC-NEXT: i32.store hidden_global($pop0), $0 +; NON-PIC-NEXT: end_function + + store i32 %n, i32* @hidden_global + ret void +} + +; For non-hidden globals PIC code needs to call special rtld-created 'g$' +; functions to find the runtime offset. + +@external_global = external global i32 + +define i32 @load_external_global() { +; PIC-LABEL: load_external_global: +; PIC: i32.call $push0=, g$external_global@FUNCTION +; PIC-NEXT: i32.load $push1=, external_global($pop0) +; PIC-NEXT: end_function + +; NON-PIC-LABEL: load_external_global: +; NON-PIC: i32.const $push0=, 0 +; NON-PIC-NEXT: i32.load $push1=, external_global($pop0) +; NON-PIC-NEXT: end_function + + %1 = load i32, i32* @external_global + ret i32 %1 +} + +; Store to a non-hidden global + +define void @store_external_global(i32 %n) { +; PIC-LABEL: store_external_global: +; PIC: i32.call $push0=, g$external_global@FUNCTION +; PIC-NEXT: i32.store external_global($pop0), $0 +; PIC-NEXT: end_function + +; NON-PIC-LABEL: store_external_global: +; NON-PIC: i32.const $push0=, 0 +; NON-PIC-NEXT: i32.store external_global($pop0), $0 +; NON-PIC-NEXT: end_function + + store i32 %n, i32* @external_global + ret void +} + +; CHECK-PIC: .globaltype __memory_base, i32 Index: test/CodeGen/WebAssembly/indirect-import.ll =================================================================== --- test/CodeGen/WebAssembly/indirect-import.ll +++ test/CodeGen/WebAssembly/indirect-import.ll @@ -70,10 +70,10 @@ attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } -; CHECK: .functype extern_fd, f32, f64 +; CHECK: .functype extern_sret, void, i32 ; CHECK: .functype extern_vj, void, i64 +; CHECK: .functype extern_i128ret, void, i32, i64 +; CHECK: .functype extern_struct, void, i32 ; CHECK: .functype extern_v, void ; CHECK: .functype extern_ijidf, i32, i64, i32, f64, f32 -; CHECK: .functype extern_struct, void, i32 -; CHECK: .functype extern_sret, void, i32 -; CHECK: .functype extern_i128ret, void, i32, i64 +; CHECK: .functype extern_fd, f32, f64