Skip to content

[APX] fix bugs in NDD code gen #115809

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion src/coreclr/jit/emit.h
Original file line number Diff line number Diff line change
Expand Up @@ -2356,7 +2356,7 @@ class emitter
#endif // TARGET_ARM

insUpdateModes emitInsUpdateMode(instruction ins);
insFormat emitInsModeFormat(instruction ins, insFormat base);
insFormat emitInsModeFormat(instruction ins, insFormat base, bool useNDD = false);

static const BYTE emitInsModeFmtTab[];
#ifdef DEBUG
Expand Down
144 changes: 86 additions & 58 deletions src/coreclr/jit/emitxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3839,12 +3839,55 @@ unsigned const emitter::emitInsModeFmtCnt = ArrLen(emitInsModeFmtTab);
* Combine the given base format with the update mode of the instruction.
*/

inline emitter::insFormat emitter::emitInsModeFormat(instruction ins, insFormat base)
inline emitter::insFormat emitter::emitInsModeFormat(instruction ins, insFormat base, bool useNDD)
{
assert(IF_RRD + IUM_RD == IF_RRD);
assert(IF_RRD + IUM_WR == IF_RWR);
assert(IF_RRD + IUM_RW == IF_RRW);

#ifdef TARGET_AMD64
if (useNDD)
{
assert(IsApxNDDEncodableInstruction(ins));
if (ins == INS_rcl_N || ins == INS_rcr_N || ins == INS_rol_N || ins == INS_ror_N || ins == INS_shl_N ||
ins == INS_shr_N || ins == INS_sar_N)
{
// shift instructions has its own instruction format.
return IF_RWR_RRD_SHF;
}
// NDD instructions have the following operand encoding:
// 1. (w, r, r) for binary
// 2. (w, r) for unary
// These format are not properly tracked by the original IF table, we need to fix them here.
switch (base)
{
case IF_RRD_RRD_RRD:
return IF_RWR_RRD_RRD;

case IF_RRD_RRD_ARD:
return IF_RWR_RRD_ARD;

case IF_RRD_RRD_CNS:
return IF_RWR_RRD_CNS;

case IF_RRD_RRD_SRD:
return IF_RWR_RRD_SRD;

// we only apply NDD on unary instructions with register uses.
case IF_RRD_RRD:
return IF_RWR_RRD;

default:
// There should not be any other instruction format with NDD feature.
unreached();
}
}
else
{
return (insFormat)(base + emitInsUpdateMode(ins));
}
#endif

return (insFormat)(base + emitInsUpdateMode(ins));
}

Expand Down Expand Up @@ -6487,7 +6530,9 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
}
else
{
fmt = useNDD ? emitInsModeFormat(ins, IF_RWR_RRD_ARD) : emitInsModeFormat(ins, IF_RRD_ARD);
// NDD allow instructions have different register on dst and src1.
insFormat base = useNDD ? IF_RRD_RRD_ARD : IF_RRD_ARD;
fmt = emitInsModeFormat(ins, base, useNDD);
}
}
else
Expand Down Expand Up @@ -7800,7 +7845,8 @@ bool emitter::EmitMovsxAsCwde(instruction ins, emitAttr size, regNumber dst, reg
// srcReg -- The source register
// canSkip -- true if the move can be elided when dstReg == srcReg, otherwise false
//
void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regNumber srcReg, bool canSkip)
bool emitter::emitIns_Mov(
instruction ins, emitAttr attr, regNumber dstReg, regNumber srcReg, bool canSkip, bool useApxNdd)
{
// Only move instructions can use emitIns_Mov
assert(IsMovInstruction(ins));
Expand Down Expand Up @@ -7889,14 +7935,24 @@ void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regN

if (IsRedundantMov(ins, fmt, attr, dstReg, srcReg, canSkip))
{
return;
// Move is redundant, no need to emit anything
return false;
}

if (EmitMovsxAsCwde(ins, size, dstReg, srcReg))
{
return;
// Move is redundant, no need to emit anything
return false;
}

#ifdef TARGET_AMD64
if (useApxNdd)
{
// Move is required, but the caller is APX NDD aware
return true;
}
#endif // TARGET_AMD64

instrDesc* id = emitNewInstrSmall(attr);
id->idIns(ins);
id->idInsFmt(fmt);
Expand All @@ -7908,6 +7964,9 @@ void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regN

dispIns(id);
emitCurIGsize += sz;

// Move is required and the caller is not APX NDD aware, we emitted the move
return true;
}

