Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LoongArch64] change instructions in IMAGE_REL_LOONGARCH64_PC & fix genCodeForJumpCompare #103399

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/coreclr/inc/utilcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -3311,6 +3311,26 @@ void PutArm64Rel21(UINT32 * pCode, INT32 imm21);
//*****************************************************************************
void PutArm64Rel12(UINT32 * pCode, INT32 imm12);

//*****************************************************************************
// Extract the PC-Relative page address and page offset from pcalau12i+add/ld
//*****************************************************************************
INT64 GetLoongArch64PC12(UINT32 * pCode);

//*****************************************************************************
// Extract the jump offset into pcaddu18i+jirl instructions
//*****************************************************************************
INT64 GetLoongArch64JIR(UINT32 * pCode);

//*****************************************************************************
// Deposit the PC-Relative page address and page offset into pcalau12i+add/ld
//*****************************************************************************
void PutLoongArch64PC12(UINT32 * pCode, INT64 imm);

//*****************************************************************************
// Deposit the jump offset into pcaddu18i+jirl instructions
//*****************************************************************************
void PutLoongArch64JIR(UINT32 * pCode, INT64 imm);

//*****************************************************************************
// Returns whether the offset fits into bl instruction
//*****************************************************************************
Expand Down
19 changes: 18 additions & 1 deletion src/coreclr/jit/codegenloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4007,7 +4007,24 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree)
unreached();
}

emit->emitIns_I_la(EA_PTRSIZE, REG_RA, imm);
GenTreeIntCon* con = op2->AsIntCon();

emitAttr attr = emitActualTypeSize(op2Type);
// TODO-CQ: Currently we cannot do this for all handles because of
// https://github.com/dotnet/runtime/issues/60712
if (con->ImmedValNeedsReloc(compiler))
{
attr = EA_SET_FLG(attr, EA_CNS_RELOC_FLG);
}

if (op2Type == TYP_BYREF)
{
attr = EA_SET_FLG(attr, EA_BYREF_FLG);
}

