diff --git a/lld/test/wasm/shared.ll b/lld/test/wasm/shared.ll --- a/lld/test/wasm/shared.ll +++ b/lld/test/wasm/shared.ll @@ -1,10 +1,11 @@ -; RUN: llc -O0 -filetype=obj %s -o %t.o +; RUN: llc -O0 -relocation-model=pic -filetype=obj %s -o %t.o ; RUN: wasm-ld -shared -o %t.wasm %t.o ; RUN: obj2yaml %t.wasm | FileCheck %s target triple = "wasm32-unknown-unknown" @data = hidden global i32 2, align 4 +@data_external = external global i32 @indirect_func = local_unnamed_addr global i32 ()* @foo, align 4 @indirect_func_external = local_unnamed_addr global void ()* @func_external, align 4 @@ -13,18 +14,18 @@ ; To ensure we use __stack_pointer %ptr = alloca i32 %0 = load i32, i32* @data, align 4 - ; TODO(sbc): Re-enable once the codegen supports generating the correct - ; relocation type when referencing external data in shared libraries. - ; %1 = load i32, i32* @data_external, align 4 %1 = load i32 ()*, i32 ()** @indirect_func, align 4 call i32 %1() ret i32 %0 } -declare void @func_external() - -@data_external = external global i32 +define default i32 @load_data_external() { +entry: + %0 = load i32, i32* @data_external, align 4 + ret i32 %0 +} +declare void @func_external() ; check for dylink section at start @@ -64,11 +65,11 @@ ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: GlobalType: I32 ; CHECK-NEXT: GlobalMutable: false -; XCHECK-NEXT: - Module: env -; XCHECK-NEXT: Field: data_external -; XCHECK-NEXT: Kind: GLOBAL -; XCHECK-NEXT: GlobalType: I32 -; XCHECK-NEXT: GlobalMutable: true +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: data_external +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: GlobalType: I32 +; CHECK-NEXT: GlobalMutable: true ; CHECK-NEXT: - Module: env ; CHECK-NEXT: Field: func_external ; CHECK-NEXT: Kind: FUNCTION diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -152,9 +152,12 @@ return TypeMap[Reloc.Index]; case R_WASM_FUNCTION_INDEX_LEB: return getFunctionSymbol(Reloc.Index)->getFunctionIndex(); - case R_WASM_GLOBAL_INDEX_LEB: - return getGlobalSymbol(Reloc.Index)->getGlobalIndex(); - case R_WASM_EVENT_INDEX_LEB: + case R_WASM_GLOBAL_INDEX_LEB: { + const Symbol* Sym = Symbols[Reloc.Index]; + if (auto UD = dyn_cast(Sym)) + return UD->getGlobalIndex(); + return cast(Sym)->getGlobalIndex(); + } case R_WASM_EVENT_INDEX_LEB: return getEventSymbol(Reloc.Index)->getEventIndex(); case R_WASM_FUNCTION_OFFSET_I32: if (auto *Sym = dyn_cast(getFunctionSymbol(Reloc.Index))) { diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h --- a/llvm/include/llvm/MC/MCSymbolWasm.h +++ b/llvm/include/llvm/MC/MCSymbolWasm.h @@ -18,6 +18,7 @@ bool IsWeak = false; bool IsHidden = false; bool IsComdat = false; + mutable bool IsUsedInGOT = false; Optional ImportModule; Optional ImportName; wasm::WasmSignature *Signature = nullptr; @@ -78,6 +79,9 @@ } void setImportName(StringRef Name) { ImportName = Name; } + void setUsedInGOT() const { IsUsedInGOT = true; } + bool isUsedInGOT() const { return IsUsedInGOT; } + const wasm::WasmSignature *getSignature() const { return Signature; } void setSignature(wasm::WasmSignature *Sig) { Signature = Sig; } diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -259,7 +259,6 @@ DenseMap SignatureIndices; SmallVector Signatures; - SmallVector Globals; SmallVector DataSegments; unsigned NumFunctionImports = 0; unsigned NumGlobalImports = 0; @@ -294,7 +293,6 @@ CustomSectionsRelocations.clear(); SignatureIndices.clear(); Signatures.clear(); - Globals.clear(); DataSegments.clear(); SectionFunctions.clear(); NumFunctionImports = 0; @@ -324,7 +322,6 @@ void writeImportSection(ArrayRef Imports, uint32_t DataSize, uint32_t NumElements); void writeFunctionSection(ArrayRef Functions); - void writeGlobalSection(); void writeExportSection(ArrayRef Exports); void writeElemSection(ArrayRef TableElems); void writeCodeSection(const MCAssembler &Asm, const MCAsmLayout &Layout, @@ -543,6 +540,9 @@ SymA->setUsedInReloc(); } + if (Type == wasm::R_WASM_GLOBAL_INDEX_LEB) + SymA->setUsedInGOT(); + WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection); LLVM_DEBUG(dbgs() << "WasmReloc: " << Rec << "\n"); @@ -785,26 +785,6 @@ endSection(Section); } -void WasmObjectWriter::writeGlobalSection() { - if (Globals.empty()) - return; - - SectionBookkeeping Section; - startSection(Section, wasm::WASM_SEC_GLOBAL); - - encodeULEB128(Globals.size(), W.OS); - for (const WasmGlobal &Global : Globals) { - writeValueType(static_cast(Global.Type.Type)); - W.OS << char(Global.Type.Mutable); - - W.OS << char(wasm::WASM_OPCODE_I32_CONST); - encodeSLEB128(Global.InitialValue, W.OS); - W.OS << char(wasm::WASM_OPCODE_END); - } - - endSection(Section); -} - void WasmObjectWriter::writeEventSection(ArrayRef Events) { if (Events.empty()) return; @@ -1207,7 +1187,7 @@ Import.SigIndex = getFunctionType(WS); Imports.push_back(Import); WasmIndices[&WS] = NumFunctionImports++; - } else if (WS.isGlobal()) { + } else if (WS.isGlobal() || WS.isUsedInGOT()) { if (WS.isWeak()) report_fatal_error("undefined global symbol cannot be weak"); @@ -1215,7 +1195,10 @@ Import.Module = WS.getImportModule(); Import.Field = WS.getImportName(); Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; - Import.Global = WS.getGlobalType(); + if (WS.isGlobal()) + Import.Global = WS.getGlobalType(); + else + Import.Global = {wasm::WASM_TYPE_I32, true}; Imports.push_back(Import); WasmIndices[&WS] = NumGlobalImports++; } else if (WS.isEvent()) { @@ -1579,7 +1562,6 @@ writeFunctionSection(Functions); // Skip the "table" section; we import the table instead. // Skip the "memory" section; we import the memory instead. - writeGlobalSection(); writeEventSection(Events); writeExportSection(Exports); writeElemSection(TableElems); diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -747,7 +747,7 @@ object_error::parse_failed); break; case wasm::R_WASM_GLOBAL_INDEX_LEB: - if (!isValidGlobalSymbol(Reloc.Index)) + if (!isValidGlobalSymbol(Reloc.Index) && !isValidDataSymbol(Reloc.Index)) return make_error("Bad relocation global index", object_error::parse_failed); break; diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -87,14 +87,19 @@ /// Target Operand Flag enum. enum TOF { - MO_NO_FLAG = 0, + MO_NO_FLAG, // Flags to indicate the type of the symbol being referenced MO_SYMBOL_FUNCTION = 0x1, MO_SYMBOL_GLOBAL = 0x2, MO_SYMBOL_EVENT = 0x4, MO_SYMBOL_MASK = 0x7, + + // Address of data symbol via a wasm global. This adds a level of indirection + // similar to the GOT on native platforms. + MO_GOT = 0x8, }; + } // end namespace WebAssemblyII } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -46,6 +46,10 @@ return Ref->getKind() == MCSymbolRefExpr::VK_WebAssembly_TYPEINDEX; } +static bool isGOTRef(const MCSymbolRefExpr *Ref) { + return Ref->getKind() == MCSymbolRefExpr::VK_GOT; +} + static const MCSection *getFixupSection(const MCExpr *Expr) { if (auto SyExp = dyn_cast(Expr)) { if (SyExp->getSymbol().isInSection()) @@ -85,7 +89,7 @@ else return wasm::R_WASM_FUNCTION_INDEX_LEB; } - if (SymA.isGlobal()) + if (SymA.isGlobal() || isGOTRef(RefA)) return wasm::R_WASM_GLOBAL_INDEX_LEB; if (SymA.isEvent()) return wasm::R_WASM_EVENT_INDEX_LEB; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -151,7 +151,7 @@ return MVT::INVALID_SIMPLE_VALUE_TYPE; } bool computeAddress(const Value *Obj, Address &Addr); - void materializeLoadStoreOperands(Address &Addr); + bool materializeLoadStoreOperands(Address &Addr); void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB, MachineMemOperand *MMO); unsigned maskI1Value(unsigned Reg, const Value *V); @@ -374,19 +374,22 @@ return Addr.getReg() != 0; } -void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) { +bool WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) { + LLVM_DEBUG(dbgs() << "materializeLoadStoreOperands\n"); if (Addr.isRegBase()) { unsigned Reg = Addr.getReg(); if (Reg == 0) { - Reg = createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass - : &WebAssembly::I32RegClass); - unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 - : WebAssembly::CONST_I32; + const GlobalValue *GV = Addr.getGlobalValue(); + if (GV && TLI.isPositionIndependent()) + return false; + Reg = createResultReg(&WebAssembly::I32RegClass); + unsigned Opc = WebAssembly::CONST_I32; BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg) .addImm(0); Addr.setReg(Reg); } } + return true; } void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr, @@ -396,7 +399,10 @@ // TODO: Disable SetP2AlignOperands for FastISel and just do it here. MIB.addImm(0); - if (const GlobalValue *GV = Addr.getGlobalValue()) + const GlobalValue *GV = Addr.getGlobalValue(); + + // For PIC code the global address in the stack already + if (GV && !TLI.isPositionIndependent()) MIB.addGlobalAddress(GV, Addr.getOffset()); else MIB.addImm(Addr.getOffset()); @@ -604,12 +610,12 @@ } unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) { - if (const auto *GV = dyn_cast(C)) { - unsigned ResultReg = - createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass - : &WebAssembly::I32RegClass); - unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 - : WebAssembly::CONST_I32; + if (const GlobalValue *GV = dyn_cast(C)) { + LLVM_DEBUG(dbgs() << "fastMaterializeConstant " << *GV << "\n"); + if (TLI.isPositionIndependent()) + return 0; + unsigned ResultReg = createResultReg(&WebAssembly::I32RegClass); + unsigned Opc = WebAssembly::CONST_I32; BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) .addGlobalAddress(GV); return ResultReg; @@ -741,6 +747,11 @@ return false; bool IsDirect = Func != nullptr; + if (IsDirect && TLI.isPositionIndependent() && TM.shouldAssumeDSOLocal(*Func->getParent(), Func)) { + LLVM_DEBUG(dbgs() << "call to non-dso-local function: " << *Func << "\n"); + IsDirect = false; + } + if (!IsDirect && isa(Call->getCalledValue())) return false; @@ -1181,7 +1192,8 @@ return false; } - materializeLoadStoreOperands(Addr); + if (!materializeLoadStoreOperands(Addr)) + return false; unsigned ResultReg = createResultReg(RC); auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), @@ -1233,7 +1245,8 @@ return false; } - materializeLoadStoreOperands(Addr); + if (!materializeLoadStoreOperands(Addr)) + return false; unsigned ValueReg = getRegForValue(Store->getValueOperand()); if (ValueReg == 0) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def --- a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def @@ -17,7 +17,11 @@ HANDLE_NODETYPE(CALL0) HANDLE_NODETYPE(RETURN) HANDLE_NODETYPE(ARGUMENT) +// A wrapper node for TargetExternalSymbol, TargetGlobalAddress, and MCSymbol HANDLE_NODETYPE(Wrapper) +// A special wapper used in PIC code for __memory_base/__table_base relcative +// access. +HANDLE_NODETYPE(WrapperPIC) HANDLE_NODETYPE(BR_IF) HANDLE_NODETYPE(BR_TABLE) HANDLE_NODETYPE(SHUFFLE) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -759,9 +759,8 @@ } 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; + SDValue Res = DAG.getNode(Opcode, DL, InTyList, Ops); if (Ins.empty()) { Chain = Res; } else { @@ -983,9 +982,37 @@ "Unexpected target flags on generic GlobalAddressSDNode"); if (GA->getAddressSpace() != 0) fail(DL, DAG, "WebAssembly only expects the 0 address space"); - return DAG.getNode( - WebAssemblyISD::Wrapper, DL, VT, - DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset())); + + const GlobalValue *GV = GA->getGlobal(); + LLVM_DEBUG(dbgs() << "LowerGlobalAddress: " << *GV << "\n"); + + unsigned Flags = 0; + if (isPositionIndependent()) { + if (getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)) { + MachineFunction &MF = DAG.getMachineFunction(); + MVT PtrVT = getPointerTy(MF.getDataLayout()); + const char *BaseName; + if (GV->getValueType()->isFunctionTy()) + BaseName = MF.createExternalSymbolName("__table_base"); + else + BaseName = MF.createExternalSymbolName("__memory_base"); + SDValue BaseAddr = + DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, + DAG.getTargetExternalSymbol(BaseName, PtrVT)); + + SDValue SymAddr = DAG.getNode( + WebAssemblyISD::WrapperPIC, DL, VT, + DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset())); + + return DAG.getNode(ISD::ADD, DL, VT, BaseAddr, SymAddr); + } else { + Flags |= WebAssemblyII::MO_GOT; + } + } + + return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, + DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, + GA->getOffset(), Flags)); } SDValue diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -15,6 +15,9 @@ // WebAssembly Instruction Predicate Definitions. //===----------------------------------------------------------------------===// +def IsPIC : Predicate<"TM.isPositionIndependent()">; +def IsNotPIC : Predicate<"!TM.isPositionIndependent()">; + def HasAddr32 : Predicate<"!Subtarget->hasAddr64()">; def HasAddr64 : Predicate<"Subtarget->hasAddr64()">; @@ -67,14 +70,16 @@ SDTCisVT<1, iPTR>]>; def SDT_WebAssemblyCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>; -def SDT_WebAssemblyCall0 : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; -def SDT_WebAssemblyCall1 : SDTypeProfile<1, -1, [SDTCisPtrTy<1>]>; -def SDT_WebAssemblyBrTable : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; -def SDT_WebAssemblyArgument : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>; -def SDT_WebAssemblyReturn : SDTypeProfile<0, -1, []>; -def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, - SDTCisPtrTy<0>]>; -def SDT_WebAssemblyThrow : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; +def SDT_WebAssemblyCall0 : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; +def SDT_WebAssemblyCall1 : SDTypeProfile<1, -1, [SDTCisPtrTy<1>]>; +def SDT_WebAssemblyBrTable : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; +def SDT_WebAssemblyArgument : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>; +def SDT_WebAssemblyReturn : SDTypeProfile<0, -1, []>; +def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, + SDTCisPtrTy<0>]>; +def SDT_WebAssemblyWrapperPIC : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, + SDTCisPtrTy<0>]>; +def SDT_WebAssemblyThrow : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; //===----------------------------------------------------------------------===// // WebAssembly-specific DAG Nodes. @@ -101,6 +106,8 @@ SDT_WebAssemblyReturn, [SDNPHasChain]>; def WebAssemblywrapper : SDNode<"WebAssemblyISD::Wrapper", SDT_WebAssemblyWrapper>; +def WebAssemblywrapperPIC : SDNode<"WebAssemblyISD::WrapperPIC", + SDT_WebAssemblyWrapperPIC>; def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow, [SDNPHasChain, SDNPVariadic]>; @@ -295,11 +302,22 @@ } // isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)), - (CONST_I32 tglobaladdr:$addr)>; + (CONST_I32 tglobaladdr:$addr)>, Requires<[IsNotPIC]>; + +def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)), + (GLOBAL_GET_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>; + +def : Pat<(i32 (WebAssemblywrapperPIC tglobaladdr:$addr)), + (CONST_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>; + +def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), + (GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC]>; + def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), - (CONST_I32 texternalsym:$addr)>; -def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>; -def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>; + (CONST_I32 texternalsym:$addr)>, Requires<[IsNotPIC]>; + +def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>, Requires<[IsNotPIC]>; +def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>, Requires<[IsNotPIC]>; //===----------------------------------------------------------------------===// // Additional sets of instructions. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td @@ -95,7 +95,7 @@ class LoadPatGlobalAddr : Pat<(ty (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)))), - (inst 0, tglobaladdr:$off, I32:$addr)>; + (inst 0, tglobaladdr:$off, I32:$addr)>, Requires<[IsNotPIC]>; def : LoadPatGlobalAddr; def : LoadPatGlobalAddr; @@ -113,7 +113,7 @@ class LoadPatGlobalAddrOffOnly : Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off))), - (inst 0, tglobaladdr:$off, (CONST_I32 0))>; + (inst 0, tglobaladdr:$off, (CONST_I32 0))>, Requires<[IsNotPIC]>; def : LoadPatGlobalAddrOffOnly; def : LoadPatGlobalAddrOffOnly; @@ -285,7 +285,7 @@ class StorePatGlobalAddr : Pat<(kind ty:$val, (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off))), - (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>; + (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>, Requires<[IsNotPIC]>; def : StorePatGlobalAddr; def : StorePatGlobalAddr; def : StorePatGlobalAddr; @@ -301,7 +301,7 @@ class StorePatGlobalAddrOffOnly : Pat<(kind ty:$val, (WebAssemblywrapper tglobaladdr:$off)), - (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>; + (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>, Requires<[IsNotPIC]>; def : StorePatGlobalAddrOffOnly; def : StorePatGlobalAddrOffOnly; def : StorePatGlobalAddrOffOnly; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h @@ -33,7 +33,7 @@ MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; MCOperand lowerSymbolOperand(MCSymbol *Sym, int64_t Offset, bool IsFunc, - bool IsGlob, bool IsEvent) const; + bool IsGlob, bool IsEvent, unsigned flags) const; public: WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -73,11 +73,12 @@ auto *WasmSym = cast(Printer.GetExternalSymbolSymbol(Name)); const WebAssemblySubtarget &Subtarget = Printer.getSubtarget(); - // Except for the two exceptions (__stack_pointer and __cpp_exception), all - // 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) { + // Except for certain known symbols, all 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 || + strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0) { WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); WasmSym->setGlobalType(wasm::WasmGlobalType{ uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64 @@ -118,12 +119,16 @@ return WasmSym; } -MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(MCSymbol *Sym, - int64_t Offset, - bool IsFunc, bool IsGlob, - bool IsEvent) const { - const MCExpr *Expr = - MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx); +MCOperand WebAssemblyMCInstLower::lowerSymbolOperand( + MCSymbol *Sym, int64_t Offset, bool IsFunc, bool IsGlob, bool IsEvent, + unsigned TargetFlags) const { + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; + if (TargetFlags == WebAssemblyII::MO_GOT) { + if (Offset != 0) + report_fatal_error("GOT symbol references do not support offsets"); + Kind = MCSymbolRefExpr::VK_GOT; + } + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Kind, Ctx); if (Offset != 0) { if (IsFunc) @@ -230,11 +235,9 @@ break; } case MachineOperand::MO_GlobalAddress: - assert(MO.getTargetFlags() == WebAssemblyII::MO_NO_FLAG && - "WebAssembly does not use target flags on GlobalAddresses"); MCOp = lowerSymbolOperand(GetGlobalAddressSymbol(MO), MO.getOffset(), MO.getGlobal()->getValueType()->isFunctionTy(), - false, false); + false, false, MO.getTargetFlags()); break; case MachineOperand::MO_ExternalSymbol: // The target flag indicates whether this is a symbol for a @@ -245,7 +248,8 @@ GetExternalSymbolSymbol(MO), /*Offset=*/0, (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_FUNCTION) != 0, (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_GLOBAL) != 0, - (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_EVENT) != 0); + (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_EVENT) != 0, + MO.getTargetFlags()); break; case MachineOperand::MO_MCSymbol: // This is currently used only for LSDA symbols (GCC_except_table), @@ -253,7 +257,7 @@ assert(MO.getTargetFlags() == 0 && "WebAssembly does not use target flags on MCSymbol"); MCOp = lowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, false, - false); + false, MO.getTargetFlags()); break; } diff --git a/llvm/test/CodeGen/WebAssembly/address-offsets.ll b/llvm/test/CodeGen/WebAssembly/address-offsets.ll --- a/llvm/test/CodeGen/WebAssembly/address-offsets.ll +++ b/llvm/test/CodeGen/WebAssembly/address-offsets.ll @@ -1,4 +1,6 @@ -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=CHECK,NON-PIC +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -relocation-model=pic | FileCheck %s -check-prefixes=CHECK,PIC + ; Test folding constant offsets and symbols into load and store addresses under ; a variety of circumstances. @@ -10,8 +12,10 @@ ; CHECK-LABEL: load_test0: ; CHECK-NEXT: .functype load_test0 () -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 0{{$}} -; CHECK-NEXT: i32.load $push1=, g+40($pop0){{$}} +; NON-PIC-NEXT: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.load $push1=, g+40($pop0){{$}} +; PIC-NEXT: global.get $push0=, g@GOT{{$}} +; PIC-NEXT: i32.load $push1=, 40($pop0){{$}} ; CHECK-NEXT: return $pop1{{$}} define i32 @load_test0() { %t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 @@ -20,8 +24,10 @@ ; CHECK-LABEL: load_test0_noinbounds: ; CHECK-NEXT: .functype load_test0_noinbounds () -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 0{{$}} -; CHECK-NEXT: i32.load $push1=, g+40($pop0){{$}} +; NON-PIC-NEXT: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.load $push1=, g+40($pop0){{$}} +; PIC-NEXT: global.get $push0=, g@GOT{{$}} +; PIC-NEXT: i32.load $push1=, 40($pop0){{$}} ; CHECK-NEXT: return $pop1{{$}} define i32 @load_test0_noinbounds() { %t = load i32, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 @@ -34,7 +40,7 @@ ; CHECK-LABEL: load_test1: ; CHECK-NEXT: .functype load_test1 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -47,7 +53,7 @@ ; CHECK-LABEL: load_test2: ; CHECK-NEXT: .functype load_test2 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -60,7 +66,7 @@ ; CHECK-LABEL: load_test3: ; CHECK-NEXT: .functype load_test3 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -73,7 +79,7 @@ ; CHECK-LABEL: load_test4: ; CHECK-NEXT: .functype load_test4 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -85,7 +91,7 @@ ; CHECK-LABEL: load_test5: ; CHECK-NEXT: .functype load_test5 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -97,7 +103,7 @@ ; CHECK-LABEL: load_test6: ; CHECK-NEXT: .functype load_test6 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -110,7 +116,7 @@ ; CHECK-LABEL: load_test7: ; CHECK-NEXT: .functype load_test7 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -123,7 +129,7 @@ ; CHECK-LABEL: load_test8: ; CHECK-NEXT: .functype load_test8 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEX T: i32.const $push0=, 2{{$}} ; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} ; CHECK-NEX T: return $pop2{{$}} @@ -135,10 +141,16 @@ } ; CHECK-LABEL: load_test9: -; CHECK-NEXT: .functype load_test9 () -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 0{{$}} -; CHECK-NEXT: i32.load $push1=, g-40($pop0){{$}} -; CHECK-NEXT: return $pop1{{$}} +; CHECK-NEXT: .functype load_test9 () -> (i32){{$}} +; NON-PIC-NEXT: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.load $push1=, g-40($pop0){{$}} +; NON-PIC_NEXT: return $pop1{{$}} + +; PIC-NEXT: global.get $push1=, g@GOT{{$}} +; PIC-NEXT: i32.const $push0=, -40{{$}} +; PIC-NEXT: i32.add $push2=, $pop1, $pop0{{$}} +; PIC-NEXT: i32.load $push3=, 0($pop2) +; PIC-NEXT: return $pop3{{$}} define i32 @load_test9() { %t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4 ret i32 %t @@ -146,12 +158,21 @@ ; CHECK-LABEL: load_test10: ; CHECK-NEXT: .functype load_test10 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.const $push2=, g-40{{$}} -; CHECK-NEXT: i32.add $push3=, $pop1, $pop2{{$}} -; CHECK-NEXT: i32.load $push4=, 0($pop3){{$}} -; CHECK-NEXT: return $pop4{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push2=, g-40{{$}} +; NON-PIC-NEXT: i32.add $push3=, $pop1, $pop2{{$}} +; NON-PIC-NEXT: i32.load $push4=, 0($pop3){{$}} +; NON-PIC-NEXT: return $pop4{{$}} + +; PIC-NEXT: global.get $push2=, g@GOT{{$}} +; PIC-NEXT: i32.const $push0=, 2{{$}} +; PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; PIC-NEXT: i32.add $push3=, $pop2, $pop1{{$}} +; PIC-NEXT: i32.const $push4=, -40{{$}} +; PIC-NEXT: i32.add $push5=, $pop3, $pop4{{$}} +; PIC-NEXT: i32.load $push6=, 0($pop5){{$}} +; PIC-NEXT: return $pop6{{$}} define i32 @load_test10(i32 %n) { %add = add nsw i32 %n, -10 %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -183,13 +204,13 @@ ; CHECK-LABEL: load_test12: ; CHECK-NEXT: .functype load_test12 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} -; CHECK-NEXT: return $pop5{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} define i32 @load_test12(i32* %p, i32 %n) { %add = add nsw i32 %n, 10 %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add @@ -199,13 +220,13 @@ ; CHECK-LABEL: load_test13: ; CHECK-NEXT: .functype load_test13 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} -; CHECK-NEXT: return $pop5{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} define i32 @load_test13(i32* %p, i32 %n) { %add = add nsw i32 10, %n %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add @@ -215,11 +236,11 @@ ; CHECK-LABEL: load_test14: ; CHECK-NEXT: .functype load_test14 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}} -; CHECK-NEXT: return $pop3{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}} +; CHECK-NEXT: return $pop3{{$}} define i32 @load_test14(i32* %p, i32 %n) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -229,13 +250,13 @@ ; CHECK-LABEL: load_test15: ; CHECK-NEXT: .functype load_test15 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} -; CHECK-NEXT: return $pop5{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} define i32 @load_test15(i32* %p, i32 %n) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n @@ -245,13 +266,13 @@ ; CHECK-LABEL: load_test16: ; CHECK-NEXT: .functype load_test16 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} -; CHECK-NEXT: return $pop5{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} define i32 @load_test16(i32* %p, i32 %n) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n @@ -261,13 +282,13 @@ ; CHECK-LABEL: load_test17: ; CHECK-NEXT: .functype load_test17 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} -; CHECK-NEXT: return $pop5{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} define i32 @load_test17(i32* %p, i32 %n) { %add = add nsw i32 %n, 10 %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add @@ -277,11 +298,11 @@ ; CHECK-LABEL: load_test18: ; CHECK-NEXT: .functype load_test18 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}} -; CHECK-NEXT: return $pop3{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}} +; CHECK-NEXT: return $pop3{{$}} define i32 @load_test18(i32* %p, i32 %n) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -291,13 +312,13 @@ ; CHECK-LABEL: load_test19: ; CHECK-NEXT: .functype load_test19 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} -; CHECK-NEXT: return $pop5{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} define i32 @load_test19(i32* %p, i32 %n) { %add = add nsw i32 10, %n %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add @@ -307,10 +328,10 @@ ; CHECK-LABEL: load_test20: ; CHECK-NEXT: .functype load_test20 (i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, -40{{$}} -; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, 0($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEXT: i32.const $push0=, -40{{$}} +; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.load $push2=, 0($pop1){{$}} +; CHECK-NEXT: return $pop2{{$}} define i32 @load_test20(i32* %p) { %arrayidx = getelementptr inbounds i32, i32* %p, i32 -10 %t = load i32, i32* %arrayidx, align 4 @@ -319,13 +340,13 @@ ; CHECK-LABEL: load_test21: ; CHECK-NEXT: .functype load_test21 (i32, i32) -> (i32){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, -40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} -; CHECK-NEXT: return $pop5{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, -40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} define i32 @load_test21(i32* %p, i32 %n) { %add = add nsw i32 %n, -10 %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add @@ -335,9 +356,11 @@ ; CHECK-LABEL: store_test0: ; CHECK-NEXT: .functype store_test0 (i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 0{{$}} -; CHECK-NEXT: i32.store g+40($pop0), $0{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.store g+40($pop0), $0{{$}} +; PIC-NEXT: global.get $push0=, g@GOT{{$}} +; PIC-NEXT: i32.store 40($pop0), $0 +; CHECK-NEXT: return{{$}} define void @store_test0(i32 %i) { store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 ret void @@ -345,9 +368,11 @@ ; CHECK-LABEL: store_test0_noinbounds: ; CHECK-NEXT: .functype store_test0_noinbounds (i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 0{{$}} -; CHECK-NEXT: i32.store g+40($pop0), $0{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.store g+40($pop0), $0{{$}} +; PIC-NEXT: global.get $push0=, g@GOT{{$}} +; PIC-NEXT: i32.store 40($pop0), $0{{$}} +; CHECK-NEXT: return{{$}} define void @store_test0_noinbounds(i32 %i) { store i32 %i, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 ret void @@ -355,8 +380,8 @@ ; CHECK-LABEL: store_test1: ; CHECK-NEXT: .functype store_test1 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test1(i32 %n, i32 %i) { @@ -368,8 +393,8 @@ ; CHECK-LABEL: store_test2: ; CHECK-NEXT: .functype store_test2 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test2(i32 %n, i32 %i) { @@ -381,8 +406,8 @@ ; CHECK-LABEL: store_test3: ; CHECK-NEXT: .functype store_test3 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test3(i32 %n, i32 %i) { @@ -394,8 +419,8 @@ ; CHECK-LABEL: store_test4: ; CHECK-NEXT: .functype store_test4 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test4(i32 %n, i32 %i) { @@ -406,8 +431,8 @@ ; CHECK-LABEL: store_test5: ; CHECK-NEXT: .functype store_test5 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test5(i32 %n, i32 %i) { @@ -418,8 +443,8 @@ ; CHECK-LABEL: store_test6: ; CHECK-NEXT: .functype store_test6 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test6(i32 %n, i32 %i) { @@ -431,8 +456,8 @@ ; CHECK-LABEL: store_test7: ; CHECK-NEXT: .functype store_test7 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test7(i32 %n, i32 %i) { @@ -444,8 +469,8 @@ ; CHECK-LABEL: store_test8: ; CHECK-NEXT: .functype store_test8 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}} ; CHECK-NEX T: return{{$}} define void @store_test8(i32 %n, i32 %i) { @@ -457,9 +482,13 @@ ; CHECK-LABEL: store_test9: ; CHECK-NEXT: .functype store_test9 (i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 0{{$}} -; CHECK-NEXT: i32.store g-40($pop0), $0{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.store g-40($pop0), $0{{$}} +; PIC-NEXT: global.get $push1=, g@GOT{{$}} +; PIC-NEXT: i32.const $push0=, -40{{$}} +; PIC-NEXT: i32.add $push2=, $pop1, $pop0{{$}} +; PIC-NEXT: i32.store 0($pop2), $0 +; CHECK-NEXT: return{{$}} define void @store_test9(i32 %i) { store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4 ret void @@ -467,12 +496,19 @@ ; CHECK-LABEL: store_test10: ; CHECK-NEXT: .functype store_test10 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.const $push2=, g-40{{$}} -; CHECK-NEXT: i32.add $push3=, $pop1, $pop2{{$}} -; CHECK-NEXT: i32.store 0($pop3), $1{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.const $push2=, g-40{{$}} +; NON-PIC-NEXT: i32.add $push3=, $pop1, $pop2{{$}} +; NON-PIC-NEXT: i32.store 0($pop3), $1{{$}} +; PIC-NEXT: global.get $push2=, g@GOT{{$}} +; PIC-NEXT: i32.const $push0=, 2{{$}} +; PIC-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; PIC-NEXT: i32.add $push3=, $pop2, $pop1{{$}} +; PIC-NEXT: i32.const $push4=, -40{{$}} +; PIC-NEXT: i32.add $push5=, $pop3, $pop4{{$}} +; PIC-NEXT: i32.store 0($pop5), $1{{$}} +; CHECK-NEXT: return{{$}} define void @store_test10(i32 %n, i32 %i) { %add = add nsw i32 %n, -10 %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -482,8 +518,8 @@ ; CHECK-LABEL: store_test11: ; CHECK-NEXT: .functype store_test11 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.store 40($0), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEXT: i32.store 40($0), $1{{$}} +; CHECK-NEXT: return{{$}} define void @store_test11(i32* %p, i32 %i) { %arrayidx = getelementptr inbounds i32, i32* %p, i32 10 store i32 %i, i32* %arrayidx, align 4 @@ -492,10 +528,10 @@ ; CHECK-LABEL: store_test11_noinbounds: ; CHECK-NEXT: .functype store_test11_noinbounds (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 40{{$}} -; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store 0($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEXT: i32.const $push0=, 40{{$}} +; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.store 0($pop1), $1{{$}} +; CHECK-NEXT: return{{$}} define void @store_test11_noinbounds(i32* %p, i32 %i) { %arrayidx = getelementptr i32, i32* %p, i32 10 store i32 %i, i32* %arrayidx, align 4 @@ -504,13 +540,13 @@ ; CHECK-LABEL: store_test12: ; CHECK-NEXT: .functype store_test12 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.store 0($pop4), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.const $push3=, 40{{$}} +; NON-PIC-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; NON-PIC-NEXT: i32.store 0($pop4), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test12(i32* %p, i32 %n, i32 %i) { %add = add nsw i32 %n, 10 %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add @@ -520,13 +556,13 @@ ; CHECK-LABEL: store_test13: ; CHECK-NEXT: .functype store_test13 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.store 0($pop4), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.const $push3=, 40{{$}} +; NON-PIC-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; NON-PIC-NEXT: i32.store 0($pop4), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test13(i32* %p, i32 %n, i32 %i) { %add = add nsw i32 10, %n %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add @@ -536,11 +572,11 @@ ; CHECK-LABEL: store_test14: ; CHECK-NEXT: .functype store_test14 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.store 40($pop2), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.store 40($pop2), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test14(i32* %p, i32 %n, i32 %i) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -550,13 +586,13 @@ ; CHECK-LABEL: store_test15: ; CHECK-NEXT: .functype store_test15 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.store 0($pop4), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.const $push3=, 40{{$}} +; NON-PIC-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; NON-PIC-NEXT: i32.store 0($pop4), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test15(i32* %p, i32 %n, i32 %i) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n @@ -566,13 +602,13 @@ ; CHECK-LABEL: store_test16: ; CHECK-NEXT: .functype store_test16 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.store 0($pop4), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.const $push3=, 40{{$}} +; NON-PIC-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; NON-PIC-NEXT: i32.store 0($pop4), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test16(i32* %p, i32 %n, i32 %i) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n @@ -582,13 +618,13 @@ ; CHECK-LABEL: store_test17: ; CHECK-NEXT: .functype store_test17 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.store 0($pop4), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.const $push3=, 40{{$}} +; NON-PIC-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; NON-PIC-NEXT: i32.store 0($pop4), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test17(i32* %p, i32 %n, i32 %i) { %add = add nsw i32 %n, 10 %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add @@ -598,11 +634,11 @@ ; CHECK-LABEL: store_test18: ; CHECK-NEXT: .functype store_test18 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.store 40($pop2), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.store 40($pop2), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test18(i32* %p, i32 %n, i32 %i) { %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -612,13 +648,13 @@ ; CHECK-LABEL: store_test19: ; CHECK-NEXT: .functype store_test19 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, 40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.store 0($pop4), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.const $push3=, 40{{$}} +; NON-PIC-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; NON-PIC-NEXT: i32.store 0($pop4), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test19(i32* %p, i32 %n, i32 %i) { %add = add nsw i32 10, %n %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add @@ -628,10 +664,10 @@ ; CHECK-LABEL: store_test20: ; CHECK-NEXT: .functype store_test20 (i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, -40{{$}} -; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store 0($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, -40{{$}} +; NON-PIC-NEXT: i32.add $push1=, $0, $pop0{{$}} +; NON-PIC-NEXT: i32.store 0($pop1), $1{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test20(i32* %p, i32 %i) { %arrayidx = getelementptr inbounds i32, i32* %p, i32 -10 store i32 %i, i32* %arrayidx, align 4 @@ -640,13 +676,13 @@ ; CHECK-LABEL: store_test21: ; CHECK-NEXT: .functype store_test21 (i32, i32, i32) -> (){{$}} -; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, -40{{$}} -; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.store 0($pop4), $2{{$}} -; CHECK-NEXT: return{{$}} +; NON-PIC-NEXT: i32.const $push0=, 2{{$}} +; NON-PIC-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; NON-PIC-NEXT: i32.add $push2=, $0, $pop1{{$}} +; NON-PIC-NEXT: i32.const $push3=, -40{{$}} +; NON-PIC-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; NON-PIC-NEXT: i32.store 0($pop4), $2{{$}} +; NON-PIC-NEXT: return{{$}} define void @store_test21(i32* %p, i32 %n, i32 %i) { %add = add nsw i32 %n, -10 %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add diff --git a/llvm/test/CodeGen/WebAssembly/call-pic.ll b/llvm/test/CodeGen/WebAssembly/call-pic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/call-pic.ll @@ -0,0 +1,42 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -relocation-model=pic -fast-isel=1 | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -relocation-model=pic -fast-isel=0 | FileCheck %s +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +declare i32 @foo() +declare i32 @bar() +declare hidden i32 @hidden_function(); + +@indirect_func = global i32 ()* @foo + +define void @call_indirect_func() { +; CHECK-LABEL: call_indirect_func: +; CHECK: global.get $push[[L0:[0-9]+]]=, __memory_base{{$}} +; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, indirect_func{{$}} +; CHECK-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: i32.load $push[[L3:[0-9]+]]=, 0($pop[[L2]]){{$}} +; CHECK-NEXT: i32.call_indirect $push[[L4:[0-9]+]]=, $pop[[L3]]{{$}} + %1 = load i32 ()*, i32 ()** @indirect_func, align 4 + %call = call i32 %1() + ret void +} + +define i8* @get_function_address() { +; CHECK-LABEL: get_function_address: +; CHECK: global.get $push[[L0:[0-9]+]]=, bar@GOT{{$}} +; CHECK-NEXT: return $pop[[L0]]{{$}} +; CHECK-NEXT: end_function{{$}} + + ret i8* bitcast (i32 ()* @bar to i8*) +} + +define i8* @get_function_address_hidden() { +; CHECK-LABEL: get_function_address_hidden: +; CHECK: global.get $push[[L0:[0-9]+]]=, __table_base{{$}} +; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, hidden_function{{$}} +; CHECK-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: return $pop[[L2]]{{$}} +; CHECK-NEXT: end_function{{$}} + + ret i8* bitcast (i32 ()* @hidden_function to i8*) +} diff --git a/llvm/test/CodeGen/WebAssembly/load-store-pic.ll b/llvm/test/CodeGen/WebAssembly/load-store-pic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/load-store-pic.ll @@ -0,0 +1,153 @@ +; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=NON-PIC,CHECK +; RUN: llc < %s -asm-verbose=false -relocation-model=pic -fast-isel -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=PIC,CHECK +; RUN: llc < %s -asm-verbose=false -relocation-model=pic -fast-isel=false -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=PIC,CHECK + +; Test that globals assemble as expected with -fPIC. +; We test here both with and without fast-isel. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +@hidden_global = external hidden global i32 +@hidden_global_array = external hidden global [10 x i32] +@external_global = external global i32 +@external_global_array = external global [10 x 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() { +; CHECK-LABEL: load_hidden_global: +; PIC: global.get $push[[L0:[0-9]+]]=, __memory_base{{$}} +; PIC-NEXT: i32.const $push[[L1:[0-9]+]]=, hidden_global{{$}} +; PIC-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; PIC-NEXT: i32.load $push[[L3:[0-9]+]]=, 0($pop[[L2]]){{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.load $push1=, hidden_global($pop0){{$}} +; CHECK-NEXT: end_function + + %1 = load i32, i32* @hidden_global + ret i32 %1 +} + +define i32 @load_hidden_global_offset() { +; CHECK-LABEL: load_hidden_global_offset: +; PIC: global.get $push[[L0:[0-9]+]]=, __memory_base{{$}} +; PIC-NEXT: i32.const $push[[L1:[0-9]+]]=, hidden_global_array{{$}} +; PIC-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1:[0-9]+]]{{$}} +; PIC-NEXT: i32.const $push[[L3:[0-9]+]]=, 20{{$}} +; PIC-NEXT: i32.add $push[[L4:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; PIC-NEXT: i32.load $push{{[0-9]+}}=, 0($pop[[L4]]){{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT:i32.load $push1=, hidden_global_array+20($pop0){{$}} +; CHECK-NEXT: end_function + + %1 = getelementptr [10 x i32], [10 x i32]* @hidden_global_array, i32 0, i32 5 + %2 = load i32, i32* %1 + ret i32 %2 +} + +; Store to a hidden global + +define void @store_hidden_global(i32 %n) { +; CHECK-LABEL: store_hidden_global: +; PIC: global.get $push[[L0:[0-9]+]]=, __memory_base{{$}} +; PIC-NEXT: i32.const $push[[L1:[0-9]+]]=, hidden_global{{$}} +; PIC-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; PIC-NEXT: i32.store 0($pop[[L2]]), $0{{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.store hidden_global($pop0), $0{{$}} +; CHECK-NEXT: end_function + + store i32 %n, i32* @hidden_global + ret void +} + +define void @store_hidden_global_offset(i32 %n) { +; CHECK-LABEL: store_hidden_global_offset: +; PIC: global.get $push[[L0:[0-9]+]]=, __memory_base{{$}} +; PIC-NEXT: i32.const $push[[L1:[0-9]+]]=, hidden_global_array{{$}} +; PIC-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; PIC-NEXT: i32.const $push[[L3:[0-9]+]]=, 20{{$}} +; PIC-NEXT: i32.add $push[[L4:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; PIC-NEXT: i32.store 0($pop[[L4]]), $0{{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.store hidden_global_array+20($pop0), $0{{$}} +; CHECK-NEXT: end_function + + %1 = getelementptr [10 x i32], [10 x i32]* @hidden_global_array, i32 0, i32 5 + store i32 %n, i32* %1 + ret void +} + +; For non-hidden globals PIC code has to load the address from a wasm global +; using the @GOT relocation type. + + +define i32 @load_external_global() { +; CHECK-LABEL: load_external_global: +; PIC: global.get $push[[L0:[0-9]+]]=, external_global@GOT{{$}} +; PIC-NEXT: i32.load $push{{[0-9]+}}=, 0($pop[[L0]]){{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.load $push1=, external_global($pop0){{$}} +; CHECK-NEXT: end_function + + %1 = load i32, i32* @external_global + ret i32 %1 +} + +define i32 @load_external_global_offset() { +; CHECK-LABEL: load_external_global_offset: +; PIC: global.get $push[[L0:[0-9]+]]=, external_global_array@GOT{{$}} +; PIC-NEXT: i32.const $push[[L1:[0-9]+]]=, 20{{$}} +; PIC-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; PIC-NEXT: i32.load $push{{[0-9]+}}=, 0($pop[[L2]]){{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.load $push1=, external_global_array+20($pop0){{$}} +; CHECK-NEXT: end_function + + %1 = getelementptr [10 x i32], [10 x i32]* @external_global_array, i32 0, i32 5 + %2 = load i32, i32* %1 + ret i32 %2 +} + +; Store to a non-hidden global via the wasm global. + +define void @store_external_global(i32 %n) { +; CHECK-LABEL: store_external_global: +; PIC: global.get $push[[L0:[0-9]+]]=, external_global@GOT{{$}} +; PIC-NEXT: i32.store 0($pop[[L0]]), $0{{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.store external_global($pop0), $0{{$}} +; CHECK-NEXT: end_function + + store i32 %n, i32* @external_global + ret void +} + +define void @store_external_global_offset(i32 %n) { +; CHECK-LABEL: store_external_global_offset: +; PIC: global.get $push[[L0:[0-9]+]]=, external_global_array@GOT{{$}} +; PIC-NEXT: i32.const $push[[L1:[0-9]+]]=, 20{{$}} +; PIC-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; PIC-NEXT: i32.store 0($pop[[L2]]), $0{{$}} + +; NON-PIC: i32.const $push0=, 0{{$}} +; NON-PIC-NEXT: i32.store external_global_array+20($pop0), $0{{$}} +; CHECK-NEXT: end_function + + %1 = getelementptr [10 x i32], [10 x i32]* @external_global_array, i32 0, i32 5 + store i32 %n, i32* %1 + ret void +} + +; PIC: .globaltype __memory_base, i32 diff --git a/llvm/test/MC/WebAssembly/reloc-pic.s b/llvm/test/MC/WebAssembly/reloc-pic.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/reloc-pic.s @@ -0,0 +1,67 @@ +# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj < %s | obj2yaml | FileCheck %s + +# Verify that @GOT relocation entryes result in R_WASM_GLOBAL_INDEX_LEB against +# a data symbol and that the corresponding data symbol is imported as a wasm +# global. + +load_default_global: + .functype load_default_global () -> (i32) + global.get default_global@GOT + i32.load 0 + end_function + +.size default_global, 4 + +# CHECK: --- !WASM +# CHECK-NEXT: FileHeader: +# CHECK-NEXT: Version: 0x00000001 +# CHECK-NEXT: Sections: +# CHECK-NEXT: - Type: TYPE +# CHECK-NEXT: Signatures: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: ReturnType: I32 +# CHECK-NEXT: ParamTypes: [] +# CHECK-NEXT: - Type: IMPORT +# CHECK-NEXT: Imports: +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __linear_memory +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Memory: +# CHECK-NEXT: Initial: 0x00000000 +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __indirect_function_table +# CHECK-NEXT: Kind: TABLE +# CHECK-NEXT: Table: +# CHECK-NEXT: ElemType: FUNCREF +# CHECK-NEXT: Limits: +# CHECK-NEXT: Initial: 0x00000000 +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: default_global +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Type: FUNCTION +# CHECK-NEXT: FunctionTypes: [ 0 ] +# CHECK-NEXT: - Type: CODE +# CHECK-NEXT: Relocations: +# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: Offset: 0x00000004 +# CHECK-NEXT: Functions: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Locals: [] +# CHECK-NEXT: Body: 2380808080002800000B +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: linking +# CHECK-NEXT: Version: 2 +# CHECK-NEXT: SymbolTable: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Name: load_default_global +# CHECK-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-NEXT: Function: 0 +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Kind: DATA +# CHECK-NEXT: Name: default_global +# CHECK-NEXT: Flags: [ UNDEFINED ] +# CHECK-NEXT: ...