Skip to content

Commit e8e1451

Browse files
Support PUTARG_SPLIT(STRUCT LCL_VAR/LCL_FLD) on ARM/64 (#70861)
* LowerPutArgStk -> LowerPutArgStkOrSplit * Support "PUTARG_SPLIT(STRUCT LCL_VAR/LCL_FLD)" on ARM/64
1 parent 198b8bf commit e8e1451

File tree

7 files changed

+73
-94
lines changed

7 files changed

+73
-94
lines changed

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,65 +1193,39 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
11931193
else
11941194
{
11951195
var_types targetType = source->TypeGet();
1196-
assert(source->OperGet() == GT_OBJ);
1197-
assert(varTypeIsStruct(targetType));
1196+
assert(source->isContained() && varTypeIsStruct(targetType));
11981197

1199-
regNumber baseReg = treeNode->ExtractTempReg();
1200-
regNumber addrReg = REG_NA;
1198+
regNumber baseReg = treeNode->ExtractTempReg();
1199+
unsigned srcLclNum = BAD_VAR_NUM;
1200+
unsigned srcLclOffset = 0;
1201+
regNumber addrReg = REG_NA;
1202+
var_types addrType = TYP_UNDEF;
1203+
ClassLayout* layout = nullptr;
12011204

1202-
GenTreeLclVarCommon* varNode = nullptr;
1203-
GenTree* addrNode = nullptr;
1204-
1205-
addrNode = source->AsOp()->gtOp1;
1206-
1207-
// addrNode can either be a GT_LCL_VAR_ADDR or an address expression
1208-
//
1209-
if (addrNode->OperGet() == GT_LCL_VAR_ADDR)
1205+
if (source->OperIsLocalRead())
12101206
{
1211-
// We have a GT_OBJ(GT_LCL_VAR_ADDR)
1212-
//
1213-
// We will treat this case the same as above
1214-
// (i.e if we just had this GT_LCL_VAR directly as the source)
1215-
// so update 'source' to point this GT_LCL_VAR_ADDR node
1216-
// and continue to the codegen for the LCL_VAR node below
1217-
//
1218-
varNode = addrNode->AsLclVarCommon();
1219-
addrNode = nullptr;
1220-
}
1207+
srcLclNum = source->AsLclVarCommon()->GetLclNum();
1208+
srcLclOffset = source->AsLclVarCommon()->GetLclOffs();
1209+
layout = source->AsLclVarCommon()->GetLayout(compiler);
1210+
LclVarDsc* varDsc = compiler->lvaGetDesc(srcLclNum);
12211211

1222-
// Either varNode or addrNOde must have been setup above,
1223-
// the xor ensures that only one of the two is setup, not both
1224-
assert((varNode != nullptr) ^ (addrNode != nullptr));
1225-
1226-
// This is the varNum for our load operations,
1227-
// only used when we have a struct with a LclVar source
1228-
unsigned srcVarNum = BAD_VAR_NUM;
1229-
1230-
if (varNode != nullptr)
1231-
{
1232-
assert(varNode->isContained());
1233-
srcVarNum = varNode->GetLclNum();
1234-
LclVarDsc* varDsc = compiler->lvaGetDesc(srcVarNum);
1235-
1236-
// This struct also must live in the stack frame.
1237-
// And it can't live in a register.
1212+
// This struct must live on the stack frame.
12381213
assert(varDsc->lvOnFrame && !varDsc->lvRegister);
12391214
}
1240-
else // addrNode is used
1215+
else // we must have a GT_OBJ
12411216
{
1242-
// Generate code to load the address that we need into a register
1243-
addrReg = genConsumeReg(addrNode);
1217+
layout = source->AsObj()->GetLayout();
1218+
addrReg = genConsumeReg(source->AsObj()->Addr());
1219+
addrType = source->AsObj()->Addr()->TypeGet();
12441220

12451221
// If addrReg equal to baseReg, we use the last target register as alternative baseReg.
12461222
// Because the candidate mask for the internal baseReg does not include any of the target register,
12471223
// we can ensure that baseReg, addrReg, and the last target register are not all same.
12481224
assert(baseReg != addrReg);
1249-
1250-
// We don't split HFA struct
1251-
assert(!compiler->IsHfa(source->AsObj()->GetLayout()->GetClassHandle()));
12521225
}
12531226

1254-
ClassLayout* layout = source->AsObj()->GetLayout();
1227+
// We don't split HFAs.
1228+
assert(!compiler->IsHfa(layout->GetClassHandle()));
12551229

12561230
// Put on stack first
12571231
unsigned structOffset = treeNode->gtNumRegs * TARGET_POINTER_SIZE;
@@ -1284,10 +1258,10 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
12841258
unsigned moveSize = genTypeSize(type);
12851259

12861260
instruction loadIns = ins_Load(type);
1287-
if (varNode != nullptr)
1261+
if (srcLclNum != BAD_VAR_NUM)
12881262
{
12891263
// Load from our local source
1290-
emit->emitIns_R_S(loadIns, attr, baseReg, srcVarNum, structOffset);
1264+
emit->emitIns_R_S(loadIns, attr, baseReg, srcLclNum, srcLclOffset + structOffset);
12911265
}
12921266
else
12931267
{
@@ -1315,18 +1289,17 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
13151289
regNumber targetReg = treeNode->GetRegNumByIdx(idx);
13161290
var_types type = treeNode->GetRegType(idx);
13171291

1318-
if (varNode != nullptr)
1292+
if (srcLclNum != BAD_VAR_NUM)
13191293
{
13201294
// Load from our local source
1321-
emit->emitIns_R_S(INS_ldr, emitTypeSize(type), targetReg, srcVarNum, structOffset);
1295+
emit->emitIns_R_S(INS_ldr, emitTypeSize(type), targetReg, srcLclNum, srcLclOffset + structOffset);
13221296
}
13231297
else
13241298
{
13251299
// check for case of destroying the addrRegister while we still need it
13261300
if (targetReg == addrReg && idx != treeNode->gtNumRegs - 1)
13271301
{
13281302
assert(targetReg != baseReg);
1329-
var_types addrType = addrNode->TypeGet();
13301303
emit->emitIns_Mov(INS_mov, emitActualTypeSize(addrType), baseReg, addrReg, /* canSkip */ false);
13311304
addrReg = baseReg;
13321305
}

src/coreclr/jit/lower.cpp

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,23 +1095,7 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
10951095
argSplit->SetRegNumByIdx(callArg->AbiInfo.GetRegNum(regIndex), regIndex);
10961096
}
10971097

1098-
if (arg->OperGet() == GT_OBJ)
1099-
{
1100-
arg->SetContained();
1101-
if (arg->AsObj()->Addr()->OperGet() == GT_LCL_VAR_ADDR)
1102-
{
1103-
MakeSrcContained(arg, arg->AsObj()->Addr());
1104-
}
1105-
1106-
ClassLayout* layout = arg->AsObj()->GetLayout();
1107-
1108-
// Set type of registers
1109-
for (unsigned index = 0; index < callArg->AbiInfo.NumRegs; index++)
1110-
{
1111-
argSplit->m_regType[index] = layout->GetGCPtrType(index);
1112-
}
1113-
}
1114-
else
1098+
if (arg->OperIs(GT_FIELD_LIST))
11151099
{
11161100
unsigned regIndex = 0;
11171101
for (GenTreeFieldList::Use& use : arg->AsFieldList()->Uses())
@@ -1133,6 +1117,16 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
11331117
// Clear the register assignment on the fieldList node, as these are contained.
11341118
arg->SetRegNum(REG_NA);
11351119
}
1120+
else
1121+
{
1122+
ClassLayout* layout = arg->GetLayout(comp);
1123+
1124+
// Set type of registers
1125+
for (unsigned index = 0; index < callArg->AbiInfo.NumRegs; index++)
1126+
{
1127+
argSplit->m_regType[index] = layout->GetGCPtrType(index);
1128+
}
1129+
}
11361130
}
11371131
else
11381132
#endif // FEATURE_ARG_SPLIT
@@ -1382,9 +1376,9 @@ void Lowering::LowerArg(GenTreeCall* call, CallArg* callArg, bool late)
13821376

13831377
arg = *ppArg;
13841378

1385-
if (arg->OperIs(GT_PUTARG_STK))
1379+
if (arg->OperIsPutArgStk() || arg->OperIsPutArgSplit())
13861380
{
1387-
LowerPutArgStk(arg->AsPutArgStk());
1381+
LowerPutArgStkOrSplit(arg->AsPutArgStk());
13881382
}
13891383
}
13901384

src/coreclr/jit/lower.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,10 @@ class Lowering final : public Phase
308308
void LowerBlockStore(GenTreeBlk* blkNode);
309309
void LowerBlockStoreCommon(GenTreeBlk* blkNode);
310310
void ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenTree* addr);
311-
void LowerPutArgStk(GenTreePutArgStk* tree);
311+
void LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode);
312+
#ifdef TARGET_XARCH
313+
void LowerPutArgStk(GenTreePutArgStk* putArgStk);
314+
#endif // TARGET_XARCH
312315

