diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 02971fbde2b8c4..ae862974158728 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -863,8 +863,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // Generate code for a GT_BITCAST that is not contained. void genCodeForBitCast(GenTreeOp* treeNode); +#if defined(TARGET_XARCH) // Generate the instruction to move a value between register files void genBitCast(var_types targetType, regNumber targetReg, var_types srcType, regNumber srcReg); +#endif // TARGET_XARCH struct GenIntCastDesc { diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index a10afeb758b6a2..955a7bba1e9a9c 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -7396,6 +7396,7 @@ GenTree* Compiler::gtNewPutArgReg(var_types type, GenTree* arg, regNumber argReg GenTree* Compiler::gtNewBitCastNode(var_types type, GenTree* arg) { assert(arg != nullptr); + assert(type != TYP_STRUCT); GenTree* node = nullptr; #if defined(TARGET_ARM) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 916cb93e187a93..f7c67808bdd77c 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3779,6 +3779,7 @@ var_types LclVarDsc::GetRegisterType(const GenTreeLclVarCommon* tree) const { if (lclVarType == TYP_STRUCT) { + assert(!tree->OperIsLocalField() && "do not expect struct local fields."); lclVarType = GetLayout()->GetRegisterType(); } targetType = lclVarType; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 389c29f7643056..8cbef1709faa6b 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3073,10 +3073,11 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) DISPTREERANGE(BlockRange(), lclStore); JITDUMP("\n"); - GenTree* src = lclStore->gtGetOp1(); - LclVarDsc* varDsc = comp->lvaGetDesc(lclStore); - bool srcIsMultiReg = src->IsMultiRegNode(); - bool dstIsMultiReg = lclStore->IsMultiRegLclVar(); + GenTree* src = lclStore->gtGetOp1(); + LclVarDsc* varDsc = comp->lvaGetDesc(lclStore); + + const bool srcIsMultiReg = src->IsMultiRegNode(); + const bool dstIsMultiReg = lclStore->IsMultiRegLclVar(); if (!dstIsMultiReg && varTypeIsStruct(varDsc)) { @@ -3098,25 +3099,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } } - if ((varTypeUsesFloatReg(lclStore) != varTypeUsesFloatReg(src)) && !lclStore->IsPhiDefn() && - (src->TypeGet() != TYP_STRUCT)) - { - if (m_lsra->isRegCandidate(varDsc)) - { - GenTree* bitcast = comp->gtNewBitCastNode(lclStore->TypeGet(), src); - lclStore->gtOp1 = bitcast; - src = lclStore->gtGetOp1(); - BlockRange().InsertBefore(lclStore, bitcast); - ContainCheckBitCast(bitcast); - } - else - { - // This is an actual store, we'll just retype it. - lclStore->gtType = src->TypeGet(); - } - } - - if (srcIsMultiReg || lclStore->IsMultiRegLclVar()) + if (srcIsMultiReg || dstIsMultiReg) { const ReturnTypeDesc* retTypeDesc = nullptr; if (src->OperIs(GT_CALL)) @@ -3125,14 +3108,16 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } CheckMultiRegLclVar(lclStore->AsLclVar(), retTypeDesc); } + + const var_types lclRegType = varDsc->GetRegisterType(lclStore); + if ((lclStore->TypeGet() == TYP_STRUCT) && !srcIsMultiReg) { bool convertToStoreObj; if (src->OperGet() == GT_CALL) { - GenTreeCall* call = src->AsCall(); - const ClassLayout* layout = varDsc->GetLayout(); - const var_types regType = layout->GetRegisterType(); + GenTreeCall* call = src->AsCall(); + const ClassLayout* layout = varDsc->GetLayout(); #ifdef DEBUG const unsigned slotCount = layout->GetSlotCount(); @@ -3140,7 +3125,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) // Windows x64 doesn't have multireg returns, // x86 uses it only for long return type, not for structs. assert(slotCount == 1); - assert(regType != TYP_UNDEF); + assert(lclRegType != TYP_UNDEF); #else // !TARGET_XARCH || UNIX_AMD64_ABI if (!varDsc->lvIsHfa()) { @@ -3153,7 +3138,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) unsigned size = layout->GetSize(); assert((size <= 8) || (size == 16)); bool isPowerOf2 = (((size - 1) & size) == 0); - bool isTypeDefined = (regType != TYP_UNDEF); + bool isTypeDefined = (lclRegType != TYP_UNDEF); assert(isPowerOf2 == isTypeDefined); } } @@ -3161,7 +3146,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) #endif // DEBUG #if !defined(WINDOWS_AMD64_ABI) - if (!call->HasMultiRegRetVal() && (regType == TYP_UNDEF)) + if (!call->HasMultiRegRetVal() && (lclRegType == TYP_UNDEF)) { // If we have a single return register, // but we can't retype it as a primitive type, we must spill it. @@ -3182,9 +3167,8 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) else if (src->OperIs(GT_CNS_INT)) { assert(src->IsIntegralConst(0) && "expected an INIT_VAL for non-zero init."); - var_types regType = varDsc->GetRegisterType(); #ifdef FEATURE_SIMD - if (varTypeIsSIMD(regType)) + if (varTypeIsSIMD(lclRegType)) { CorInfoType simdBaseJitType = comp->getBaseJitTypeOfSIMDLocal(lclStore); if (simdBaseJitType == CORINFO_TYPE_UNDEF) @@ -3193,7 +3177,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) simdBaseJitType = CORINFO_TYPE_FLOAT; } GenTreeSIMD* simdTree = - comp->gtNewSIMDNode(regType, src, SIMDIntrinsicInit, simdBaseJitType, varDsc->lvExactSize); + comp->gtNewSIMDNode(lclRegType, src, SIMDIntrinsicInit, simdBaseJitType, varDsc->lvExactSize); BlockRange().InsertAfter(src, simdTree); LowerSIMD(simdTree); src = simdTree; @@ -3245,6 +3229,20 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } } + // src and dst can be in registers, check if we need a bitcast. + if (!src->TypeIs(TYP_STRUCT) && (varTypeUsesFloatReg(lclRegType) != varTypeUsesFloatReg(src))) + { + assert(!srcIsMultiReg && !dstIsMultiReg); + assert(lclStore->OperIsLocalStore()); + assert(lclRegType != TYP_UNDEF); + + GenTree* bitcast = comp->gtNewBitCastNode(lclRegType, src); + lclStore->gtOp1 = bitcast; + src = lclStore->gtGetOp1(); + BlockRange().InsertBefore(lclStore, bitcast); + ContainCheckBitCast(bitcast); + } + LowerStoreLoc(lclStore); JITDUMP("lowering store lcl var/field (after):\n"); DISPTREERANGE(BlockRange(), lclStore); @@ -6571,8 +6569,10 @@ void Lowering::ContainCheckBitCast(GenTree* node) { op1->SetContained(); } - LclVarDsc* varDsc = &comp->lvaTable[op1->AsLclVar()->GetLclNum()]; - if (!m_lsra->isRegCandidate(varDsc)) + const LclVarDsc* varDsc = comp->lvaGetDesc(op1->AsLclVar()); + // TODO-Cleanup: we want to check if the local is already known not + // to be on reg, for example, because local enreg is disabled. + if (varDsc->lvDoNotEnregister) { op1->SetContained(); } diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 6774a4ef1766b4..7edf8c7103f152 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -1549,6 +1549,9 @@ void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const assert(storeLoc->OperIsLocalStore()); GenTree* op1 = storeLoc->gtGetOp1(); +#if 0 + // TODO-ARMARCH-CQ: support contained bitcast under STORE_LCL_VAR/FLD, + // currently codegen does not expect it. if (op1->OperIs(GT_BITCAST)) { // If we know that the source of the bitcast will be in a register, then we can make @@ -1561,6 +1564,7 @@ void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const return; } } +#endif const LclVarDsc* varDsc = comp->lvaGetDesc(storeLoc); diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.cs b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.cs new file mode 100644 index 00000000000000..f51dbd03a4bb2c --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Runtime_54466 +{ + public class Test + { + static int Main() + { + return t(1, 1, 1, 1, Vector2.One, Vector2.One, Vector2.One, Vector2.One); + } + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + static int t(int a1, int a2, int a3, int a4, Vector2 x1, Vector2 x2, Vector2 x3, Vector2 x4) + { + if (x1 != Vector2.One) + { + Console.WriteLine("FAIL"); + return 101; + } + return 100; + } + } + +} + + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.csproj new file mode 100644 index 00000000000000..1100f420532dc8 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + +