Skip to content

Commit 9924705

Browse files
Optimize Vector64 and Vector128.Create methods (#36267)
* Updating the Vector64 and Vector128 Create methods to be marked Intrinsic on ARM64 * Updating the JIT to emit constants for Vector64 and Vector128.Create * Fixing lookupNamedIntrinsic and impIntrinsic to throw PNSE for unsupported mustExpand intrinsics * Fixing impIntrinsic to directly use gtNewMustThrowException * Move gtNewMustThrowException to not depend on FEATURE_HW_INTRINSICS * Applying formatting patch * Add basic support for GT_CLS_VAR_ADDR to the ARM64 JIT * Update lookupNamedIntrinsic to handle System.Runtime.Intrinsics for unsupported platforms * Fixing INS_ldr in emitIns_R_C to use isValidVectorLSDatasize * Elaborate on why we specially recognize the HWIntrinsics even on platforms that don't support them
1 parent b221687 commit 9924705

File tree

16 files changed

+898
-533
lines changed

16 files changed

+898
-533
lines changed

src/coreclr/src/jit/compiler.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,14 +2657,15 @@ class Compiler
26572657
NamedIntrinsic hwIntrinsicID);
26582658
GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode(
26592659
var_types type, GenTree* op1, GenTree* op2, GenTree* op3, NamedIntrinsic hwIntrinsicID);
2660-
GenTree* gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd);
26612660
CORINFO_CLASS_HANDLE gtGetStructHandleForHWSIMD(var_types simdType, var_types simdBaseType);
26622661
var_types getBaseTypeFromArgIfNeeded(NamedIntrinsic intrinsic,
26632662
CORINFO_CLASS_HANDLE clsHnd,
26642663
CORINFO_SIG_INFO* sig,
26652664
var_types baseType);
26662665
#endif // FEATURE_HW_INTRINSICS
26672666

2667+
GenTree* gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd);
2668+
26682669
GenTreeLclFld* gtNewLclFldNode(unsigned lnum, var_types type, unsigned offset);
26692670
GenTree* gtNewInlineCandidateReturnExpr(GenTree* inlineCandidate, var_types type);
26702671

@@ -3713,17 +3714,17 @@ class Compiler
37133714
CorInfoIntrinsics intrinsicID,
37143715
bool tailCall);
37153716
NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method);
3717+
GenTree* impUnsupportedNamedIntrinsic(unsigned helper,
3718+
CORINFO_METHOD_HANDLE method,
3719+
CORINFO_SIG_INFO* sig,
3720+
bool mustExpand);
37163721