/*****************************************************************************
Expand All @@ -7923,13 +7982,16 @@ void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNum
emitIns_Mov(ins, attr, reg1, reg2, /* canSkip */ false);
}

// Checking EVEX.ND and NDD compatibility together in case the ND slot is overridden by other features.
bool useNDD = ((instOptions & INS_OPTS_EVEX_nd_MASK) != 0) && IsApxNDDEncodableInstruction(ins);

emitAttr size = EA_SIZE(attr);

assert(size <= EA_64BYTE);
noway_assert(emitVerifyEncodable(ins, size, reg1, reg2));

/* Special case: "XCHG" uses a different format */
insFormat fmt = (ins == INS_xchg) ? IF_RRW_RRW : emitInsModeFormat(ins, IF_RRD_RRD);
insFormat fmt = (ins == INS_xchg) ? IF_RRW_RRW : emitInsModeFormat(ins, IF_RRD_RRD, useNDD);

instrDesc* id = emitNewInstrSmall(attr);
id->idIns(ins);
Expand All @@ -7941,11 +8003,6 @@ void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNum
SetEvexNfIfNeeded(id, instOptions);
SetEvexDFVIfNeeded(id, instOptions);

if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins))
{
id->idInsFmt(IF_RWR_RRD);
}

if ((instOptions & INS_OPTS_EVEX_b_MASK) != INS_OPTS_NONE)
{
// if EVEX.b needs to be set in this path, then it should be embedded rounding.
Expand Down Expand Up @@ -7977,8 +8034,11 @@ void emitter::emitIns_R_R_I(

instrDesc* id = emitNewInstrSC(attr, ival);

// Checking EVEX.ND and NDD compatibility together in case the ND slot is overridden by other features.
bool useNDD = ((instOptions & INS_OPTS_EVEX_nd_MASK) != 0) && IsApxNDDEncodableInstruction(ins);

id->idIns(ins);
id->idInsFmt(emitInsModeFormat(ins, IF_RRD_RRD_CNS));
id->idInsFmt(emitInsModeFormat(ins, IF_RRD_RRD_CNS, useNDD));
id->idReg1(reg1);
id->idReg2(reg2);

Expand All @@ -8001,31 +8061,6 @@ void emitter::emitIns_R_R_I(
SetEvexEmbMaskIfNeeded(id, instOptions);
SetEvexNdIfNeeded(id, instOptions);

if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins))
{
// need to fix the instruction opcode for legacy instructions as they has different opcode for RI form.
code = insCodeMI(ins);
// need to fix the instructions format for NDD legacy instructions.
insFormat fmt;
switch (ins)
{
case INS_shl_N:
case INS_shr_N:
case INS_sar_N:
case INS_ror_N:
case INS_rol_N:
case INS_rcr_N:
case INS_rcl_N:
fmt = IF_RWR_RRD_SHF;
break;

default:
fmt = IF_RWR_RRD_CNS;
break;
}
id->idInsFmt(fmt);
}

UNATIVE_OFFSET sz = emitInsSizeRR(id, code, ival);
id->idCodeSize(sz);

Expand Down Expand Up @@ -8397,9 +8432,12 @@ void emitter::emitIns_R_R_R(
assert(IsSimdInstruction(ins) || IsApxExtendedEvexInstruction(ins));
assert(IsThreeOperandAVXInstruction(ins) || IsKInstruction(ins) || IsApxExtendedEvexInstruction(ins));

// Checking EVEX.ND and NDD compatibility together in case the ND slot is overridden by other features.
bool useNDD = ((instOptions & INS_OPTS_EVEX_nd_MASK) != 0) && IsApxNDDEncodableInstruction(ins);

instrDesc* id = emitNewInstr(attr);
id->idIns(ins);
id->idInsFmt((ins == INS_mulx) ? IF_RWR_RWR_RRD : emitInsModeFormat(ins, IF_RRD_RRD_RRD));
id->idInsFmt((ins == INS_mulx) ? IF_RWR_RWR_RRD : emitInsModeFormat(ins, IF_RRD_RRD_RRD, useNDD));
id->idReg1(targetReg);
id->idReg2(reg1);
id->idReg3(reg2);
Expand All @@ -8414,12 +8452,6 @@ void emitter::emitIns_R_R_R(
SetEvexNdIfNeeded(id, instOptions);
SetEvexNfIfNeeded(id, instOptions);

if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins))
{
// need to fix the instructions format for NDD legacy instructions.
id->idInsFmt(IF_RWR_RRD_RRD);
}

UNATIVE_OFFSET sz = emitInsSizeRR(id, insCodeRM(ins));
id->idCodeSize(sz);

Expand All @@ -8435,8 +8467,11 @@ void emitter::emitIns_R_R_S(

instrDesc* id = emitNewInstr(attr);

// Checking EVEX.ND and NDD compatibility together in case the ND slot is overridden by other features.
bool useNDD = ((instOptions & INS_OPTS_EVEX_nd_MASK) != 0) && IsApxNDDEncodableInstruction(ins);

id->idIns(ins);
id->idInsFmt((ins == INS_mulx) ? IF_RWR_RWR_SRD : emitInsModeFormat(ins, IF_RRD_RRD_SRD));
id->idInsFmt((ins == INS_mulx) ? IF_RWR_RWR_SRD : emitInsModeFormat(ins, IF_RRD_RRD_SRD, useNDD));
id->idReg1(reg1);
id->idReg2(reg2);
id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
Expand All @@ -8445,11 +8480,6 @@ void emitter::emitIns_R_R_S(
SetEvexEmbMaskIfNeeded(id, instOptions);
SetEvexNdIfNeeded(id, instOptions);

if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins))
{
id->idInsFmt(IF_RWR_RRD_SRD);
}

#ifdef DEBUG
id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs;
#endif
Expand Down Expand Up @@ -10642,17 +10672,15 @@ regNumber emitter::emitIns_BASE_R_R_RM(
regNumber r = REG_NA;
assert(regOp->isUsedFromReg());

if (DoJitUseApxNDD(ins) && regOp->GetRegNum() != targetReg)
{
r = emitInsBinary(ins, attr, regOp, rmOp, targetReg);
}
else
bool useApxNdd = DoJitUseApxNDD(ins);

if (emitIns_Mov(INS_mov, attr, targetReg, regOp->GetRegNum(), true, useApxNdd) && useApxNdd)
{
emitIns_Mov(INS_mov, attr, targetReg, regOp->GetRegNum(), /*canSkip*/ true);
r = emitInsBinary(ins, attr, treeNode, rmOp);
return emitInsBinary(ins, attr, regOp, rmOp, targetReg);
}

return r;
return emitInsBinary(ins, attr, treeNode, rmOp);
;
}

//----------------------------------------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/emitxarch.h
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,8 @@ void emitIns_R_I(instruction ins,
insOpts instOptions = INS_OPTS_NONE DEBUGARG(size_t targetHandle = 0)
DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));

void emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regNumber srgReg, bool canSkip);
bool emitIns_Mov(
instruction ins, emitAttr attr, regNumber dstReg, regNumber srgReg, bool canSkip, bool useApxNdd = false);

void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts instOptions = INS_OPTS_NONE);

Expand Down
Loading