bool IsWeak = false;
bool IsHidden = false;
bool IsComdat = false;
+ mutable bool IsUsedInGOT = false;
Optional<std::string> ImportModule;
Optional<std::string> ImportName;
wasm::WasmSignature *Signature = nullptr;
}
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; }
// Maps function/global symbols to the function/global/event/section index
// space.
DenseMap<const MCSymbolWasm *, uint32_t> WasmIndices;
+ DenseMap<const MCSymbolWasm *, uint32_t> GOTIndices;
// Maps data symbols to the Wasm segment and offset/size with the segment.
DenseMap<const MCSymbolWasm *, wasm::WasmDataReference> DataLocations;
DenseMap<WasmSignature, uint32_t, WasmSignatureDenseMapInfo> SignatureIndices;
SmallVector<WasmSignature, 4> Signatures;
- SmallVector<WasmGlobal, 4> Globals;
SmallVector<WasmDataSegment, 4> DataSegments;
unsigned NumFunctionImports = 0;
unsigned NumGlobalImports = 0;
DataRelocations.clear();
TypeIndices.clear();
WasmIndices.clear();
+ GOTIndices.clear();
TableIndices.clear();
DataLocations.clear();
CustomSections.clear();
CustomSectionsRelocations.clear();
SignatureIndices.clear();
Signatures.clear();
- Globals.clear();
DataSegments.clear();
SectionFunctions.clear();
NumFunctionImports = 0;
void writeImportSection(ArrayRef<wasm::WasmImport> Imports, uint32_t DataSize,
uint32_t NumElements);
void writeFunctionSection(ArrayRef<WasmFunction> Functions);
- void writeGlobalSection();
void writeExportSection(ArrayRef<wasm::WasmExport> Exports);
void writeElemSection(ArrayRef<uint32_t> TableElems);
void writeCodeSection(const MCAssembler &Asm, const MCAsmLayout &Layout,
SymA->setUsedInReloc();
}
+ if (RefA->getKind() == MCSymbolRefExpr::VK_GOT)
+ SymA->setUsedInGOT();
+
WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection);
LLVM_DEBUG(dbgs() << "WasmReloc: " << Rec << "\n");
// useable.
uint32_t
WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry) {
+ if (RelEntry.Type == wasm::R_WASM_GLOBAL_INDEX_LEB && !RelEntry.Symbol->isGlobal()) {
+ assert(GOTIndices.count(RelEntry.Symbol) > 0 && "symbol not found in GOT index space");
+ return GOTIndices[RelEntry.Symbol];
+ }
+
switch (RelEntry.Type) {
case wasm::R_WASM_TABLE_INDEX_SLEB:
case wasm::R_WASM_TABLE_INDEX_I32: {
case wasm::R_WASM_GLOBAL_INDEX_LEB:
case wasm::R_WASM_EVENT_INDEX_LEB:
// Provisional value is function/global/event Wasm index
- if (!WasmIndices.count(RelEntry.Symbol))
- report_fatal_error("symbol not found in wasm index space: " +
- RelEntry.Symbol->getName());
+ assert(WasmIndices.count(RelEntry.Symbol) > 0 && "symbol not found in wasm index space");
return WasmIndices[RelEntry.Symbol];
case wasm::R_WASM_FUNCTION_OFFSET_I32:
case wasm::R_WASM_SECTION_OFFSET_I32: {
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<wasm::ValType>(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<wasm::WasmEventType> Events) {
if (Events.empty())
return;
Import.Kind = wasm::WASM_EXTERNAL_FUNCTION;
Import.SigIndex = getFunctionType(WS);
Imports.push_back(Import);
+ assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = NumFunctionImports++;
} else if (WS.isGlobal()) {
if (WS.isWeak())
report_fatal_error("undefined global symbol cannot be weak");
wasm::WasmImport Import;
- Import.Module = WS.getImportModule();
Import.Field = WS.getImportName();
Import.Kind = wasm::WASM_EXTERNAL_GLOBAL;
+ Import.Module = WS.getImportModule();
Import.Global = WS.getGlobalType();
Imports.push_back(Import);
+ assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = NumGlobalImports++;
} else if (WS.isEvent()) {
if (WS.isWeak())
Import.Event.Attribute = wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION;
Import.Event.SigIndex = getEventType(WS);
Imports.push_back(Import);
+ assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = NumEventImports++;
}
}
}
+ // Add imports for GOT globals
+ for (const MCSymbol &S : Asm.symbols()) {
+ const auto &WS = static_cast<const MCSymbolWasm &>(S);
+ if (WS.isUsedInGOT()) {
+ wasm::WasmImport Import;
+ if (WS.isFunction())
+ Import.Module = "GOT.func";
+ else
+ Import.Module = "GOT.mem";
+ Import.Field = WS.getName();
+ Import.Kind = wasm::WASM_EXTERNAL_GLOBAL;
+ Import.Global = {wasm::WASM_TYPE_I32, true};
+ Imports.push_back(Import);
+ assert(GOTIndices.count(&WS) == 0);
+ GOTIndices[&WS] = NumGlobalImports++;
+ }
+ }
+
// Populate DataSegments and CustomSections, which must be done before
// populating DataLocations.
for (MCSection &Sec : Asm) {
wasm::WasmEventType Event;
Event.SigIndex = getEventType(WS);
Event.Attribute = wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION;
+ assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = Index;
Events.push_back(Event);
} else {
// An import; the index was assigned above.
+ assert(WasmIndices.count(&WS) > 0);
Index = WasmIndices.find(&WS)->second;
}
LLVM_DEBUG(dbgs() << " -> event index: " << WasmIndices.find(&WS)->second
if (ResolvedSym->isFunction()) {
assert(WasmIndices.count(ResolvedSym) > 0);
uint32_t WasmIndex = WasmIndices.find(ResolvedSym)->second;
+ assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = WasmIndex;
LLVM_DEBUG(dbgs() << " -> index:" << WasmIndex << "\n");
} else if (ResolvedSym->isData()) {
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);
object_error::parse_failed);
break;
case wasm::R_WASM_GLOBAL_INDEX_LEB:
- if (!isValidGlobalSymbol(Reloc.Index))
+ // R_WASM_GLOBAL_INDEX_LEB are can be used against function and data
+ // symbols to refer to thier GOT enties.
+ if (!isValidGlobalSymbol(Reloc.Index) &&
+ !isValidDataSymbol(Reloc.Index) &&
+ !isValidFunctionSymbol(Reloc.Index))
return make_error<GenericBinaryError>("Bad relocation global index",
object_error::parse_failed);
break;
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
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<MCSymbolRefExpr>(Expr)) {
if (SyExp->getSymbol().isInSection())
case WebAssembly::fixup_code_sleb128_i64:
llvm_unreachable("fixup_sleb128_i64 not implemented yet");
case WebAssembly::fixup_code_uleb128_i32:
+ if (SymA.isGlobal() || isGOTRef(RefA))
+ return wasm::R_WASM_GLOBAL_INDEX_LEB;
if (SymA.isFunction()) {
if (isFunctionSignatureRef(RefA))
return wasm::R_WASM_TYPE_INDEX_LEB;
else
return wasm::R_WASM_FUNCTION_INDEX_LEB;
}
- if (SymA.isGlobal())
- return wasm::R_WASM_GLOBAL_INDEX_LEB;
if (SymA.isEvent())
return wasm::R_WASM_EVENT_INDEX_LEB;
return wasm::R_WASM_MEMORY_ADDR_LEB;
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);
return Addr.getReg() != 0;
}
-void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) {
+bool WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) {
if (Addr.isRegBase()) {
unsigned Reg = Addr.getReg();
if (Reg == 0) {
+ const GlobalValue *GV = Addr.getGlobalValue();
+ if (GV && TLI.isPositionIndependent())
+ return false;
Reg = createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
: &WebAssembly::I32RegClass);
unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64
Addr.setReg(Reg);
}
}
+ return true;
}
void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr,
}
unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) {
- if (const auto *GV = dyn_cast<GlobalValue>(C)) {
+ if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) {
+ if (TLI.isPositionIndependent())
+ return 0;
unsigned ResultReg =
createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
: &WebAssembly::I32RegClass);
return false;
}
- materializeLoadStoreOperands(Addr);
+ if (!materializeLoadStoreOperands(Addr))
+ return false;
unsigned ResultReg = createResultReg(RC);
auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc),
return false;
}
- materializeLoadStoreOperands(Addr);
+ if (!materializeLoadStoreOperands(Addr))
+ return false;
unsigned ValueReg = getRegForValue(Store->getValueOperand());
if (ValueReg == 0)
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)
FINode = DAG.getIntPtrConstant(0, DL);
}
+ if (Callee->getOpcode() == ISD::GlobalAddress) {
+ // If the callee is a GlobalAddress node (quite common, every direct call
+ // is) turn it into a TargetGlobalAddress node so that LowerGlobalAddress
+ // doesn't at MO_GOT which is not needed for direct calls.
+ GlobalAddressSDNode* GA = cast<GlobalAddressSDNode>(Callee);
+ Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL,
+ getPointerTy(DAG.getDataLayout()),
+ GA->getOffset());
+ Callee = DAG.getNode(WebAssemblyISD::Wrapper, DL,
+ getPointerTy(DAG.getDataLayout()), Callee);
+ }
+
// Compute the operands for the CALLn node.
SmallVector<SDValue, 16> Ops;
Ops.push_back(Chain);
"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()));
+
+ unsigned Flags = 0;
+ if (isPositionIndependent()) {
+ const GlobalValue *GV = GA->getGlobal();
+ 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
// WebAssembly Instruction Predicate Definitions.
//===----------------------------------------------------------------------===//
+def IsPIC : Predicate<"TM.isPositionIndependent()">;
+def IsNotPIC : Predicate<"!TM.isPositionIndependent()">;
+
def HasAddr32 : Predicate<"!Subtarget->hasAddr64()">;
def HasAddr64 : Predicate<"Subtarget->hasAddr64()">;
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.
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]>;
} // 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)>;
+ (CONST_I32 texternalsym:$addr)>, Requires<[IsNotPIC]>;
+
def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>;
def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>;
class LoadPatGlobalAddr<ValueType ty, PatFrag kind, NI inst> :
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<i32, load, LOAD_I32>;
def : LoadPatGlobalAddr<i64, load, LOAD_I64>;
class LoadPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off))),
- (inst 0, tglobaladdr:$off, (CONST_I32 0))>;
+ (inst 0, tglobaladdr:$off, (CONST_I32 0))>, Requires<[IsNotPIC]>;
def : LoadPatGlobalAddrOffOnly<i32, load, LOAD_I32>;
def : LoadPatGlobalAddrOffOnly<i64, load, LOAD_I64>;
class StorePatGlobalAddr<ValueType ty, PatFrag kind, NI inst> :
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<i32, store, STORE_I32>;
def : StorePatGlobalAddr<i64, store, STORE_I64>;
def : StorePatGlobalAddr<f32, store, STORE_F32>;
class StorePatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
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<i32, store, STORE_I32>;
def : StorePatGlobalAddrOffOnly<i64, store, STORE_I64>;
def : StorePatGlobalAddrOffOnly<f32, store, STORE_F32>;
auto *WasmSym = cast<MCSymbolWasm>(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) {
+ bool Mutable = strcmp(Name, "__stack_pointer") == 0;
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
WasmSym->setGlobalType(wasm::WasmGlobalType{
uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
: wasm::WASM_TYPE_I32),
- true});
+ Mutable});
return WasmSym;
}
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, unsigned TargetFlags) const {
+ MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None;
+ if (TargetFlags & WebAssemblyII::MO_GOT)
+ Kind = MCSymbolRefExpr::VK_GOT;
+ const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Kind, Ctx);
if (Offset != 0) {
- if (IsFunc)
+ if (TargetFlags & WebAssemblyII::MO_GOT)
+ report_fatal_error("GOT symbol references do not support offsets");
+ unsigned Type = TargetFlags & WebAssemblyII::MO_SYMBOL_MASK;
+ assert((Type == WebAssemblyII::MO_SYMBOL_FUNCTION) == IsFunc);
+ if (Type == WebAssemblyII::MO_SYMBOL_FUNCTION || IsFunc)
report_fatal_error("Function addresses with offsets not supported");
- if (IsGlob)
+ if (Type == WebAssemblyII::MO_SYMBOL_GLOBAL)
report_fatal_error("Global indexes with offsets not supported");
- if (IsEvent)
+ if (Type == WebAssemblyII::MO_SYMBOL_EVENT)
report_fatal_error("Event indexes with offsets not supported");
Expr =
MCBinaryExpr::createAdd(Expr, MCConstantExpr::create(Offset, Ctx), Ctx);
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);
+ MO.getTargetFlags());
break;
case MachineOperand::MO_ExternalSymbol:
// The target flag indicates whether this is a symbol for a
assert((MO.getTargetFlags() & ~WebAssemblyII::MO_SYMBOL_MASK) == 0 &&
"WebAssembly uses only symbol flags on ExternalSymbols");
MCOp = lowerSymbolOperand(
- 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);
+ GetExternalSymbolSymbol(MO), /*Offset=*/0, false, MO.getTargetFlags());
break;
case MachineOperand::MO_MCSymbol:
// This is currently used only for LSDA symbols (GCC_except_table),
// because global addresses or other external symbols are handled above.
assert(MO.getTargetFlags() == 0 &&
"WebAssembly does not use target flags on MCSymbol");
- MCOp = lowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, false,
- false);
+ MCOp = lowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, MO.getTargetFlags());
break;
}
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;
+ unsigned flags) const;
public:
WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer)
-; 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.
; 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
; 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
; 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{{$}}
; 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{{$}}
; 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{{$}}
; 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{{$}}
; 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{{$}}
; 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{{$}}
; 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{{$}}
; 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{{$}}
}
; 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
; 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
; 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
; 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
; 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) {
; 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) {
; 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) {
; 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) {
; 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) {
; 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) {
; 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) {
; 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) {
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
--- /dev/null
+; 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 void @call_direct() {
+; CHECK-LABEL: call_direct:
+; CHECK: .functype call_direct () -> ()
+; CHECK-NEXT: i32.call $push0=, foo{{$}}
+; CHECK-NEXT: drop $pop0{{$}}
+; CHECK-NEXT: return{{$}}
+ %call = call i32 @foo()
+ 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*)
+}
--- /dev/null
+; 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
--- /dev/null
+# 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
+# against the corrsponding function or data symbol and that the corresponding
+# data symbols are imported as a wasm globals.
+
+load_default_data:
+ .functype load_default_data () -> (i32)
+ global.get default_data@GOT
+ i32.load 0
+ end_function
+
+load_default_func:
+ .functype load_default_func () -> (i32)
+ global.get default_func@GOT
+ i32.load 0
+ end_function
+
+.size default_data, 4
+.functype default_func () -> (i32)
+
+# 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_func
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: SigIndex: 0
+# CHECK-NEXT: - Module: GOT.mem
+# CHECK-NEXT: Field: default_data
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I32
+# CHECK-NEXT: GlobalMutable: true
+# CHECK-NEXT: - Module: GOT.func
+# CHECK-NEXT: Field: default_func
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I32
+# CHECK-NEXT: GlobalMutable: true
+# CHECK-NEXT: - Type: FUNCTION
+# CHECK-NEXT: FunctionTypes: [ 0, 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: - Type: R_WASM_GLOBAL_INDEX_LEB
+# CHECK-NEXT: Index: 3
+# CHECK-NEXT: Offset: 0x00000010
+# CHECK-NEXT: Functions:
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Locals: []
+# CHECK-NEXT: Body: 2380808080002800000B
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Locals: []
+# CHECK-NEXT: Body: 2381808080002800000B
+# 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_data
+# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
+# CHECK-NEXT: Function: 1
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Name: default_data
+# CHECK-NEXT: Flags: [ UNDEFINED ]
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Name: load_default_func
+# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
+# CHECK-NEXT: Function: 2
+# CHECK-NEXT: - Index: 3
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Name: default_func
+# CHECK-NEXT: Flags: [ UNDEFINED ]
+# CHECK-NEXT: Function: 0
+# CHECK-NEXT: ...