diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 54680df80ecd24..c03032987e5ddd 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -2176,9 +2176,8 @@ void CodeGen::genCodeForConstant(GenTree* treeNode) icon = treeNode->AsIntConCommon(); if (icon->IsIconHandle()) { - // Wasm has no absolute-address literals; every handle is materialized as a module-base- - // relative constant and relocated. compReloc is always on for a real AOT compile, so a - // handle only reaches here without needing a reloc under a cross-VM SuperPMI replay. + // Handles need linker relocations for NativeAOT or image-relative relocations for R2R. + // A handle only reaches here without needing a reloc under a cross-VM SuperPMI replay. assert(icon->ImmedValNeedsReloc(m_compiler) || m_compiler->RunningSuperPmiReplay()); GetEmitter()->emitAddressConstant((void*)icon->IntegralValue()); WasmProduceReg(treeNode); @@ -3150,11 +3149,18 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree) if (addr->isContained() && !addr->OperIs(GT_LEA)) { - // A contained address constant folds into the memarg offset, so emit just the image base here. + // A contained address constant folds into the memarg offset, so emit just the address base here. // assert(addr->IsIconHandle() && !tree->TypeIs(TYP_SIMD12)); assert(addr->AsIntConCommon()->ImmedValNeedsReloc(m_compiler)); - GetEmitter()->emitImageBase(); + if (m_compiler->IsNativeAot()) + { + GetEmitter()->emitIns_I(INS_i32_const, EA_PTRSIZE, 0); + } + else + { + GetEmitter()->emitImageBase(); + } GetEmitter()->emitIns_MemargAddress(ins_Load(type), emitActualTypeSize(type), (void*)addr->AsIntConCommon()->IntegralValue()); } diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 805171f2d9e02a..e2788216d58528 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -198,19 +198,25 @@ bool emitter::emitInsIsStore(instruction ins) // void emitter::emitImageBase() { + assert(!m_compiler->IsNativeAot()); emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->imageBase); } //------------------------------------------------------------------------ // emitAddressConstant: Emit a memory address constant, like an indirection cell. -// This will automatically make use of relocations and the module base (imageBase). +// NativeAOT uses linker-relocated absolute addresses; R2R adds the module base. void emitter::emitAddressConstant(void* address) { - // Load our module base from the image base global, then load our address constant, then sum them. - emitImageBase(); + if (!m_compiler->IsNativeAot()) + { + emitImageBase(); + } emitIns_I(INS_i32_const_address, EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), (cnsval_ssize_t)address); - emitIns(INS_i32_add); + if (!m_compiler->IsNativeAot()) + { + emitIns(INS_i32_add); + } } //------------------------------------------------------------------------ @@ -219,12 +225,13 @@ void emitter::emitAddressConstant(void* address) // Arguments: // ins - the load or store instruction // attr - emit attributes -// address - the address constant, relocated relative to the module base +// address - the relocatable address constant // // Notes: -// The module base must already be on the stack; see emitImageBase. This folds the addition that -// emitAddressConstant would otherwise emit into the memarg offset. The memarg offset is always a -// relocation, never a raw address, so the caller must only reach here when relocating. +// The memory-address base (zero for NativeAOT, image base for R2R) must already be on the stack. +// This folds the addition that emitAddressConstant would otherwise emit into the memarg offset. +// The memarg offset is always a relocation, never a raw address, so the caller must only reach here +// when relocating. // void emitter::emitIns_MemargAddress(instruction ins, emitAttr attr, void* address) { @@ -252,9 +259,7 @@ void emitter::emitFuncletAddressConstant(cnsval_ssize_t funcletId) // emitDataOffsetConstant: Emit a constant whose value is the linear-memory // address of an entry in this method's JIT-emitted data (constants) section. // -// The encoding mirrors emitAddressConstant: we load the image base global -// (the module image base), emit a relocated i32.const for the entry's offset, -// then add them. At output time the reloc target is resolved to the host +// The encoding mirrors emitAddressConstant. At output time the reloc target is resolved to the host // pointer of the data section entry, which crossgen2 maps to the per-method // read-only data symbol + offset. // @@ -264,8 +269,10 @@ void emitter::emitFuncletAddressConstant(cnsval_ssize_t funcletId) // void emitter::emitDataOffsetConstant(UNATIVE_OFFSET dataOffs) { - emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, - (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->imageBase); + if (!m_compiler->IsNativeAot()) + { + emitImageBase(); + } instrDesc* id = emitNewInstrSC(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), (cnsval_ssize_t)dataOffs); id->idIns(INS_i32_const_dataoffs); @@ -273,7 +280,10 @@ void emitter::emitDataOffsetConstant(UNATIVE_OFFSET dataOffs) dispIns(id); appendToCurIG(id); - emitIns(INS_i32_add); + if (!m_compiler->IsNativeAot()) + { + emitIns(INS_i32_add); + } } /***************************************************************************** @@ -1007,9 +1017,9 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_MEMADDR: { dst += emitOutputOpcode(dst, ins); - // TODO-WASM: The below reloc we're emitting here is specific to R2R and assumes the address we want - // is an offset from __image_base - dst += emitOutputConstant(dst, id, SIGNED, CorInfoReloc::WASM_MEMORY_ADDR_REL_SLEB); + CorInfoReloc reloc = m_compiler->IsNativeAot() ? CorInfoReloc::WASM_MEMORY_ADDR_SLEB + : CorInfoReloc::WASM_MEMORY_ADDR_REL_SLEB; + dst += emitOutputConstant(dst, id, SIGNED, reloc); break; } case IF_FUNCLETIDX: @@ -1038,7 +1048,9 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutputOpcode(dst, ins); UNATIVE_OFFSET dataOffs = (UNATIVE_OFFSET)emitGetInsSC(id); BYTE* target = emitDataOffsetToPtr(dataOffs); - emitRecordRelocation(dst, target, CorInfoReloc::WASM_MEMORY_ADDR_REL_SLEB); + CorInfoReloc reloc = m_compiler->IsNativeAot() ? CorInfoReloc::WASM_MEMORY_ADDR_SLEB + : CorInfoReloc::WASM_MEMORY_ADDR_REL_SLEB; + emitRecordRelocation(dst, target, reloc); dst += emitOutputPaddedReloc(dst); break; } @@ -1102,9 +1114,9 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) assert(align <= UINT32_MAX); // spec says memarg alignment is u32 assert(align < 64); // spec says align > 2^6 produces a memidx for multiple memories. dst += emitOutputULEB128(dst, align); - // TODO-WASM: as with IF_MEMADDR, this reloc is specific to R2R and assumes the address we want is an - // offset from __image_base. - dst += emitOutputConstant(dst, id, UNSIGNED, CorInfoReloc::WASM_MEMORY_ADDR_REL_LEB); + CorInfoReloc reloc = + m_compiler->IsNativeAot() ? CorInfoReloc::WASM_MEMORY_ADDR_LEB : CorInfoReloc::WASM_MEMORY_ADDR_REL_LEB; + dst += emitOutputConstant(dst, id, UNSIGNED, reloc); break; } case IF_LOCAL_DECL: diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 2feb985d1d0435..a8dffecc1c2001 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -580,15 +580,14 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode) // Contain a relocatable address constant so it folds into the load's memarg offset. // - // Codegen for such a constant is "global.get $imageBase; i32.const ; i32.add"; containing it lets - // genCodeForIndir emit just the image base and put the relocated address in the memarg instead. Only loads - // can do this: the parent emits the base in place of the address, and a store cannot because its value - // operand has already been pushed. A multiply-used address is excluded because codegen re-materializes it - // from a register that would no longer hold the full address. + // The load emits the address base and puts the relocated address in the memarg. A store cannot contain + // its address because its value operand has already been pushed. Multiply-used addresses are excluded + // because codegen re-materializes them from registers that must still hold the full address. // - GenTree* addr = indirNode->Addr(); + GenTree* addr = indirNode->Addr(); + bool isNativeFunctionAddress = m_compiler->IsNativeAot() && addr->IsIconHandle(GTF_ICON_FTN_ADDR); if (indirNode->OperIs(GT_IND) && !indirNode->TypeIs(TYP_SIMD12) && addr->IsIconHandle() && - addr->AsIntConCommon()->ImmedValNeedsReloc(m_compiler) && + !isNativeFunctionAddress && addr->AsIntConCommon()->ImmedValNeedsReloc(m_compiler) && ((addr->gtLIRFlags & LIR::Flags::MultiplyUsed) == LIR::Flags::None)) { MakeSrcContained(indirNode, addr); diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmRelocatableObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmRelocatableObjectWriter.cs index b6c9ab8224433a..068a20f4955fa9 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmRelocatableObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmRelocatableObjectWriter.cs @@ -389,6 +389,7 @@ private unsafe int WriteRelocationEntries(SectionDataEmitter section, int conten RelocType.WASM_TABLE_INDEX_I32 => WasmRelocationType.TableIndexI32, RelocType.WASM_TABLE_INDEX_I64 => WasmRelocationType.TableIndexI64, RelocType.WASM_MEMORY_ADDR_LEB => WasmRelocationType.MemoryAddrLeb, + RelocType.WASM_MEMORY_ADDR_SLEB when isFunction => WasmRelocationType.TableIndexSleb, RelocType.WASM_MEMORY_ADDR_SLEB => WasmRelocationType.MemoryAddrSleb, RelocType.WASM_MEMORY_ADDR_REL_SLEB when isFunction => WasmRelocationType.TableIndexSleb, RelocType.WASM_MEMORY_ADDR_REL_SLEB => WasmRelocationType.MemoryAddrRelSleb,