From aafbd36564465e566a1eb922e24eeacc1f100dd9 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 12 Sep 2023 22:44:26 +0200 Subject: [PATCH 01/13] JIT: Add an explicit IR representation for parameter definitions --- src/coreclr/jit/codegen.h | 1 + src/coreclr/jit/codegenxarch.cpp | 19 ++++ src/coreclr/jit/compiler.h | 5 + src/coreclr/jit/compiler.hpp | 1 + src/coreclr/jit/gentree.cpp | 18 ++++ src/coreclr/jit/gentree.h | 21 ++++ src/coreclr/jit/gtlist.h | 1 + src/coreclr/jit/gtstructs.h | 1 + src/coreclr/jit/liveness.cpp | 1 + src/coreclr/jit/lower.cpp | 169 ++++++++++++++++++++++++++++++- src/coreclr/jit/lower.h | 2 + src/coreclr/jit/lsra.cpp | 138 +++++++++++++++++-------- src/coreclr/jit/lsra.h | 14 +++ src/coreclr/jit/lsrabuild.cpp | 77 +++++++++++++- src/coreclr/jit/lsraxarch.cpp | 4 + src/coreclr/jit/promotion.cpp | 7 +- 16 files changed, 431 insertions(+), 48 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index e00dff0f7bacd..b7de42f85424a 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1152,6 +1152,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode); #endif void genCodeForPhysReg(GenTreePhysReg* tree); + void genCodeForGetParamReg(GenTreeGetParamReg* tree); void genCodeForNullCheck(GenTreeIndir* tree); void genCodeForCmpXchg(GenTreeCmpXchg* tree); void genCodeForReuseVal(GenTree* treeNode); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 10e03e6952148..4124452832b77 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -2087,6 +2087,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCodeForPhysReg(treeNode->AsPhysReg()); break; + case GT_GETPARAM_REG: + genCodeForGetParamReg(treeNode->AsGetParamReg()); + break; + case GT_NULLCHECK: genCodeForNullCheck(treeNode->AsIndir()); break; @@ -4519,6 +4523,21 @@ void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree) genProduceReg(tree); } +void CodeGen::genCodeForGetParamReg(GenTreeGetParamReg* tree) +{ + assert(tree->OperIs(GT_GETPARAM_REG)); + + var_types targetType = tree->TypeGet(); + regNumber targetReg = tree->GetRegNum(); + + regNumber srcReg = tree->GetArgReg(compiler); + + inst_Mov(targetType, targetReg, srcReg, /* canSkip */ true); + genTransferRegGCState(targetReg, srcReg); + + genProduceReg(tree); +} + //--------------------------------------------------------------------- // genCodeForNullCheck - generate code for a GT_NULLCHECK node // diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f1f96514ee04d..974f4221e17fc 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -657,6 +657,8 @@ class LclVarDsc unsigned char lvRedefinedInEmbeddedStatement : 1; // Local has redefinitions inside embedded statements that // disqualify it from local copy prop. + + unsigned char lvExplicitParamInit : 1; // This parameter has explicit IR to initialize it from registers. private: unsigned char lvIsNeverNegative : 1; // The local is known to be never negative @@ -2477,6 +2479,8 @@ class Compiler GenTree* gtNewPhysRegNode(regNumber reg, var_types type); + GenTreeGetParamReg* gtNewGetParamRegNode(unsigned lclNum, int regIndex, var_types type); + GenTree* gtNewJmpTableNode(); GenTree* gtNewIndOfIconHandleNode(var_types indType, size_t value, GenTreeFlags iconFlags, bool isInvariant); @@ -11182,6 +11186,7 @@ class GenTreeVisitor case GT_JMPTABLE: case GT_CLS_VAR_ADDR: case GT_PHYSREG: + case GT_GETPARAM_REG: case GT_EMITNOP: case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 24c009c88607c..10c73c180a32c 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4479,6 +4479,7 @@ void GenTree::VisitOperands(TVisitor visitor) case GT_JMPTABLE: case GT_CLS_VAR_ADDR: case GT_PHYSREG: + case GT_GETPARAM_REG: case GT_EMITNOP: case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 874541864c037..22fa888ef447f 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6419,6 +6419,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_JMPTABLE: case GT_CLS_VAR_ADDR: case GT_PHYSREG: + case GT_GETPARAM_REG: case GT_EMITNOP: case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: @@ -7256,6 +7257,12 @@ GenTree* Compiler::gtNewPhysRegNode(regNumber reg, var_types type) return result; } +GenTreeGetParamReg* Compiler::gtNewGetParamRegNode(unsigned lclNum, int regIndex, var_types type) +{ + GenTreeGetParamReg* result = new (this, GT_GETPARAM_REG) GenTreeGetParamReg(lclNum, regIndex, type); + return result; +} + GenTree* Compiler::gtNewJmpTableNode() { return new (this, GT_JMPTABLE) GenTree(GT_JMPTABLE, TYP_I_IMPL); @@ -8555,6 +8562,12 @@ void GenTreeOp::CheckDivideByConstOptimized(Compiler* comp) } } +regNumber GenTreeGetParamReg::GetArgReg(Compiler* comp) +{ + LclVarDsc* argDsc = comp->lvaGetDesc(gtArgNum); + return gtRegIndex == 0 ? argDsc->GetArgReg() : argDsc->GetOtherArgReg(); +} + //------------------------------------------------------------------------ // gtNewPutArgReg: Creates a new PutArgReg node. // @@ -9878,6 +9891,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_JMPTABLE: case GT_CLS_VAR_ADDR: case GT_PHYSREG: + case GT_GETPARAM_REG: case GT_EMITNOP: case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: @@ -11946,6 +11960,10 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) printf(" %s", getRegName(tree->AsPhysReg()->gtSrcReg)); break; + case GT_GETPARAM_REG: + printf(" %s", getRegName(tree->AsGetParamReg()->GetArgReg(this))); + break; + case GT_IL_OFFSET: printf(" "); tree->AsILOffset()->gtStmtDI.Dump(true); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 1b20ba741f281..2cd154c1c609c 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3085,6 +3085,27 @@ struct GenTreePhysReg : public GenTree #endif }; +struct GenTreeGetParamReg : public GenTree +{ + unsigned gtArgNum; + int gtRegIndex; + + GenTreeGetParamReg(unsigned argNum, int regIndex, var_types type) + : GenTree(GT_GETPARAM_REG, type) + , gtArgNum(argNum) + , gtRegIndex(regIndex) + { + } + +#if DEBUGGABLE_GENTREE + GenTreeGetParamReg() : GenTree() + { + } +#endif + + regNumber GetArgReg(Compiler* comp); +}; + /* gtIntCon -- integer constant (GT_CNS_INT) */ struct GenTreeIntCon : public GenTreeIntConCommon { diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index b3a3d2dbb470c..df21d2ac06aac 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -315,6 +315,7 @@ GTNODE(PUTARG_STK , GenTreePutArgStk ,0,0,GTK_UNOP|GTK_NOVALUE|DBK_NOTHI #if FEATURE_ARG_SPLIT GTNODE(PUTARG_SPLIT , GenTreePutArgSplit ,0,0,GTK_UNOP|DBK_NOTHIR) // operator that places outgoing arg in registers with stack (split struct in ARM32) #endif // FEATURE_ARG_SPLIT +GTNODE(GETPARAM_REG , GenTreePhysReg ,0,0,GTK_LEAF|DBK_NOTHIR) // use of parameter; only expected at the beginning of the first BB. GTNODE(SWAP , GenTreeOp ,0,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) // op1 and op2 swap (registers) GTNODE(COPY , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies GTNODE(RELOAD , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index 317e0b3f071b7..173ed4aec1ffb 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -106,6 +106,7 @@ GTSTRUCT_1(PutArgSplit , GT_PUTARG_SPLIT) GTSTRUCT_1(PutArgStk , GT_PUTARG_STK) #endif // !FEATURE_ARG_SPLIT GTSTRUCT_1(PhysReg , GT_PHYSREG) +GTSTRUCT_1(GetParamReg , GT_GETPARAM_REG) #ifdef FEATURE_HW_INTRINSICS GTSTRUCT_1(HWIntrinsic , GT_HWINTRINSIC) #endif // FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 62f0e1784a1d3..1c76433edd016 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1998,6 +1998,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_CNS_VEC: case GT_CLS_VAR_ADDR: case GT_PHYSREG: + case GT_GETPARAM_REG: // These are all side-effect-free leaf nodes. if (node->IsUnusedValue()) { diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 16df99387128a..5b0943758b937 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4121,7 +4121,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret) JITDUMP("lowering GT_RETURN\n"); DISPNODE(ret); - JITDUMP("============"); + JITDUMP("============\n"); GenTree* retVal = ret->gtGetOp1(); // There are two kinds of retyping: @@ -7106,6 +7106,11 @@ PhaseStatus Lowering::DoPhase() LowerBlock(block); } + if (comp->compEnregLocals()) + { + LowerMultiregParams(); + } + #ifdef DEBUG JITDUMP("Lower has completed modifying nodes.\n"); if (VERBOSE) @@ -7558,6 +7563,168 @@ bool Lowering::CheckMultiRegLclVar(GenTreeLclVar* lclNode, int registerCount) return canEnregisterAsSingleReg || canEnregisterAsMultiReg; } +void Lowering::LowerMultiregParams() +{ +#if FEATURE_MULTIREG_ARGS + if (comp->fgFirstBB->bbPreds != nullptr) + { + comp->fgEnsureFirstBBisScratch(); + } + + GenTree* insertAfter = nullptr; + LIR::Range& firstBBRange = LIR::AsRange(comp->fgFirstBB); + + // Write (parts of) parameters passed in registers back to the local. + for (unsigned argNum = 0; argNum < comp->info.compArgsCount; argNum++) + { + LclVarDsc* argDsc = comp->lvaGetDesc(argNum); + JITDUMP("Checking arg V%02u for explicit IR init\n", argNum); + + if (!argDsc->lvIsRegArg || argDsc->lvPromoted) + { + JITDUMP(" not a reg arg or promoted\n"); + continue; + } + + if (argDsc->TypeGet() != TYP_STRUCT) + { + JITDUMP(" not TYP_STRUCT\n"); + continue; + } + + regNumber argReg1 = argDsc->GetArgReg(); + regNumber argReg2 = argDsc->GetOtherArgReg(); + JITDUMP(" regs: %s %s\n", getRegName(argReg1), getRegName(argReg2)); + + if ((argReg1 == REG_NA) || !genIsValidIntReg(argReg1)) + { + // TODO: Floating point on SysV can pass eightbytes in xmm0, e.g. + // struct { float X, float Y, float Z, int W } passes X, Y in xmm0 + // and Z, W in rdi. + JITDUMP(" first reg is %s\n", getRegName(argReg1)); + continue; + } + + ClassLayout* layout = argDsc->GetLayout(); + if (((layout->GetSize() % TARGET_POINTER_SIZE) != 0) && !isPow2(layout->GetSize() % TARGET_POINTER_SIZE)) + { + JITDUMP(" remainder size %d is not a power of 2\n", layout->GetSize() % TARGET_POINTER_SIZE); + continue; + } + + assert((argReg2 != REG_NA) == (layout->GetSize() > TARGET_POINTER_SIZE)); + + JITDUMP("Marking V%02u as an explicitly inited parameter\n", argNum); + argDsc->lvExplicitParamInit = 1; + + auto getCanonType = [layout](unsigned offset) { + unsigned sizeLeft = (layout->GetSize() - offset) % TARGET_POINTER_SIZE; + if (sizeLeft >= TARGET_POINTER_SIZE) + { + return layout->GetGCPtrType(offset / TARGET_POINTER_SIZE); + } + + if (sizeLeft > 4) + { + return TYP_LONG; + } + + return TYP_INT; + }; + + GenTree* paramReg1 = comp->gtNewGetParamRegNode(argNum, 0, getCanonType(0)); + GenTree* store1 = comp->gtNewStoreLclFldNode(argNum, paramReg1->TypeGet(), 0, paramReg1); + firstBBRange.InsertAfter(insertAfter, paramReg1, store1); + + insertAfter = store1; + + DISPTREERANGE(firstBBRange, store1); + + if (argReg2 != REG_NA) + { + GenTree* paramReg2 = comp->gtNewGetParamRegNode(argNum, 1, getCanonType(TARGET_POINTER_SIZE)); + GenTree* store2 = comp->gtNewStoreLclFldNode(argNum, paramReg2->TypeGet(), TARGET_POINTER_SIZE, paramReg2); + firstBBRange.InsertAfter(insertAfter, paramReg2, store2); + + insertAfter = store2; + + DISPTREERANGE(firstBBRange, store2); + } + } + + for (GenTree* node : firstBBRange) + { + if (!node->OperIs(GT_LCL_FLD)) + { + continue; + } + + GenTreeLclFld* fld = node->AsLclFld(); + if (fld->TypeIs(TYP_STRUCT)) + { + continue; + } + + LclVarDsc* dsc = comp->lvaGetDesc(fld); + + if (!dsc->lvExplicitParamInit) + { + continue; + } + + unsigned offs = fld->GetLclOffs(); + + regNumber reg1 = dsc->GetArgReg(); + regNumber reg2 = dsc->GetOtherArgReg(); + + if (!genIsValidIntReg(reg1)) + { + // TODO: Floating point on SysV can pass eightbytes in xmm0, e.g. + // struct { float X, float Y, float Z, int W } passes X, Y in xmm0 + // and Z, W in rdi. + continue; + } + + if ((reg2 != REG_NA) && !genIsValidIntReg(reg2)) + { + continue; + } + + unsigned reg2Offs = TARGET_POINTER_SIZE; + + int regIndex; + if (offs == 0) + { + regIndex = 0; + } + else if (offs == reg2Offs) + { + regIndex = 1; + } + else + { + continue; + } + + if (varTypeIsIntegralOrI(fld)) + { + LIR::Use fldUse; + if (firstBBRange.TryGetUse(fld, &fldUse)) + { + JITDUMP("[%06u] is a parameter register use, optimizing it into GETPARAM_REG\n", Compiler::dspTreeID(fld)); + GenTree* newNode = comp->gtNewGetParamRegNode(fld->GetLclNum(), regIndex, fld->TypeGet()); + fldUse.ReplaceWith(newNode); + + firstBBRange.InsertAfter(fld, newNode); + firstBBRange.Remove(fld); + + DISPTREERANGE(firstBBRange, fldUse.User()); + } + } + } +#endif +} + //------------------------------------------------------------------------ // Containment Analysis //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 4f303007af293..e949901c6e840 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -125,6 +125,8 @@ class Lowering final : public Phase void LowerBlock(BasicBlock* block); GenTree* LowerNode(GenTree* node); + void LowerMultiregParams(); + bool IsCFGCallArgInvariantInRange(GenTree* node, GenTree* endExclusive); // ------------------------------ diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 16951d4f68696..2cda2708284fd 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -2437,6 +2437,23 @@ void LinearScan::checkLastUses(BasicBlock* block) } #endif // DEBUG + +int LinearScan::getExplicitParamIntervalRegIndex(Interval* interval) +{ + assert(interval->isExplicitParam && (interval->varNum < compiler->info.compArgsCount)); + assert(explicitParamIntervals != nullptr); + for (int i = 0; i < MAX_ARG_REG_COUNT; i++) + { + if (explicitParamIntervals[interval->varNum].Intervals[i] == interval) + { + return i; + } + } + + noway_assert(!"expected to find explicit param interval reg index"); + return -1; +} + //------------------------------------------------------------------------ // findPredBlockForLiveIn: Determine which block should be used for the register locations of the live-in variables. // @@ -5199,36 +5216,46 @@ void LinearScan::allocateRegisters() INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_ZERO_REF, currentInterval)); currentRefPosition.lastUse = true; } - LclVarDsc* varDsc = currentInterval->getLocalVar(compiler); - assert(varDsc != nullptr); - assert(!blockInfo[compiler->fgFirstBB->bbNum].hasEHBoundaryIn || currentInterval->isWriteThru); - if (blockInfo[compiler->fgFirstBB->bbNum].hasEHBoundaryIn || - blockInfo[compiler->fgFirstBB->bbNum].hasEHPred) - { - allocate = false; - } - else if (refType == RefTypeParamDef && (varDsc->lvRefCntWtd() <= BB_UNITY_WEIGHT) && - (!currentRefPosition.lastUse || (currentInterval->physReg == REG_STK))) - { - // If this is a low ref-count parameter, and either it is used (def is not the last use) or it's - // passed on the stack, don't allocate a register. - // Note that if this is an unused register parameter we don't want to set allocate to false because that - // will cause us to allocate stack space to spill it. - allocate = false; - } - else if ((currentInterval->physReg == REG_STK) && nextRefPosition->treeNode->OperIs(GT_BITCAST)) + + if (currentInterval->isLocalVar) { - // In the case of ABI mismatches, avoid allocating a register only to have to immediately move - // it to a different register file. - allocate = false; + LclVarDsc* varDsc = currentInterval->getLocalVar(compiler); + assert(varDsc != nullptr); + assert(!blockInfo[compiler->fgFirstBB->bbNum].hasEHBoundaryIn || currentInterval->isWriteThru); + if (blockInfo[compiler->fgFirstBB->bbNum].hasEHBoundaryIn || + blockInfo[compiler->fgFirstBB->bbNum].hasEHPred) + { + allocate = false; + } + else if (refType == RefTypeParamDef && (varDsc->lvRefCntWtd() <= BB_UNITY_WEIGHT) && + (!currentRefPosition.lastUse || (currentInterval->physReg == REG_STK))) + { + // If this is a low ref-count parameter, and either it is used (def is not the last use) or it's + // passed on the stack, don't allocate a register. + // Note that if this is an unused register parameter we don't want to set allocate to false because that + // will cause us to allocate stack space to spill it. + allocate = false; + } + else if ((currentInterval->physReg == REG_STK) && nextRefPosition->treeNode->OperIs(GT_BITCAST)) + { + // In the case of ABI mismatches, avoid allocating a register only to have to immediately move + // it to a different register file. + allocate = false; + } + else if ((currentInterval->isWriteThru) && (refType == RefTypeZeroInit)) + { + // For RefTypeZeroInit which is a write thru, there is no need to allocate register + // right away. It can be assigned when actually definition occurs. + // In future, see if avoiding allocation for RefTypeZeroInit gives any benefit in general. + allocate = false; + } } - else if ((currentInterval->isWriteThru) && (refType == RefTypeZeroInit)) + else { - // For RefTypeZeroInit which is a write thru, there is no need to allocate register - // right away. It can be assigned when actually definition occurs. - // In future, see if avoiding allocation for RefTypeZeroInit gives any benefit in general. - allocate = false; + assert(currentInterval->isExplicitParam); + // always allocate these; fall through with allocate == true } + if (!allocate) { INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_NO_ENTRY_REG_ALLOCATED, currentInterval)); @@ -7143,21 +7170,29 @@ void LinearScan::resolveRegisters() } Interval* interval = currentRefPosition->getInterval(); - assert(interval != nullptr && interval->isLocalVar); - resolveLocalRef(nullptr, nullptr, currentRefPosition); - regNumber reg = REG_STK; - int varIndex = interval->getVarIndex(compiler); + assert(interval != nullptr); - if (!currentRefPosition->spillAfter && currentRefPosition->registerAssignment != RBM_NONE) + if (interval->isLocalVar) { - reg = currentRefPosition->assignedReg(); + resolveLocalRef(nullptr, nullptr, currentRefPosition); + regNumber reg = REG_STK; + int varIndex = interval->getVarIndex(compiler); + + if (!currentRefPosition->spillAfter && currentRefPosition->registerAssignment != RBM_NONE) + { + reg = currentRefPosition->assignedReg(); + } + else + { + reg = REG_STK; + interval->isActive = false; + } + setVarReg(entryVarToRegMap, varIndex, reg); } else { - reg = REG_STK; - interval->isActive = false; + assert(interval->isExplicitParam); } - setVarReg(entryVarToRegMap, varIndex, reg); } } else @@ -9963,8 +9998,12 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode) } Interval* interval = currentRefPosition->getInterval(); - assert(interval != nullptr && interval->isLocalVar); - printf(" V%02d", interval->varNum); + assert((interval != nullptr) && (interval->isLocalVar || interval->isExplicitParam)); + if (interval->isLocalVar) + printf(" V%02d", interval->varNum); + else + printf(" V%02d,%d", interval->varNum, getExplicitParamIntervalRegIndex(interval)); + if (mode == LSRA_DUMP_POST) { regNumber reg; @@ -9976,12 +10015,23 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode) { reg = currentRefPosition->assignedReg(); } - const LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum); - printf("("); - regNumber assignedReg = varDsc->GetRegNum(); - regNumber argReg = (varDsc->lvIsRegArg) ? varDsc->GetArgReg() : REG_STK; - assert(reg == assignedReg || varDsc->lvRegister == false); + regNumber argReg; + + if (interval->isLocalVar) + { + const LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum); + regNumber assignedReg = varDsc->GetRegNum(); + argReg = (varDsc->lvIsRegArg) ? varDsc->GetArgReg() : REG_STK; + assert(reg == assignedReg || varDsc->lvRegister == false); + } + else + { + LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum); + argReg = getExplicitParamIntervalRegIndex(interval) == 0 ? varDsc->GetArgReg() : varDsc->GetOtherArgReg(); + } + + printf("("); if (reg != argReg) { printf(getRegName(argReg)); @@ -10317,9 +10367,9 @@ void LinearScan::dumpLsraAllocationEvent( break; case LSRA_EVENT_ZERO_REF: - assert(interval != nullptr && interval->isLocalVar); + assert(interval != nullptr && (interval->isLocalVar || interval->isExplicitParam)); dumpRefPositionShort(activeRefPosition, currentBlock); - printf("NoRef "); + printf("NoRef "); dumpRegRecords(); break; diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index 20941e45f9d1b..bebe0c609c30b 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -1568,6 +1568,15 @@ class LinearScan : public LinearScanInterface // Map from tracked variable index to Interval*. Interval** localVarIntervals; + struct ExplicitParamIntervals + { + Interval* Intervals[MAX_ARG_REG_COUNT] = {}; + }; + + ExplicitParamIntervals* explicitParamIntervals; + + int getExplicitParamIntervalRegIndex(Interval* interval); + // Set of blocks that have been visited. BlockSet bbVisitedSet; void markBlockVisited(BasicBlock* block) @@ -1936,6 +1945,8 @@ class LinearScan : public LinearScanInterface // These methods return the number of sources. int BuildNode(GenTree* tree); + int BuildGetParamReg(GenTreeGetParamReg* tree); + void UpdatePreferencesOfDyingLocal(Interval* interval); void getTgtPrefOperands(GenTree* tree, GenTree* op1, GenTree* op2, bool* prefOp1, bool* prefOp2); bool supportsSpecialPutArg(); @@ -2233,6 +2244,9 @@ class Interval : public Referenceable } #endif + // True if this interval is an explicit parameter interval. + bool isExplicitParam : 1; + // True if this interval is associated with a lclVar that is written to memory at each definition. bool isWriteThru : 1; diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 8c9025f61b703..5e4449640e1b6 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -471,6 +471,16 @@ void LinearScan::associateRefPosWithInterval(RefPosition* rp) rp->lastUse = (rp->refType != RefTypeExpUse) && (rp->refType != RefTypeParamDef) && (rp->refType != RefTypeZeroInit) && !extendLifetimes(); } + else if (theInterval->isExplicitParam) + { + RefPosition* prevRP = theInterval->recentRefPosition; + if (prevRP != nullptr) + { + prevRP->lastUse = false; + } + + rp->lastUse = true; + } else if (rp->refType == RefTypeUse) { checkConflictingDefUse(rp); @@ -1890,7 +1900,7 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree, LsraLocation currentLoc } if ((newRefPosition->registerAssignment != oldAssignment) && (newRefPosition->refType == RefTypeUse) && - !interval->isLocalVar) + !interval->isLocalVar && !interval->isExplicitParam) { #ifdef TARGET_ARM64 RefPosition* defRefPos = interval->firstRefPosition; @@ -2291,7 +2301,7 @@ void LinearScan::buildIntervals() { LclVarDsc* argDsc = compiler->lvaGetDescByTrackedIndex(varIndex); - if (!argDsc->lvIsParam) + if (!argDsc->lvIsParam || argDsc->lvExplicitParamInit) { continue; } @@ -2384,6 +2394,50 @@ void LinearScan::buildIntervals() } } + explicitParamIntervals = nullptr; + for (unsigned argNum = 0; argNum < compiler->info.compArgsCount; argNum++) + { + LclVarDsc* argDsc = compiler->lvaGetDesc(argNum); + if (!argDsc->lvExplicitParamInit) + { + continue; + } + + if (explicitParamIntervals == nullptr) + { + explicitParamIntervals = new (compiler, CMK_LSRA_Interval) ExplicitParamIntervals[compiler->info.compArgsCount]; + } + + ExplicitParamIntervals& paramIntervals = explicitParamIntervals[argNum]; + size_t index = 0; + assert((argDsc->GetArgReg() != REG_NA) && genIsValidIntReg(argDsc->GetArgReg())); + ClassLayout* layout = argDsc->GetLayout(); + + var_types reg1Type = layout->GetSize() >= TARGET_POINTER_SIZE ? layout->GetGCPtrType(0) : TYP_I_IMPL; + Interval* interval = newInterval(reg1Type); + interval->isExplicitParam = true; + interval->varNum = argNum; + assignPhysReg(argDsc->GetArgReg(), interval); + paramIntervals.Intervals[index++] = interval; + RefPosition* pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetArgReg())); + pos->setRegOptional(true); + + if (argDsc->GetOtherArgReg() != REG_NA) + { + var_types reg2Type = layout->GetSize() >= TARGET_POINTER_SIZE * 2 ? layout->GetGCPtrType(1) : TYP_I_IMPL; + interval = newInterval(reg2Type); + interval->isExplicitParam = true; + interval->varNum = argNum; + assignPhysReg(argDsc->GetOtherArgReg(), interval); + paramIntervals.Intervals[index++] = interval; + + pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetOtherArgReg())); + pos->setRegOptional(true); + } + + assert(index <= ArrLen(paramIntervals.Intervals)); + } + // If there is a secret stub param, it is also live in if (compiler->info.compPublishStubParam) { @@ -4065,6 +4119,25 @@ int LinearScan::BuildReturn(GenTree* tree) return 0; } +int LinearScan::BuildGetParamReg(GenTreeGetParamReg* tree) +{ + assert(tree->gtArgNum < compiler->info.compArgsCount); + assert(explicitParamIntervals != nullptr); + ExplicitParamIntervals& intervals = explicitParamIntervals[tree->gtArgNum]; + + assert((unsigned)tree->gtRegIndex < ArrLen(intervals.Intervals)); + Interval* interval = intervals.Intervals[tree->gtRegIndex]; + + assert(!tree->isContained()); + + RefPosition* useRefPos = newRefPosition(interval, currentLoc, RefTypeUse, tree, RBM_NONE); + useRefPos->setRegOptional(tree->IsRegOptional()); + + BuildDef(tree); + // Consumes no defs. + return 0; +} + //------------------------------------------------------------------------ // supportsSpecialPutArg: Determine if we can support specialPutArgs // diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 5d54c08ebb790..fea74fe24bfa7 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -620,6 +620,10 @@ int LinearScan::BuildNode(GenTree* tree) } break; + case GT_GETPARAM_REG: + srcCount = BuildGetParamReg(tree->AsGetParamReg()); + break; + } // end switch (tree->OperGet()) // We need to be sure that we've set srcCount and dstCount appropriately. diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 52163f4db0cce..36bcc4d9ca435 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -713,8 +713,11 @@ class LocalUses unsigned countReadBacks = 0; weight_t countReadBacksWtd = 0; // For parameters or OSR locals we always need one read back. - if (lcl->lvIsParam || lcl->lvIsOSRLocal) + if ((lcl->lvIsParam && !lcl->lvIsRegArg) || lcl->lvIsOSRLocal) // { + // TODO-CQ: Fields in structs passed in registers can still have + // cost to extract out of the register if they do not map cleanly. + countReadBacks++; countReadBacksWtd += comp->fgFirstBB->getBBWeight(comp); } @@ -818,6 +821,8 @@ class LocalUses return false; } + + //------------------------------------------------------------------------ // ClearInducedAccesses: // Clear the stored induced access metrics. From a7d5fcd830cf3702ae9ae681281f9f292471dcc9 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 13 Sep 2023 02:01:35 +0200 Subject: [PATCH 02/13] WIP --- src/coreclr/jit/codegen.h | 1 - src/coreclr/jit/codegencommon.cpp | 10 +++ src/coreclr/jit/codegenlinear.cpp | 9 +++ src/coreclr/jit/codegenxarch.cpp | 36 ++++----- src/coreclr/jit/gentree.cpp | 5 ++ src/coreclr/jit/jitconfigvalues.h | 2 + src/coreclr/jit/lower.cpp | 50 ++++++++---- src/coreclr/jit/lsra.cpp | 69 ++++++++++++---- src/coreclr/jit/lsra.h | 6 +- src/coreclr/jit/lsrabuild.cpp | 126 +++++++++++++++--------------- src/coreclr/jit/lsraxarch.cpp | 3 +- src/coreclr/jit/promotion.cpp | 2 +- 12 files changed, 200 insertions(+), 119 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index b7de42f85424a..e00dff0f7bacd 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1152,7 +1152,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode); #endif void genCodeForPhysReg(GenTreePhysReg* tree); - void genCodeForGetParamReg(GenTreeGetParamReg* tree); void genCodeForNullCheck(GenTreeIndir* tree); void genCodeForCmpXchg(GenTreeCmpXchg* tree); void genCodeForReuseVal(GenTree* treeNode); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 76c0212ceaba5..f71d54ce357dc 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2939,6 +2939,16 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere continue; } + if (varDsc->lvExplicitParamInit) + { + regState->rsCalleeRegArgMaskLiveIn &= ~genRegMask(varDsc->GetArgReg()); + if (varDsc->GetOtherArgReg() != REG_NA) + { + regState->rsCalleeRegArgMaskLiveIn &= ~genRegMask(varDsc->GetOtherArgReg()); + } + continue; + } + // When we have a promoted struct we have two possible LclVars that can represent the incoming argument // in the regArgTab[], either the original TYP_STRUCT argument or the introduced lvStructField. // We will use the lvStructField if we have a TYPE_INDEPENDENT promoted struct field otherwise diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 1e50106464556..433c3422e32b4 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -1512,6 +1512,15 @@ regNumber CodeGen::genConsumeReg(GenTree* tree) inst_Mov(regType, tree->GetRegNum(), varDsc->GetRegNum(), /* canSkip */ true); } } + else if (tree->OperIs(GT_GETPARAM_REG)) + { + var_types targetType = tree->TypeGet(); + regNumber targetReg = tree->GetRegNum(); + + regNumber srcReg = tree->AsGetParamReg()->GetArgReg(compiler); + + inst_Mov(targetType, targetReg, srcReg, /* canSkip */ true); + } genUnspillRegIfNeeded(tree); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 4124452832b77..4c2a275dfc545 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -1918,6 +1918,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCodeForStoreLclVar(treeNode->AsLclVar()); break; + case GT_GETPARAM_REG: + // Handled in genConsumeReg. + break; + case GT_RETFILT: case GT_RETURN: genReturn(treeNode); @@ -2087,10 +2091,6 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCodeForPhysReg(treeNode->AsPhysReg()); break; - case GT_GETPARAM_REG: - genCodeForGetParamReg(treeNode->AsGetParamReg()); - break; - case GT_NULLCHECK: genCodeForNullCheck(treeNode->AsIndir()); break; @@ -4523,20 +4523,20 @@ void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree) genProduceReg(tree); } -void CodeGen::genCodeForGetParamReg(GenTreeGetParamReg* tree) -{ - assert(tree->OperIs(GT_GETPARAM_REG)); - - var_types targetType = tree->TypeGet(); - regNumber targetReg = tree->GetRegNum(); - - regNumber srcReg = tree->GetArgReg(compiler); - - inst_Mov(targetType, targetReg, srcReg, /* canSkip */ true); - genTransferRegGCState(targetReg, srcReg); - - genProduceReg(tree); -} +//void CodeGen::genCodeForGetParamReg(GenTreeGetParamReg* tree) +//{ +// assert(tree->OperIs(GT_GETPARAM_REG)); +// +// var_types targetType = tree->TypeGet(); +// regNumber targetReg = tree->GetRegNum(); +// +// regNumber srcReg = tree->GetArgReg(compiler); +// +// inst_Mov(targetType, targetReg, srcReg, /* canSkip */ true); +// genTransferRegGCState(targetReg, srcReg); +// +// genProduceReg(tree); +//} //--------------------------------------------------------------------- // genCodeForNullCheck - generate code for a GT_NULLCHECK node diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 22fa888ef447f..81e1dcb59b246 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -8564,8 +8564,13 @@ void GenTreeOp::CheckDivideByConstOptimized(Compiler* comp) regNumber GenTreeGetParamReg::GetArgReg(Compiler* comp) { +#if FEATURE_MULTIREG_ARGS LclVarDsc* argDsc = comp->lvaGetDesc(gtArgNum); return gtRegIndex == 0 ? argDsc->GetArgReg() : argDsc->GetOtherArgReg(); +#endif + + unreached(); + return REG_NA; } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 4079a87631def..b760a03677408 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -248,6 +248,8 @@ CONFIG_STRING(JitStressRange, W("JitStressRange")) // Internal Jit /// JIT Hardware Intrinsics /// CONFIG_INTEGER(EnableIncompleteISAClass, W("EnableIncompleteISAClass"), 0) // Enable testing not-yet-implemented + +CONFIG_INTEGER(JitExplicitParameterDefs, W("JitExplicitParameterDefs"), 1) #endif // defined(DEBUG) CONFIG_METHODSET(JitDisasm, W("JitDisasm")) // Print codegen for given methods diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 5b0943758b937..7bdc77a00b06d 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7566,6 +7566,13 @@ bool Lowering::CheckMultiRegLclVar(GenTreeLclVar* lclNode, int registerCount) void Lowering::LowerMultiregParams() { #if FEATURE_MULTIREG_ARGS +#ifdef DEBUG + if (JitConfig.JitExplicitParameterDefs() == 0) + { + return; + } +#endif + if (comp->fgFirstBB->bbPreds != nullptr) { comp->fgEnsureFirstBBisScratch(); @@ -7592,6 +7599,12 @@ void Lowering::LowerMultiregParams() continue; } + if (!argDsc->lvDoNotEnregister) + { + JITDUMP(" may be enregistered\n"); + continue; + } + regNumber argReg1 = argDsc->GetArgReg(); regNumber argReg2 = argDsc->GetOtherArgReg(); JITDUMP(" regs: %s %s\n", getRegName(argReg1), getRegName(argReg2)); @@ -7618,7 +7631,7 @@ void Lowering::LowerMultiregParams() argDsc->lvExplicitParamInit = 1; auto getCanonType = [layout](unsigned offset) { - unsigned sizeLeft = (layout->GetSize() - offset) % TARGET_POINTER_SIZE; + unsigned sizeLeft = layout->GetSize() - offset; if (sizeLeft >= TARGET_POINTER_SIZE) { return layout->GetGCPtrType(offset / TARGET_POINTER_SIZE); @@ -7652,15 +7665,23 @@ void Lowering::LowerMultiregParams() } } - for (GenTree* node : firstBBRange) + GenTree* next = nullptr; + for (GenTree* cur = firstBBRange.FirstNode(); cur != nullptr; cur = next) { - if (!node->OperIs(GT_LCL_FLD)) + next = cur->gtNext; + + if (!cur->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_STORE_LCL_VAR, GT_STORE_LCL_FLD, GT_GETPARAM_REG)) + { + break; + } + + if (!cur->OperIs(GT_LCL_FLD)) { continue; } - GenTreeLclFld* fld = node->AsLclFld(); - if (fld->TypeIs(TYP_STRUCT)) + GenTreeLclFld* fld = cur->AsLclFld(); + if (!varTypeIsIntegralOrI(fld)) { continue; } @@ -7706,20 +7727,17 @@ void Lowering::LowerMultiregParams() continue; } - if (varTypeIsIntegralOrI(fld)) + LIR::Use fldUse; + if (firstBBRange.TryGetUse(fld, &fldUse)) { - LIR::Use fldUse; - if (firstBBRange.TryGetUse(fld, &fldUse)) - { - JITDUMP("[%06u] is a parameter register use, optimizing it into GETPARAM_REG\n", Compiler::dspTreeID(fld)); - GenTree* newNode = comp->gtNewGetParamRegNode(fld->GetLclNum(), regIndex, fld->TypeGet()); - fldUse.ReplaceWith(newNode); + JITDUMP("[%06u] is a parameter register use, optimizing it into GETPARAM_REG\n", Compiler::dspTreeID(fld)); + GenTree* newNode = comp->gtNewGetParamRegNode(fld->GetLclNum(), regIndex, fld->TypeGet()); + fldUse.ReplaceWith(newNode); - firstBBRange.InsertAfter(fld, newNode); - firstBBRange.Remove(fld); + firstBBRange.InsertAfter(fld, newNode); + firstBBRange.Remove(fld); - DISPTREERANGE(firstBBRange, fldUse.User()); - } + DISPTREERANGE(firstBBRange, fldUse.User()); } } #endif diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 2cda2708284fd..a71807c17ed0b 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -2440,6 +2440,7 @@ void LinearScan::checkLastUses(BasicBlock* block) int LinearScan::getExplicitParamIntervalRegIndex(Interval* interval) { +#if FEATURE_MULTIREG_ARGS assert(interval->isExplicitParam && (interval->varNum < compiler->info.compArgsCount)); assert(explicitParamIntervals != nullptr); for (int i = 0; i < MAX_ARG_REG_COUNT; i++) @@ -2449,6 +2450,7 @@ int LinearScan::getExplicitParamIntervalRegIndex(Interval* interval) return i; } } +#endif noway_assert(!"expected to find explicit param interval reg index"); return -1; @@ -3475,7 +3477,7 @@ void LinearScan::spillInterval(Interval* interval, RefPosition* fromRefPosition // If not allocated a register, Lcl var def/use ref positions even if reg optional // should be marked as spillAfter. Note that if it is a WriteThru interval, the value is always // written to the stack, but the WriteThru indicates that the register is no longer live. - if (fromRefPosition->RegOptional() && !(interval->isLocalVar && fromRefPosition->IsActualRef())) + if (fromRefPosition->RegOptional() && ((!interval->isLocalVar && !interval->isExplicitParam) || !fromRefPosition->IsActualRef())) { fromRefPosition->registerAssignment = RBM_NONE; } @@ -3516,9 +3518,17 @@ void LinearScan::spillInterval(Interval* interval, RefPosition* fromRefPosition // on entry to this block. if (fromRefPosition->nodeLocation <= curBBStartLocation) { - // This must be a lclVar interval - assert(interval->isLocalVar); - setInVarRegForBB(curBBNum, interval->varNum, REG_STK); + if (interval->isLocalVar) + { + setInVarRegForBB(curBBNum, interval->varNum, REG_STK); + } + else + { + assert(interval->isExplicitParam); + // this requires a lookaside data structure to tell prolog generation which temp to spill into. + // This would be necessary if we didn't limit the IR patterns for GETPARAM_REG. + unreached(); + } } } @@ -7041,14 +7051,6 @@ void LinearScan::updateMaxSpill(RefPosition* refPosition) Interval* interval = refPosition->getInterval(); if (!interval->isLocalVar) { - GenTree* treeNode = refPosition->treeNode; - if (treeNode == nullptr) - { - assert(RefTypeIsUse(refType)); - treeNode = interval->firstRefPosition->treeNode; - } - assert(treeNode != nullptr); - // The tmp allocation logic 'normalizes' types to a small number of // types that need distinct stack locations from each other. // Those types are currently gc refs, byrefs, <= 4 byte non-GC items, @@ -7056,13 +7058,29 @@ void LinearScan::updateMaxSpill(RefPosition* refPosition) // LSRA is agnostic to those choices but needs // to know what they are here. var_types type; - if (!treeNode->IsMultiRegNode()) + + if (interval->isExplicitParam) { - type = getDefType(treeNode); + type = interval->registerType; } else { - type = treeNode->GetRegTypeByIndex(refPosition->getMultiRegIdx()); + GenTree* treeNode = refPosition->treeNode; + if (treeNode == nullptr) + { + assert(RefTypeIsUse(refType)); + treeNode = interval->firstRefPosition->treeNode; + } + assert(treeNode != nullptr); + + if (!treeNode->IsMultiRegNode()) + { + type = getDefType(treeNode); + } + else + { + type = treeNode->GetRegTypeByIndex(refPosition->getMultiRegIdx()); + } } type = RegSet::tmpNormalizeType(type); @@ -7192,6 +7210,19 @@ void LinearScan::resolveRegisters() else { assert(interval->isExplicitParam); + // TODO-ExplicitParamDefs: spilling requires a data structure on the side + if (!currentRefPosition->spillAfter && (currentRefPosition->registerAssignment != RBM_NONE)) + { + LclVarDsc* argDsc = compiler->lvaGetDesc(interval->varNum); + assert(currentRefPosition->assignedReg() == (getExplicitParamIntervalRegIndex(interval) == 0 ? argDsc->GetArgReg() : argDsc->GetOtherArgReg())); + } + else + { + interval->isActive = false; + //updateMaxSpill(currentRefPosition); + currentSpill[interval->registerType]++; + maxSpill[interval->registerType] = max(maxSpill[interval->registerType], currentSpill[interval->registerType]); + } } } } @@ -10000,9 +10031,13 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode) Interval* interval = currentRefPosition->getInterval(); assert((interval != nullptr) && (interval->isLocalVar || interval->isExplicitParam)); if (interval->isLocalVar) + { printf(" V%02d", interval->varNum); + } else + { printf(" V%02d,%d", interval->varNum, getExplicitParamIntervalRegIndex(interval)); + } if (mode == LSRA_DUMP_POST) { @@ -10016,7 +10051,7 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode) reg = currentRefPosition->assignedReg(); } - regNumber argReg; + regNumber argReg = REG_NA; if (interval->isLocalVar) { @@ -10025,11 +10060,13 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode) argReg = (varDsc->lvIsRegArg) ? varDsc->GetArgReg() : REG_STK; assert(reg == assignedReg || varDsc->lvRegister == false); } +#if FEATURE_MULTIREG_ARGS else { LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum); argReg = getExplicitParamIntervalRegIndex(interval) == 0 ? varDsc->GetArgReg() : varDsc->GetOtherArgReg(); } +#endif printf("("); if (reg != argReg) diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index bebe0c609c30b..f7c8da756ad57 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -637,6 +637,8 @@ class LinearScan : public LinearScanInterface template void buildIntervals(); + void buildExplicitParamIntervals(); + // This is where the actual assignment is done #ifdef TARGET_ARM64 template @@ -1568,12 +1570,14 @@ class LinearScan : public LinearScanInterface // Map from tracked variable index to Interval*. Interval** localVarIntervals; +#if FEATURE_MULTIREG_ARGS struct ExplicitParamIntervals { Interval* Intervals[MAX_ARG_REG_COUNT] = {}; }; ExplicitParamIntervals* explicitParamIntervals; +#endif int getExplicitParamIntervalRegIndex(Interval* interval); @@ -1945,8 +1949,6 @@ class LinearScan : public LinearScanInterface // These methods return the number of sources. int BuildNode(GenTree* tree); - int BuildGetParamReg(GenTreeGetParamReg* tree); - void UpdatePreferencesOfDyingLocal(Interval* interval); void getTgtPrefOperands(GenTree* tree, GenTree* op1, GenTree* op2, bool* prefOp1, bool* prefOp2); bool supportsSpecialPutArg(); diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 5e4449640e1b6..91c96cb59e909 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -2387,56 +2387,14 @@ void LinearScan::buildIntervals() else { noway_assert(argDsc->lvIsParam); - if (!argDsc->lvTracked && argDsc->lvIsRegArg) + if ((!argDsc->lvTracked || argDsc->lvExplicitParamInit) && argDsc->lvIsRegArg) { updateRegStateForArg(argDsc); } } } - explicitParamIntervals = nullptr; - for (unsigned argNum = 0; argNum < compiler->info.compArgsCount; argNum++) - { - LclVarDsc* argDsc = compiler->lvaGetDesc(argNum); - if (!argDsc->lvExplicitParamInit) - { - continue; - } - - if (explicitParamIntervals == nullptr) - { - explicitParamIntervals = new (compiler, CMK_LSRA_Interval) ExplicitParamIntervals[compiler->info.compArgsCount]; - } - - ExplicitParamIntervals& paramIntervals = explicitParamIntervals[argNum]; - size_t index = 0; - assert((argDsc->GetArgReg() != REG_NA) && genIsValidIntReg(argDsc->GetArgReg())); - ClassLayout* layout = argDsc->GetLayout(); - - var_types reg1Type = layout->GetSize() >= TARGET_POINTER_SIZE ? layout->GetGCPtrType(0) : TYP_I_IMPL; - Interval* interval = newInterval(reg1Type); - interval->isExplicitParam = true; - interval->varNum = argNum; - assignPhysReg(argDsc->GetArgReg(), interval); - paramIntervals.Intervals[index++] = interval; - RefPosition* pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetArgReg())); - pos->setRegOptional(true); - - if (argDsc->GetOtherArgReg() != REG_NA) - { - var_types reg2Type = layout->GetSize() >= TARGET_POINTER_SIZE * 2 ? layout->GetGCPtrType(1) : TYP_I_IMPL; - interval = newInterval(reg2Type); - interval->isExplicitParam = true; - interval->varNum = argNum; - assignPhysReg(argDsc->GetOtherArgReg(), interval); - paramIntervals.Intervals[index++] = interval; - - pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetOtherArgReg())); - pos->setRegOptional(true); - } - - assert(index <= ArrLen(paramIntervals.Intervals)); - } + buildExplicitParamIntervals(); // If there is a secret stub param, it is also live in if (compiler->info.compPublishStubParam) @@ -2880,6 +2838,55 @@ void LinearScan::buildIntervals() #endif // DEBUG } +void LinearScan::buildExplicitParamIntervals() +{ +#if FEATURE_MULTIREG_ARGS + explicitParamIntervals = nullptr; + for (unsigned argNum = 0; argNum < compiler->info.compArgsCount; argNum++) + { + LclVarDsc* argDsc = compiler->lvaGetDesc(argNum); + if (!argDsc->lvExplicitParamInit) + { + continue; + } + + if (explicitParamIntervals == nullptr) + { + explicitParamIntervals = new (compiler, CMK_LSRA_Interval) ExplicitParamIntervals[compiler->info.compArgsCount]; + } + + ExplicitParamIntervals& paramIntervals = explicitParamIntervals[argNum]; + size_t index = 0; + assert((argDsc->GetArgReg() != REG_NA) && genIsValidIntReg(argDsc->GetArgReg())); + ClassLayout* layout = argDsc->GetLayout(); + + var_types reg1Type = layout->GetSize() >= TARGET_POINTER_SIZE ? layout->GetGCPtrType(0) : TYP_I_IMPL; + Interval* interval = newInterval(reg1Type); + interval->isExplicitParam = true; + interval->varNum = argNum; + assignPhysReg(argDsc->GetArgReg(), interval); + paramIntervals.Intervals[index++] = interval; + RefPosition* pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetArgReg())); + pos->setRegOptional(true); + + if (argDsc->GetOtherArgReg() != REG_NA) + { + var_types reg2Type = layout->GetSize() >= TARGET_POINTER_SIZE * 2 ? layout->GetGCPtrType(1) : TYP_I_IMPL; + interval = newInterval(reg2Type); + interval->isExplicitParam = true; + interval->varNum = argNum; + assignPhysReg(argDsc->GetOtherArgReg(), interval); + paramIntervals.Intervals[index++] = interval; + + pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetOtherArgReg())); + pos->setRegOptional(true); + } + + assert(index <= ArrLen(paramIntervals.Intervals)); + } +#endif +} + #ifdef DEBUG //------------------------------------------------------------------------ // validateIntervals: A DEBUG-only method that checks that: @@ -3297,6 +3304,18 @@ RefPosition* LinearScan::BuildUse(GenTree* operand, regMaskTP candidates, int mu buildUpperVectorRestoreRefPosition(interval, currentLoc, operand, true, (unsigned)multiRegIdx); #endif } +#if FEATURE_MULTIREG_ARGS + else if (operand->OperIs(GT_GETPARAM_REG)) + { + GenTreeGetParamReg* gpr = operand->AsGetParamReg(); + assert(gpr->gtArgNum < compiler->info.compArgsCount); + assert(explicitParamIntervals != nullptr); + ExplicitParamIntervals& intervals = explicitParamIntervals[gpr->gtArgNum]; + + assert((unsigned)gpr->gtRegIndex < ArrLen(intervals.Intervals)); + interval = intervals.Intervals[gpr->gtRegIndex]; + } +#endif else { RefInfoListNode* refInfo = defList.removeListNode(operand, multiRegIdx); @@ -4119,25 +4138,6 @@ int LinearScan::BuildReturn(GenTree* tree) return 0; } -int LinearScan::BuildGetParamReg(GenTreeGetParamReg* tree) -{ - assert(tree->gtArgNum < compiler->info.compArgsCount); - assert(explicitParamIntervals != nullptr); - ExplicitParamIntervals& intervals = explicitParamIntervals[tree->gtArgNum]; - - assert((unsigned)tree->gtRegIndex < ArrLen(intervals.Intervals)); - Interval* interval = intervals.Intervals[tree->gtRegIndex]; - - assert(!tree->isContained()); - - RefPosition* useRefPos = newRefPosition(interval, currentLoc, RefTypeUse, tree, RBM_NONE); - useRefPos->setRegOptional(tree->IsRegOptional()); - - BuildDef(tree); - // Consumes no defs. - return 0; -} - //------------------------------------------------------------------------ // supportsSpecialPutArg: Determine if we can support specialPutArgs // diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index fea74fe24bfa7..da89e672815a2 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -621,8 +621,7 @@ int LinearScan::BuildNode(GenTree* tree) break; case GT_GETPARAM_REG: - srcCount = BuildGetParamReg(tree->AsGetParamReg()); - break; + return 0; } // end switch (tree->OperGet()) diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 36bcc4d9ca435..0ff4e8f4fd67b 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -713,7 +713,7 @@ class LocalUses unsigned countReadBacks = 0; weight_t countReadBacksWtd = 0; // For parameters or OSR locals we always need one read back. - if ((lcl->lvIsParam && !lcl->lvIsRegArg) || lcl->lvIsOSRLocal) // + if ((lcl->lvIsParam/* && !lcl->lvIsRegArg*/) || lcl->lvIsOSRLocal) // { // TODO-CQ: Fields in structs passed in registers can still have // cost to extract out of the register if they do not map cleanly. From d5772be9b6aac6c4e73f46e7ab7b32c6abda9684 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 13 Sep 2023 22:43:24 +0200 Subject: [PATCH 03/13] Fix build, skip SSE cases --- src/coreclr/jit/codegencommon.cpp | 2 ++ src/coreclr/jit/lower.cpp | 7 +++++++ src/coreclr/jit/lsra.cpp | 11 +++++++---- src/coreclr/jit/lsra.h | 3 ++- src/coreclr/jit/lsrabuild.cpp | 1 - 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index f71d54ce357dc..c0e9db6632027 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2942,10 +2942,12 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere if (varDsc->lvExplicitParamInit) { regState->rsCalleeRegArgMaskLiveIn &= ~genRegMask(varDsc->GetArgReg()); +#if FEATURE_MULTIREG_ARGS if (varDsc->GetOtherArgReg() != REG_NA) { regState->rsCalleeRegArgMaskLiveIn &= ~genRegMask(varDsc->GetOtherArgReg()); } +#endif continue; } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 7bdc77a00b06d..aa99baa7aff74 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7618,6 +7618,13 @@ void Lowering::LowerMultiregParams() continue; } + // We support no second reg or a GPR second reg. + if ((argReg2 != REG_NA) && !genIsValidIntReg(argReg2)) + { + JITDUMP(" second reg is %s\n", getRegName(argReg2)); + continue; + } + ClassLayout* layout = argDsc->GetLayout(); if (((layout->GetSize() % TARGET_POINTER_SIZE) != 0) && !isPow2(layout->GetSize() % TARGET_POINTER_SIZE)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index a71807c17ed0b..31f2114e75332 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -1353,7 +1353,7 @@ PhaseStatus LinearScan::doLinearScan() // We initialize this in the constructor based on opt settings, // but we don't want to spend time on the lclVar parts of LinearScan // if we have no tracked locals. - if (enregisterLocalVars && (compiler->lvaTrackedCount == 0)) + if (enregisterLocalVars && (compiler->lvaTrackedCount == 0) && (explicitParamIntervals == nullptr)) { enregisterLocalVars = false; } @@ -7214,14 +7214,17 @@ void LinearScan::resolveRegisters() if (!currentRefPosition->spillAfter && (currentRefPosition->registerAssignment != RBM_NONE)) { LclVarDsc* argDsc = compiler->lvaGetDesc(interval->varNum); +#if FEATURE_MULTIREG_ARGS assert(currentRefPosition->assignedReg() == (getExplicitParamIntervalRegIndex(interval) == 0 ? argDsc->GetArgReg() : argDsc->GetOtherArgReg())); +#endif } else { - interval->isActive = false; + //interval->isActive = false; //updateMaxSpill(currentRefPosition); - currentSpill[interval->registerType]++; - maxSpill[interval->registerType] = max(maxSpill[interval->registerType], currentSpill[interval->registerType]); + //currentSpill[interval->registerType]++; + //maxSpill[interval->registerType] = max(maxSpill[interval->registerType], currentSpill[interval->registerType]); + unreached(); } } } diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index f7c8da756ad57..ddc2946350c47 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -2152,6 +2152,7 @@ class Interval : public Referenceable , isUpperVector(false) , isPartiallySpilled(false) #endif + , isExplicitParam(false) , isWriteThru(false) , isSingleDef(false) #ifdef DEBUG @@ -2457,7 +2458,7 @@ class RefPosition #endif // TARGET_ARM64 // Last Use - this may be true for multiple RefPositions in the same Interval - unsigned char lastUse : 1; + unsigned char lastUse; // Spill and Copy info // reload indicates that the value was spilled, and must be reloaded here. diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 91c96cb59e909..9f3f73edaab8d 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -174,7 +174,6 @@ Interval* LinearScan::newInterval(RegisterType theRegisterType) // Notes: // This is used to create RefPositions for both RegRecords and Intervals, // so it does only the common initialization. -// RefPosition* LinearScan::newRefPositionRaw(LsraLocation nodeLocation, GenTree* treeNode, RefType refType) { refPositions.emplace_back(curBBNum, nodeLocation, treeNode, refType DEBUG_ARG(currBuildNode)); From 01111dd57e2bddbf63ab2f6de95df3426027d682 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 13 Sep 2023 22:43:51 +0200 Subject: [PATCH 04/13] Run jit-format --- src/coreclr/jit/codegenxarch.cpp | 2 +- src/coreclr/jit/gentree.h | 6 ++---- src/coreclr/jit/jitconfigvalues.h | 2 +- src/coreclr/jit/lower.cpp | 6 +++--- src/coreclr/jit/lsra.cpp | 35 ++++++++++++++++++------------- src/coreclr/jit/lsrabuild.cpp | 20 ++++++++++-------- src/coreclr/jit/lsraxarch.cpp | 4 ++-- src/coreclr/jit/promotion.cpp | 4 +--- 8 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 4c2a275dfc545..c0ad53c390289 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4523,7 +4523,7 @@ void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree) genProduceReg(tree); } -//void CodeGen::genCodeForGetParamReg(GenTreeGetParamReg* tree) +// void CodeGen::genCodeForGetParamReg(GenTreeGetParamReg* tree) //{ // assert(tree->OperIs(GT_GETPARAM_REG)); // diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 2cd154c1c609c..e60ea625a977d 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3088,12 +3088,10 @@ struct GenTreePhysReg : public GenTree struct GenTreeGetParamReg : public GenTree { unsigned gtArgNum; - int gtRegIndex; + int gtRegIndex; GenTreeGetParamReg(unsigned argNum, int regIndex, var_types type) - : GenTree(GT_GETPARAM_REG, type) - , gtArgNum(argNum) - , gtRegIndex(regIndex) + : GenTree(GT_GETPARAM_REG, type), gtArgNum(argNum), gtRegIndex(regIndex) { } diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index b760a03677408..46789b7a6d3f9 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -250,7 +250,7 @@ CONFIG_STRING(JitStressRange, W("JitStressRange")) // Internal Jit CONFIG_INTEGER(EnableIncompleteISAClass, W("EnableIncompleteISAClass"), 0) // Enable testing not-yet-implemented CONFIG_INTEGER(JitExplicitParameterDefs, W("JitExplicitParameterDefs"), 1) -#endif // defined(DEBUG) +#endif // defined(DEBUG) CONFIG_METHODSET(JitDisasm, W("JitDisasm")) // Print codegen for given methods CONFIG_INTEGER(JitDisasmTesting, W("JitDisasmTesting"), 0) // Display BEGIN METHOD/END METHOD anchors for disasm testing diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index aa99baa7aff74..43b5d37aecd34 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7578,7 +7578,7 @@ void Lowering::LowerMultiregParams() comp->fgEnsureFirstBBisScratch(); } - GenTree* insertAfter = nullptr; + GenTree* insertAfter = nullptr; LIR::Range& firstBBRange = LIR::AsRange(comp->fgFirstBB); // Write (parts of) parameters passed in registers back to the local. @@ -7650,10 +7650,10 @@ void Lowering::LowerMultiregParams() } return TYP_INT; - }; + }; GenTree* paramReg1 = comp->gtNewGetParamRegNode(argNum, 0, getCanonType(0)); - GenTree* store1 = comp->gtNewStoreLclFldNode(argNum, paramReg1->TypeGet(), 0, paramReg1); + GenTree* store1 = comp->gtNewStoreLclFldNode(argNum, paramReg1->TypeGet(), 0, paramReg1); firstBBRange.InsertAfter(insertAfter, paramReg1, store1); insertAfter = store1; diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 31f2114e75332..49c62f04584d8 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -2437,7 +2437,6 @@ void LinearScan::checkLastUses(BasicBlock* block) } #endif // DEBUG - int LinearScan::getExplicitParamIntervalRegIndex(Interval* interval) { #if FEATURE_MULTIREG_ARGS @@ -3477,7 +3476,8 @@ void LinearScan::spillInterval(Interval* interval, RefPosition* fromRefPosition // If not allocated a register, Lcl var def/use ref positions even if reg optional // should be marked as spillAfter. Note that if it is a WriteThru interval, the value is always // written to the stack, but the WriteThru indicates that the register is no longer live. - if (fromRefPosition->RegOptional() && ((!interval->isLocalVar && !interval->isExplicitParam) || !fromRefPosition->IsActualRef())) + if (fromRefPosition->RegOptional() && + ((!interval->isLocalVar && !interval->isExplicitParam) || !fromRefPosition->IsActualRef())) { fromRefPosition->registerAssignment = RBM_NONE; } @@ -5238,11 +5238,12 @@ void LinearScan::allocateRegisters() allocate = false; } else if (refType == RefTypeParamDef && (varDsc->lvRefCntWtd() <= BB_UNITY_WEIGHT) && - (!currentRefPosition.lastUse || (currentInterval->physReg == REG_STK))) + (!currentRefPosition.lastUse || (currentInterval->physReg == REG_STK))) { // If this is a low ref-count parameter, and either it is used (def is not the last use) or it's // passed on the stack, don't allocate a register. - // Note that if this is an unused register parameter we don't want to set allocate to false because that + // Note that if this is an unused register parameter we don't want to set allocate to false because + // that // will cause us to allocate stack space to spill it. allocate = false; } @@ -7193,7 +7194,7 @@ void LinearScan::resolveRegisters() if (interval->isLocalVar) { resolveLocalRef(nullptr, nullptr, currentRefPosition); - regNumber reg = REG_STK; + regNumber reg = REG_STK; int varIndex = interval->getVarIndex(compiler); if (!currentRefPosition->spillAfter && currentRefPosition->registerAssignment != RBM_NONE) @@ -7202,7 +7203,7 @@ void LinearScan::resolveRegisters() } else { - reg = REG_STK; + reg = REG_STK; interval->isActive = false; } setVarReg(entryVarToRegMap, varIndex, reg); @@ -7215,15 +7216,18 @@ void LinearScan::resolveRegisters() { LclVarDsc* argDsc = compiler->lvaGetDesc(interval->varNum); #if FEATURE_MULTIREG_ARGS - assert(currentRefPosition->assignedReg() == (getExplicitParamIntervalRegIndex(interval) == 0 ? argDsc->GetArgReg() : argDsc->GetOtherArgReg())); + assert(currentRefPosition->assignedReg() == (getExplicitParamIntervalRegIndex(interval) == 0 + ? argDsc->GetArgReg() + : argDsc->GetOtherArgReg())); #endif } else { - //interval->isActive = false; - //updateMaxSpill(currentRefPosition); - //currentSpill[interval->registerType]++; - //maxSpill[interval->registerType] = max(maxSpill[interval->registerType], currentSpill[interval->registerType]); + // interval->isActive = false; + // updateMaxSpill(currentRefPosition); + // currentSpill[interval->registerType]++; + // maxSpill[interval->registerType] = max(maxSpill[interval->registerType], + // currentSpill[interval->registerType]); unreached(); } } @@ -10058,16 +10062,17 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode) if (interval->isLocalVar) { - const LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum); - regNumber assignedReg = varDsc->GetRegNum(); - argReg = (varDsc->lvIsRegArg) ? varDsc->GetArgReg() : REG_STK; + const LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum); + regNumber assignedReg = varDsc->GetRegNum(); + argReg = (varDsc->lvIsRegArg) ? varDsc->GetArgReg() : REG_STK; assert(reg == assignedReg || varDsc->lvRegister == false); } #if FEATURE_MULTIREG_ARGS else { LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum); - argReg = getExplicitParamIntervalRegIndex(interval) == 0 ? varDsc->GetArgReg() : varDsc->GetOtherArgReg(); + argReg = getExplicitParamIntervalRegIndex(interval) == 0 ? varDsc->GetArgReg() + : varDsc->GetOtherArgReg(); } #endif diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 9f3f73edaab8d..c5e0305e64480 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -2840,7 +2840,7 @@ void LinearScan::buildIntervals() void LinearScan::buildExplicitParamIntervals() { #if FEATURE_MULTIREG_ARGS - explicitParamIntervals = nullptr; + explicitParamIntervals = nullptr; for (unsigned argNum = 0; argNum < compiler->info.compArgsCount; argNum++) { LclVarDsc* argDsc = compiler->lvaGetDesc(argNum); @@ -2851,29 +2851,31 @@ void LinearScan::buildExplicitParamIntervals() if (explicitParamIntervals == nullptr) { - explicitParamIntervals = new (compiler, CMK_LSRA_Interval) ExplicitParamIntervals[compiler->info.compArgsCount]; + explicitParamIntervals = + new (compiler, CMK_LSRA_Interval) ExplicitParamIntervals[compiler->info.compArgsCount]; } ExplicitParamIntervals& paramIntervals = explicitParamIntervals[argNum]; - size_t index = 0; + size_t index = 0; assert((argDsc->GetArgReg() != REG_NA) && genIsValidIntReg(argDsc->GetArgReg())); ClassLayout* layout = argDsc->GetLayout(); - var_types reg1Type = layout->GetSize() >= TARGET_POINTER_SIZE ? layout->GetGCPtrType(0) : TYP_I_IMPL; - Interval* interval = newInterval(reg1Type); + var_types reg1Type = layout->GetSize() >= TARGET_POINTER_SIZE ? layout->GetGCPtrType(0) : TYP_I_IMPL; + Interval* interval = newInterval(reg1Type); interval->isExplicitParam = true; - interval->varNum = argNum; + interval->varNum = argNum; assignPhysReg(argDsc->GetArgReg(), interval); paramIntervals.Intervals[index++] = interval; - RefPosition* pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetArgReg())); + RefPosition* pos = + newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, genRegMask(argDsc->GetArgReg())); pos->setRegOptional(true); if (argDsc->GetOtherArgReg() != REG_NA) { var_types reg2Type = layout->GetSize() >= TARGET_POINTER_SIZE * 2 ? layout->GetGCPtrType(1) : TYP_I_IMPL; - interval = newInterval(reg2Type); + interval = newInterval(reg2Type); interval->isExplicitParam = true; - interval->varNum = argNum; + interval->varNum = argNum; assignPhysReg(argDsc->GetOtherArgReg(), interval); paramIntervals.Intervals[index++] = interval; diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index da89e672815a2..c7f75fbc08bfc 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -620,8 +620,8 @@ int LinearScan::BuildNode(GenTree* tree) } break; - case GT_GETPARAM_REG: - return 0; + case GT_GETPARAM_REG: + return 0; } // end switch (tree->OperGet()) diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 0ff4e8f4fd67b..b628b1f8c7da8 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -713,7 +713,7 @@ class LocalUses unsigned countReadBacks = 0; weight_t countReadBacksWtd = 0; // For parameters or OSR locals we always need one read back. - if ((lcl->lvIsParam/* && !lcl->lvIsRegArg*/) || lcl->lvIsOSRLocal) // + if ((lcl->lvIsParam /* && !lcl->lvIsRegArg*/) || lcl->lvIsOSRLocal) // { // TODO-CQ: Fields in structs passed in registers can still have // cost to extract out of the register if they do not map cleanly. @@ -821,8 +821,6 @@ class LocalUses return false; } - - //------------------------------------------------------------------------ // ClearInducedAccesses: // Clear the stored induced access metrics. From 6d82c81c8e7620c44383e88eadb52ac8d54ef6fb Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 13 Sep 2023 23:11:12 +0200 Subject: [PATCH 05/13] Fix lastUse --- src/coreclr/jit/lsra.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index ddc2946350c47..17b057d78889c 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -2458,7 +2458,7 @@ class RefPosition #endif // TARGET_ARM64 // Last Use - this may be true for multiple RefPositions in the same Interval - unsigned char lastUse; + unsigned char lastUse : 1; // Spill and Copy info // reload indicates that the value was spilled, and must be reloaded here. From a8699d217874c237d0e3afef1212e628d1391d67 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 13 Sep 2023 23:33:08 +0200 Subject: [PATCH 06/13] Fix gtlist.h --- src/coreclr/jit/gtlist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index df21d2ac06aac..7b38184b08b39 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -315,7 +315,7 @@ GTNODE(PUTARG_STK , GenTreePutArgStk ,0,0,GTK_UNOP|GTK_NOVALUE|DBK_NOTHI #if FEATURE_ARG_SPLIT GTNODE(PUTARG_SPLIT , GenTreePutArgSplit ,0,0,GTK_UNOP|DBK_NOTHIR) // operator that places outgoing arg in registers with stack (split struct in ARM32) #endif // FEATURE_ARG_SPLIT -GTNODE(GETPARAM_REG , GenTreePhysReg ,0,0,GTK_LEAF|DBK_NOTHIR) // use of parameter; only expected at the beginning of the first BB. +GTNODE(GETPARAM_REG , GenTreeGetParamReg ,0,0,GTK_LEAF|DBK_NOTHIR) // use of parameter; only expected at the beginning of the first BB. GTNODE(SWAP , GenTreeOp ,0,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) // op1 and op2 swap (registers) GTNODE(COPY , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies GTNODE(RELOAD , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. From 38ecd44ccf1866c4f4008e232de4c86690ebd028 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 13 Sep 2023 23:51:25 +0200 Subject: [PATCH 07/13] Fix build and assert --- src/coreclr/jit/lsra.cpp | 2 +- src/coreclr/jit/lsra.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 49c62f04584d8..928faead16b46 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -1752,7 +1752,7 @@ void LinearScan::identifyCandidates() VarSetOps::AssignNoCopy(compiler, splitOrSpilledVars, VarSetOps::MakeEmpty(compiler)); // We set enregisterLocalVars to true only if there are tracked lclVars - assert(compiler->lvaCount != 0); + assert(compiler->lvaCount != 0 || (explicitParamIntervals != nullptr)); } else if (compiler->lvaCount == 0) { diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index 17b057d78889c..b9798c42c6afa 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -1570,14 +1570,12 @@ class LinearScan : public LinearScanInterface // Map from tracked variable index to Interval*. Interval** localVarIntervals; -#if FEATURE_MULTIREG_ARGS struct ExplicitParamIntervals { Interval* Intervals[MAX_ARG_REG_COUNT] = {}; }; ExplicitParamIntervals* explicitParamIntervals; -#endif int getExplicitParamIntervalRegIndex(Interval* interval); From c5bd66457de664dcd8e531e1326c3327f2893fca Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 14 Sep 2023 01:17:19 +0200 Subject: [PATCH 08/13] Fix for arm64, disable for arm32 --- src/coreclr/jit/codegenarmarch.cpp | 4 ++++ src/coreclr/jit/codegenxarch.cpp | 15 --------------- src/coreclr/jit/lclvars.cpp | 2 ++ src/coreclr/jit/lower.cpp | 14 ++++++++++++++ src/coreclr/jit/lsra.cpp | 1 + src/coreclr/jit/lsraarm.cpp | 3 +++ src/coreclr/jit/lsraarm64.cpp | 3 +++ 7 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index afeb4e1a439d5..2b793ae369536 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -277,6 +277,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCodeForStoreLclVar(treeNode->AsLclVar()); break; + case GT_GETPARAM_REG: + // Handled in genConsumeReg. + break; + case GT_RETFILT: case GT_RETURN: genReturn(treeNode); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index c0ad53c390289..f733358306da5 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4523,21 +4523,6 @@ void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree) genProduceReg(tree); } -// void CodeGen::genCodeForGetParamReg(GenTreeGetParamReg* tree) -//{ -// assert(tree->OperIs(GT_GETPARAM_REG)); -// -// var_types targetType = tree->TypeGet(); -// regNumber targetReg = tree->GetRegNum(); -// -// regNumber srcReg = tree->GetArgReg(compiler); -// -// inst_Mov(targetType, targetReg, srcReg, /* canSkip */ true); -// genTransferRegGCState(targetReg, srcReg); -// -// genProduceReg(tree); -//} - //--------------------------------------------------------------------- // genCodeForNullCheck - generate code for a GT_NULLCHECK node // diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index e1a23da69d4a5..93731f300a0ce 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1163,6 +1163,8 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un // registers and stack { printf(" stack slots:%d", cSlots - ix); + JITDUMP("%s\n is reg arg: %s\n", getRegName(varDsc->GetOtherArgReg()), + varDsc->lvIsRegArg ? "yes" : "no"); break; } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 43b5d37aecd34..f1a4fb7964d94 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7573,6 +7573,10 @@ void Lowering::LowerMultiregParams() } #endif +#ifdef TARGET_ARM + return; +#endif + if (comp->fgFirstBB->bbPreds != nullptr) { comp->fgEnsureFirstBBisScratch(); @@ -7625,6 +7629,16 @@ void Lowering::LowerMultiregParams() continue; } + // Hacky fix for bad GetOtherArgReg() in case of arg split to x7, stack on win-arm64. + // We should fix this. + if (compFeatureArgSplit() && hasFixedRetBuffReg() && (argReg2 == theFixedRetBuffReg())) + { + JITDUMP(" second reg is %s\n", getRegName(argReg2)); + continue; + } + + // if ( hasFixedRetBuffReg() && (argReg2 == + ClassLayout* layout = argDsc->GetLayout(); if (((layout->GetSize() % TARGET_POINTER_SIZE) != 0) && !isPow2(layout->GetSize() % TARGET_POINTER_SIZE)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 928faead16b46..26b635037e1a9 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -3322,6 +3322,7 @@ void LinearScan::checkAndAssignInterval(RegRecord* regRec, Interval* interval) // RefPosition for the Interval that was NOT a copyReg. if (assignedInterval->assignedReg == regRec) { + assignedInterval->dump(compiler); assert(assignedInterval->isActive == false); assignedInterval->physReg = REG_NA; } diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp index c16cc9162b3d4..e1b49a1a6aed4 100644 --- a/src/coreclr/jit/lsraarm.cpp +++ b/src/coreclr/jit/lsraarm.cpp @@ -734,6 +734,9 @@ int LinearScan::BuildNode(GenTree* tree) BuildDef(tree); break; + case GT_GETPARAM_REG: + return 0; + default: #ifdef DEBUG char message[256]; diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 4322a75390ab1..47fe52ff3d443 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -1283,6 +1283,9 @@ int LinearScan::BuildNode(GenTree* tree) srcCount = BuildSelect(tree->AsOp()); break; + case GT_GETPARAM_REG: + return 0; + } // end switch (tree->OperGet()) if (tree->IsUnusedValue() && (dstCount != 0)) From 9072cb2cdaf329f58995b93edb817bda13473b69 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 14 Sep 2023 01:53:30 +0200 Subject: [PATCH 09/13] Oops --- src/coreclr/jit/lclvars.cpp | 2 -- src/coreclr/jit/lower.cpp | 2 -- src/coreclr/jit/lsra.cpp | 1 - 3 files changed, 5 deletions(-) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 93731f300a0ce..e1a23da69d4a5 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1163,8 +1163,6 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un // registers and stack { printf(" stack slots:%d", cSlots - ix); - JITDUMP("%s\n is reg arg: %s\n", getRegName(varDsc->GetOtherArgReg()), - varDsc->lvIsRegArg ? "yes" : "no"); break; } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index f1a4fb7964d94..59627be67dac4 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7637,8 +7637,6 @@ void Lowering::LowerMultiregParams() continue; } - // if ( hasFixedRetBuffReg() && (argReg2 == - ClassLayout* layout = argDsc->GetLayout(); if (((layout->GetSize() % TARGET_POINTER_SIZE) != 0) && !isPow2(layout->GetSize() % TARGET_POINTER_SIZE)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 26b635037e1a9..928faead16b46 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -3322,7 +3322,6 @@ void LinearScan::checkAndAssignInterval(RegRecord* regRec, Interval* interval) // RefPosition for the Interval that was NOT a copyReg. if (assignedInterval->assignedReg == regRec) { - assignedInterval->dump(compiler); assert(assignedInterval->isActive == false); assignedInterval->physReg = REG_NA; } From 176b8eba8e1b8309a66af497ce595a85d7d7394f Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 14 Sep 2023 12:47:09 +0200 Subject: [PATCH 10/13] Fixes - Allow GT_IL_OFFSET, GT_NOP - Skip OSR locals that won't be in registers - Make use of pointer-size alignment for frame allocations --- src/coreclr/jit/lower.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 59627be67dac4..3f99d7f05efa7 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7609,6 +7609,12 @@ void Lowering::LowerMultiregParams() continue; } + if (argDsc->lvIsOSRLocal) + { + JITDUMP(" is an OSR local\n"); + continue; + } + regNumber argReg1 = argDsc->GetArgReg(); regNumber argReg2 = argDsc->GetOtherArgReg(); JITDUMP(" regs: %s %s\n", getRegName(argReg1), getRegName(argReg2)); @@ -7649,19 +7655,18 @@ void Lowering::LowerMultiregParams() JITDUMP("Marking V%02u as an explicitly inited parameter\n", argNum); argDsc->lvExplicitParamInit = 1; - auto getCanonType = [layout](unsigned offset) { - unsigned sizeLeft = layout->GetSize() - offset; - if (sizeLeft >= TARGET_POINTER_SIZE) + auto getCanonType = [layout, argDsc](unsigned offset) { + unsigned structSizeLeft = layout->GetSize() - offset; + if (structSizeLeft >= TARGET_POINTER_SIZE) { return layout->GetGCPtrType(offset / TARGET_POINTER_SIZE); } - if (sizeLeft > 4) - { - return TYP_LONG; - } - - return TYP_INT; + // Structs are always pointer size aligned on the frame, so we can + // always store the full register (allowing e.g. str,str -> stp + // optimization). + assert((argDsc->lvSize() - offset) >= TARGET_POINTER_SIZE); + return TYP_I_IMPL; }; GenTree* paramReg1 = comp->gtNewGetParamRegNode(argNum, 0, getCanonType(0)); @@ -7689,7 +7694,8 @@ void Lowering::LowerMultiregParams() { next = cur->gtNext; - if (!cur->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_STORE_LCL_VAR, GT_STORE_LCL_FLD, GT_GETPARAM_REG)) + if (!cur->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_STORE_LCL_VAR, GT_STORE_LCL_FLD, GT_GETPARAM_REG, GT_IL_OFFSET, + GT_NOP)) { break; } From d6ce275ff57d560c62e9098879a04a831e356e09 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 14 Sep 2023 13:18:51 +0200 Subject: [PATCH 11/13] Normalize properly --- src/coreclr/jit/lower.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 3f99d7f05efa7..73a18a14749c5 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7756,10 +7756,16 @@ void Lowering::LowerMultiregParams() if (firstBBRange.TryGetUse(fld, &fldUse)) { JITDUMP("[%06u] is a parameter register use, optimizing it into GETPARAM_REG\n", Compiler::dspTreeID(fld)); - GenTree* newNode = comp->gtNewGetParamRegNode(fld->GetLclNum(), regIndex, fld->TypeGet()); - fldUse.ReplaceWith(newNode); + GenTree* newNode = comp->gtNewGetParamRegNode(fld->GetLclNum(), regIndex, genActualType(fld)); + firstBBRange.InsertBefore(fld, newNode); - firstBBRange.InsertAfter(fld, newNode); + if (varTypeIsSmall(fld)) + { + newNode = comp->gtNewCastNode(genActualType(fld), newNode, false, fld->TypeGet()); + firstBBRange.InsertBefore(fld, newNode); + } + + fldUse.ReplaceWith(newNode); firstBBRange.Remove(fld); DISPTREERANGE(firstBBRange, fldUse.User()); From 6f73ab85737a33165489baf91a73835829d75295 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 14 Sep 2023 14:06:54 +0200 Subject: [PATCH 12/13] Handle small compares on xarch, skip contained nodes --- src/coreclr/jit/lower.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 73a18a14749c5..72fa793e5c656 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7705,6 +7705,11 @@ void Lowering::LowerMultiregParams() continue; } + if (cur->isContained()) + { + continue; + } + GenTreeLclFld* fld = cur->AsLclFld(); if (!varTypeIsIntegralOrI(fld)) { @@ -7761,8 +7766,25 @@ void Lowering::LowerMultiregParams() if (varTypeIsSmall(fld)) { - newNode = comp->gtNewCastNode(genActualType(fld), newNode, false, fld->TypeGet()); - firstBBRange.InsertBefore(fld, newNode); + // Hacky: compares on XARCH are special and do not need the cast (and can assert if we insert it here). + GenTree* user = fldUse.User(); + bool isSmallCmp = false; +#ifdef TARGET_XARCH + if (user->OperIsCompare() || user->OperIs(GT_CMP, GT_TEST)) + { + if (user->gtGetOp1()->TypeGet() == user->gtGetOp2()->TypeGet()) + { + isSmallCmp = true; + newNode->gtType = fld->TypeGet(); + } + } +#endif + + if (!isSmallCmp) + { + newNode = comp->gtNewCastNode(genActualType(fld), newNode, false, fld->TypeGet()); + firstBBRange.InsertBefore(fld, newNode); + } } fldUse.ReplaceWith(newNode); From 0289bd48fc23da66fc4e83a6ff2fe553683b5937 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 14 Sep 2023 15:41:22 +0200 Subject: [PATCH 13/13] Fix build --- src/coreclr/jit/lower.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 72fa793e5c656..63a8cd51c1940 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7655,7 +7655,7 @@ void Lowering::LowerMultiregParams() JITDUMP("Marking V%02u as an explicitly inited parameter\n", argNum); argDsc->lvExplicitParamInit = 1; - auto getCanonType = [layout, argDsc](unsigned offset) { + auto getCanonType = [=](unsigned offset) { unsigned structSizeLeft = layout->GetSize() - offset; if (structSizeLeft >= TARGET_POINTER_SIZE) {