313316
bool TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* parent);
314317

src/coreclr/jit/lowerarmarch.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -576,19 +576,19 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
576576
}
577577

578578
//------------------------------------------------------------------------
579-
// LowerPutArgStk: Lower a GT_PUTARG_STK.
579+
// LowerPutArgStkOrSplit: Lower a GT_PUTARG_STK/GT_PUTARG_SPLIT.
580580
//
581581
// Arguments:
582582
// putArgStk - The node to lower
583583
//
584-
void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
584+
void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode)
585585
{
586-
GenTree* src = putArgStk->Data();
586+
GenTree* src = putArgNode->Data();
587587

588588
if (src->TypeIs(TYP_STRUCT))
589589
{
590590
// STRUCT args (FIELD_LIST / OBJ / LCL_VAR / LCL_FLD) will always be contained.
591-
MakeSrcContained(putArgStk, src);
591+
MakeSrcContained(putArgNode, src);
592592

593593
// TODO-ADDR: always perform this transformation in local morph and delete this code.
594594
if (src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIsLocalAddr())
@@ -607,7 +607,8 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
607607
}
608608
else if (src->OperIs(GT_LCL_VAR))
609609
{
610-
// TODO-1stClassStructs: support struct enregistration here by retyping "src" to its register type.
610+
// TODO-1stClassStructs: support struct enregistration here by retyping "src" to its register type for
611+
// the non-split case.
611612
comp->lvaSetVarDoNotEnregister(src->AsLclVar()->GetLclNum() DEBUGARG(DoNotEnregisterReason::IsStructArg));
612613
}
613614
}

