// In cases where an instruction only supports const immediate operands, we
// need to generate functionally correct code when the operand is not constant
//
-// This is required by the HW Intrinsic design to handle:
+// This is required by the HW Intrinsic design to handle indirect calls, such as:
// debugger calls
// reflection
// call backs
auto intrinsicID = node->gtHWIntrinsicId;
auto intrinsicInfo = comp->getHWIntrinsicInfo(node->gtHWIntrinsicId);
+ //
+ // Lower unsupported Unsigned Compare Zero intrinsics to their trivial transformations
+ //
+ // ARM64 does not support most forms of compare zero for Unsigned values
+ // This is because some are non-sensical, and the rest are trivial transformations of other operators
+ //
if ((intrinsicInfo.flags & HWIntrinsicInfo::LowerCmpUZero) && varTypeIsUnsigned(node->gtSIMDBaseType))
{
auto setAllVector = node->gtSIMDSize > 8 ? NI_ARM64_SIMD_SetAllVector128 : NI_ARM64_SIMD_SetAllVector64;
switch (intrinsicID)
{
case NI_ARM64_SIMD_GE_ZERO:
- // Always true
+ // Unsigned >= 0 ==> Always true
node->gtHWIntrinsicId = setAllVector;
node->gtOp.gtOp1 = comp->gtNewLconNode(~0ULL);
BlockRange().InsertBefore(node, node->gtOp.gtOp1);
BlockRange().Remove(origOp1);
break;
case NI_ARM64_SIMD_GT_ZERO:
- // Same as !EQ
+ // Unsigned > 0 ==> !(Unsigned == 0)
node->gtOp.gtOp1 =
comp->gtNewSimdHWIntrinsicNode(node->TypeGet(), node->gtOp.gtOp1, NI_ARM64_SIMD_EQ_ZERO,
node->gtSIMDBaseType, node->gtSIMDSize);
BlockRange().InsertBefore(node, node->gtOp.gtOp1);
break;
case NI_ARM64_SIMD_LE_ZERO:
- // Same as EQ
+ // Unsigned <= 0 ==> Unsigned == 0
node->gtHWIntrinsicId = NI_ARM64_SIMD_EQ_ZERO;
break;
case NI_ARM64_SIMD_LT_ZERO:
- // Always false
+ // Unsigned < 0 ==> Always false
node->gtHWIntrinsicId = setAllVector;
node->gtOp.gtOp1 = comp->gtNewIconNode(0);
BlockRange().InsertBefore(node, node->gtOp.gtOp1);