//------------------------------------------------------------------------ // LowerStoreLoc: Lower a store of a lclVar // // Arguments: // storeLoc - the local store (GT_STORE_LCL_FLD or GT_STORE_LCL_VAR) // // Notes: // This involves: // - Widening operations of unsigneds. // void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc) { // Try to widen the ops if they are going into a local var. GenTree* op1 = storeLoc->gtGetOp1(); if ((storeLoc->gtOper == GT_STORE_LCL_VAR) && (op1->gtOper == GT_CNS_INT)) { GenTreeIntCon* con = op1->AsIntCon(); ssize_t ival = con->gtIconVal; unsigned varNum = storeLoc->gtLclNum; LclVarDsc* varDsc = comp->lvaTable + varNum; if (varDsc->lvIsSIMDType()) { noway_assert(storeLoc->gtType != TYP_STRUCT); } unsigned size = genTypeSize(storeLoc); // If we are storing a constant into a local variable // we extend the size of the store here if ((size < 4) && !varTypeIsStruct(varDsc)) { if (!varTypeIsUnsigned(varDsc)) { if (genTypeSize(storeLoc) == 1) { if ((ival & 0x7f) != ival) { ival = ival | 0xffffff00; } } else { assert(genTypeSize(storeLoc) == 2); if ((ival & 0x7fff) != ival) { ival = ival | 0xffff0000; } } } // A local stack slot is at least 4 bytes in size, regardless of // what the local var is typed as, so auto-promote it here // unless it is a field of a promoted struct // TODO-CQ: if the field is promoted shouldn't we also be able to do this? if (!varDsc->lvIsStructField) { storeLoc->gtType = TYP_INT; con->SetIconValue(ival); } } } if (storeLoc->OperIs(GT_STORE_LCL_FLD)) { // We should only encounter this for lclVars that are lvDoNotEnregister. verifyLclFldDoNotEnregister(storeLoc->gtLclNum); } ContainCheckStoreLoc(storeLoc); }
//---------------------------------------------------------------------------------------------- // Lowering::LowerHWIntrinsic: Perform containment analysis for a hardware intrinsic node. // // Arguments: // node - The hardware intrinsic node. // void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) { auto intrinsicID = node->gtHWIntrinsicId; auto intrinsicInfo = HWIntrinsicInfo::lookup(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; auto origOp1 = node->gtOp.gtOp1; switch (intrinsicID) { case NI_ARM64_SIMD_GT_ZERO: // Unsigned > 0 ==> !(Unsigned == 0) node->gtOp.gtOp1 = comp->gtNewSimdHWIntrinsicNode(node->TypeGet(), node->gtOp.gtOp1, NI_ARM64_SIMD_EQ_ZERO, node->gtSIMDBaseType, node->gtSIMDSize); node->gtHWIntrinsicId = NI_ARM64_SIMD_BitwiseNot; BlockRange().InsertBefore(node, node->gtOp.gtOp1); break; case NI_ARM64_SIMD_LE_ZERO: // Unsigned <= 0 ==> Unsigned == 0 node->gtHWIntrinsicId = NI_ARM64_SIMD_EQ_ZERO; break; case NI_ARM64_SIMD_GE_ZERO: case NI_ARM64_SIMD_LT_ZERO: // Unsigned >= 0 ==> Always true // Unsigned < 0 ==> Always false node->gtHWIntrinsicId = setAllVector; node->gtOp.gtOp1 = comp->gtNewLconNode((intrinsicID == NI_ARM64_SIMD_GE_ZERO) ? ~0ULL : 0ULL); BlockRange().InsertBefore(node, node->gtOp.gtOp1); if ((origOp1->gtFlags & GTF_ALL_EFFECT) == 0) { BlockRange().Remove(origOp1, true); } else { origOp1->SetUnusedValue(); } break; default: assert(!"Unhandled LowerCmpUZero case"); } } ContainCheckHWIntrinsic(node); }
//------------------------------------------------------------------------ // DecomposeCast: Decompose GT_CAST. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_CAST); GenTree* cast = use.Def()->AsCast(); GenTree* loResult = nullptr; GenTree* hiResult = nullptr; var_types srcType = cast->CastFromType(); var_types dstType = cast->CastToType(); if ((cast->gtFlags & GTF_UNSIGNED) != 0) { srcType = genUnsignedType(srcType); } if (varTypeIsLong(srcType)) { if (cast->gtOverflow() && (varTypeIsUnsigned(srcType) != varTypeIsUnsigned(dstType))) { GenTree* srcOp = cast->gtGetOp1(); noway_assert(srcOp->OperGet() == GT_LONG); GenTree* loSrcOp = srcOp->gtGetOp1(); GenTree* hiSrcOp = srcOp->gtGetOp2(); // // When casting between long types an overflow check is needed only if the types // have different signedness. In both cases (long->ulong and ulong->long) we only // need to check if the high part is negative or not. Use the existing cast node // to perform a int->uint cast of the high part to take advantage of the overflow // check provided by codegen. // loResult = loSrcOp; hiResult = cast; hiResult->gtType = TYP_INT; hiResult->AsCast()->gtCastType = TYP_UINT; hiResult->gtFlags &= ~GTF_UNSIGNED; hiResult->gtOp.gtOp1 = hiSrcOp; Range().Remove(cast); Range().Remove(srcOp); Range().InsertAfter(hiSrcOp, hiResult); } else { NYI("Unimplemented long->long no-op cast decomposition"); } } else if (varTypeIsIntegralOrI(srcType)) { if (cast->gtOverflow() && !varTypeIsUnsigned(srcType) && varTypeIsUnsigned(dstType)) { // // An overflow check is needed only when casting from a signed type to ulong. // Change the cast type to uint to take advantage of the overflow check provided // by codegen and then zero extend the resulting uint to ulong. // loResult = cast; loResult->AsCast()->gtCastType = TYP_UINT; loResult->gtType = TYP_INT; hiResult = m_compiler->gtNewZeroConNode(TYP_INT); Range().InsertAfter(loResult, hiResult); } else { if (varTypeIsUnsigned(srcType)) { loResult = cast->gtGetOp1(); hiResult = m_compiler->gtNewZeroConNode(TYP_INT); Range().Remove(cast); Range().InsertAfter(loResult, hiResult); } else { LIR::Use src(Range(), &(cast->gtOp.gtOp1), cast); unsigned lclNum = src.ReplaceWithLclVar(m_compiler, m_blockWeight); loResult = src.Def(); GenTree* loCopy = m_compiler->gtNewLclvNode(lclNum, TYP_INT); GenTree* shiftBy = m_compiler->gtNewIconNode(31, TYP_INT); hiResult = m_compiler->gtNewOperNode(GT_RSH, TYP_INT, loCopy, shiftBy); Range().Remove(cast); Range().InsertAfter(loResult, loCopy, shiftBy, hiResult); m_compiler->lvaIncRefCnts(loCopy); } } } else { NYI("Unimplemented cast decomposition"); } return FinalizeDecomposition(use, loResult, hiResult); }