instGen_Set_Reg_To_Imm(attr, REG_RA, imm,
INS_FLAGS_DONT_CARE DEBUGARG(con->gtTargetHandle) DEBUGARG(con->gtFlags));
regSet.verifyRegUsed(REG_RA);
regs = (int)REG_RA << 5;
}
else
Expand Down
14 changes: 7 additions & 7 deletions src/coreclr/jit/emitloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2058,10 +2058,10 @@ void emitter::emitIns_R_AI(instruction ins,

// INS_OPTS_RELOC: placeholders. 2-ins:
// case:EA_HANDLE_CNS_RELOC
// pcaddu12i reg, off-hi-20bits
// pcalau12i reg, off-hi-20bits
// addi_d reg, reg, off-lo-12bits
// case:EA_PTR_DSP_RELOC
// pcaddu12i reg, off-hi-20bits
// pcalau12i reg, off-hi-20bits
// ld_d reg, reg, off-lo-12bits

instrDesc* id = emitNewInstr(attr);
Expand Down Expand Up @@ -3231,21 +3231,21 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
case INS_OPTS_RELOC:
{
// case:EA_HANDLE_CNS_RELOC
// pcaddu12i reg, off-hi-20bits
// pcalau12i reg, off-hi-20bits
// addi_d reg, reg, off-lo-12bits
// case:EA_PTR_DSP_RELOC
// pcaddu12i reg, off-hi-20bits
// pcalau12i reg, off-hi-20bits
// ld_d reg, reg, off-lo-12bits

regNumber reg1 = id->idReg1();

*(code_t*)dstRW = 0x1c000000 | (code_t)reg1;
*(code_t*)dstRW = 0x1a000000 | (code_t)reg1;

dstRW += 4;

#ifdef DEBUG
code = emitInsCode(INS_pcaddu12i);
assert(code == 0x1c000000);
code = emitInsCode(INS_pcalau12i);
assert(code == 0x1a000000);
code = emitInsCode(INS_addi_d);
assert(code == 0x02c00000);
code = emitInsCode(INS_ld_d);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public enum RelocType
IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13, // Thumb2: based B, BL
IMAGE_REL_BASED_THUMB_MOV32_PCREL = 0x14, // Thumb2: based MOVW/MOVT
IMAGE_REL_BASED_ARM64_BRANCH26 = 0x15, // Arm64: B, BL
IMAGE_REL_BASED_LOONGARCH64_PC = 0x16, // LoongArch64: pcaddu12i+imm12
IMAGE_REL_BASED_LOONGARCH64_PC = 0x16, // LoongArch64: pcalau12i+imm12
IMAGE_REL_BASED_LOONGARCH64_JIR = 0x17, // LoongArch64: pcaddu18i+jirl
IMAGE_REL_BASED_RISCV64_PC = 0x18, // RiscV64: auipc
IMAGE_REL_BASED_RELPTR32 = 0x7C, // 32-bit relative address from byte starting reloc
Expand Down Expand Up @@ -334,43 +334,39 @@ private static unsafe int GetLoongArch64PC12(uint* pCode)

// then get the low 12 bits,
pcInstr = *(pCode + 1);
imm += ((int)(((pcInstr >> 10) & 0xFFF) << 20)) >> 20;
imm |= (int)((pcInstr >> 10) & 0xFFF);

return imm;
}

// case:EA_HANDLE_CNS_RELOC
// pcaddu12i reg, off-hi-20bits
// pcalau12i reg, off-hi-20bits
// addi_d reg, reg, off-lo-12bits
// case:EA_PTR_DSP_RELOC
// pcaddu12i reg, off-hi-20bits
// pcalau12i reg, off-hi-20bits
// ld_d reg, reg, off-lo-12bits
private static unsafe void PutLoongArch64PC12(uint* pCode, long imm32)
private static unsafe void PutLoongArch64PC12(uint* pCode, long imm)
{
// Verify that we got a valid offset
Debug.Assert((int)imm32 == imm32);
Debug.Assert((int)imm == imm);

uint pcInstr = *pCode;

Debug.Assert((pcInstr & 0xFE000000) == 0x1c000000); // Must be pcaddu12i

int relOff = (int)imm32 & 0x800;
int imm = (int)imm32 + relOff;
relOff = ((imm & 0x7ff) - relOff) & 0xfff;
Debug.Assert((pcInstr & 0xFE000000) == 0x1a000000); // Must be pcalau12i

// Assemble the pc-relative high 20 bits of 'imm32' into the pcaddu12i instruction
pcInstr |= (uint)(((imm >> 12) & 0xFFFFF) << 5);
// Assemble the pc-relative high 20 bits of 'imm' into the pcalau12i instruction
pcInstr |= (uint)((imm >> 7) & 0x1FFFFE0);

*pCode = pcInstr; // write the assembled instruction

pcInstr = *(pCode + 1);

// Assemble the pc-relative low 12 bits of 'imm32' into the addid or ld instruction
pcInstr |= (uint)(relOff << 10);
// Assemble the pc-relative low 12 bits of 'imm' into the addid or ld instruction
pcInstr |= (uint)((imm & 0xFFF) << 10);

*(pCode + 1) = pcInstr; // write the assembled instruction

Debug.Assert(GetLoongArch64PC12(pCode) == imm32);
Debug.Assert(GetLoongArch64PC12(pCode) == imm);
}

private static unsafe long GetLoongArch64JIR(uint* pCode)
Expand Down Expand Up @@ -402,14 +398,14 @@ private static unsafe void PutLoongArch64JIR(uint* pCode, long imm38)
long imm = imm38 + relOff;
relOff = (((imm & 0x1ffff) - relOff) >> 2) & 0xffff;

// Assemble the pc-relative high 20 bits of 'imm38' into the pcaddu12i instruction
// Assemble the pc-relative high 20 bits of 'imm38' into the pcaddu18i instruction
pcInstr |= (uint)(((imm >> 18) & 0xFFFFF) << 5);

*pCode = pcInstr; // write the assembled instruction

pcInstr = *(pCode + 1);

// Assemble the pc-relative low 18 bits of 'imm38' into the addid or ld instruction
// Assemble the pc-relative low 18 bits of 'imm38' into the jirl instruction
pcInstr |= (uint)(relOff << 10);

*(pCode + 1) = pcInstr; // write the assembled instruction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@ public void EmitMOV(Register regDst, Register regSrc)
public void EmitMOV(Register regDst, ISymbolNode symbol)
{
Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_LOONGARCH64_PC);
// pcaddu12i reg, off-hi-20bits
Builder.EmitUInt(0x1c000000u | (uint)regDst);
// pcalau12i reg, off-hi-20bits
Builder.EmitUInt(0x1a000000u | (uint)regDst);

// addi_d reg, reg, off-lo-12bits
Builder.EmitUInt(0x02c00000u | (uint)(((uint)regDst << 5) | (uint)regDst));
}