37173722
#ifdef FEATURE_HW_INTRINSICS
37183723
GenTree* impHWIntrinsic(NamedIntrinsic intrinsic,
37193724
CORINFO_CLASS_HANDLE clsHnd,
37203725
CORINFO_METHOD_HANDLE method,
37213726
CORINFO_SIG_INFO* sig,
37223727
bool mustExpand);
3723-
GenTree* impUnsupportedHWIntrinsic(unsigned helper,
3724-
CORINFO_METHOD_HANDLE method,
3725-
CORINFO_SIG_INFO* sig,
3726-
bool mustExpand);
37273728
GenTree* impSimdAsHWIntrinsic(NamedIntrinsic intrinsic,
37283729
CORINFO_CLASS_HANDLE clsHnd,
37293730
CORINFO_METHOD_HANDLE method,

src/coreclr/src/jit/emitarm64.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7548,7 +7548,7 @@ void emitter::emitIns_R_C(
75487548
fmt = IF_LARGELDC;
75497549
if (isVectorRegister(reg))
75507550
{
7551-
assert(isValidScalarDatasize(size));
7551+
assert(isValidVectorLSDatasize(size));
75527552
// For vector (float/double) register, we should have an integer address reg to
75537553
// compute long address which consists of page address and page offset.
75547554
// For integer constant, this is not needed since the dest reg can be used to
@@ -7561,6 +7561,7 @@ void emitter::emitIns_R_C(
75617561
assert(isValidGeneralDatasize(size));
75627562
}
75637563
break;
7564+
75647565
default:
75657566
unreached();
75667567
}
@@ -12718,7 +12719,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
1271812719

1271912720
if (addr->isContained())
1272012721
{
12721-
assert(addr->OperGet() == GT_LCL_VAR_ADDR || addr->OperGet() == GT_LEA);
12722+
assert(addr->OperGet() == GT_CLS_VAR_ADDR || addr->OperGet() == GT_LCL_VAR_ADDR || addr->OperGet() == GT_LEA);
1272212723

1272312724
int offset = 0;
1272412725
DWORD lsl = 0;
@@ -12795,7 +12796,13 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
1279512796
}
1279612797
else // no Index register
1279712798
{
12798-
if (emitIns_valid_imm_for_ldst_offset(offset, emitTypeSize(indir->TypeGet())))
12799+
if (addr->OperGet() == GT_CLS_VAR_ADDR)
12800+
{
12801+
// Get a temp integer register to compute long address.
12802+
regNumber addrReg = indir->GetSingleTempReg();
12803+
emitIns_R_C(ins, attr, dataReg, addrReg, addr->AsClsVar()->gtClsVarHnd, 0);
12804+
}
12805+
else if (emitIns_valid_imm_for_ldst_offset(offset, emitTypeSize(indir->TypeGet())))
1279912806
{
1280012807
// Then load/store dataReg from/to [memBase + offset]
1280112808
emitIns_R_R_I(ins, attr, dataReg, memBase->GetRegNum(), offset);

src/coreclr/src/jit/gentree.cpp

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18659,40 +18659,6 @@ GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(
1865918659
GenTreeHWIntrinsic(type, gtNewArgList(op1, op2, op3), hwIntrinsicID, TYP_UNKNOWN, 0);
1866018660
}
1866118661

18662-
//---------------------------------------------------------------------------------------
18663-
// gtNewMustThrowException:
18664-
// create a throw node (calling into JIT helper) that must be thrown.
18665-
// The result would be a comma node: COMMA(jithelperthrow(void), x) where x's type should be specified.
18666-
//
18667-
// Arguments
18668-
// helper - JIT helper ID
18669-
// type - return type of the node
18670-
//
18671-
// Return Value
18672-
// pointer to the throw node
18673-
//
18674-
GenTree* Compiler::gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd)
18675-
{
18676-
GenTreeCall* node = gtNewHelperCallNode(helper, TYP_VOID);
18677-
node->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN;
18678-
if (type != TYP_VOID)
18679-
{
18680-
unsigned dummyTemp = lvaGrabTemp(true DEBUGARG("dummy temp of must thrown exception"));
18681-
if (type == TYP_STRUCT)
18682-
{
18683-
lvaSetStruct(dummyTemp, clsHnd, false);
18684-
type = lvaTable[dummyTemp].lvType; // struct type is normalized
18685-
}
18686-
else
18687-
{
18688-
lvaTable[dummyTemp].lvType = type;
18689-
}
18690-
GenTree* dummyNode = gtNewLclvNode(dummyTemp, type);
18691-
return gtNewOperNode(GT_COMMA, type, node, dummyNode);
18692-
}
18693-
return node;
18694-
}
18695-
1869618662
// Returns true for the HW Instrinsic instructions that have MemoryLoad semantics, false otherwise
1869718663
bool GenTreeHWIntrinsic::OperIsMemoryLoad() const
1869818664
{
@@ -18782,6 +18748,40 @@ bool GenTreeHWIntrinsic::OperIsMemoryLoadOrStore() const
1878218748

1878318749
#endif // FEATURE_HW_INTRINSICS
1878418750

18751+
//---------------------------------------------------------------------------------------
18752+
// gtNewMustThrowException:
18753+
// create a throw node (calling into JIT helper) that must be thrown.
18754+
// The result would be a comma node: COMMA(jithelperthrow(void), x) where x's type should be specified.
18755+
//
18756+
// Arguments
18757+
// helper - JIT helper ID
18758+
// type - return type of the node
18759+
//
18760+
// Return Value
18761+
// pointer to the throw node
18762+
//
18763+
GenTree* Compiler::gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd)
18764+
{
18765+
GenTreeCall* node = gtNewHelperCallNode(helper, TYP_VOID);
18766+
node->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN;
18767+
if (type != TYP_VOID)
18768+
{
18769+
unsigned dummyTemp = lvaGrabTemp(true DEBUGARG("dummy temp of must thrown exception"));
18770+
if (type == TYP_STRUCT)
18771+
{
18772+
lvaSetStruct(dummyTemp, clsHnd, false);
18773+
type = lvaTable[dummyTemp].lvType; // struct type is normalized
18774+
}
18775+
else
18776+
{
18777+
lvaTable[dummyTemp].lvType = type;
18778+
}
18779+
GenTree* dummyNode = gtNewLclvNode(dummyTemp, type);
18780+
return gtNewOperNode(GT_COMMA, type, node, dummyNode);
18781+
}
18782+
return node;
18783+
}
18784+
1878518785
//---------------------------------------------------------------------------------------
1878618786
// InitializeStructReturnType:
1878718787
// Initialize the Return Type Descriptor for a method that returns a struct type

src/coreclr/src/jit/hwintrinsic.cpp

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -89,50 +89,6 @@ var_types Compiler::getBaseTypeFromArgIfNeeded(NamedIntrinsic intrinsic,
8989
return baseType;
9090
}
9191

92-
//------------------------------------------------------------------------
93-
// impUnsupportedHWIntrinsic: returns a node for an unsupported HWIntrinsic
94-
//
95-
// Arguments:
96-
// helper - JIT helper ID for the exception to be thrown
97-
// method - method handle of the intrinsic function.
98-
// sig - signature of the intrinsic call
99-
// mustExpand - true if the intrinsic must return a GenTree*; otherwise, false
100-
//
101-
// Return Value:
102-
// a gtNewMustThrowException if mustExpand is true; otherwise, nullptr
103-
//
104-
GenTree* Compiler::impUnsupportedHWIntrinsic(unsigned helper,
105-
CORINFO_METHOD_HANDLE method,
106-
CORINFO_SIG_INFO* sig,
107-
bool mustExpand)
108-
{
109-
// We've hit some error case and may need to return a node for the given error.
110-
//
111-
// When `mustExpand=false`, we are attempting to inline the intrinsic directly into another method. In this
112-
// scenario, we need to return `nullptr` so that a GT_CALL to the intrinsic is emitted instead. This is to
113-
// ensure that everything continues to behave correctly when optimizations are enabled (e.g. things like the
114-
// inliner may expect the node we return to have a certain signature, and the `MustThrowException` node won't
115-
// match that).
116-
//
117-
// When `mustExpand=true`, we are in a GT_CALL to the intrinsic and are attempting to JIT it. This will generally
118-
// be in response to an indirect call (e.g. done via reflection) or in response to an earlier attempt returning
119-
// `nullptr` (under `mustExpand=false`). In that scenario, we are safe to return the `MustThrowException` node.
120-
121-
if (mustExpand)
122-
{
123-
for (unsigned i = 0; i < sig->numArgs; i++)
124-
{
125-
impPopStack();
126-
}
127-
128-
return gtNewMustThrowException(helper, JITtype2varType(sig->retType), sig->retTypeClass);
129-
}
130-
else
131-
{
132-
return nullptr;
133-
}
134-
}
135-
13692
CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleForHWSIMD(var_types simdType, var_types simdBaseType)
13793
{
13894
if (simdType == TYP_SIMD16)

src/coreclr/src/jit/hwintrinsicarm64.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,47 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
357357
assert(retNode->gtType == getSIMDTypeForSize(getSIMDTypeSizeInBytes(sig->retTypeSigClass)));
358358
break;
359359
}
360+
361+
case NI_Vector64_Create:
362+
case NI_Vector128_Create:
363+
{
364+
// We shouldn't handle this as an intrinsic if the
365+
// respective ISAs have been disabled by the user.
366+
367+
if (!compExactlyDependsOn(InstructionSet_AdvSimd))
368+
{
369+
break;
370+
}
371+
372+
if (sig->numArgs == 1)
373+
{
374+
op1 = impPopStack().val;
375+
retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, baseType, simdSize);
376+
}
377+
else if (sig->numArgs == 2)
378+
{
379+
op2 = impPopStack().val;
380+
op1 = impPopStack().val;
381+
retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, baseType, simdSize);
382+
}
383+
else
384+
{
385+
assert(sig->numArgs >= 3);
386+
387+
GenTreeArgList* tmp = nullptr;
388+
389+
for (unsigned i = 0; i < sig->numArgs; i++)
390+
{
391+
tmp = gtNewArgList(impPopStack().val);
392+
tmp->gtOp2 = op1;
393+
op1 = tmp;
394+
}
395+
396+
retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, baseType, simdSize);
397+
}
398+
break;
399+
}
400+
360401
case NI_Vector64_get_Count:
361402
case NI_Vector128_get_Count:
362403
{

src/coreclr/src/jit/hwintrinsiccodegenarm64.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,10 +583,9 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
583583
GetEmitter()->emitIns_R_I(ins, emitSize, targetReg, 0, INS_OPTS_4S);
584584
break;
585585

586-
case NI_Vector64_Create:
587-
case NI_Vector128_Create:
588586
case NI_AdvSimd_DuplicateToVector64:
589587
case NI_AdvSimd_DuplicateToVector128:
588+
case NI_AdvSimd_Arm64_DuplicateToVector64:
590589
case NI_AdvSimd_Arm64_DuplicateToVector128:
591590
{
592591
if (varTypeIsFloating(intrin.baseType))
@@ -596,6 +595,11 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
596595
const double dataValue = intrin.op1->AsDblCon()->gtDconVal;
597596
GetEmitter()->emitIns_R_F(INS_fmov, emitSize, targetReg, dataValue, opt);
598597
}
598+
else if (intrin.id == NI_AdvSimd_Arm64_DuplicateToVector64)
599+
{
600+
assert(intrin.baseType == TYP_DOUBLE);
601+
GetEmitter()->emitIns_R_R(ins, emitSize, targetReg, op1Reg, opt);
602+
}
599603
else
600604
{
601605
GetEmitter()->emitIns_R_R_I(ins, emitSize, targetReg, op1Reg, 0, opt);

0 commit comments

Comments
 (0)