[RISC-V] Add DynamicHelpers to riscv64 stubs (#94735)
authorAleksandr Shaurtaev <38426614+ashaurtaev@users.noreply.github.com>
Wed, 22 Nov 2023 13:24:21 +0000 (16:24 +0300)
committerGleb Balykov <g.balykov@samsung.com>
Fri, 15 Dec 2023 12:28:32 +0000 (15:28 +0300)
* Add DynamicHelpers to riscv64 stubs

* Code review feedback

* Apply suggestions from code review

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
src/coreclr/jit/emitriscv64.h
src/coreclr/vm/riscv64/cgencpu.h
src/coreclr/vm/riscv64/stubs.cpp

index ce20775..ea9149e 100644 (file)
@@ -112,6 +112,12 @@ static bool isValidSimm20(ssize_t value)
     return -(((int)1) << 19) <= value && value < (((int)1) << 19);
 };
 
+// Returns true if 'value' is a legal unsigned immediate 20 bit encoding.
+static bool isValidUimm20(ssize_t value)
+{
+    return (0 == (value >> 20));
+};
+
 // Returns true if 'value' is a legal signed immediate 21 bit encoding.
 static bool isValidSimm21(ssize_t value)
 {
index 13b20a5..1a33214 100644 (file)
@@ -355,7 +355,12 @@ public:
     static bool isValidSimm12(int value) {
         return -( ((int)1) << 11 ) <= value && value < ( ((int)1) << 11 );
     }
-
+    static bool isValidSimm13(int value) {
+        return -(((int)1) << 12) <= value && value < (((int)1) << 12);
+    }
+    static bool isValidUimm20(int value) {
+        return (0 == (value >> 20));
+    }
     void EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall);
     void EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect);
 
index a308af6..af42aa8 100644 (file)
@@ -1248,6 +1248,29 @@ static unsigned RTypeInstr(unsigned opcode, unsigned funct3, unsigned funct7, un
     return opcode | (rd << 7) | (funct3 << 12) | (rs1 << 15) | (rs2 << 20) | (funct7 << 25);
 }
 
+static unsigned UTypeInstr(unsigned opcode, unsigned rd, int imm20)
+{
+    _ASSERTE(!(opcode >> 7));
+    _ASSERTE(!(rd >> 5));
+    _ASSERTE(StubLinkerCPU::isValidUimm20(imm20));
+    return opcode | (rd << 7) | (imm20 << 12);
+}
+
+static unsigned BTypeInstr(unsigned opcode, unsigned funct3, unsigned rs1, unsigned rs2, int imm13)
+{
+    _ASSERTE(!(opcode >> 7));
+    _ASSERTE(!(funct3 >> 3));
+    _ASSERTE(!(rs1 >> 5));
+    _ASSERTE(!(rs2 >> 5));
+    _ASSERTE(StubLinkerCPU::isValidSimm13(imm13));
+    _ASSERTE(IS_ALIGNED(imm13, 2));
+    int immLo1 = (imm13 >> 11) & 0x1;
+    int immLo4 = (imm13 >> 1) & 0xf;
+    int immHi6 = (imm13 >> 5) & 0x3f;
+    int immHi1 = (imm13 >> 12) & 0x1;
+    return opcode | (immLo4 << 8) | (funct3 << 12) | (rs1 << 15) | (rs2 << 20) | (immHi6 << 25) | (immLo1 << 7) | (immHi1 << 31);
+}
+
 void StubLinkerCPU::EmitLoad(IntReg dest, IntReg srcAddr, int offset)
 {
     Emit32(ITypeInstr(0x3, 0x3, dest, srcAddr, offset));  // ld
@@ -1507,14 +1530,22 @@ void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall)
 //
 // Allocation of dynamic helpers
 //
-
 #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR)
 
 #define BEGIN_DYNAMIC_HELPER_EMIT(size) \
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-#define END_DYNAMIC_HELPER_EMIT() \
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
+    SIZE_T cb = size; \
+    SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
+    BYTE * pStartRX = (BYTE *)(void*)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
+    ExecutableWriterHolder<BYTE> startWriterHolder(pStartRX, cbAligned); \
+    BYTE * pStart = startWriterHolder.GetRW(); \
+    size_t rxOffset = pStartRX - pStart; \
+    BYTE * p = pStart;
 