// pcaddi regDst, 0
// pcalau12i regDst, 0
public void EmitPC(Register regDst)
{
Debug.Assert((uint)regDst > 0 && (uint)regDst < 32);
Builder.EmitUInt(0x18000000 | (uint)regDst);
Builder.EmitUInt(0x1a000000 | (uint)regDst);
}

// addi.d regDst, regSrc, imm12
Expand Down Expand Up @@ -93,7 +93,7 @@ public void EmitJMP(ISymbolNode symbol)
{
if (symbol.RepresentsIndirectionCell)
{
// pcaddi R21, 0
// pcalau12i R21, 0
EmitPC(Register.R21);

EmitLD(Register.R21, Register.R21, 0x10);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ public void ProcessRelocation(RelocType relocationType, int sourceRVA, int targe
}

case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC:
{
relocationLength = 8;
delta = (int)(targetRVA - (sourceRVA & ~0xfff) + ((targetRVA & 0x800) << 1));
break;
}
case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR:
{
relocationLength = 8;
Expand Down
16 changes: 8 additions & 8 deletions src/coreclr/tools/r2rdump/CoreDisTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1416,15 +1416,15 @@ private void ProbeLoongArch64Quirks(RuntimeFunction rtf, int imageOffset, int rt
else if (IsLoongArch64JirlRAInstruction(instr, out uint rj, out int imm))
{
// Common Pattern:
// pcaddu12i
// pcalau12i
// ld.d
// jirl ra, rj, 0
//
// pcaddu12i
// pcalau12i
// addi.d
// ld.d
// jirl ra, rj, 0
// There may exist some irrelevant instructions between pcaddu12i and jirl.
// There may exist some irrelevant instructions between pcalau12i and jirl.
// We need to find relevant instructions based on rj to calculate the jump address.
uint register = rj;
int immediate = imm;
Expand All @@ -1444,11 +1444,11 @@ private void ProbeLoongArch64Quirks(RuntimeFunction rtf, int imageOffset, int rt
immediate += imm;
}
}
else if (IsLoongArch64Pcaddu12iInstruction(instr, out rd, out imm))
else if (IsLoongArch64Pcalau12iInstruction(instr, out rd, out imm))
{
if (rd == register)
{
immediate += currentPC + imm;
immediate += (currentPC & ~0xfff) + imm;
isFound = true;
break;
}
Expand Down Expand Up @@ -1549,14 +1549,14 @@ private bool IsLoongArch64JirlRAInstruction(uint ins, out uint rj, out int offs)
}

/// <summary>
/// Determine whether a given instruction is a PCADDU12I.
/// Determine whether a given instruction is a PCALAU12I.
/// </summary>
/// <param name="ins">Assembly code of instruction</param>
private bool IsLoongArch64Pcaddu12iInstruction(uint ins, out uint rd, out int imm)
private bool IsLoongArch64Pcalau12iInstruction(uint ins, out uint rd, out int imm)
{
rd = 0;
imm = 0;
if (((ins >> 25) & 0x3f) == 0xe)
if (((ins >> 25) & 0x3f) == 0xd)
{
rd = ins & 0x1f;
imm = (int)((ins >> 5) & 0xfffff) << 12;
Expand Down
94 changes: 94 additions & 0 deletions src/coreclr/utilcode/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2286,6 +2286,100 @@ void PutArm64Rel12(UINT32 * pCode, INT32 imm12)
_ASSERTE(GetArm64Rel12(pCode) == imm12);
}

//*****************************************************************************
// Extract the PC-Relative page address and page offset from pcalau12i+add/ld
//*****************************************************************************
INT64 GetLoongArch64PC12(UINT32 * pCode)
{
UINT32 pcInstr = *pCode;

// first get the high 20 bits,
INT64 imm = (INT64)(((pcInstr >> 5) & 0xFFFFF) << 12);

// then get the low 12 bits,
pcInstr = *(pCode + 1);
imm |= (INT64)((pcInstr >> 10) & 0xFFF);

return imm;
}

//*****************************************************************************
// Extract the jump offset into pcaddu18i+jirl instructions
//*****************************************************************************
INT64 GetLoongArch64JIR(UINT32 * pCode)
{
UINT32 pcInstr = *pCode;

// first get the high 20 bits,
INT64 imm = ((INT64)((pcInstr >> 5) & 0xFFFFF) << 18);

// then get the low 18 bits
pcInstr = *(pCode + 1);
imm += ((INT64)((INT16)((pcInstr >> 10) & 0xFFFF))) << 2;

return imm;
}

//*****************************************************************************
// Deposit the PC-Relative page address and page offset into pcalau12i+add/ld
//*****************************************************************************
void PutLoongArch64PC12(UINT32 * pCode, INT64 imm)
{
// Verify that we got a valid offset
_ASSERTE((INT32)imm == imm);

UINT32 pcInstr = *pCode;

_ASSERTE((pcInstr & 0xFE000000) == 0x1a000000); // Must be pcalau12i

// Assemble the pc-relative high 20 bits of 'imm' into the pcalau12i instruction
pcInstr |= (UINT32)((imm >> 7) & 0x1FFFFE0);

*pCode = pcInstr; // write the assembled instruction

pcInstr = *(pCode + 1);

// Assemble the pc-relative low 12 bits of 'imm' into the addid or ld instruction
pcInstr |= (UINT32)((imm & 0xFFF) << 10);

*(pCode + 1) = pcInstr; // write the assembled instruction

_ASSERTE(GetLoongArch64PC12(pCode) == imm);
}

//*****************************************************************************
// Deposit the jump offset into pcaddu18i+jirl instructions
//*****************************************************************************
void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38)
{
// Verify that we got a valid offset
_ASSERTE((imm38 >= -0x2000000000L) && (imm38 < 0x2000000000L));

_ASSERTE((imm38 & 0x3) == 0); // the low two bits must be zero

UINT32 pcInstr = *pCode;

_ASSERTE(pcInstr == 0x1e00000e); // Must be pcaddu18i R14, 0

INT64 relOff = imm38 & 0x20000;
INT64 imm = imm38 + relOff;
relOff = (((imm & 0x1ffff) - relOff) >> 2) & 0xffff;

// Assemble the pc-relative high 20 bits of 'imm38' into the pcaddu18i instruction
pcInstr |= (UINT32)(((imm >> 18) & 0xFFFFF) << 5);

*pCode = pcInstr; // write the assembled instruction

pcInstr = *(pCode + 1);

// Assemble the pc-relative low 18 bits of 'imm38' into the jirl instruction
pcInstr |= (UINT32)(relOff << 10);

*(pCode + 1) = pcInstr; // write the assembled instruction

_ASSERTE(GetLoongArch64JIR(pCode) == imm38);
}

//======================================================================
// This function returns true, if it can determine that the instruction pointer
// refers to a code address that belongs in the range of the given image.
Expand Down
22 changes: 22 additions & 0 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11421,6 +11421,28 @@ void CEEJitInfo::recordRelocation(void * location,

#endif // TARGET_ARM64

#ifdef TARGET_LOONGARCH64
case IMAGE_REL_LOONGARCH64_PC:
{
_ASSERTE(addlDelta == 0);

INT64 imm = (INT64)target - ((INT64)location & 0xFFFFFFFFFFFFF000LL);
imm += ((INT64)target & 0x800) << 1;
PutLoongArch64PC12((UINT32 *)locationRW, imm);
}
break;

case IMAGE_REL_LOONGARCH64_JIR:
{
_ASSERTE(addlDelta == 0);

INT64 imm = (INT64)target - (INT64)location;
PutLoongArch64JIR((UINT32 *)locationRW, imm);
}
break;

#endif // TARGET_LOONGARCH64

default:
_ASSERTE(!"Unknown reloc type");
break;
Expand Down
Loading