if (op3->isContained())
{
// Handle vector element to vector element case
+ //
+ // If op3 is contained this is because lowering found an opportunity to contain a Simd.Extract in a Simd.Insert
+ //
regNumber op3Reg = op3->gtGetOp1()->gtRegNum;
assert(genIsValidFloatReg(op3Reg));
+
+ // op3 containment currently only occurs when
+ // + op3 is a Simd.Extract() (gtHWIntrinsicId == NI_ARM64_SIMD_GetItem)
+ // + element & srcLane are immediate constants
assert(op2->isContainedIntOrIImmed());
assert(op3->OperIs(GT_HWIntrinsic));
assert(op3->AsHWIntrinsic()->gtHWIntrinsicId == NI_ARM64_SIMD_GetItem);
int element = (int)op2->AsIntConCommon()->IconValue();
int srcLane = (int)op3->gtGetOp2()->AsIntConCommon()->IconValue();
+ // Emit mov targetReg[element], op3Reg[srcLane]
getEmitter()->emitIns_R_R_I_I(INS_mov, baseTypeSize, targetReg, op3Reg, element, srcLane);
}
else
{
// Handle scalar to vector element case
- // TODO-ARM64-CQ handle containing scalar const where possible
+ // TODO-ARM64-CQ handle containing op3 scalar const where possible
regNumber op3Reg = op3->gtRegNum;
auto emitSwCase = [&](int element) {
GenTree* op3 = argList->Rest()->Rest()->Current();
- // TODO-ARM64-CQ Support containing NI_ARM64_SIMD_GetItem (vector element to element move)
+ // In the HW intrinsics C# API there is no direct way to specify a vector element to element mov
+ // VX[a] = VY[b]
+ // In C# this would naturally be expressed by
+ // Insert(VX, a, Extract(VY, b))
+ // If both a & b are immediate constants contain the extract/getItem so that we can emit
+ // the single instruction mov Vx[a], Vy[b]
if (op3->OperIs(GT_HWIntrinsic) && (op3->AsHWIntrinsic()->gtHWIntrinsicId == NI_ARM64_SIMD_GetItem))
{
ContainCheckHWIntrinsic(op3->AsHWIntrinsic());