+#define END_DYNAMIC_HELPER_EMIT() \
+    _ASSERTE(pStart + cb == p); \
+    while (p < pStart + cbAligned) { *(DWORD*)p = 0xffffff0f/*badcode*/; p += 4; }\
+    ClrFlushInstructionCache(pStart, cbAligned); \
+    return (PCODE)pStart
 // Uses x8 as scratch register to store address of data label
 // After load x8 is increment to point to next data
 // only accepts positive offsets
@@ -1525,68 +1556,450 @@ static void LoadRegPair(BYTE* p, int reg1, int reg2, UINT32 offset)
 
 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    EmitHelperWithArg(p, rxOffset, pAllocator, arg, target);
+
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 // Caller must ensure sufficient byte are allocated including padding (if applicable)
 void DynamicHelpers::EmitHelperWithArg(BYTE*& p, size_t rxOffset, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
+    STANDARD_VM_CONTRACT;
+
+    const IntReg RegR0 = 0, RegT0 = 5, RegA0 = 10;
+
+    *(DWORD*)p = UTypeInstr(0x17, RegT0, 0);// auipc t0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA0, RegT0, 16);// ld a0, 16(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegT0, RegT0, 24);;// ld t0, 24(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegT0, 0);// jalr zero, 0(t0)
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // target
+    *(PCODE*)p = target;
+    p += 8;
 }
 
 PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    EmitHelperWithArg(p, rxOffset, pAllocator, arg, target);
+
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(48);
+
+    const IntReg RegR0 = 0, RegT0 = 5, RegA0 = 10, RegA1 = 11;
+
+    *(DWORD*)p = UTypeInstr(0x17, RegT0, 0);// auipc t0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA0, RegT0, 24);// ld a0, 24(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA1, RegT0, 32);// ld a1, 32(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegT0, RegT0, 40);// ld t0, 40(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegT0, 0);// jalr x0, 0(t0)
+    p += 4;
+
+    *(DWORD*)p = ITypeInstr(0x13, 0, RegR0, RegR0, 0);// nop, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // arg2
+    *(TADDR*)p = arg2;
+    p += 8;
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(40);
+
+    const IntReg RegR0 = 0, RegT0 = 5, RegA0 = 10, RegA1 = 11;
+
+    *(DWORD*)p = UTypeInstr(0x17, RegT0, 0);// auipc t0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x13, 0, RegA1, RegA0, 0);// addi a1, a0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA0, RegT0, 24);// ld a0, 24(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegT0, RegT0, 32);// ld t0, 32(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegT0, 0);// jalr x0, 0(t0)
+    p += 4;
+
+    *(DWORD*)p = ITypeInstr(0x13, 0, RegR0, RegR0, 0);// nop, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(4);
+
+    const IntReg RegR0 = 0, RegRa = 1;
+
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegRa, 0);// ret
+    p += 4;
+
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(24);
+
+    const IntReg RegR0 = 0, RegRa = 1, RegT0 = 5, RegA0 = 10;
+
+    *(DWORD*)p = UTypeInstr(0x17, RegT0, 0);// auipc t0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA0, RegT0, 16);// ld a0, 16(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegRa, 0);// ret
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x13, 0, RegR0, RegR0, 0);// nop, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    const IntReg RegR0 = 0, RegRa = 1, RegT0 = 5, RegA0 = 10;
+
+    *(DWORD*)p = UTypeInstr(0x17, RegT0, 0);// auipc t0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA0, RegT0, 24);// ld a0, 24(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA0, RegA0, 0);// ld a0,0(a0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x13, 0, RegA0, RegA0, offset & 0xfff);// addi a0, a0, offset
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegRa, 0);// ret
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x13, 0, RegR0, RegR0, 0);// nop, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    const IntReg RegR0 = 0, RegT0 = 5, RegA2 = 12;
+
+    *(DWORD*)p = UTypeInstr(0x17, RegT0, 0);// auipc t0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA2, RegT0, 16);// ld a2,16(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegT0, RegT0, 24);// ld t0,24(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegT0, 0);// jalr x0, 0(t0)
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(48);
+
+    const IntReg RegR0 = 0, RegT0 = 5, RegA2 = 12, RegA3 = 1;
+
+    *(DWORD*)p = UTypeInstr(0x17, RegT0, 0);// auipc t0, 0
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA2, RegT0, 24);// ld a2,24(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA3, RegT0, 32);// ld a3,32(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegT0, RegT0, 40);;// ld t0,40(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegT0, 0);// jalr x0, 0(t0)
+    p += 4;
+    *(DWORD*)p = ITypeInstr(0x13, 0, RegR0, RegR0, 0);// nop, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // arg2
+    *(TADDR*)p = arg2;
+    p += 8;
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+    END_DYNAMIC_HELPER_EMIT();
 }
 
 PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
 {
-    _ASSERTE(!"RISCV64: not implementation on riscv64!!!");
-    return NULL;
+    STANDARD_VM_CONTRACT;
+    const IntReg RegR0 = 0, RegA0 = 10, RegT2 = 7, RegT4 = 29, RegT5 = 30;
+
+    PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
+        GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
+        GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
+
+    GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
+    ExecutableWriterHolder<GenericHandleArgs> argsWriterHolder(pArgs, sizeof(GenericHandleArgs));
+    argsWriterHolder.GetRW()->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
+    argsWriterHolder.GetRW()->signature = pLookup->signature;
+    argsWriterHolder.GetRW()->module = (CORINFO_MODULE_HANDLE)pModule;
+
+    WORD slotOffset = (WORD)(dictionaryIndexAndSlot & 0xFFFF) * sizeof(Dictionary*);
+
+    // It's available only via the run-time helper function
+    if (pLookup->indirections == CORINFO_USEHELPER)
+    {
+        BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+        // a0 already contains generic context parameter
+        // reuse EmitHelperWithArg for below two operations
+        // a1 <- pArgs
+        // branch to helperAddress
+        EmitHelperWithArg(p, rxOffset, pAllocator, (TADDR)pArgs, helperAddress);
+
+        END_DYNAMIC_HELPER_EMIT();
+    }
+    else
+    {
+        int codeSize = 0;
+        int indirectionsDataSize = 0;
+        if (pLookup->testForNull || pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK)
+        {
+            //mv t2, a0
+            codeSize += 4;
+        }
+
+        for (WORD i = 0; i < pLookup->indirections; i++) {
+            _ASSERTE(pLookup->offsets[i] >= 0);
+            if (i == pLookup->indirections - 1 && pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK)
+            {
+                codeSize += (pLookup->sizeOffset > 2047 ? 24 : 16);
+                indirectionsDataSize += (pLookup->sizeOffset > 2047 ? 4 : 0);
+            }
+
+            codeSize += (pLookup->offsets[i] > 2047 ? 12 : 4); // if( > 2047) (12 bytes) else 4 bytes for instructions.
+            indirectionsDataSize += (pLookup->offsets[i] > 2047 ? 4 : 0); // 4 bytes for storing indirection offset values
+        }
+
+        codeSize += indirectionsDataSize ? 4 : 0; // auipc
+
+        if (pLookup->testForNull)
+        {
+            codeSize += 12; // beq-ret-addi
+
+            //padding for 8-byte align (required by EmitHelperWithArg)
+            codeSize = ALIGN_UP(codeSize, 8);
+
+            codeSize += 32; // size of EmitHelperWithArg
+        }
+        else
+        {
+            codeSize += 4; /* jalr */
+        }
+
+        // the offset value of data_label.
+        uint dataOffset = codeSize;
+
+        codeSize += indirectionsDataSize;
+
+        BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
+
+        BYTE * old_p = p;
+
+        if (indirectionsDataSize)
+        {
+            _ASSERTE(codeSize < 2047);
+
+            //auipc t4, 0
+            *(DWORD*)p = UTypeInstr(0x17, RegT4, 0);
+            p += 4;
+        }
+
+        if (pLookup->testForNull || pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK)
+        {
+            *(DWORD*)p = ITypeInstr(0x13, 0, RegT2, RegA0, 0);// addi t2, a0, 0
+            p += 4;
+        }
+
+        BYTE* pBLTCall = NULL;
+
+        for (WORD i = 0; i < pLookup->indirections; i++)
+        {
+            if (i == pLookup->indirections - 1 && pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK)
+            {
+                _ASSERTE(pLookup->testForNull && i > 0);
+
+                if (pLookup->sizeOffset > 2047)
+                {
+                    *(DWORD*)p = ITypeInstr(0x3, 0x2, RegT4, RegT4, dataOffset);// lw  t4, dataOffset(t4)
+                    p += 4;
+                    *(DWORD*)p = RTypeInstr(0x33, 0, 0, RegT5, RegA0, RegT4);// add  t5, a0, t4
+                    p += 4;
+                    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegT5, RegT5, 0);// ld  t5, 0(t5)
+                    p += 4;
+
+                    // move to next indirection offset data
+                    dataOffset += 4;
+                }
+                else
+                {
+                    *(DWORD*)p = ITypeInstr(0x3, 0x3, RegT5, RegA0, (UINT32)pLookup->sizeOffset);// ld  t5, #(pLookup->sizeOffset)(a0)
+                    p += 4;
+                }
+                // lui  t4, (slotOffset&0xfffff000)>>12
+                *(DWORD*)p = UTypeInstr(0x37, RegT4, (((UINT32)slotOffset & 0xfffff000) >> 12));
+                p += 4;
+                *(DWORD*)p = ITypeInstr(0x13, 0, RegT4, RegT4, slotOffset & 0xfff);// addi  t4, t4, (slotOffset&0xfff)
+                p += 4;
+                // blt  t4, t5, CALL HELPER
+                pBLTCall = p;       // Offset filled later
+                p += 4;
+            }
+
+            if (pLookup->offsets[i] > 2047)
+            {
+                _ASSERTE(dataOffset < 2047);
+                *(DWORD*)p = ITypeInstr(0x3, 0x2, RegT4, RegT4, dataOffset & 0xfff);// lw  t4, dataOffset(t4)
+                p += 4;
+                *(DWORD*)p = RTypeInstr(0x33, 0, 0, RegA0, RegA0, RegT4);// add  a0, a0, t4
+                p += 4;
+                *(DWORD*)p = ITypeInstr(0x3, 0x2, RegA0, RegA0, 0);// lw  a0, 0(a0)
+                p += 4;
+                // move to next indirection offset data
+                dataOffset += 4; // add 4 as next data is at 4 bytes from previous data
+            }
+            else
+            {
+                // offset must be 8 byte aligned
+                _ASSERTE((pLookup->offsets[i] & 0x7) == 0);
+                *(DWORD*)p = ITypeInstr(0x3, 0x3, RegA0, RegA0, (UINT32)pLookup->offsets[i]);// ld  a0, #(pLookup->offsets[i])(a0)
+                p += 4;
+            }
+        }
+
+        // No null test required
+        if (!pLookup->testForNull)
+        {
+            _ASSERTE(pLookup->sizeOffset == CORINFO_NO_SIZE_CHECK);
+            *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegRa, 0);// ret
+            p += 4;
+        }
+        else
+        {
+            // beq  a0, x0, CALL HELPER:
+            *(DWORD*)p = BTypeInstr(0x63, 0, RegA0, RegR0, 8);
+            p += 4;
+
+            *(DWORD*)p = ITypeInstr(0x67, 0, RegR0, RegRa, 0);// ret
+            p += 4;
+
+            // CALL HELPER:
+            if (pBLTCall != NULL)
+                *(DWORD*)pBLTCall = BTypeInstr(0x63, 0x4, RegT4, RegT5, (UINT32)(p - pBLTCall));
+
+            *(DWORD*)p = ITypeInstr(0x13, 0, RegA0, RegT2, 0);// addi  a0, t2, 0
+            p += 4;
+            if ((uintptr_t)(p - old_p) & 0x7)
+            {
+                // nop, padding for 8-byte align (required by EmitHelperWithArg)
+                *(DWORD*)p = ITypeInstr(0x13, 0, RegR0, RegR0, 0);
+                p += 4;
+            }
+
+            // reuse EmitHelperWithArg for below two operations
+            // a1 <- pArgs
+            // branch to helperAddress
+            EmitHelperWithArg(p, rxOffset, pAllocator, (TADDR)pArgs, helperAddress);
+        }
+
+        // data_label:
+        for (WORD i = 0; i < pLookup->indirections; i++)
+        {
+            if (i == pLookup->indirections - 1 && pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK && pLookup->sizeOffset > 2047)
+            {
+                *(UINT32*)p = (UINT32)pLookup->sizeOffset;
+                p += 4;
+            }
+            if (pLookup->offsets[i] > 2047)
+            {
+                *(UINT32*)p = (UINT32)pLookup->offsets[i];
+                p += 4;
+            }
+        }
+
+        END_DYNAMIC_HELPER_EMIT();
+    }
 }
 #endif // FEATURE_READYTORUN