diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 818a969122fea7..4bf01cac421d3f 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -4654,12 +4654,18 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) { costEx = IND_COST_EX; costSz = 2; - /* Sign-extend and zero-extend are more expensive to load */ + + // Some types are more expensive to load than others. if (varTypeIsSmall(tree->TypeGet())) { costEx += 1; costSz += 1; } + else if (tree->TypeIs(TYP_STRUCT)) + { + costEx += 2 * IND_COST_EX; + costSz += 2 * 2; + } } #if defined(TARGET_AMD64) // increase costSz for floating point locals @@ -4685,8 +4691,8 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) } else if (tree->TypeIs(TYP_STRUCT)) { - costEx += IND_COST_EX; - costSz += 2; + costEx += 2 * IND_COST_EX; + costSz += 2 * 2; } break; @@ -4916,17 +4922,15 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) break; case GT_ADDR: + if (op1->OperIsLocalRead()) + { + costEx = 3; + costSz = 3; + goto DONE; + } costEx = 0; costSz = 1; - - // If we have a GT_ADDR of an GT_IND we can just copy the costs from indOp1 - if (op1->OperGet() == GT_IND) - { - GenTree* indOp1 = op1->AsOp()->gtOp1; - costEx = indOp1->GetCostEx(); - costSz = indOp1->GetCostSz(); - } break; case GT_ARR_LENGTH: diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 4ca754e9c993b9..a34f295bee1fe2 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1095,7 +1095,7 @@ class LocalAddressVisitor final : public GenTreeVisitor // | Partial | LCL_FLD | OBJ/LCL_FLD | LCL_FLD | // |------------|---------|-------------|---------| // - // * - On x86/Windows x64 only. + // * - On XArch only. // // |------------|------|------|--------|----------| // | SIMD | CALL | ASG | RETURN | HWI/SIMD | @@ -1113,9 +1113,9 @@ class LocalAddressVisitor final : public GenTreeVisitor if (user->IsCall()) { -#if !defined(WINDOWS_AMD64_ABI) && !defined(TARGET_X86) +#if !defined(TARGET_XARCH) return IndirTransform::None; -#endif // !defined(WINDOWS_AMD64_ABI) && !defined(TARGET_X86) +#endif // !defined(TARGET_XARCH) } if (match == StructMatch::Compatible) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 563e34dad23c0b..5891114a273ecd 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1300,7 +1300,8 @@ void CallArgs::SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs) GenTree* argx = arg->GetEarlyNode(); assert(argx != nullptr); - if ((argx->gtOper == GT_LCL_VAR) || (argx->gtOper == GT_LCL_FLD)) + // As a CQ heuristic, sort TYP_STRUCT args using the cost estimation below. + if (!argx->TypeIs(TYP_STRUCT) && argx->OperIs(GT_LCL_VAR, GT_LCL_FLD)) { noway_assert(curInx <= endTab); @@ -1346,9 +1347,8 @@ void CallArgs::SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs) assert(argx != nullptr); // We should have already handled these kinds of args - assert(argx->gtOper != GT_LCL_VAR); - assert(argx->gtOper != GT_LCL_FLD); - assert(argx->gtOper != GT_CNS_INT); + assert((!argx->OperIs(GT_LCL_VAR, GT_LCL_FLD) || argx->TypeIs(TYP_STRUCT)) && + !argx->OperIs(GT_CNS_INT)); // This arg should either have no persistent side effects or be the last one in our table // assert(((argx->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0) || (curInx == (argCount-1))); @@ -3197,6 +3197,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // - This is irrelevant for X86, since structs are always passed by value on the stack. GenTree* lclVar = fgIsIndirOfAddrOfLocal(argObj); + bool argIsLocal = (lclVar != nullptr) || argObj->OperIsLocalRead(); bool canTransform = false; if (structBaseType != TYP_STRUCT) @@ -3214,7 +3215,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // or UNIX_AMD64_ABI cases where they will be passed in registers. else { - canTransform = (lclVar != nullptr); + canTransform = argIsLocal; passingSize = genTypeSize(structBaseType); } #endif // TARGET_ARM64 || UNIX_AMD64_ABI || TARGET_LOONGARCH64 @@ -3244,7 +3245,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) makeOutArgCopy = true; } } - else if (lclVar == nullptr) + else if (!argIsLocal) { // This should only be the case of a value directly producing a known struct type. assert(argObj->TypeGet() != TYP_STRUCT); @@ -3722,9 +3723,10 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg) { argNode = fgMorphLclArgToFieldlist(lcl); } +#ifndef TARGET_XARCH else if (argNode->TypeGet() == TYP_STRUCT) { - // If this is a non-register struct, it must be referenced from memory. + // ARM/ARM64/LoongArch64 backends do not support local nodes as sources of some stack args. if (!actualArg->OperIs(GT_OBJ)) { // Create an Obj of the temp to use it as a call argument. @@ -3732,8 +3734,9 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg) argNode = gtNewObjNode(lvaGetStruct(lcl->GetLclNum()), argNode); } // Its fields will need to be accessed by address. - lvaSetVarDoNotEnregister(lcl->GetLclNum() DEBUG_ARG(DoNotEnregisterReason::IsStructArg)); + lvaSetVarDoNotEnregister(lcl->GetLclNum() DEBUGARG(DoNotEnregisterReason::IsStructArg)); } +#endif // !TARGET_XARCH } return argNode;