src/coreclr/jit/lowerloongarch64.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,19 +417,19 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
417417
}
418418

419419
//------------------------------------------------------------------------
420-
// LowerPutArgStk: Lower a GT_PUTARG_STK.
420+
// LowerPutArgStkOrSplit: Lower a GT_PUTARG_STK/GT_PUTARG_SPLIT.
421421
//
422422
// Arguments:
423-
// putArgStk - The node to lower
423+
// putArgNode - The node to lower
424424
//
425-
void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
425+
void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode)
426426
{
427-
GenTree* src = putArgStk->Data();
427+
GenTree* src = putArgNode->Data();
428428

429429
if (src->TypeIs(TYP_STRUCT))
430430
{
431431
// STRUCT args (FIELD_LIST / OBJ) will always be contained.
432-
MakeSrcContained(putArgStk, src);
432+
MakeSrcContained(putArgNode, src);
433433

434434
// Additionally, codegen supports containment of local addresses under OBJs.
435435
if (src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIs(GT_LCL_VAR_ADDR))

src/coreclr/jit/lowerxarch.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,18 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
462462
addrMode->SetContained();
463463
}
464464

465+
//------------------------------------------------------------------------
466+
// LowerPutArgStkOrSplit: Lower a GT_PUTARG_STK/GT_PUTARG_SPLIT.
467+
//
468+
// Arguments:
469+
// putArgNode - The node of interest
470+
//
471+
void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode)
472+
{
473+
assert(putArgNode->OperIs(GT_PUTARG_STK)); // No split args on XArch.
474+
LowerPutArgStk(putArgNode);
475+
}
476+
465477
//------------------------------------------------------------------------
466478
// LowerPutArgStk: Lower a GT_PUTARG_STK.
467479
//

src/coreclr/jit/lsraarmarch.cpp

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode)
492492
int srcCount = 0;
493493
assert(argNode->gtOper == GT_PUTARG_SPLIT);
494494

495-
GenTree* putArgChild = argNode->gtGetOp1();
495+
GenTree* src = argNode->gtGetOp1();
496496

497497
// Registers for split argument corresponds to source
498498
int dstCount = argNode->gtNumRegs;
@@ -506,7 +506,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode)
506506
argNode->SetRegNumByIdx(thisArgReg, i);
507507
}
508508

509-
if (putArgChild->OperGet() == GT_FIELD_LIST)
509+
if (src->OperGet() == GT_FIELD_LIST)
510510
{
511511
// Generated code:
512512
// 1. Consume all of the items in the GT_FIELD_LIST (source)
@@ -517,7 +517,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode)
517517
// To avoid redundant moves, have the argument operand computed in the
518518
// register in which the argument is passed to the call.
519519

520-
for (GenTreeFieldList::Use& use : putArgChild->AsFieldList()->Uses())
520+
for (GenTreeFieldList::Use& use : src->AsFieldList()->Uses())
521521
{
522522
GenTree* node = use.GetNode();
523523
assert(!node->isContained());
@@ -548,29 +548,25 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode)
548548
}
549549
}
550550
srcCount += sourceRegCount;
551-
assert(putArgChild->isContained());
551+
assert(src->isContained());
552552
}
553553
else
554554
{
555-
assert(putArgChild->TypeGet() == TYP_STRUCT);
556-
assert(putArgChild->OperGet() == GT_OBJ);
555+
assert(src->TypeIs(TYP_STRUCT) && src->isContained());
557556

558557
// We can use a ldr/str sequence so we need an internal register
559558
buildInternalIntRegisterDefForNode(argNode, allRegs(TYP_INT) & ~argMask);
560559

561-
GenTree* objChild = putArgChild->gtGetOp1();
562-
if (objChild->OperGet() == GT_LCL_VAR_ADDR)
560+
if (src->OperIs(GT_OBJ))
563561
{
564-
// We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR
565-
// as one contained operation
566-
//
567-
assert(objChild->isContained());
562+
// We will generate code that loads from the OBJ's address, which must be in a register.
563+
srcCount = BuildOperandUses(src->AsObj()->Addr());
568564
}
569565
else
570566
{
571-
srcCount = BuildIndirUses(putArgChild->AsIndir());
567+
// We will generate all of the code for the GT_PUTARG_SPLIT and LCL_VAR/LCL_FLD as one contained operation.
568+
assert(src->OperIsLocalRead());
572569
}
573-
assert(putArgChild->isContained());
574570
}
575571
buildInternalRegisterUses();
576572
BuildDefs(argNode, dstCount, argMask);

0 commit comments

Comments
 (0)