Skip to content

Commit fcbc76a

Browse files
authored
JIT: Remove PUTARG_SPLIT (#116074)
Instead replace arguments that are passed split across registers and stack by two arguments in lowering. With recent ABI and class layout work this is now straightforward.
1 parent 567505f commit fcbc76a

32 files changed

+366
-1495
lines changed

src/coreclr/jit/abi.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,12 @@ var_types ABIPassingSegment::GetRegisterType() const
156156

157157
//-----------------------------------------------------------------------------
158158
// GetRegisterType:
159-
// Return the smallest type larger or equal to Size that most naturally
160-
// represents the register this segment is passed in, taking into account the
161-
// GC info of the specified layout.
159+
// Return the smallest type larger or equal to Size that most naturally
160+
// represents the register this segment is passed in, taking into account the
161+
// GC info of the specified layout.
162+
//
163+
// Parameters:
164+
// layout - The layout of the class that this segment is part of
162165
//
163166
// Return Value:
164167
// A type that matches ABIPassingSegment::Size and the register.

src/coreclr/jit/codegen.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -872,9 +872,6 @@ class CodeGen final : public CodeGenInterface
872872
void genIntrinsic(GenTreeIntrinsic* treeNode);
873873
void genPutArgStk(GenTreePutArgStk* treeNode);
874874
void genPutArgReg(GenTreeOp* tree);
875-
#if FEATURE_ARG_SPLIT
876-
void genPutArgSplit(GenTreePutArgSplit* treeNode);
877-
#endif // FEATURE_ARG_SPLIT
878875

879876
#if defined(TARGET_XARCH)
880877
unsigned getBaseVarForPutArgStk(GenTree* treeNode);
@@ -1095,10 +1092,6 @@ class CodeGen final : public CodeGenInterface
10951092
regNumber dstReg,
10961093
regNumber srcReg,
10971094
regNumber sizeReg);
1098-
#if FEATURE_ARG_SPLIT
1099-
void genConsumeArgSplitStruct(GenTreePutArgSplit* putArgNode);
1100-
#endif // FEATURE_ARG_SPLIT
1101-
11021095
void genConsumeRegs(GenTree* tree);
11031096
void genConsumeOperands(GenTreeOp* tree);
11041097
#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 0 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -428,10 +428,6 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
428428
genPutArgReg(treeNode->AsOp());
429429
break;
430430

431-
case GT_PUTARG_SPLIT:
432-
genPutArgSplit(treeNode->AsPutArgSplit());
433-
break;
434-
435431
case GT_CALL:
436432
genCall(treeNode->AsCall());
437433
break;
@@ -1161,237 +1157,6 @@ void CodeGen::genPutArgReg(GenTreeOp* tree)
11611157
genProduceReg(tree);
11621158
}
11631159

1164-
//---------------------------------------------------------------------
1165-
// genPutArgSplit - generate code for a GT_PUTARG_SPLIT node
1166-
//
1167-
// Arguments
1168-
// tree - the GT_PUTARG_SPLIT node
1169-
//
1170-
// Return value:
1171-
// None
1172-
//
1173-
void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
1174-
{
1175-
assert(treeNode->OperIs(GT_PUTARG_SPLIT));
1176-
1177-
GenTree* source = treeNode->gtOp1;
1178-
emitter* emit = GetEmitter();
1179-
unsigned varNumOut = compiler->lvaOutgoingArgSpaceVar;
1180-
unsigned argOffsetMax = compiler->lvaOutgoingArgSpaceSize;
1181-
1182-
if (source->OperIs(GT_FIELD_LIST))
1183-
{
1184-
// Evaluate each of the GT_FIELD_LIST items into their register
1185-
// and store their register into the outgoing argument area
1186-
unsigned regIndex = 0;
1187-
unsigned firstOnStackOffs = UINT_MAX;
1188-
1189-
for (GenTreeFieldList::Use& use : source->AsFieldList()->Uses())
1190-
{
1191-
GenTree* nextArgNode = use.GetNode();
1192-
regNumber fieldReg = nextArgNode->GetRegNum();
1193-
genConsumeReg(nextArgNode);
1194-
1195-
if (regIndex >= treeNode->gtNumRegs)
1196-
{
1197-
if (firstOnStackOffs == UINT_MAX)
1198-
{
1199-
firstOnStackOffs = use.GetOffset();
1200-
}
1201-
1202-
var_types type = use.GetType();
1203-
unsigned offset = treeNode->getArgOffset() + use.GetOffset() - firstOnStackOffs;
1204-
// We can't write beyond the outgoing arg area
1205-
assert((offset + genTypeSize(type)) <= argOffsetMax);
1206-
1207-
// Emit store instructions to store the registers produced by the GT_FIELD_LIST into the outgoing
1208-
// argument area
1209-
emit->emitIns_S_R(ins_Store(type), emitActualTypeSize(type), fieldReg, varNumOut, offset);
1210-
}
1211-
else
1212-
{
1213-
var_types type = treeNode->GetRegType(regIndex);
1214-
regNumber argReg = treeNode->GetRegNumByIdx(regIndex);
1215-
1216-
// If child node is not already in the register we need, move it
1217-
inst_Mov(type, argReg, fieldReg, /* canSkip */ true);
1218-
1219-
regIndex++;
1220-
}
1221-
}
1222-
}
1223-
else
1224-
{
1225-
var_types targetType = source->TypeGet();
1226-
assert(source->isContained() && varTypeIsStruct(targetType));
1227-
1228-
// We need a register to store intermediate values that we are loading
1229-
// from the source into. We can usually use one of the target registers
1230-
// that will be overridden anyway. The exception is when the source is
1231-
// in a register and that register is the unique target register we are
1232-
// placing. LSRA will always allocate an internal register when there
1233-
// is just one target register to handle this situation.
1234-
//
1235-
int firstRegToPlace;
1236-
regNumber valueReg = REG_NA;
1237-
unsigned srcLclNum = BAD_VAR_NUM;
1238-
unsigned srcLclOffset = 0;
1239-
regNumber addrReg = REG_NA;
1240-
var_types addrType = TYP_UNDEF;
1241-
ClassLayout* layout = nullptr;
1242-
1243-
if (source->OperIsLocalRead())
1244-
{
1245-
srcLclNum = source->AsLclVarCommon()->GetLclNum();
1246-
srcLclOffset = source->AsLclVarCommon()->GetLclOffs();
1247-
layout = source->AsLclVarCommon()->GetLayout(compiler);
1248-
LclVarDsc* varDsc = compiler->lvaGetDesc(srcLclNum);
1249-
1250-
// This struct must live on the stack frame.
1251-
assert(varDsc->lvOnFrame && !varDsc->lvRegister);
1252-
1253-
// No possible conflicts, just use the first register as the value register.
1254-
firstRegToPlace = 0;
1255-
valueReg = treeNode->GetRegNumByIdx(0);
1256-
}
1257-
else // we must have a GT_BLK
1258-
{
1259-
layout = source->AsBlk()->GetLayout();
1260-
addrReg = genConsumeReg(source->AsBlk()->Addr());
1261-
addrType = source->AsBlk()->Addr()->TypeGet();
1262-
1263-
regNumber allocatedValueReg = REG_NA;
1264-
if (treeNode->gtNumRegs == 1)
1265-
{
1266-
allocatedValueReg = internalRegisters.Extract(treeNode);
1267-
}
1268-
1269-
// Pick a register to store intermediate values in for the to-stack
1270-
// copy. It must not conflict with addrReg. We try to prefer an
1271-
// argument register since those can always use thumb encoding.
1272-
valueReg = treeNode->GetRegNumByIdx(0);
1273-
if (valueReg == addrReg)
1274-
{
1275-
if (treeNode->gtNumRegs == 1)
1276-
{
1277-
valueReg = allocatedValueReg;
1278-
}
1279-
else
1280-
{
1281-
// Prefer argument register that can always use thumb encoding.
1282-
valueReg = treeNode->GetRegNumByIdx(1);
1283-
}
1284-
}
1285-
1286-
// Find first register to place. If we are placing addrReg, then
1287-
// make sure we place it last to avoid clobbering its value.
1288-
//
1289-
// The loop below will start at firstRegToPlace and place
1290-
// treeNode->gtNumRegs registers in order, with wraparound. For
1291-
// example, if the registers to place are r0, r1, r2=addrReg, r3
1292-
// then we will set firstRegToPlace = 3 (r3) and the loop below
1293-
// will place r3, r0, r1, r2. The last placement will clobber
1294-
// addrReg.
1295-
firstRegToPlace = 0;
1296-
for (unsigned i = 0; i < treeNode->gtNumRegs; i++)
1297-
{
1298-
if (treeNode->GetRegNumByIdx(i) == addrReg)
1299-
{
1300-
firstRegToPlace = i + 1;
1301-
break;
1302-
}
1303-
}
1304-
}
1305-
1306-
// Put on stack first
1307-
unsigned structOffset = treeNode->gtNumRegs * TARGET_POINTER_SIZE;
1308-
unsigned remainingSize = layout->GetSize() - structOffset;
1309-
unsigned argOffsetOut = treeNode->getArgOffset();
1310-
1311-
assert((remainingSize > 0) && (roundUp(remainingSize, TARGET_POINTER_SIZE) == treeNode->GetStackByteSize()));
1312-
while (remainingSize > 0)
1313-
{
1314-
var_types type;
1315-
if (remainingSize >= TARGET_POINTER_SIZE)
1316-
{
1317-
type = layout->GetGCPtrType(structOffset / TARGET_POINTER_SIZE);
1318-
}
1319-
else if (remainingSize >= 4)
1320-
{
1321-
type = TYP_INT;
1322-
}
1323-
else if (remainingSize >= 2)
1324-
{
1325-
type = TYP_USHORT;
1326-
}
1327-
else
1328-
{
1329-
assert(remainingSize == 1);
1330-
type = TYP_UBYTE;
1331-
}
1332-
1333-
emitAttr attr = emitActualTypeSize(type);
1334-
unsigned moveSize = genTypeSize(type);
1335-
1336-
instruction loadIns = ins_Load(type);
1337-
if (srcLclNum != BAD_VAR_NUM)
1338-
{
1339-
// Load from our local source
1340-
emit->emitIns_R_S(loadIns, attr, valueReg, srcLclNum, srcLclOffset + structOffset);
1341-
}
1342-
else
1343-
{
1344-
assert(valueReg != addrReg);
1345-
1346-
// Load from our address expression source
1347-
emit->emitIns_R_R_I(loadIns, attr, valueReg, addrReg, structOffset);
1348-
}
1349-
1350-
// Emit the instruction to store the register into the outgoing argument area
1351-
emit->emitIns_S_R(ins_Store(type), attr, valueReg, varNumOut, argOffsetOut);
1352-
argOffsetOut += moveSize;
1353-
assert(argOffsetOut <= argOffsetMax);
1354-
1355-
remainingSize -= moveSize;
1356-
structOffset += moveSize;
1357-
}
1358-
1359-
// Place registers starting from firstRegToPlace. It should ensure we
1360-
// place addrReg last (if we place it at all).
1361-
structOffset = static_cast<unsigned>(firstRegToPlace) * TARGET_POINTER_SIZE;
1362-
unsigned curRegIndex = firstRegToPlace;
1363-
1364-
for (unsigned regsPlaced = 0; regsPlaced < treeNode->gtNumRegs; regsPlaced++)
1365-
{
1366-
if (curRegIndex == treeNode->gtNumRegs)
1367-
{
1368-
curRegIndex = 0;
1369-
structOffset = 0;
1370-
}
1371-
1372-
regNumber targetReg = treeNode->GetRegNumByIdx(curRegIndex);
1373-
var_types type = treeNode->GetRegType(curRegIndex);
1374-
1375-
if (srcLclNum != BAD_VAR_NUM)
1376-
{
1377-
// Load from our local source
1378-
emit->emitIns_R_S(INS_ldr, emitTypeSize(type), targetReg, srcLclNum, srcLclOffset + structOffset);
1379-
}
1380-
else
1381-
{
1382-
assert((addrReg != targetReg) || (regsPlaced == treeNode->gtNumRegs - 1));
1383-
1384-
// Load from our address expression source
1385-
emit->emitIns_R_R_I(INS_ldr, emitTypeSize(type), targetReg, addrReg, structOffset);
1386-
}
1387-
1388-
curRegIndex++;
1389-
structOffset += TARGET_POINTER_SIZE;
1390-
}
1391-
}
1392-
genProduceReg(treeNode);
1393-
}
1394-
13951160
#ifdef FEATURE_SIMD
13961161
//----------------------------------------------------------------------------------
13971162
// genMultiRegStoreToSIMDLocal: store multi-reg value to a single-reg SIMD local

src/coreclr/jit/codegencommon.cpp

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7218,36 +7218,6 @@ void CodeGen::genCallPlaceRegArgs(GenTreeCall* call)
72187218
}
72197219
#endif
72207220

7221-
#if FEATURE_ARG_SPLIT
7222-
if (argNode->OperIs(GT_PUTARG_SPLIT))
7223-
{
7224-
assert(compFeatureArgSplit());
7225-
genConsumeArgSplitStruct(argNode->AsPutArgSplit());
7226-
unsigned regIndex = 0;
7227-
for (const ABIPassingSegment& seg : abiInfo.Segments())
7228-
{
7229-
if (!seg.IsPassedInRegister())
7230-
{
7231-
continue;
7232-
}
7233-
7234-
regNumber allocReg = argNode->AsPutArgSplit()->GetRegNumByIdx(regIndex);
7235-
var_types type = argNode->AsPutArgSplit()->GetRegType(regIndex);
7236-
inst_Mov(genActualType(type), seg.GetRegister(), allocReg, /* canSkip */ true);
7237-
7238-
if (call->IsFastTailCall())
7239-
{
7240-
// We won't actually consume the register here -- keep it alive into the epilog.
7241-
gcInfo.gcMarkRegPtrVal(seg.GetRegister(), type);
7242-
}
7243-
7244-
regIndex++;
7245-
}
7246-
7247-
continue;
7248-
}
7249-
#endif
7250-
72517221
if (abiInfo.HasExactlyOneRegisterSegment())
72527222
{
72537223
regNumber argReg = abiInfo.Segment(0).GetRegister();

src/coreclr/jit/codegenlinear.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,29 +1826,6 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode,
18261826
}
18271827
}
18281828

1829-
#if FEATURE_ARG_SPLIT
1830-
//------------------------------------------------------------------------
1831-
// genConsumeArgRegSplit: Consume register(s) in Call node to set split struct argument.
1832-
//
1833-
// Arguments:
1834-
// putArgNode - the PUTARG_STK tree.
1835-
//
1836-
// Return Value:
1837-
// None.
1838-
//
1839-
void CodeGen::genConsumeArgSplitStruct(GenTreePutArgSplit* putArgNode)
1840-
{
1841-
assert(putArgNode->OperIs(GT_PUTARG_SPLIT));
1842-
assert(putArgNode->gtHasReg(compiler));
1843-
1844-
genUnspillRegIfNeeded(putArgNode);
1845-
1846-
gcInfo.gcMarkRegSetNpt(putArgNode->gtGetRegMask());
1847-
1848-
genCheckConsumeNode(putArgNode);
1849-
}
1850-
#endif // FEATURE_ARG_SPLIT
1851-
18521829
//------------------------------------------------------------------------
18531830
// genPutArgStkFieldList: Generate code for a putArgStk whose source is a GT_FIELD_LIST
18541831
//

0 commit comments

Comments
 (0)