summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyungwoo Lee <kyulee@microsoft.com>2016-04-29 10:29:28 -0700
committerKyungwoo Lee <kyulee@microsoft.com>2016-04-29 14:52:46 -0700
commit45798f661f8c8c042f3582cde8b611d1c9c7343f (patch)
tree0d75dad9935f95c5bf4bd309899a37456e4478bd
parent601b1051c1022d5f764224e35be59f02a6074ad0 (diff)
downloadcoreclr-45798f661f8c8c042f3582cde8b611d1c9c7343f.tar.gz
coreclr-45798f661f8c8c042f3582cde8b611d1c9c7343f.tar.bz2
coreclr-45798f661f8c8c042f3582cde8b611d1c9c7343f.zip
ARM64: Enabling Crossgen End-to-End Mscorlib
Fixes https://github.com/dotnet/coreclr/issues/4350 Fixes https://github.com/dotnet/coreclr/issues/4615 This is a bit large change across VM/Zap/JIT to properly support crossgen scenario. 1. Fix incorrect `ldr` encoding with size. 2. Enforce JIT data following JIT code per method by allocating them together. This guarantees correct PC-relative encoding for such constant data access without fix-up. 3. For the general fix-up data acceess, use `adrp/add` instruction pairs with fix-ups. Two more relocations types are implemented in all sides. 4. Interface dispatch stub is now implemented which is needed for interface call for crossgen. I've verified hello world runs with mscorlib.ni.dll.
-rw-r--r--src/inc/clrnt.h8
-rw-r--r--src/inc/utilcode.h36
-rw-r--r--src/jit/codegenarm64.cpp9
-rw-r--r--src/jit/emit.cpp20
-rw-r--r--src/jit/emit.h1
-rw-r--r--src/jit/emitarm64.cpp120
-rw-r--r--src/jit/emitfmtsarm64.h2
-rw-r--r--src/jit/instrsarm64.h2
-rw-r--r--src/utilcode/util.cpp81
-rw-r--r--src/vm/arm64/asmhelpers.asm25
-rw-r--r--src/vm/arm64/stubs.cpp7
-rw-r--r--src/vm/jitinterface.cpp26
-rw-r--r--src/zap/zapinfo.cpp13
-rw-r--r--src/zap/zaprelocs.cpp27
14 files changed, 340 insertions, 37 deletions
diff --git a/src/inc/clrnt.h b/src/inc/clrnt.h
index 9adae3770f..c15bd48fa8 100644
--- a/src/inc/clrnt.h
+++ b/src/inc/clrnt.h
@@ -985,6 +985,14 @@ RtlVirtualUnwind(
#define IMAGE_REL_ARM64_BRANCH26 0x0003 // 26 bit offset << 2 & sign ext. for B & BL
#endif
+#ifndef IMAGE_REL_ARM64_PAGEBASE_REL21
+#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x0004 // ADRP 21 bit PC-relative page address
+#endif
+
+#ifndef IMAGE_REL_ARM64_PAGEOFFSET_12A
+#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x0006 // ADD 12 bit page offset
+#endif
+
#endif
#endif // CLRNT_H_
diff --git a/src/inc/utilcode.h b/src/inc/utilcode.h
index bb7c8bad81..fc3af0ecd5 100644
--- a/src/inc/utilcode.h
+++ b/src/inc/utilcode.h
@@ -4526,11 +4526,31 @@ void PutThumb2BlRel24(UINT16 * p, INT32 imm24);
INT32 GetArm64Rel28(UINT32 * pCode);
//*****************************************************************************
+// Extract the PC-Relative page address from an adrp instruction
+//*****************************************************************************
+INT32 GetArm64Rel21(UINT32 * pCode);
+
+//*****************************************************************************
+// Extract the page offset from an add instruction
+//*****************************************************************************
+INT32 GetArm64Rel12(UINT32 * pCode);
+
+//*****************************************************************************
// Deposit the PC-Relative offset 'imm28' into a b or bl instruction
//*****************************************************************************
void PutArm64Rel28(UINT32 * pCode, INT32 imm28);
//*****************************************************************************
+// Deposit the PC-Relative page address 'imm21' into an adrp instruction
+//*****************************************************************************
+void PutArm64Rel21(UINT32 * pCode, INT32 imm21);
+
+//*****************************************************************************
+// Deposit the page offset 'imm12' into an add instruction
+//*****************************************************************************
+void PutArm64Rel12(UINT32 * pCode, INT32 imm12);
+
+//*****************************************************************************
// Returns whether the offset fits into bl instruction
//*****************************************************************************
inline bool FitsInThumb2BlRel24(INT32 imm24)
@@ -4547,6 +4567,22 @@ inline bool FitsInRel28(INT32 val32)
}
//*****************************************************************************
+// Returns whether the offset fits into an Arm64 adrp instruction
+//*****************************************************************************
+inline bool FitsInRel21(INT32 val32)
+{
+ return (val32 >= 0) && (val32 <= 0x001FFFFF);
+}
+
+//*****************************************************************************
+// Returns whether the offset fits into an Arm64 add instruction
+//*****************************************************************************
+inline bool FitsInRel12(INT32 val32)
+{
+ return (val32 >= 0) && (val32 <= 0x00000FFF);
+}
+
+//*****************************************************************************
// Returns whether the offset fits into an Arm64 b or bl instruction
//*****************************************************************************
inline bool FitsInRel28(INT64 val64)
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index 7b1b2fa92b..bbc46db678 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.cpp
@@ -2157,12 +2157,11 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size,
{
size = EA_SIZE(size); // Strip any Reloc flags from size if we aren't doing relocs
}
-
+
if (EA_IS_RELOC(size))
{
- // Emit a data section constant for a relocatable integer constant.
- CORINFO_FIELD_HANDLE hnd = getEmitter()->emitLiteralConst(imm);
- getEmitter()->emitIns_R_C(INS_ldr, size, reg, hnd, 0);
+ // This emits a pair of adrp/add (two instructions) with fix-ups.
+ getEmitter()->emitIns_R_AI(INS_adrp, size, reg, imm);
}
else if (imm == 0)
{
@@ -2252,7 +2251,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types tar
// We must load the FP constant from the constant pool
// Emit a data section constant for the float or double constant.
CORINFO_FIELD_HANDLE hnd = emit->emitFltOrDblConst(dblConst);
- emit->emitIns_R_C(INS_ldr, size, targetReg, hnd, 0);
+ emit->emitIns_R_C(INS_ldr, size, targetReg, hnd, 0);
}
}
break;
diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp
index b9787fa7d6..a4a20693a4 100644
--- a/src/jit/emit.cpp
+++ b/src/jit/emit.cpp
@@ -4497,12 +4497,32 @@ unsigned emitter::emitEndCodeGen(Compiler *comp,
}
#endif
+#ifdef _TARGET_ARM64_
+ // For arm64, we want to allocate JIT data always adjacent to code similar to what native compiler does.
+ // This way allows us to use a single `ldr` to access such data like float constant/jmp table.
+ if (emitTotalColdCodeSize > 0)
+ {
+ // JIT data might be far away from the cold code.
+ NYI_ARM64("Need to handle fix-up to data from cold code.");
+ }
+
+ emitCmpHandle->allocMem(emitTotalHotCodeSize + emitConsDsc.dsdOffs, emitTotalColdCodeSize,
+ 0,
+ xcptnsCount,
+ allocMemFlag,
+ (void**)&codeBlock, (void**)&coldCodeBlock,
+ (void**)&consBlock);
+
+ consBlock = codeBlock + emitTotalHotCodeSize;
+
+#else
emitCmpHandle->allocMem( emitTotalHotCodeSize, emitTotalColdCodeSize,
emitConsDsc.dsdOffs,
xcptnsCount,
allocMemFlag,
(void**)&codeBlock, (void**)&coldCodeBlock,
(void**)&consBlock);
+#endif
// if (emitConsDsc.dsdOffs) printf("Cons=%08X\n", consBlock);
diff --git a/src/jit/emit.h b/src/jit/emit.h
index 95dac33536..67adcdf731 100644
--- a/src/jit/emit.h
+++ b/src/jit/emit.h
@@ -1131,6 +1131,7 @@ protected:
bool idIsDspReloc() const { assert(!idIsTiny()); return _idDspReloc != 0; }
void idSetIsDspReloc(bool val = true)
{ assert(!idIsTiny()); _idDspReloc = val; }
+ bool idIsReloc() { return idIsDspReloc() || idIsCnsReloc(); }
#endif
diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp
index e4ad5c77b8..2a0bf954b6 100644
--- a/src/jit/emitarm64.cpp
+++ b/src/jit/emitarm64.cpp
@@ -814,7 +814,7 @@ bool emitter::emitInsMayWriteToGCReg(instrDesc *id)
// These are the load/store formats with "target" registers:
- case IF_LS_1A: // LS_1A .X......iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
+ case IF_LS_1A: // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
case IF_LS_2A: // LS_2A .X.......X...... ......nnnnnttttt Rt Rn
case IF_LS_2B: // LS_2B .X.......Xiiiiii iiiiiinnnnnttttt Rt Rn imm(0-4095)
case IF_LS_2C: // LS_2C .X.......X.iiiii iiiiP.nnnnnttttt Rt Rn imm(-256..+255) pre/post inc
@@ -6314,6 +6314,9 @@ void emitter::emitIns_S_I (instruction ins,
/*****************************************************************************
*
* Add an instruction with a register + static member operands.
+ * Constant is stored into JIT data which is adjacent to code.
+ * No relocation is needed. PC-relative offset will be encoded directly into instruction.
+ *
*/
void emitter::emitIns_R_C (instruction ins,
emitAttr attr,
@@ -6321,11 +6324,6 @@ void emitter::emitIns_R_C (instruction ins,
CORINFO_FIELD_HANDLE fldHnd,
int offs)
{
-#if RELOC_SUPPORT
- // Static always need relocs
- if (!jitStaticFldIsGlobAddr(fldHnd))
- attr = EA_SET_FLG(attr, EA_DSP_RELOC_FLG);
-#endif
assert(offs >= 0);
assert(instrDesc::fitsInSmallCns(offs));
@@ -6350,7 +6348,6 @@ void emitter::emitIns_R_C (instruction ins,
}
fmt = IF_LS_1A;
break;
-
default:
break;
}
@@ -6369,7 +6366,6 @@ void emitter::emitIns_R_C (instruction ins,
dispIns(id);
appendToCurIG(id);
-
}
@@ -6413,12 +6409,61 @@ void emitter::emitIns_R_AR (instruction ins,
NYI("emitIns_R_AR");
}
-void emitter::emitIns_R_AI (instruction ins,
- emitAttr attr,
- regNumber ireg,
- ssize_t disp)
+// This computes address from the immediate which is relocatable.
+void emitter::emitIns_R_AI(instruction ins,
+ emitAttr attr,
+ regNumber ireg,
+ ssize_t addr)
{
- NYI("emitIns_R_AI");
+ assert(EA_IS_RELOC(attr));
+ emitAttr size = EA_SIZE(attr);
+ insFormat fmt = IF_DI_1E;
+ bool needAdd = false;
+ instrDescJmp* id = emitNewInstrJmp();
+
+ switch (ins)
+ {
+ case INS_adrp:
+ // This computes page address.
+ // page offset is needed using add.
+ needAdd = true;
+ break;
+ case INS_adr:
+ break;
+ default:
+ unreached();
+ }
+
+ id->idIns(ins);
+ id->idInsFmt(fmt);
+ id->idInsOpt(INS_OPTS_NONE);
+ id->idOpSize(size);
+ id->idAddr()->iiaAddr = (BYTE*)addr;
+ id->idReg1(ireg);
+ id->idSetIsDspReloc();
+
+ dispIns(id);
+ appendToCurIG(id);
+
+ if (needAdd)
+ {
+ // add reg, reg, imm
+ ins = INS_add;
+ fmt = IF_DI_2A;
+ instrDesc* id = emitAllocInstr(attr);
+ assert(id->idIsReloc());
+
+ id->idIns(ins);
+ id->idInsFmt(fmt);
+ id->idInsOpt(INS_OPTS_NONE);
+ id->idOpSize(size);
+ id->idAddr()->iiaAddr = (BYTE*)addr;
+ id->idReg1(ireg);
+ id->idReg2(ireg);
+
+ dispIns(id);
+ appendToCurIG(id);
+ }
}
void emitter::emitIns_AR_R (instruction ins,
@@ -7849,6 +7894,12 @@ BYTE* emitter::emitOutputLJ(insGroup *ig, BYTE *dst, instrDesc *i
dstAddr = emitDataOffsetToPtr(dataOffs);
dstOffs = (unsigned) ((ssize_t) (dstAddr - srcAddr) + srcOffs);
assert((dstOffs & 3) == 0);
+
+ // Failing the following assertion means the corresponding JIT data is not within +/-1MB range
+ // from the current code reference. This could happen for a large method or extremely large
+ // amount of JIT data for the method, or access it from cold method.
+ // Ideally, we should detect such case earlier to expand the code sequence using a fix-up
+ // similar to emitIns_R_AI.
assert(isValidSimm19(dstOffs));
}
else
@@ -7991,19 +8042,27 @@ BYTE* emitter::emitOutputLJ(insGroup *ig, BYTE *dst, instrDesc *i
}
else if (loadLabel)
{
- if (fmt == IF_LS_1A) // LS_1A XX......iiiiiiii iiiiiiiiiiittttt Rt simm21
+ if (fmt == IF_LS_1A) // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt simm21
{
// INS_ldr or INS_ldrsw (PC-Relative)
// Is the target a vector register?
if (isVectorRegister(id->idReg1()))
- {
- code &= 0x3FFFFFFF; // clear the size bits
- code |= insEncodeDatasizeVLS(code, id->idOpSize()); // XX
+ {
+ code |= insEncodeDatasizeVLS(code, id->idOpSize()); // XX V
code |= insEncodeReg_Vt(id->idReg1()); // ttttt
}
else
{
+ assert(isGeneralRegister(id->idReg1()));
+ // insEncodeDatasizeLS is not quite right for this case.
+ // So just specialize it.
+ if ((ins == INS_ldr) && (id->idOpSize() == EA_8BYTE))
+ {
+ // set the operation size in bit 30
+ code |= 0x40000000;
+ }
+
code |= insEncodeReg_Rt(id->idReg1()); // ttttt
}
@@ -8235,7 +8294,7 @@ size_t emitter::emitOutputInstr(insGroup *ig,
dst += emitOutputCall(ig, dst, id, code);
break;
- case IF_LS_1A: // LS_1A XX......iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
+ case IF_LS_1A: // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
assert(insOptsNone(id->idInsOpt()));
assert(id->idIsBound());
@@ -8428,9 +8487,19 @@ size_t emitter::emitOutputInstr(insGroup *ig,
case IF_DI_1E: // DI_1E .ii.....iiiiiiii iiiiiiiiiiiddddd Rd simm21
assert(insOptsNone(id->idInsOpt()));
- assert(id->idIsBound());
-
- dst = emitOutputLJ(ig, dst, id);
+ if (id->idIsReloc())
+ {
+ code = emitInsCode(ins, fmt);
+ code |= insEncodeReg_Rd(id->idReg1()); // ddddd
+ dst += emitOutput_Instr(dst, code);
+ emitRecordRelocation(odst, id->idAddr()->iiaAddr, IMAGE_REL_ARM64_PAGEBASE_REL21);
+ }
+ else
+ {
+ // Local jmp/load case which does not need a relocation.
+ assert(id->idIsBound());
+ dst = emitOutputLJ(ig, dst, id);
+ }
sz = sizeof(instrDescJmp);
break;
@@ -8461,6 +8530,13 @@ size_t emitter::emitOutputInstr(insGroup *ig,
code |= insEncodeReg_Rd(id->idReg1()); // ddddd
code |= insEncodeReg_Rn(id->idReg2()); // nnnnn
dst += emitOutput_Instr(dst, code);
+
+ if (id->idIsReloc())
+ {
+ assert(sz == sizeof(instrDesc));
+ assert(id->idAddr()->iiaAddr != nullptr);
+ emitRecordRelocation(odst, id->idAddr()->iiaAddr, IMAGE_REL_ARM64_PAGEOFFSET_12A);
+ }
break;
case IF_DI_2B: // DI_2B X.........Xnnnnn ssssssnnnnnddddd Rd Rn imm(0-63)
@@ -9920,7 +9996,7 @@ void emitter::emitDispIns(instrDesc * id,
emitDispReg(id->idReg3(), size, false);
break;
- case IF_LS_1A: // LS_1A XX......iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
+ case IF_LS_1A: // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
assert(insOptsNone(id->idInsOpt()));
emitDispReg(id->idReg1(), size, true);
imm = emitGetInsSC(id);
diff --git a/src/jit/emitfmtsarm64.h b/src/jit/emitfmtsarm64.h
index 722e48c580..06cde03f8c 100644
--- a/src/jit/emitfmtsarm64.h
+++ b/src/jit/emitfmtsarm64.h
@@ -121,7 +121,7 @@ IF_DEF(BI_1B, IS_NONE, JMP) // BI_1B B.......bbbbbiii
IF_DEF(BR_1A, IS_NONE, CALL) // BR_1A ................ ......nnnnn..... Rn ret
IF_DEF(BR_1B, IS_NONE, CALL) // BR_1B ................ ......nnnnn..... Rn br blr
-IF_DEF(LS_1A, IS_NONE, JMP) // LS_1A .X......iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
+IF_DEF(LS_1A, IS_NONE, JMP) // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB)
IF_DEF(LS_2A, IS_NONE, NONE) // LS_2A .X.......X...... ......nnnnnttttt Rt Rn
IF_DEF(LS_2B, IS_NONE, NONE) // LS_2B .X.......Xiiiiii iiiiiinnnnnttttt Rt Rn imm(0-4095)
IF_DEF(LS_2C, IS_NONE, NONE) // LS_2C .X.......X.iiiii iiiiP.nnnnnttttt Rt Rn imm(-256..+255) pre/post inc
diff --git a/src/jit/instrsarm64.h b/src/jit/instrsarm64.h
index 21fddc5fe7..51ec30e0db 100644
--- a/src/jit/instrsarm64.h
+++ b/src/jit/instrsarm64.h
@@ -91,7 +91,7 @@ INST5(ldr, "ldr", 0,LD, IF_EN5A, 0xB9400000, 0xB9400000, 0xB8400000,
// ldr Rt,[Xn+pimm12] LS_2B 1X11100101iiiiii iiiiiinnnnnttttt B940 0000 imm(0-4095<<{2,3})
// ldr Rt,[Xn+simm9] LS_2C 1X111000010iiiii iiiiPPnnnnnttttt B840 0000 [Xn imm(-256..+255) pre/post/no inc]
// ldr Rt,[Xn,(Rm,ext,shl)] LS_3A 1X111000011mmmmm oooS10nnnnnttttt B860 0800 [Xn, ext(Rm) LSL {0,2,3}]
- // ldr Vt/Rt,[PC+simm19<<2] LS_1A XX011000iiiiiiii iiiiiiiiiiittttt 1800 0000 [PC +- imm(1MB)]
+ // ldr Vt/Rt,[PC+simm19<<2] LS_1A XX011V00iiiiiiii iiiiiiiiiiittttt 1800 0000 [PC +- imm(1MB)]
INST5(ldrsw, "ldrsw", 0,LD, IF_EN5A, 0xB9800000, 0xB9800000, 0xB8800000, 0xB8A00800, 0x98000000)
// ldrsw Rt,[Xn] LS_2A 1011100110000000 000000nnnnnttttt B980 0000
diff --git a/src/utilcode/util.cpp b/src/utilcode/util.cpp
index f801f94b60..50643d4a7e 100644
--- a/src/utilcode/util.cpp
+++ b/src/utilcode/util.cpp
@@ -2684,6 +2684,41 @@ INT32 GetArm64Rel28(UINT32 * pCode)
}
//*****************************************************************************
+// Extract the PC-Relative offset from an adrp instruction
+//*****************************************************************************
+INT32 GetArm64Rel21(UINT32 * pCode)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT32 addInstr = *pCode;
+
+ // 23-5 bits for the high part. Shift it by 5.
+ INT32 immhi = (((INT32)(addInstr & 0xFFFFE0))) >> 5;
+ // 30,29 bits for the lower part. Shift it by 29.
+ INT32 immlo = ((INT32)(addInstr & 0x60000000)) >> 29;
+
+ // Merge them
+ INT32 imm21 = (immhi << 2) | immlo;
+
+ return imm21;
+}
+
+//*****************************************************************************
+// Extract the PC-Relative offset from an add instruction
+//*****************************************************************************
+INT32 GetArm64Rel12(UINT32 * pCode)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT32 addInstr = *pCode;
+
+ // 21-10 contains value. Mask 12 bits and shift by 10 bits.
+ INT32 imm12 = (INT32)(addInstr & 0x003FFC00) >> 10;
+
+ return imm12;
+}
+
+//*****************************************************************************
// Deposit the PC-Relative offset 'imm28' into a b or bl instruction
//*****************************************************************************
void PutArm64Rel28(UINT32 * pCode, INT32 imm28)
@@ -2706,6 +2741,52 @@ void PutArm64Rel28(UINT32 * pCode, INT32 imm28)
_ASSERTE(GetArm64Rel28(pCode) == imm28);
}
+//*****************************************************************************
+// Deposit the PC-Relative offset 'imm21' into an adrp instruction
+//*****************************************************************************
+void PutArm64Rel21(UINT32 * pCode, INT32 imm21)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInRel21(imm21));
+
+ UINT32 adrpInstr = *pCode;
+ // Check adrp opcode 1ii1 0000 ...
+ _ASSERTE((adrpInstr & 0x9F000000) == 0x90000000);
+
+ adrpInstr &= 0x9F00001F; // keep bits 31, 28-24, 4-0.
+ INT32 immlo = imm21 & 0x03; // Extract low 2 bits which will occupy 30-29 bits.
+ INT32 immhi = (imm21 & 0x1FFFFC) >> 2; // Extract high 19 bits which will occupy 23-5 bits.
+ adrpInstr |= ((immlo << 29) | (immhi << 5));
+
+ *pCode = adrpInstr; // write the assembled instruction
+
+ _ASSERTE(GetArm64Rel21(pCode) == imm21);
+}
+
+//*****************************************************************************
+// Deposit the PC-Relative offset 'imm12' into an add instruction
+//*****************************************************************************
+void PutArm64Rel12(UINT32 * pCode, INT32 imm12)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInRel12(imm12));
+
+ UINT32 addInstr = *pCode;
+ // Check add opcode 1001 0001 00...
+ _ASSERTE((addInstr & 0xFFC00000) == 0x91000000);
+
+ addInstr &= 0xFFC003FF; // keep bits 31-22, 9-0
+ addInstr |= (imm12 << 10); // Occupy 21-10.
+
+ *pCode = addInstr; // write the assembled instruction
+
+ _ASSERTE(GetArm64Rel12(pCode) == imm12);
+}
+
//---------------------------------------------------------------------
// Splits a command line into argc/argv lists, using the VC7 parsing rules.
//
diff --git a/src/vm/arm64/asmhelpers.asm b/src/vm/arm64/asmhelpers.asm
index 7c89f0f897..8bf4da6710 100644
--- a/src/vm/arm64/asmhelpers.asm
+++ b/src/vm/arm64/asmhelpers.asm
@@ -16,6 +16,7 @@
IMPORT PreStubWorker
IMPORT NDirectImportWorker
IMPORT VSD_ResolveWorker
+ IMPORT StubDispatchFixupWorker
IMPORT JIT_InternalThrow
IMPORT ComPreStubWorker
IMPORT COMToCLRWorker
@@ -1175,7 +1176,6 @@ Fail
NESTED_END
-
#ifdef FEATURE_READYTORUN
NESTED_ENTRY DelayLoad_MethodCall
@@ -1221,5 +1221,28 @@ Fail
DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj
#endif // FEATURE_READYTORUN
+#ifdef FEATURE_PREJIT
+;; ------------------------------------------------------------------
+;; void StubDispatchFixupStub(args in regs x0-x7 & stack, x11:IndirectionCellAndFlags, x12:DispatchToken)
+;;
+;; The stub dispatch thunk which transfers control to StubDispatchFixupWorker.
+ NESTED_ENTRY StubDispatchFixupStub
+
+ PROLOG_WITH_TRANSITION_BLOCK
+
+ add x0, sp, #__PWTB_TransitionBlock ; pTransitionBlock
+ and x1, x11, #-4 ; Indirection cell
+ mov x2, #0 ; sectionIndex
+ mov x3, #0 ; pModule
+ bl StubDispatchFixupWorker
+ mov x9, x0
+
+ EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+
+ EPILOG_BRANCH_REG x9
+
+ NESTED_END
+#endif
+
; Must be at very end of file
END \ No newline at end of file
diff --git a/src/vm/arm64/stubs.cpp b/src/vm/arm64/stubs.cpp
index c50fdca7f7..e1b8768e83 100644
--- a/src/vm/arm64/stubs.cpp
+++ b/src/vm/arm64/stubs.cpp
@@ -1046,13 +1046,6 @@ extern "C" void GenericComPlusCallStub(void)
}
#endif // FEATURE_COMINTEROP
-#ifdef FEATURE_PREJIT
-extern "C" void StubDispatchFixupStub()
-{
- _ASSERTE(!"ARM64:NYI");
-}
-#endif
-
//ARM64TODO: check if this should be amd64 and win64
#ifdef _WIN64
extern "C" void PInvokeStubForHostInner(DWORD dwStackSize, LPVOID pStackFrame, LPVOID pTarget)
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index cf0e456140..7b7c0713f5 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -10762,6 +10762,32 @@ void CEEJitInfo::recordRelocation(void * location,
PutArm64Rel28((UINT32*) fixupLocation, (INT32)delta);
}
break;
+
+ case IMAGE_REL_ARM64_PAGEBASE_REL21:
+ {
+ _ASSERTE(slot == 0);
+ _ASSERTE(addlDelta == 0);
+
+ // Write the 21 bits pc-relative page address into location.
+ INT64 targetPage = (INT64)target & 0xFFFFFFFFFFFFF000LL;
+ INT64 lcoationPage = (INT64)location & 0xFFFFFFFFFFFFF000LL;
+ INT64 relPage = (INT64)(targetPage - lcoationPage);
+ INT32 imm21 = (INT32)(relPage >> 12) & 0x1FFFFF;
+ PutArm64Rel21((UINT32 *)location, imm21);
+ }
+ break;
+
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A:
+ {
+ _ASSERTE(slot == 0);
+ _ASSERTE(addlDelta == 0);
+
+ // Write the 12 bits page offset into location.
+ INT32 imm12 = (INT32)target & 0xFFFLL;
+ PutArm64Rel12((UINT32 *)location, imm12);
+ }
+ break;
+
#endif // _TARGET_ARM64_
default:
diff --git a/src/zap/zapinfo.cpp b/src/zap/zapinfo.cpp
index 345a920902..2e0ddb569a 100644
--- a/src/zap/zapinfo.cpp
+++ b/src/zap/zapinfo.cpp
@@ -2469,6 +2469,8 @@ void ZapInfo::recordRelocation(void *location, void *target,
#if defined(_TARGET_ARM64_)
case IMAGE_REL_ARM64_BRANCH26:
+ case IMAGE_REL_ARM64_PAGEBASE_REL21:
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A:
break;
#endif
@@ -2583,6 +2585,17 @@ void ZapInfo::recordRelocation(void *location, void *target,
ThrowHR(COR_E_OVERFLOW);
PutArm64Rel28((UINT32 *)location, targetOffset);
break;
+ case IMAGE_REL_ARM64_PAGEBASE_REL21:
+ if (!FitsInRel21(targetOffset))
+ ThrowHR(COR_E_OVERFLOW);
+ PutArm64Rel21((UINT32 *)location, targetOffset);
+ break;
+
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A:
+ if (!FitsInRel12(targetOffset))
+ ThrowHR(COR_E_OVERFLOW);
+ PutArm64Rel12((UINT32 *)location, targetOffset);
+ break;
#endif
default:
diff --git a/src/zap/zaprelocs.cpp b/src/zap/zaprelocs.cpp
index dd21b09632..04708c2adb 100644
--- a/src/zap/zaprelocs.cpp
+++ b/src/zap/zaprelocs.cpp
@@ -120,6 +120,24 @@ void ZapBaseRelocs::WriteReloc(PVOID pSrc, int offset, ZapNode * pTarget, int ta
PutArm64Rel28((UINT32 *)pLocation,relOffset);
}
return;
+
+ case IMAGE_REL_ARM64_PAGEBASE_REL21:
+ {
+ TADDR pSitePage = ((TADDR)m_pImage->GetBaseAddress() + rva) & 0xFFFFFFFFFFFFF000LL;
+ TADDR pActualTargetPage = pActualTarget & 0xFFFFFFFFFFFFF000LL;
+
+ INT64 relPage = (INT64)(pActualTargetPage - pSitePage);
+ INT32 imm21 = (INT32)(relPage >> 12) & 0x1FFFFF;
+ PutArm64Rel21((UINT32 *)pLocation, imm21);
+ }
+ return;
+
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A:
+ {
+ INT32 imm12 = (INT32)(pActualTarget & 0xFFFLL);
+ PutArm64Rel12((UINT32 *)pLocation, imm12);
+ }
+ return;
#endif
default:
@@ -276,6 +294,15 @@ void ZapBlobWithRelocs::Save(ZapWriter * pZapWriter)
case IMAGE_REL_ARM64_BRANCH26:
targetOffset = (int)GetArm64Rel28((UINT32*)pLocation);
break;
+
+ case IMAGE_REL_ARM64_PAGEBASE_REL21:
+ targetOffset = (int)GetArm64Rel21((UINT32*)pLocation);
+ break;
+
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A:
+ targetOffset = (int)GetArm64Rel12((UINT32*)pLocation);
+ break;
+
#endif // defined(_TARGET_ARM64_)
default: