return assertion;
}
-/*****************************************************************************
- *
- * A simple helper routine so not all callers need to supply a AssertionDsc*
- * if they don't care about it. Refer overloaded method optCreateAssertion.
- *
- */
-AssertionIndex Compiler::optCreateAssertion(GenTree* op1, GenTree* op2, optAssertionKind assertionKind)
-{
- AssertionDsc assertionDsc;
- return optCreateAssertion(op1, op2, assertionKind, &assertionDsc);
-}
-
-/*****************************************************************************
- *
- * We attempt to create the following assertion:
- *
- * op1 assertionKind op2
- *
- * If we can create the assertion then update 'assertion' if we are
- * unsuccessful assertion->assertionKind will be OAK_INVALID. If we are
- * successful in creating the assertion we call optAddAssertion which adds
- * the assertion to our assertion table.
- *
- * If we are able to create the assertion the return value is the
- * assertionIndex for this assertion otherwise the return value is
- * NO_ASSERTION_INDEX and we could not create the assertion.
- *
- */
+//------------------------------------------------------------------------
+// optCreateAssertion: Create an (op1 assertionKind op2) assertion.
+//
+// Arguments:
+// op1 - the first assertion operand
+// op2 - the second assertion operand
+// assertionKind - the assertion kind
+// helperCallArgs - when true this indicates that the assertion operands
+// are the arguments of a type cast helper call such as
+// CORINFO_HELP_ISINSTANCEOFCLASS
+// Return Value:
+// The new assertion index or NO_ASSERTION_INDEX if a new assertion
+// was not created.
+//
+// Notes:
+// Assertion creation may fail either because the provided assertion
+// operands aren't supported or because the assertion table is full.
+//
AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
GenTree* op2,
optAssertionKind assertionKind,
- AssertionDsc* assertion)
+ bool helperCallArgs)
{
- memset(assertion, 0, sizeof(AssertionDsc));
- //
- // If we cannot create an assertion using op1 and op2 then the assertionKind
- // must be OAK_INVALID, so we initialize it to OAK_INVALID and only change it
- // to a valid assertion when everything is good.
- //
- assertion->assertionKind = OAK_INVALID;
- bool haveArgs = false;
+ assert((op1 != nullptr) && !op1->OperIs(GT_LIST));
+ assert((op2 == nullptr) || !op2->OperIs(GT_LIST));
+ assert(!helperCallArgs || (op2 != nullptr));
+
+ AssertionDsc assertion;
+ memset(&assertion, 0, sizeof(AssertionDsc));
+ assert(assertion.assertionKind == OAK_INVALID);
+
var_types toType;
if (op1->gtOper == GT_ARR_BOUNDS_CHECK)
if (assertionKind == OAK_NO_THROW)
{
GenTreeBoundsChk* arrBndsChk = op1->AsBoundsChk();
- assertion->assertionKind = assertionKind;
- assertion->op1.kind = O1K_ARR_BND;
- assertion->op1.bnd.vnIdx = vnStore->VNConservativeNormalValue(arrBndsChk->gtIndex->gtVNPair);
- assertion->op1.bnd.vnLen = vnStore->VNConservativeNormalValue(arrBndsChk->gtArrLen->gtVNPair);
+ assertion.assertionKind = assertionKind;
+ assertion.op1.kind = O1K_ARR_BND;
+ assertion.op1.bnd.vnIdx = vnStore->VNConservativeNormalValue(arrBndsChk->gtIndex->gtVNPair);
+ assertion.op1.bnd.vnLen = vnStore->VNConservativeNormalValue(arrBndsChk->gtArrLen->gtVNPair);
goto DONE_ASSERTION;
}
}
//
- // Did we receive Helper call args?
- //
- if (op1->gtOper == GT_LIST)
- {
- if (op2->gtOper != GT_LIST)
- {
- goto DONE_ASSERTION; // Don't make an assertion
- }
- op1 = op1->gtOp.gtOp1;
- op2 = op2->gtOp.gtOp1;
- haveArgs = true;
- }
-
- //
// Are we trying to make a non-null assertion?
//
if (op2 == nullptr)
{
- assert(haveArgs == false);
//
// Must an OAK_NOT_EQUAL assertion
//
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->op1.kind = O1K_VALUE_NUMBER;
+ assertion.op1.kind = O1K_VALUE_NUMBER;
}
else
{
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->op1.kind = O1K_LCLVAR;
- assertion->op1.lcl.lclNum = lclNum;
- assertion->op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
- vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
+ assertion.op1.kind = O1K_LCLVAR;
+ assertion.op1.lcl.lclNum = lclNum;
+ assertion.op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
+ vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
}
- assertion->op1.vn = vn;
- assertion->assertionKind = assertionKind;
- assertion->op2.kind = O2K_CONST_INT;
- assertion->op2.vn = ValueNumStore::VNForNull();
- assertion->op2.u1.iconVal = 0;
- assertion->op2.u1.iconFlags = 0;
+ assertion.op1.vn = vn;
+ assertion.assertionKind = assertionKind;
+ assertion.op2.kind = O2K_CONST_INT;
+ assertion.op2.vn = ValueNumStore::VNForNull();
+ assertion.op2.u1.iconVal = 0;
+ assertion.op2.u1.iconFlags = 0;
#ifdef _TARGET_64BIT_
- assertion->op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
-#endif // _TARGET_64BIT_
+ assertion.op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
+#endif // _TARGET_64BIT_
}
//
// Are we making an assertion about a local variable?
goto DONE_ASSERTION; // Don't make an assertion
}
- if (haveArgs)
+ if (helperCallArgs)
{
//
// Must either be an OAK_EQUAL or an OAK_NOT_EQUAL assertion
if (op2->gtOper == GT_IND)
{
- op2 = op2->gtOp.gtOp1;
- assertion->op2.kind = O2K_IND_CNS_INT;
+ op2 = op2->gtOp.gtOp1;
+ assertion.op2.kind = O2K_IND_CNS_INT;
}
else
{
- assertion->op2.kind = O2K_CONST_INT;
+ assertion.op2.kind = O2K_CONST_INT;
}
if (op2->gtOper != GT_CNS_INT)
// where a class can be sealed, but they don't behave as exact types because casts to
// non-base types sometimes still succeed.
//
- assertion->op1.kind = O1K_SUBTYPE;
- assertion->op1.lcl.lclNum = lclNum;
- assertion->op1.vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
- assertion->op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
- assertion->op2.u1.iconVal = op2->gtIntCon.gtIconVal;
- assertion->op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
- assertion->op2.u1.iconFlags = op2->GetIconHandleFlag();
+ assertion.op1.kind = O1K_SUBTYPE;
+ assertion.op1.lcl.lclNum = lclNum;
+ assertion.op1.vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
+ assertion.op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
+ assertion.op2.u1.iconVal = op2->gtIntCon.gtIconVal;
+ assertion.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
+ assertion.op2.u1.iconFlags = op2->GetIconHandleFlag();
//
// Ok everything has been set and the assertion looks good
//
- assertion->assertionKind = assertionKind;
+ assertion.assertionKind = assertionKind;
}
- else // !haveArgs
+ else // !helperCallArgs
{
/* Skip over a GT_COMMA node(s), if necessary */
while (op2->gtOper == GT_COMMA)
op2 = op2->gtOp.gtOp2;
}
- assertion->op1.kind = O1K_LCLVAR;
- assertion->op1.lcl.lclNum = lclNum;
- assertion->op1.vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
- assertion->op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
+ assertion.op1.kind = O1K_LCLVAR;
+ assertion.op1.lcl.lclNum = lclNum;
+ assertion.op1.vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
+ assertion.op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
switch (op2->gtOper)
{
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->op2.kind = op2Kind;
- assertion->op2.lconVal = 0;
- assertion->op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
+ assertion.op2.kind = op2Kind;
+ assertion.op2.lconVal = 0;
+ assertion.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
if (op2->gtOper == GT_CNS_INT)
{
goto DONE_ASSERTION; // Don't make an assertion
}
#endif // _TARGET_ARM_
- assertion->op2.u1.iconVal = op2->gtIntCon.gtIconVal;
- assertion->op2.u1.iconFlags = op2->GetIconHandleFlag();
+ assertion.op2.u1.iconVal = op2->gtIntCon.gtIconVal;
+ assertion.op2.u1.iconFlags = op2->GetIconHandleFlag();
#ifdef _TARGET_64BIT_
if (op2->TypeGet() == TYP_LONG || op2->TypeGet() == TYP_BYREF)
{
- assertion->op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
+ assertion.op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
}
#endif // _TARGET_64BIT_
}
else if (op2->gtOper == GT_CNS_LNG)
{
- assertion->op2.lconVal = op2->gtLngCon.gtLconVal;
+ assertion.op2.lconVal = op2->gtLngCon.gtLconVal;
}
else
{
{
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->op2.dconVal = op2->gtDblCon.gtDconVal;
+ assertion.op2.dconVal = op2->gtDblCon.gtDconVal;
}
//
// Ok everything has been set and the assertion looks good
//
- assertion->assertionKind = assertionKind;
+ assertion.assertionKind = assertionKind;
}
break;
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->op2.kind = O2K_LCLVAR_COPY;
- assertion->op2.lcl.lclNum = lclNum2;
- assertion->op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
- assertion->op2.lcl.ssaNum = op2->AsLclVarCommon()->GetSsaNum();
+ assertion.op2.kind = O2K_LCLVAR_COPY;
+ assertion.op2.lcl.lclNum = lclNum2;
+ assertion.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
+ assertion.op2.lcl.ssaNum = op2->AsLclVarCommon()->GetSsaNum();
//
// Ok everything has been set and the assertion looks good
//
- assertion->assertionKind = assertionKind;
+ assertion.assertionKind = assertionKind;
}
break;
case TYP_UINT:
case TYP_INT:
#endif // _TARGET_64BIT_
- assertion->op2.u2.loBound = AssertionDsc::GetLowerBoundForIntegralType(toType);
- assertion->op2.u2.hiBound = AssertionDsc::GetUpperBoundForIntegralType(toType);
+ assertion.op2.u2.loBound = AssertionDsc::GetLowerBoundForIntegralType(toType);
+ assertion.op2.u2.hiBound = AssertionDsc::GetUpperBoundForIntegralType(toType);
break;
default:
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->op2.kind = O2K_SUBRANGE;
- assertion->assertionKind = OAK_SUBRANGE;
+ assertion.op2.kind = O2K_SUBRANGE;
+ assertion.assertionKind = OAK_SUBRANGE;
}
break;
}
- } // else // !haveArgs
+ } // else // !helperCallArgs
} // if (op1->gtOper == GT_LCL_VAR)
//
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->op1.kind = O1K_EXACT_TYPE;
- assertion->op1.lcl.lclNum = lclNum;
- assertion->op1.vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
- assertion->op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
+ assertion.op1.kind = O1K_EXACT_TYPE;
+ assertion.op1.lcl.lclNum = lclNum;
+ assertion.op1.vn = vnStore->VNConservativeNormalValue(op1->gtVNPair);
+ assertion.op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
- assert(assertion->op1.lcl.ssaNum == SsaConfig::RESERVED_SSA_NUM ||
- assertion->op1.vn ==
- vnStore->VNConservativeNormalValue(
- lvaTable[lclNum].GetPerSsaData(assertion->op1.lcl.ssaNum)->m_vnPair));
+ assert((assertion.op1.lcl.ssaNum == SsaConfig::RESERVED_SSA_NUM) ||
+ (assertion.op1.vn ==
+ vnStore->VNConservativeNormalValue(
+ lvaTable[lclNum].GetPerSsaData(assertion.op1.lcl.ssaNum)->m_vnPair)));
ssize_t cnsValue = 0;
unsigned iconFlags = 0;
goto DONE_ASSERTION; // Don't make an assertion
}
- assertion->assertionKind = assertionKind;
- assertion->op2.kind = O2K_IND_CNS_INT;
- assertion->op2.u1.iconVal = cnsValue;
- assertion->op2.vn = vnStore->VNConservativeNormalValue(op2->gtOp.gtOp1->gtVNPair);
+ assertion.assertionKind = assertionKind;
+ assertion.op2.kind = O2K_IND_CNS_INT;
+ assertion.op2.u1.iconVal = cnsValue;
+ assertion.op2.vn = vnStore->VNConservativeNormalValue(op2->gtOp.gtOp1->gtVNPair);
/* iconFlags should only contain bits in GTF_ICON_HDL_MASK */
assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0);
- assertion->op2.u1.iconFlags = iconFlags;
+ assertion.op2.u1.iconFlags = iconFlags;
#ifdef _TARGET_64BIT_
if (op2->gtOp.gtOp1->TypeGet() == TYP_LONG)
{
- assertion->op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
+ assertion.op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
}
#endif // _TARGET_64BIT_
}
// JIT case
else if (optIsTreeKnownIntValue(!optLocalAssertionProp, op2, &cnsValue, &iconFlags))
{
- assertion->assertionKind = assertionKind;
- assertion->op2.kind = O2K_IND_CNS_INT;
- assertion->op2.u1.iconVal = cnsValue;
- assertion->op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
+ assertion.assertionKind = assertionKind;
+ assertion.op2.kind = O2K_IND_CNS_INT;
+ assertion.op2.u1.iconVal = cnsValue;
+ assertion.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
/* iconFlags should only contain bits in GTF_ICON_HDL_MASK */
assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0);
- assertion->op2.u1.iconFlags = iconFlags;
+ assertion.op2.u1.iconFlags = iconFlags;
#ifdef _TARGET_64BIT_
if (op2->TypeGet() == TYP_LONG)
{
- assertion->op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
+ assertion.op2.u1.iconFlags |= 1; // Signify that this is really TYP_LONG
}
#endif // _TARGET_64BIT_
}
}
DONE_ASSERTION:
- if (assertion->assertionKind == OAK_INVALID)
+ if (assertion.assertionKind == OAK_INVALID)
{
return NO_ASSERTION_INDEX;
}
if (!optLocalAssertionProp)
{
- if ((assertion->op1.vn == ValueNumStore::NoVN) || (assertion->op2.vn == ValueNumStore::NoVN) ||
- (assertion->op1.vn == ValueNumStore::VNForVoid()) || (assertion->op2.vn == ValueNumStore::VNForVoid()))
+ if ((assertion.op1.vn == ValueNumStore::NoVN) || (assertion.op2.vn == ValueNumStore::NoVN) ||
+ (assertion.op1.vn == ValueNumStore::VNForVoid()) || (assertion.op2.vn == ValueNumStore::VNForVoid()))
{
return NO_ASSERTION_INDEX;
}
// TODO: only copy assertions rely on valid SSA number so we could generate more assertions here
- if ((assertion->op1.kind != O1K_VALUE_NUMBER) && (assertion->op1.lcl.ssaNum == SsaConfig::RESERVED_SSA_NUM))
+ if ((assertion.op1.kind != O1K_VALUE_NUMBER) && (assertion.op1.lcl.ssaNum == SsaConfig::RESERVED_SSA_NUM))
{
return NO_ASSERTION_INDEX;
}
}
// Now add the assertion to our assertion table
- noway_assert(assertion->op1.kind != O1K_INVALID);
- noway_assert(assertion->op1.kind == O1K_ARR_BND || assertion->op2.kind != O2K_INVALID);
- return optAddAssertion(assertion);
+ noway_assert(assertion.op1.kind != O1K_INVALID);
+ noway_assert((assertion.op1.kind == O1K_ARR_BND) || (assertion.op2.kind != O2K_INVALID));
+ return optAddAssertion(&assertion);
}
/*****************************************************************************
}
#endif
-/*****************************************************************************
- *
- * Given a "candidateAssertion", and the assertion operands op1 and op2,
- * create a complementary assertion and add it to the assertion table,
- * which can be retrieved using optFindComplementary(index)
- *
- */
-
-void Compiler::optCreateComplementaryAssertion(AssertionIndex assertionIndex, GenTree* op1, GenTree* op2)
+//------------------------------------------------------------------------
+// optCreateComplementaryAssertion: Create an assertion that is the complementary
+// of the specified assertion.
+//
+// Arguments:
+// assertionIndex - the index of the assertion
+// op1 - the first assertion operand
+// op2 - the second assertion operand
+// helperCallArgs - when true this indicates that the assertion operands
+// are the arguments of a type cast helper call such as
+// CORINFO_HELP_ISINSTANCEOFCLASS
+//
+// Notes:
+// The created complementary assertion is associated with the original
+// assertion such that it can be found by optFindComplementary.
+//
+void Compiler::optCreateComplementaryAssertion(AssertionIndex assertionIndex,
+ GenTree* op1,
+ GenTree* op2,
+ bool helperCallArgs)
{
if (assertionIndex == NO_ASSERTION_INDEX)
{
if (candidateAssertion.assertionKind == OAK_EQUAL)
{
- AssertionIndex index = optCreateAssertion(op1, op2, OAK_NOT_EQUAL);
+ AssertionIndex index = optCreateAssertion(op1, op2, OAK_NOT_EQUAL, helperCallArgs);
optMapComplementary(index, assertionIndex);
}
else if (candidateAssertion.assertionKind == OAK_NOT_EQUAL)
{
- AssertionIndex index = optCreateAssertion(op1, op2, OAK_EQUAL);
+ AssertionIndex index = optCreateAssertion(op1, op2, OAK_EQUAL, helperCallArgs);
optMapComplementary(index, assertionIndex);
}
// Are we making a subtype or exact type assertion?
if ((candidateAssertion.op1.kind == O1K_SUBTYPE) || (candidateAssertion.op1.kind == O1K_EXACT_TYPE))
{
- // Did we recieve helper call args?
- if (op1->gtOper == GT_LIST)
- {
- op1 = op1->gtOp.gtOp1;
- }
optCreateAssertion(op1, nullptr, OAK_NOT_EQUAL);
}
}
-/*****************************************************************************
- *
- * Create assertions for jtrue operands. Given operands "op1" and "op2" that
- * are used in a conditional evaluation of a jtrue stmt, create assertions
- * for the operands.
- */
-
-AssertionIndex Compiler::optCreateJtrueAssertions(GenTree* op1, GenTree* op2, Compiler::optAssertionKind assertionKind)
+//------------------------------------------------------------------------
+// optCreateJtrueAssertions: Create assertions about a JTRUE's relop operands.
+//
+// Arguments:
+// op1 - the first assertion operand
+// op2 - the second assertion operand
+// assertionKind - the assertion kind
+// helperCallArgs - when true this indicates that the assertion operands
+// are the arguments of a type cast helper call such as
+// CORINFO_HELP_ISINSTANCEOFCLASS
+// Return Value:
+// The new assertion index or NO_ASSERTION_INDEX if a new assertion
+// was not created.
+//
+// Notes:
+// Assertion creation may fail either because the provided assertion
+// operands aren't supported or because the assertion table is full.
+// If an assertion is created succesfully then an attempt is made to also
+// create a second, complementary assertion. This may too fail, for the
+// same reasons as the first one.
+//
+AssertionIndex Compiler::optCreateJtrueAssertions(GenTree* op1,
+ GenTree* op2,
+ Compiler::optAssertionKind assertionKind,
+ bool helperCallArgs)
{
- AssertionDsc candidateAssertion;
- AssertionIndex assertionIndex = optCreateAssertion(op1, op2, assertionKind, &candidateAssertion);
+ AssertionIndex assertionIndex = optCreateAssertion(op1, op2, assertionKind, helperCallArgs);
// Don't bother if we don't have an assertion on the JTrue False path. Current implementation
// allows for a complementary only if there is an assertion on the False path (tree->HasAssertion()).
if (assertionIndex != NO_ASSERTION_INDEX)
{
- optCreateComplementaryAssertion(assertionIndex, op1, op2);
+ optCreateComplementaryAssertion(assertionIndex, op1, op2, helperCallArgs);
}
return assertionIndex;
}
{
return NO_ASSERTION_INDEX;
}
- if (op1->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_ISINSTANCEOFINTERFACE) &&
- op1->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_ISINSTANCEOFARRAY) &&
- op1->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_ISINSTANCEOFCLASS) &&
- op1->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_ISINSTANCEOFANY))
- {
- return NO_ASSERTION_INDEX;
- }
- op2 = op1->gtCall.gtCallLateArgs->gtOp.gtOp2;
- op1 = op1->gtCall.gtCallLateArgs;
+ GenTreeCall* call = op1->AsCall();
- // For the assertion, ensure op1 is the object being tested.
- // Morph may have swizzled the operand order.
- GenTree* op1op = op1->gtOp.gtOp1;
-
- if (op1op->TypeGet() == TYP_I_IMPL)
+ if ((call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFINTERFACE)) ||
+ (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFARRAY)) ||
+ (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFCLASS)) ||
+ (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFANY)))
{
- jitstd::swap(op1, op2);
- op1op = op1->gtOp.gtOp1;
- }
+ // TODO-Cleanup: Arg nodes should be retrieved by other means (e.g. GetArgNode)
+ // that do not involve gtCallLateArgs directly. The helper parameter order is
+ // actually (methodTable, object) but the method table tends to end up last in
+ // gtCallLateArgs because it is usually a constant.
+ GenTree* objectNode = call->gtCallLateArgs->GetNode();
+ GenTree* methodTableNode = call->gtCallLateArgs->GetNext()->GetNode();
- assert(op1op->TypeGet() == TYP_REF);
+ // Swap the nodes if they're not ordered as expected.
+ if (objectNode->TypeGet() == TYP_I_IMPL)
+ {
+ jitstd::swap(objectNode, methodTableNode);
+ }
- // Reverse the assertion
- assert(assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL);
- assertionKind = (assertionKind == OAK_EQUAL) ? OAK_NOT_EQUAL : OAK_EQUAL;
+ assert(objectNode->TypeGet() == TYP_REF);
- if (op1op->OperIs(GT_LCL_VAR))
- {
- return optCreateJtrueAssertions(op1, op2, assertionKind);
+ // Reverse the assertion
+ assert((assertionKind == OAK_EQUAL) || (assertionKind == OAK_NOT_EQUAL));
+ assertionKind = (assertionKind == OAK_EQUAL) ? OAK_NOT_EQUAL : OAK_EQUAL;
+
+ if (objectNode->OperIs(GT_LCL_VAR))
+ {
+ return optCreateJtrueAssertions(objectNode, methodTableNode, assertionKind, /* helperCallArgs */ true);
+ }
}
return NO_ASSERTION_INDEX;
assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);
// Consume all the arg regs
- for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- assert(list->OperIsList());
-
- GenTree* argNode = list->Current();
+ GenTree* argNode = use.GetNode();
fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
assert(curArgTabEntry);
}
// Consume all the arg regs
- for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- assert(list->OperIsList());
-
- GenTree* argNode = list->Current();
+ GenTree* argNode = use.GetNode();
fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, argNode->gtSkipReloadOrCopy());
assert(curArgTabEntry);
#if defined(_TARGET_X86_) || defined(UNIX_AMD64_ABI)
// The call will pop its arguments.
// for each putarg_stk:
- ssize_t stackArgBytes = 0;
- GenTree* args = call->gtCallArgs;
- while (args)
+ ssize_t stackArgBytes = 0;
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* arg = args->gtOp.gtOp1;
- if (arg->OperGet() != GT_ARGPLACE && !(arg->gtFlags & GTF_LATE_ARG))
+ GenTree* arg = use.GetNode();
+ if (arg->OperIs(GT_PUTARG_STK) && ((arg->gtFlags & GTF_LATE_ARG) == 0))
{
- if (arg->OperGet() == GT_PUTARG_STK)
- {
- GenTree* source = arg->gtOp.gtOp1;
- unsigned size = arg->AsPutArgStk()->getArgSize();
- stackArgBytes += size;
+ GenTree* source = arg->AsPutArgStk()->gtGetOp1();
+ unsigned size = arg->AsPutArgStk()->getArgSize();
+ stackArgBytes += size;
#ifdef DEBUG
- fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
- assert(curArgTabEntry);
- assert(size == (curArgTabEntry->numSlots * TARGET_POINTER_SIZE));
+ fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
+ assert(curArgTabEntry != nullptr);
+ assert(size == (curArgTabEntry->numSlots * TARGET_POINTER_SIZE));
#ifdef FEATURE_PUT_STRUCT_ARG_STK
- if (source->TypeGet() == TYP_STRUCT)
- {
- GenTreeObj* obj = source->AsObj();
- unsigned argBytes = roundUp(obj->GetLayout()->GetSize(), TARGET_POINTER_SIZE);
+ if (source->TypeGet() == TYP_STRUCT)
+ {
+ GenTreeObj* obj = source->AsObj();
+ unsigned argBytes = roundUp(obj->GetLayout()->GetSize(), TARGET_POINTER_SIZE);
#ifdef _TARGET_X86_
- // If we have an OBJ, we must have created a copy if the original arg was not a
- // local and was not a multiple of TARGET_POINTER_SIZE.
- // Note that on x64/ux this will be handled by unrolling in genStructPutArgUnroll.
- assert((argBytes == obj->GetLayout()->GetSize()) || obj->Addr()->IsLocalAddrExpr());
+ // If we have an OBJ, we must have created a copy if the original arg was not a
+ // local and was not a multiple of TARGET_POINTER_SIZE.
+ // Note that on x64/ux this will be handled by unrolling in genStructPutArgUnroll.
+ assert((argBytes == obj->GetLayout()->GetSize()) || obj->Addr()->IsLocalAddrExpr());
#endif // _TARGET_X86_
- assert((curArgTabEntry->numSlots * TARGET_POINTER_SIZE) == argBytes);
- }
+ assert((curArgTabEntry->numSlots * TARGET_POINTER_SIZE) == argBytes);
+ }
#endif // FEATURE_PUT_STRUCT_ARG_STK
#endif // DEBUG
- }
}
- args = args->gtOp.gtOp2;
}
#endif // defined(_TARGET_X86_) || defined(UNIX_AMD64_ABI)
// For call nodes, translate late args to what they stand for.
if (tree->OperGet() == GT_CALL)
{
- GenTreeCall* call = tree->AsCall();
- GenTreeArgList* args = call->gtCallArgs;
- unsigned i = 0;
- while (args != nullptr)
+ GenTreeCall* call = tree->AsCall();
+ unsigned i = 0;
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* arg = args->Current();
- if (arg->gtFlags & GTF_LATE_ARG)
+ if ((use.GetNode()->gtFlags & GTF_LATE_ARG) != 0)
{
// Find the corresponding late arg.
GenTree* lateArg = call->fgArgInfo->GetArgNode(i);
}
}
i++;
- args = args->Rest();
}
}
struct fgArgTabEntry
{
- GenTree* node; // Initially points at the Op1 field of 'parent', but if the argument is replaced with an GT_ASG or
- // placeholder it will point at the actual argument in the gtCallLateArgs list.
- GenTree* parent; // Points at the GT_LIST node in the gtCallArgs for this argument
+ GenTree* node; // Initially points to `use`'s node, but if the argument is replaced with an GT_ASG or
+ // placeholder it will point at the actual argument node in the gtCallLateArgs list.
+ GenTreeCall::Use* use; // Points at the GenTreeCall::Use in the gtCallArgs for this argument
+ // or nullptr for the `this` argument which does not have a corresponding GenTreeCall::Use.
unsigned argNum; // The original argument number, also specifies the required argument evaluation order from the IL
fgArgInfo(Compiler* comp, GenTreeCall* call, unsigned argCount);
fgArgInfo(GenTreeCall* newCall, GenTreeCall* oldCall);
- fgArgTabEntry* AddRegArg(unsigned argNum,
- GenTree* node,
- GenTree* parent,
- regNumber regNum,
- unsigned numRegs,
- unsigned alignment,
- bool isStruct,
- bool isVararg = false);
+ fgArgTabEntry* AddRegArg(unsigned argNum,
+ GenTree* node,
+ GenTreeCall::Use* use,
+ regNumber regNum,
+ unsigned numRegs,
+ unsigned alignment,
+ bool isStruct,
+ bool isVararg = false);
#ifdef UNIX_AMD64_ABI
fgArgTabEntry* AddRegArg(unsigned argNum,
GenTree* node,
- GenTree* parent,
+ GenTreeCall::Use* use,
regNumber regNum,
unsigned numRegs,
unsigned alignment,
const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* const structDescPtr = nullptr);
#endif // UNIX_AMD64_ABI
- fgArgTabEntry* AddStkArg(unsigned argNum,
- GenTree* node,
- GenTree* parent,
- unsigned numSlots,
- unsigned alignment,
- bool isStruct,
- bool isVararg = false);
+ fgArgTabEntry* AddStkArg(unsigned argNum,
+ GenTree* node,
+ GenTreeCall::Use* use,
+ unsigned numSlots,
+ unsigned alignment,
+ bool isStruct,
+ bool isVararg = false);
void RemorphReset();
void UpdateRegArg(fgArgTabEntry* argEntry, GenTree* node, bool reMorphing);
GenTreeArgList* gtNewListNode(GenTree* op1, GenTreeArgList* op2);
+ GenTreeCall::Use* gtNewCallArgs(GenTree* node);
+ GenTreeCall::Use* gtNewCallArgs(GenTree* node1, GenTree* node2);
+ GenTreeCall::Use* gtNewCallArgs(GenTree* node1, GenTree* node2, GenTree* node3);
+ GenTreeCall::Use* gtNewCallArgs(GenTree* node1, GenTree* node2, GenTree* node3, GenTree* node4);
+ GenTreeCall::Use* gtPrependNewCallArg(GenTree* node, GenTreeCall::Use* args);
+ GenTreeCall::Use* gtInsertNewCallArgAfter(GenTree* node, GenTreeCall::Use* after);
+
GenTreeCall* gtNewCallNode(gtCallTypes callType,
CORINFO_METHOD_HANDLE handle,
var_types type,
- GenTreeArgList* args,
+ GenTreeCall::Use* args,
IL_OFFSETX ilOffset = BAD_IL_OFFSET);
- GenTreeCall* gtNewIndCallNode(GenTree* addr,
- var_types type,
- GenTreeArgList* args,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET);
+ GenTreeCall* gtNewIndCallNode(GenTree* addr,
+ var_types type,
+ GenTreeCall::Use* args,
+ IL_OFFSETX ilOffset = BAD_IL_OFFSET);
- GenTreeCall* gtNewHelperCallNode(unsigned helper, var_types type, GenTreeArgList* args = nullptr);
+ GenTreeCall* gtNewHelperCallNode(unsigned helper, var_types type, GenTreeCall::Use* args = nullptr);
GenTree* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET));
GenTree* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET));
bool gtHasLocalsWithAddrOp(GenTree* tree);
unsigned gtSetListOrder(GenTree* list, bool regs, bool isListCallArgs);
+ unsigned gtSetCallArgsOrder(const GenTreeCall::UseList& args, bool lateArgs, int* callCostEx, int* callCostSz);
void gtWalkOp(GenTree** op1, GenTree** op2, GenTree* base, bool constOnly);
GenTreeCall* impReadyToRunHelperToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken,
CorInfoHelpFunc helper,
var_types type,
- GenTreeArgList* arg = nullptr,
+ GenTreeCall::Use* args = nullptr,
CORINFO_LOOKUP_KIND* pGenericLookupKind = nullptr);
GenTree* impCastClassOrIsInstToTree(GenTree* op1,
((opcode >= CEE_STLOC_0) && (opcode <= CEE_STLOC_3)));
}
- GenTreeArgList* impPopList(unsigned count, CORINFO_SIG_INFO* sig, GenTreeArgList* prefixTree = nullptr);
+ GenTreeCall::Use* impPopCallArgs(unsigned count, CORINFO_SIG_INFO* sig, GenTreeCall::Use* prefixArgs = nullptr);
- GenTreeArgList* impPopRevList(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount = 0);
+ GenTreeCall::Use* impPopReverseCallArgs(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount = 0);
/*
* Get current IL offset with stack-empty info incoporated
BOOL impInlineIsThis(GenTree* tree, InlArgInfo* inlArgInfo);
- BOOL impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree* additionalTreesToBeEvaluatedBefore,
- GenTree* variableBeingDereferenced,
- InlArgInfo* inlArgInfo);
+ BOOL impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree* additionalTree,
+ GenTreeCall::Use* additionalCallArgs,
+ GenTree* dereferencedAddress,
+ InlArgInfo* inlArgInfo);
void impMarkInlineCandidate(GenTree* call,
CORINFO_CONTEXT_HANDLE exactContextHnd,
// Does value-numbering for a call. We interpret some helper calls.
void fgValueNumberCall(GenTreeCall* call);
- // The VN of some nodes in "args" may have changed -- reassign VNs to the arg list nodes.
- void fgUpdateArgListVNs(GenTreeArgList* args);
-
// Does value-numbering for a helper "call" that has a VN function symbol "vnf".
void fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueNumPair vnpExc);
GenTree* fgMorphCastIntoHelper(GenTree* tree, int helper, GenTree* oper);
- GenTree* fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgList* args, bool morphArgs = true);
+ GenTree* fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall::Use* args, bool morphArgs = true);
GenTree* fgMorphStackArgForVarArgs(unsigned lclNum, var_types varType, unsigned lclOffs);
GenTreeArgList* fgMorphArgList(GenTreeArgList* args, MorphAddrContext* mac);
void fgMakeOutgoingStructArgCopy(GenTreeCall* call,
- GenTree* args,
+ GenTreeCall::Use* args,
unsigned argIndex,
CORINFO_CLASS_HANDLE copyBlkClass);
AssertionIndex optAssertionGenPhiDefn(GenTree* tree);
AssertionInfo optCreateJTrueBoundsAssertion(GenTree* tree);
AssertionInfo optAssertionGenJtrue(GenTree* tree);
- AssertionIndex optCreateJtrueAssertions(GenTree* op1, GenTree* op2, Compiler::optAssertionKind assertionKind);
+ AssertionIndex optCreateJtrueAssertions(GenTree* op1,
+ GenTree* op2,
+ Compiler::optAssertionKind assertionKind,
+ bool helperCallArgs = false);
AssertionIndex optFindComplementary(AssertionIndex assertionIndex);
void optMapComplementary(AssertionIndex assertionIndex, AssertionIndex index);
// Assertion creation functions.
- AssertionIndex optCreateAssertion(GenTree* op1, GenTree* op2, optAssertionKind assertionKind);
AssertionIndex optCreateAssertion(GenTree* op1,
GenTree* op2,
optAssertionKind assertionKind,
- AssertionDsc* assertion);
- void optCreateComplementaryAssertion(AssertionIndex assertionIndex, GenTree* op1, GenTree* op2);
+ bool helperCallArgs = false);
+ void optCreateComplementaryAssertion(AssertionIndex assertionIndex,
+ GenTree* op1,
+ GenTree* op2,
+ bool helperCallArgs = false);
bool optAssertionVnInvolvesNan(AssertionDsc* assertion);
AssertionIndex optAddAssertion(AssertionDsc* assertion);
}
}
- for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
- result = WalkTree(args->pCurrent(), call);
+ result = WalkTree(&use.NodeRef(), call);
if (result == fgWalkResult::WALK_ABORT)
{
return result;
}
}
- for (GenTreeArgList* args = call->gtCallLateArgs; args != nullptr; args = args->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- result = WalkTree(args->pCurrent(), call);
+ result = WalkTree(&use.NodeRef(), call);
if (result == fgWalkResult::WALK_ABORT)
{
return result;
// Return Value:
// New CT_HELPER node
-inline GenTreeCall* Compiler::gtNewHelperCallNode(unsigned helper, var_types type, GenTreeArgList* args)
+inline GenTreeCall* Compiler::gtNewHelperCallNode(unsigned helper, var_types type, GenTreeCall::Use* args)
{
unsigned flags = s_helperCallProperties.NoThrow((CorInfoHelpFunc)helper) ? 0 : GTF_EXCEPT;
GenTreeCall* result = gtNewCallNode(CT_HELPER, eeFindHelper(helper), type, args);
{
return;
}
- if ((call->gtCallArgs != nullptr) && (call->gtCallArgs->VisitListOperands(visitor) == VisitResult::Abort))
+
+ for (GenTreeCall::Use& use : call->Args())
{
- return;
+ if (visitor(use.GetNode()) == VisitResult::Abort)
+ {
+ return;
+ }
}
- if ((call->gtCallLateArgs != nullptr) &&
- (call->gtCallLateArgs->VisitListOperands(visitor)) == VisitResult::Abort)
+
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- return;
+ if (visitor(use.GetNode()) == VisitResult::Abort)
+ {
+ return;
+ }
}
+
if (call->gtCallType == CT_INDIRECT)
{
if ((call->gtCallCookie != nullptr) && (visitor(call->gtCallCookie) == VisitResult::Abort))
unreached();
}
- GenTreeArgList* argList = m_compiler->gtNewArgList(loOp1, hiOp1, shiftByOp);
+ GenTreeCall::Use* argList = m_compiler->gtNewCallArgs(loOp1, hiOp1, shiftByOp);
GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, argList);
call->gtFlags |= shift->gtFlags & GTF_ALL_EFFECT;
{
// In such cases we still want to add the method entry callback node
- GenTreeArgList* args = gtNewArgList(gtNewIconEmbMethHndNode(info.compMethodHnd));
- GenTree* call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, args);
+ GenTreeCall::Use* args = gtNewCallArgs(gtNewIconEmbMethHndNode(info.compMethodHnd));
+ GenTree* call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, args);
stmt = gtNewStmt(call);
}
arg = gtNewIconEmbMethHndNode(info.compMethodHnd);
}
- GenTreeArgList* args = gtNewArgList(arg);
- GenTree* call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, args);
+ GenTreeCall::Use* args = gtNewCallArgs(arg);
+ GenTree* call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, args);
// Get the address of the first blocks ExecutionCount
size_t addrOfFirstExecutionCount = (size_t)&profileBlockCountsStart->ExecutionCount;
break;
}
- GenTreeArgList* argList = nullptr;
+ GenTreeCall::Use* argList = nullptr;
GenTree* opModuleIDArg;
GenTree* opClassIDArg;
}
// call the helper to get the base
- argList = gtNewArgList(opModuleIDArg, opClassIDArg);
+ argList = gtNewCallArgs(opModuleIDArg, opClassIDArg);
}
else
{
- argList = gtNewArgList(opModuleIDArg);
+ argList = gtNewCallArgs(opModuleIDArg);
}
GenTreeCall* result = gtNewHelperCallNode(helper, type, argList);
CORINFO_METHOD_HANDLE methHnd = call->gtCallMethHnd;
CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getMethodClass(methHnd);
- GenTree* targetMethod = call->gtCallArgs->Rest()->Current();
+ GenTree* targetMethod = call->gtCallArgs->GetNext()->GetNode();
noway_assert(targetMethod->TypeGet() == TYP_I_IMPL);
genTreeOps oper = targetMethod->OperGet();
CORINFO_METHOD_HANDLE targetMethodHnd = nullptr;
}
else if (oper == GT_CALL && targetMethod->gtCall.gtCallMethHnd == eeFindHelper(CORINFO_HELP_VIRTUAL_FUNC_PTR))
{
- GenTree* handleNode = targetMethod->gtCall.gtCallArgs->Rest()->Rest()->Current();
+ GenTree* handleNode = targetMethod->AsCall()->gtCallArgs->GetNext()->GetNext()->GetNode();
if (handleNode->OperGet() == GT_CNS_INT)
{
GenTreeCall* runtimeLookupCall = qmarkNode->gtOp.gtOp2->gtOp.gtOp1->AsCall();
// This could be any of CORINFO_HELP_RUNTIMEHANDLE_(METHOD|CLASS)(_LOG?)
- GenTree* tokenNode = runtimeLookupCall->gtCallArgs->gtOp.gtOp2->gtOp.gtOp1;
+ GenTree* tokenNode = runtimeLookupCall->gtCallArgs->GetNext()->GetNode();
noway_assert(tokenNode->OperGet() == GT_CNS_INT);
targetMethodHnd = CORINFO_METHOD_HANDLE(tokenNode->gtIntCon.gtCompileTimeHandle);
}
JITDUMP("optimized\n");
GenTree* thisPointer = call->gtCallObjp;
- GenTree* targetObjPointers = call->gtCallArgs->Current();
- GenTreeArgList* helperArgs = nullptr;
+ GenTree* targetObjPointers = call->gtCallArgs->GetNode();
+ GenTreeCall::Use* helperArgs = nullptr;
CORINFO_LOOKUP pLookup;
CORINFO_CONST_LOOKUP entryPoint;
info.compCompHnd->getReadyToRunDelegateCtorHelper(ldftnToken, clsHnd, &pLookup);
if (!pLookup.lookupKind.needsRuntimeLookup)
{
- helperArgs = gtNewArgList(thisPointer, targetObjPointers);
+ helperArgs = gtNewCallArgs(thisPointer, targetObjPointers);
entryPoint = pLookup.constLookup;
}
else
info.compCompHnd->getReadyToRunHelper(ldftnToken, &pLookup.lookupKind,
CORINFO_HELP_READYTORUN_GENERIC_HANDLE, &genericLookup);
GenTree* ctxTree = getRuntimeContextTree(pLookup.lookupKind.runtimeLookupKind);
- helperArgs = gtNewArgList(thisPointer, targetObjPointers, ctxTree);
+ helperArgs = gtNewCallArgs(thisPointer, targetObjPointers, ctxTree);
entryPoint = genericLookup;
}
call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, helperArgs);
{
JITDUMP("optimized\n");
- GenTree* thisPointer = call->gtCallObjp;
- GenTree* targetObjPointers = call->gtCallArgs->Current();
- GenTreeArgList* helperArgs = gtNewArgList(thisPointer, targetObjPointers);
+ GenTree* thisPointer = call->gtCallObjp;
+ GenTree* targetObjPointers = call->gtCallArgs->GetNode();
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(thisPointer, targetObjPointers);
call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, helperArgs);
call->gtCallMethHnd = alternateCtor;
- noway_assert(call->gtCallArgs->Rest()->Rest() == nullptr);
- GenTreeArgList* addArgs = nullptr;
+ noway_assert(call->gtCallArgs->GetNext()->GetNext() == nullptr);
+ GenTreeCall::Use* addArgs = nullptr;
if (ctorData.pArg5)
{
GenTree* arg5 = gtNewIconHandleNode(size_t(ctorData.pArg5), GTF_ICON_FTN_ADDR);
- addArgs = gtNewListNode(arg5, addArgs);
+ addArgs = gtPrependNewCallArg(arg5, addArgs);
}
if (ctorData.pArg4)
{
GenTree* arg4 = gtNewIconHandleNode(size_t(ctorData.pArg4), GTF_ICON_FTN_ADDR);
- addArgs = gtNewListNode(arg4, addArgs);
+ addArgs = gtPrependNewCallArg(arg4, addArgs);
}
if (ctorData.pArg3)
{
GenTree* arg3 = gtNewIconHandleNode(size_t(ctorData.pArg3), GTF_ICON_FTN_ADDR);
- addArgs = gtNewListNode(arg3, addArgs);
+ addArgs = gtPrependNewCallArg(arg3, addArgs);
}
- call->gtCallArgs->Rest()->Rest() = addArgs;
+ call->gtCallArgs->GetNext()->SetNext(addArgs);
}
else
{
tree = gtNewLclvNode(info.compTypeCtxtArg, TYP_I_IMPL);
// Call helper CORINFO_HELP_GETCLASSFROMMETHODPARAM to get the class handle
// from the method handle.
- tree = gtNewHelperCallNode(CORINFO_HELP_GETCLASSFROMMETHODPARAM, TYP_I_IMPL, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_GETCLASSFROMMETHODPARAM, TYP_I_IMPL, gtNewCallArgs(tree));
break;
}
noway_assert(tree); // tree should now contain the CORINFO_CLASS_HANDLE for the exact class.
// Given the class handle, get the pointer to the Monitor.
- tree = gtNewHelperCallNode(CORINFO_HELP_GETSYNCFROMCLASSHANDLE, TYP_I_IMPL, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_GETSYNCFROMCLASSHANDLE, TYP_I_IMPL, gtNewCallArgs(tree));
}
noway_assert(tree);
{
tree = fgGetCritSectOfStaticMethod();
tree = gtNewHelperCallNode(enter ? CORINFO_HELP_MON_ENTER_STATIC : CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID,
- gtNewArgList(tree, varAddrNode));
+ gtNewCallArgs(tree, varAddrNode));
}
else
{
tree = gtNewLclvNode(lvaThisVar, TYP_REF);
tree = gtNewHelperCallNode(enter ? CORINFO_HELP_MON_ENTER : CORINFO_HELP_MON_EXIT, TYP_VOID,
- gtNewArgList(tree, varAddrNode));
+ gtNewCallArgs(tree, varAddrNode));
}
#ifdef DEBUG
tree = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(lvaReversePInvokeFrameVar, TYP_BLK));
- tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, TYP_VOID, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, TYP_VOID, gtNewCallArgs(tree));
fgEnsureFirstBBisScratch();
tree = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(lvaReversePInvokeFrameVar, TYP_BLK));
- tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, TYP_VOID, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, TYP_VOID, gtNewCallArgs(tree));
assert(genReturnBB != nullptr);
tree = gtNewIconEmbMethHndNode(info.compMethodHnd);
tree = gtNewHelperCallNode(info.compCompHnd->getSecurityPrologHelper(info.compMethodHnd), TYP_VOID,
- gtNewArgList(tree, gtNewOperNode(GT_ADDR, TYP_BYREF,
- gtNewLclvNode(lvaSecurityObject, TYP_REF))));
+ gtNewCallArgs(tree, gtNewOperNode(GT_ADDR, TYP_BYREF,
+ gtNewLclvNode(lvaSecurityObject, TYP_REF))));
/* Create a new basic block and stick the call in it */
{
tree = fgGetCritSectOfStaticMethod();
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER_STATIC, TYP_VOID, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER_STATIC, TYP_VOID, gtNewCallArgs(tree));
}
else
{
tree = gtNewLclvNode(info.compThisArg, TYP_REF);
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER, TYP_VOID, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER, TYP_VOID, gtNewCallArgs(tree));
}
/* Create a new basic block and stick the call in it */
{
tree = fgGetCritSectOfStaticMethod();
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, gtNewCallArgs(tree));
}
else
{
tree = gtNewLclvNode(info.compThisArg, TYP_REF);
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, gtNewCallArgs(tree));
}
fgNewStmtNearEnd(genReturnBB, tree);
tree = gtNewIconEmbMethHndNode(info.compMethodHnd);
- tree = gtNewHelperCallNode(CORINFO_HELP_VERIFICATION_RUNTIME_CHECK, TYP_VOID, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_VERIFICATION_RUNTIME_CHECK, TYP_VOID, gtNewCallArgs(tree));
/* Create a new basic block and stick the call in it */
fgSetTreeSeqHelper(tree->gtCall.gtCallObjp, isLIR);
}
- /* We'll evaluate the arguments next, left to right
- * NOTE: setListOrder needs cleanup - eliminate the #ifdef afterwards */
-
- if (tree->gtCall.gtCallArgs)
+ for (GenTreeCall::Use& use : tree->AsCall()->Args())
{
- fgSetTreeSeqHelper(tree->gtCall.gtCallArgs, isLIR);
+ fgSetTreeSeqHelper(use.GetNode(), isLIR);
}
- /* Evaluate the temp register arguments list
- * This is a "hidden" list and its only purpose is to
- * extend the life of temps until we make the call */
-
- if (tree->gtCall.gtCallLateArgs)
+ for (GenTreeCall::Use& use : tree->AsCall()->LateArgs())
{
- fgSetTreeSeqHelper(tree->gtCall.gtCallLateArgs, isLIR);
+ fgSetTreeSeqHelper(use.GetNode(), isLIR);
}
if ((tree->gtCall.gtCallType == CT_INDIRECT) && (tree->gtCall.gtCallCookie != nullptr))
{
case GT_CALL:
- GenTree* args;
- GenTree* argx;
GenTreeCall* call;
call = tree->AsCall();
}
}
- for (args = call->gtCallArgs; args; args = args->gtOp.gtOp2)
+ for (GenTreeCall::Use& use : call->Args())
{
- argx = args->gtOp.gtOp1;
- fgDebugCheckFlags(argx);
+ fgDebugCheckFlags(use.GetNode());
- chkFlags |= (argx->gtFlags & GTF_SIDE_EFFECT);
+ chkFlags |= (use.GetNode()->gtFlags & GTF_SIDE_EFFECT);
- if (argx->gtFlags & GTF_ASG)
+ if ((use.GetNode()->gtFlags & GTF_ASG) != 0)
{
treeFlags |= GTF_ASG;
}
}
- for (args = call->gtCallLateArgs; args; args = args->gtOp.gtOp2)
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- argx = args->gtOp.gtOp1;
- fgDebugCheckFlags(argx);
+ fgDebugCheckFlags(use.GetNode());
- chkFlags |= (argx->gtFlags & GTF_SIDE_EFFECT);
+ chkFlags |= (use.GetNode()->gtFlags & GTF_SIDE_EFFECT);
- if (argx->gtFlags & GTF_ASG)
+ if ((use.GetNode()->gtFlags & GTF_ASG) != 0)
{
treeFlags |= GTF_ASG;
}
if (call->IsUnmanaged() && (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL))
{
- if (call->gtCallArgs->gtOp.gtOp1->OperGet() == GT_NOP)
+ if (call->gtCallArgs->GetNode()->OperGet() == GT_NOP)
{
- noway_assert(call->gtCallLateArgs->gtOp.gtOp1->TypeGet() == TYP_I_IMPL ||
- call->gtCallLateArgs->gtOp.gtOp1->TypeGet() == TYP_BYREF);
+ noway_assert(call->gtCallLateArgs->GetNode()->TypeGet() == TYP_I_IMPL ||
+ call->gtCallLateArgs->GetNode()->TypeGet() == TYP_BYREF);
}
else
{
- noway_assert(call->gtCallArgs->gtOp.gtOp1->TypeGet() == TYP_I_IMPL ||
- call->gtCallArgs->gtOp.gtOp1->TypeGet() == TYP_BYREF);
+ noway_assert(call->gtCallArgs->GetNode()->TypeGet() == TYP_I_IMPL ||
+ call->gtCallArgs->GetNode()->TypeGet() == TYP_BYREF);
}
}
break;
}
}
- if (Compare(op1->gtCall.gtCallLateArgs, op2->gtCall.gtCallLateArgs) &&
- Compare(op1->gtCall.gtCallArgs, op2->gtCall.gtCallArgs) &&
- Compare(op1->gtCall.gtControlExpr, op2->gtCall.gtControlExpr) &&
- Compare(op1->gtCall.gtCallObjp, op2->gtCall.gtCallObjp))
+ if (!Compare(op1->AsCall()->gtCallObjp, op2->AsCall()->gtCallObjp))
{
- return true;
+ return false;
}
- break;
+
+ {
+ GenTreeCall::UseIterator i1 = op1->AsCall()->Args().begin();
+ GenTreeCall::UseIterator end1 = op1->AsCall()->Args().end();
+ GenTreeCall::UseIterator i2 = op2->AsCall()->Args().begin();
+ GenTreeCall::UseIterator end2 = op2->AsCall()->Args().end();
+
+ for (; (i1 != end1) && (i2 != end2); ++i1, ++i2)
+ {
+ if (!Compare(i1->GetNode(), i2->GetNode()))
+ {
+ return false;
+ }
+ }
+
+ if ((i1 != end1) || (i2 != end2))
+ {
+ return false;
+ }
+
+ i1 = op1->AsCall()->LateArgs().begin();
+ end1 = op1->AsCall()->LateArgs().end();
+ i2 = op2->AsCall()->LateArgs().begin();
+ end2 = op2->AsCall()->LateArgs().end();
+
+ for (; (i1 != end1) && (i2 != end2); ++i1, ++i2)
+ {
+ if (!Compare(i1->GetNode(), i2->GetNode()))
+ {
+ return false;
+ }
+ }
+
+ if ((i1 != end1) || (i2 != end2))
+ {
+ return false;
+ }
+ }
+
+ if (!Compare(op1->AsCall()->gtControlExpr, op2->AsCall()->gtControlExpr))
+ {
+ return false;
+ }
+
+ return true;
case GT_ARR_ELEM:
}
}
- if (tree->gtCall.gtCallArgs)
+ for (GenTreeCall::Use& use : tree->AsCall()->Args())
{
- if (gtHasRef(tree->gtCall.gtCallArgs, lclNum, defOnly))
+ if (gtHasRef(use.GetNode(), lclNum, defOnly))
{
return true;
}
}
- if (tree->gtCall.gtCallLateArgs)
+ for (GenTreeCall::Use& use : tree->AsCall()->LateArgs())
{
- if (gtHasRef(tree->gtCall.gtCallLateArgs, lclNum, defOnly))
+ if (gtHasRef(use.GetNode(), lclNum, defOnly))
{
return true;
}
hash = genTreeHashAdd(hash, gtHashValue(temp));
}
- if (tree->gtCall.gtCallArgs)
+ for (GenTreeCall::Use& use : tree->AsCall()->Args())
{
- temp = tree->gtCall.gtCallArgs;
- assert(temp);
- hash = genTreeHashAdd(hash, gtHashValue(temp));
+ hash = genTreeHashAdd(hash, gtHashValue(use.GetNode()));
}
if (tree->gtCall.gtCallType == CT_INDIRECT)
hash = genTreeHashAdd(hash, tree->gtCall.gtCallMethHnd);
}
- if (tree->gtCall.gtCallLateArgs)
+ for (GenTreeCall::Use& use : tree->AsCall()->LateArgs())
{
- temp = tree->gtCall.gtCallLateArgs;
- assert(temp);
- hash = genTreeHashAdd(hash, gtHashValue(temp));
+ hash = genTreeHashAdd(hash, gtHashValue(use.GetNode()));
}
break;
return nxtlvl;
}
+unsigned Compiler::gtSetCallArgsOrder(const GenTreeCall::UseList& args, bool lateArgs, int* callCostEx, int* callCostSz)
+{
+ unsigned level = 0;
+ unsigned costEx = 0;
+ unsigned costSz = 0;
+
+ for (GenTreeCall::Use& use : args)
+ {
+ GenTree* argNode = use.GetNode();
+ unsigned argLevel = gtSetEvalOrder(argNode);
+
+ if (argLevel > level)
+ {
+ level = argLevel;
+ }
+
+ if (argNode->gtCostEx != 0)
+ {
+ costEx += argNode->gtCostEx;
+ costEx += lateArgs ? 0 : IND_COST_EX;
+ }
+
+ if (argNode->gtCostSz != 0)
+ {
+ costSz += argNode->gtCostSz;
+#ifdef _TARGET_XARCH_
+ if (lateArgs) // push is smaller than mov to reg
+#endif
+ {
+ costSz += 1;
+ }
+ }
+ }
+
+ *callCostEx += costEx;
+ *callCostSz += costSz;
+
+ return level;
+}
+
//-----------------------------------------------------------------------------
// gtWalkOp: Traverse and mark an address expression
//
/* Evaluate the arguments, right to left */
- if (tree->gtCall.gtCallArgs)
+ if (tree->AsCall()->gtCallArgs != nullptr)
{
- const bool isListCallArgs = true;
- const bool callArgsInRegs = false;
- lvl2 = gtSetListOrder(tree->gtCall.gtCallArgs, isListCallArgs, callArgsInRegs);
+ const bool lateArgs = false;
+ lvl2 = gtSetCallArgsOrder(tree->AsCall()->Args(), lateArgs, &costEx, &costSz);
if (level < lvl2)
{
level = lvl2;
}
- costEx += tree->gtCall.gtCallArgs->gtCostEx;
- costSz += tree->gtCall.gtCallArgs->gtCostSz;
}
/* Evaluate the temp register arguments list
* This is a "hidden" list and its only purpose is to
* extend the life of temps until we make the call */
- if (tree->gtCall.gtCallLateArgs)
+ if (tree->AsCall()->gtCallLateArgs != nullptr)
{
- const bool isListCallArgs = true;
- const bool callArgsInRegs = true;
- lvl2 = gtSetListOrder(tree->gtCall.gtCallLateArgs, isListCallArgs, callArgsInRegs);
+ const bool lateArgs = true;
+ lvl2 = gtSetCallArgsOrder(tree->AsCall()->LateArgs(), lateArgs, &costEx, &costSz);
if (level < lvl2)
{
level = lvl2;
}
- costEx += tree->gtCall.gtCallLateArgs->gtCostEx;
- costSz += tree->gtCall.gtCallLateArgs->gtCostSz;
}
if (tree->gtCall.gtCallType == CT_INDIRECT)
{
return &(call->gtCallObjp);
}
- if (this == call->gtCallArgs)
+ for (GenTreeCall::Use& use : call->Args())
{
- return reinterpret_cast<GenTree**>(&(call->gtCallArgs));
+ if (this == use.GetNode())
+ {
+ return &use.NodeRef();
+ }
}
- if (this == call->gtCallLateArgs)
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- return reinterpret_cast<GenTree**>(&(call->gtCallLateArgs));
+ if (this == use.GetNode())
+ {
+ return &use.NodeRef();
+ }
}
if (this == call->gtControlExpr)
{
return true;
}
}
- if ((call->gtCallArgs != nullptr) && call->gtCallArgs->TryGetUseList(def, use))
+ for (GenTreeCall::Use& argUse : call->Args())
{
- return true;
+ if (argUse.GetNode() == def)
+ {
+ *use = &argUse.NodeRef();
+ return true;
+ }
}
-
- return (call->gtCallLateArgs != nullptr) && call->gtCallLateArgs->TryGetUseList(def, use);
+ for (GenTreeCall::Use& argUse : call->LateArgs())
+ {
+ if (argUse.GetNode() == def)
+ {
+ *use = &argUse.NodeRef();
+ return true;
+ }
+ }
+ return false;
}
// Binary nodes
}
#endif // FEATURE_SIMD
-GenTreeCall* Compiler::gtNewIndCallNode(GenTree* addr, var_types type, GenTreeArgList* args, IL_OFFSETX ilOffset)
+GenTreeCall* Compiler::gtNewIndCallNode(GenTree* addr, var_types type, GenTreeCall::Use* args, IL_OFFSETX ilOffset)
{
return gtNewCallNode(CT_INDIRECT, (CORINFO_METHOD_HANDLE)addr, type, args, ilOffset);
}
GenTreeCall* Compiler::gtNewCallNode(
- gtCallTypes callType, CORINFO_METHOD_HANDLE callHnd, var_types type, GenTreeArgList* args, IL_OFFSETX ilOffset)
+ gtCallTypes callType, CORINFO_METHOD_HANDLE callHnd, var_types type, GenTreeCall::Use* args, IL_OFFSETX ilOffset)
{
GenTreeCall* node = new (this, GT_CALL) GenTreeCall(genActualType(type));
node->gtFlags |= (GTF_CALL | GTF_GLOB_REF);
- if (args)
+ for (GenTreeCall::Use& use : GenTreeCall::UseList(args))
{
- node->gtFlags |= (args->gtFlags & GTF_ALL_EFFECT);
+ node->gtFlags |= (use.GetNode()->gtFlags & GTF_ALL_EFFECT);
}
node->gtCallType = callType;
node->gtCallMethHnd = callHnd;
return node;
}
+GenTreeCall::Use* Compiler::gtPrependNewCallArg(GenTree* node, GenTreeCall::Use* args)
+{
+ return new (this, CMK_ASTNode) GenTreeCall::Use(node, args);
+}
+
+GenTreeCall::Use* Compiler::gtInsertNewCallArgAfter(GenTree* node, GenTreeCall::Use* after)
+{
+ after->SetNext(new (this, CMK_ASTNode) GenTreeCall::Use(node, after->GetNext()));
+ return after->GetNext();
+}
+
+GenTreeCall::Use* Compiler::gtNewCallArgs(GenTree* node)
+{
+ return new (this, CMK_ASTNode) GenTreeCall::Use(node);
+}
+
+GenTreeCall::Use* Compiler::gtNewCallArgs(GenTree* node1, GenTree* node2)
+{
+ return new (this, CMK_ASTNode) GenTreeCall::Use(node1, gtNewCallArgs(node2));
+}
+
+GenTreeCall::Use* Compiler::gtNewCallArgs(GenTree* node1, GenTree* node2, GenTree* node3)
+{
+ return new (this, CMK_ASTNode) GenTreeCall::Use(node1, gtNewCallArgs(node2, node3));
+}
+
+GenTreeCall::Use* Compiler::gtNewCallArgs(GenTree* node1, GenTree* node2, GenTree* node3, GenTree* node4)
+{
+ return new (this, CMK_ASTNode) GenTreeCall::Use(node1, gtNewCallArgs(node2, node3, node4));
+}
+
GenTreeArgList* Compiler::gtNewListNode(GenTree* op1, GenTreeArgList* op2)
{
assert((op1 != nullptr) && (op1->OperGet() != GT_LIST));
{
return curArgTabEntry;
}
- else if (curArgTabEntry->parent != nullptr)
+ else if (curArgTabEntry->use != nullptr)
{
- assert(curArgTabEntry->parent->OperIsList());
- if (curArgTabEntry->parent->Current() == node)
+ if (curArgTabEntry->use->GetNode() == node)
{
return curArgTabEntry;
}
GenTree* argx = nullptr;
unsigned regIndex = 0;
- for (GenTreeArgList *list = call->gtCall.gtCallLateArgs; list != nullptr; regIndex++, list = list->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- argx = list->Current();
+ argx = use.GetNode();
assert(!argx->IsArgPlaceHolderNode()); // No placeholder nodes are in gtCallLateArgs;
if (regIndex == lateArgInx)
{
break;
}
+ regIndex++;
}
noway_assert(argx != nullptr);
return argx;
*/
bool Compiler::gtArgIsThisPtr(fgArgTabEntry* argEntry)
{
- return (argEntry->parent == nullptr);
+ return (argEntry->use == nullptr);
}
/*****************************************************************************
GenTreeCall* copy = new (this, GT_CALL) GenTreeCall(tree->TypeGet());
copy->gtCallObjp = tree->gtCallObjp ? gtCloneExpr(tree->gtCallObjp, addFlags, deepVarNum, deepVarVal) : nullptr;
- copy->gtCallArgs =
- tree->gtCallArgs ? gtCloneExpr(tree->gtCallArgs, addFlags, deepVarNum, deepVarVal)->AsArgList() : nullptr;
copy->gtCallMoreFlags = tree->gtCallMoreFlags;
- copy->gtCallLateArgs = tree->gtCallLateArgs
- ? gtCloneExpr(tree->gtCallLateArgs, addFlags, deepVarNum, deepVarVal)->AsArgList()
- : nullptr;
+ copy->gtCallArgs = nullptr;
+ copy->gtCallLateArgs = nullptr;
+
+ GenTreeCall::Use** argsTail = ©->gtCallArgs;
+ for (GenTreeCall::Use& use : tree->Args())
+ {
+ *argsTail = gtNewCallArgs(gtCloneExpr(use.GetNode(), addFlags, deepVarNum, deepVarVal));
+ argsTail = &((*argsTail)->NextRef());
+ }
+
+ argsTail = ©->gtCallLateArgs;
+ for (GenTreeCall::Use& use : tree->LateArgs())
+ {
+ *argsTail = gtNewCallArgs(gtCloneExpr(use.GetNode(), addFlags, deepVarNum, deepVarVal));
+ argsTail = &((*argsTail)->NextRef());
+ }
#if !FEATURE_FIXED_OUT_ARGS
copy->regArgList = tree->regArgList;
// There is no information about registers of late args for platforms
// with FEATURE_FIXED_OUT_ARGS that is why this debug check is under
// !FEATURE_FIXED_OUT_ARGS.
- regNumber thisReg = REG_ARG_0;
- GenTree* lateArgs = call->gtCallLateArgs;
- regList list = call->regArgList;
- int index = 0;
- while (lateArgs != NULL)
+ regNumber thisReg = REG_ARG_0;
+ regList list = call->regArgList;
+ int index = 0;
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- assert(lateArgs->gtOper == GT_LIST);
assert(index < call->regArgListCount);
regNumber curArgReg = list[index];
if (curArgReg == thisReg)
{
- assert(result == lateArgs->gtOp.gtOp1);
+ assert(result == use.GetNode());
}
- lateArgs = lateArgs->gtOp.gtOp2;
index++;
}
#endif // !FEATURE_FIXED_OUT_ARGS && defined(DEBUG)
case GT_CALL:
{
GenTreeCall* call = AsCall();
- unsigned res = 0; // arg list(s) (including late args).
+ unsigned res = 0;
if (call->gtCallObjp != nullptr)
{
- res++; // Add objp?
+ res++;
}
- if (call->gtCallArgs != nullptr)
+ for (GenTreeCall::Use& use : call->Args())
{
- res++; // Add args?
+ res++;
}
- if (call->gtCallLateArgs != nullptr)
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- res++; // Add late args?
+ res++;
}
if (call->gtControlExpr != nullptr)
{
case GT_CALL:
{
- // The if chain below assumes that all possible children are non-null.
- // If some are null, "virtually skip them."
- // If there isn't "virtually skip it."
GenTreeCall* call = AsCall();
- if (call->gtCallObjp == nullptr)
- {
- childNum++;
- }
- if (childNum >= 1 && call->gtCallArgs == nullptr)
- {
- childNum++;
- }
- if (childNum >= 2 && call->gtCallLateArgs == nullptr)
- {
- childNum++;
- }
- if (childNum >= 3 && call->gtControlExpr == nullptr)
- {
- childNum++;
- }
- if (call->gtCallType == CT_INDIRECT)
+ if (call->gtCallObjp != nullptr)
{
- if (childNum >= 4 && call->gtCallCookie == nullptr)
+ if (childNum == 0)
{
- childNum++;
+ return call->gtCallObjp;
}
- }
- if (childNum == 0)
- {
- return call->gtCallObjp;
+ childNum--;
}
- else if (childNum == 1)
+
+ for (GenTreeCall::Use& use : call->Args())
{
- return call->gtCallArgs;
+ if (childNum == 0)
+ {
+ return use.GetNode();
+ }
+
+ childNum--;
}
- else if (childNum == 2)
+
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- return call->gtCallLateArgs;
+ if (childNum == 0)
+ {
+ return use.GetNode();
+ }
+
+ childNum--;
}
- else if (childNum == 3)
+
+ if (call->gtControlExpr != nullptr)
{
- return call->gtControlExpr;
+ if (childNum == 0)
+ {
+ return call->gtControlExpr;
+ }
+
+ childNum--;
}
- else
+
+ if ((call->gtCallType == CT_INDIRECT) && (call->gtCallCookie != nullptr))
{
- assert(call->gtCallType == CT_INDIRECT);
- if (childNum == 4)
+ if (childNum == 0)
{
return call->gtCallCookie;
}
- else
+
+ childNum--;
+ }
+
+ if (call->gtCallAddr != nullptr)
+ {
+ if (childNum == 0)
{
- assert(childNum == 5);
return call->gtCallAddr;
}
}
+
+ unreached();
}
case GT_NONE:
unreached();
case CALL_ARGS:
if (m_statePtr != nullptr)
{
- GenTreeArgList* argNode = static_cast<GenTreeArgList*>(m_statePtr);
- m_edge = &argNode->gtOp1;
- m_statePtr = argNode->Rest();
+ GenTreeCall::Use* use = static_cast<GenTreeCall::Use*>(m_statePtr);
+ m_edge = &use->NodeRef();
+ m_statePtr = use->GetNext();
return;
}
m_statePtr = call->gtCallLateArgs;
case CALL_LATE_ARGS:
if (m_statePtr != nullptr)
{
- GenTreeArgList* argNode = static_cast<GenTreeArgList*>(m_statePtr);
- m_edge = &argNode->gtOp1;
- m_statePtr = argNode->Rest();
+ GenTreeCall::Use* use = static_cast<GenTreeCall::Use*>(m_statePtr);
+ m_edge = &use->NodeRef();
+ m_statePtr = use->GetNext();
return;
}
m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_CONTROL_EXPR>;
#if !FEATURE_FIXED_OUT_ARGS
regList list = call->regArgList;
#endif
- /* process the late argument list */
int lateArgIndex = 0;
- for (GenTreeArgList* lateArgs = call->gtCallLateArgs; lateArgs;
- (lateArgIndex++, lateArgs = lateArgs->Rest()))
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- GenTree* argx;
-
- argx = lateArgs->Current();
-
- IndentInfo arcType = (lateArgs->Rest() == nullptr) ? IIArcBottom : IIArc;
- gtGetLateArgMsg(call, argx, lateArgIndex, -1, bufp, sizeof(buf));
- gtDispChild(argx, indentStack, arcType, bufp, topOnly);
+ IndentInfo arcType = (use.GetNext() == nullptr) ? IIArcBottom : IIArc;
+ gtGetLateArgMsg(call, use.GetNode(), lateArgIndex, -1, bufp, sizeof(buf));
+ gtDispChild(use.GetNode(), indentStack, arcType, bufp, topOnly);
+ lateArgIndex++;
}
}
}
//
void Compiler::gtDispArgList(GenTreeCall* call, IndentStack* indentStack)
{
- GenTree* args = call->gtCallArgs;
- unsigned argnum = 0;
- const int BufLength = 256;
- char buf[BufLength];
- char* bufp = &buf[0];
- unsigned numChildren = call->NumChildren();
- assert(numChildren != 0);
- bool argListIsLastChild = (args == call->GetChild(numChildren - 1));
+ unsigned numChildren = call->NumChildren();
+ GenTree* lastArgNode = call->GetChild(numChildren - 1);
+
+ unsigned argnum = 0;
- IndentInfo arcType = IIArc;
if (call->gtCallObjp != nullptr)
{
argnum++;
}
- while (args != nullptr)
+ for (GenTreeCall::Use& use : call->Args())
{
- assert(args->gtOper == GT_LIST);
- GenTree* arg = args->gtOp.gtOp1;
- if (!arg->IsNothingNode() && !arg->IsArgPlaceHolderNode())
+ GenTree* argNode = use.GetNode();
+ if (!argNode->IsNothingNode() && !argNode->IsArgPlaceHolderNode())
{
- gtGetArgMsg(call, arg, argnum, -1, bufp, BufLength);
- if (argListIsLastChild && (args->gtOp.gtOp2 == nullptr))
- {
- arcType = IIArcBottom;
- }
- gtDispChild(arg, indentStack, arcType, bufp, false);
+ char buf[256];
+ gtGetArgMsg(call, argNode, argnum, -1, buf, sizeof(buf));
+ gtDispChild(argNode, indentStack, (argNode == lastArgNode) ? IIArcBottom : IIArc, buf, false);
}
- args = args->gtOp.gtOp2;
argnum++;
}
}
case CORINFO_INTRINSIC_TypeNEQ:
{
noway_assert(call->TypeGet() == TYP_INT);
- GenTree* op1 = call->gtCallArgs->gtOp.gtOp1;
- GenTree* op2 = call->gtCallArgs->gtOp.gtOp2->gtOp.gtOp1;
+ GenTree* op1 = call->gtCallArgs->GetNode();
+ GenTree* op2 = call->gtCallArgs->GetNext()->GetNode();
// If either operand is known to be a RuntimeType, this can be folded
GenTree* result = gtFoldTypeEqualityCall(methodID, op1, op2);
if (ni == NI_System_Enum_HasFlag)
{
GenTree* thisOp = call->gtCallObjp;
- GenTree* flagOp = call->gtCallArgs->gtOp.gtOp1;
+ GenTree* flagOp = call->gtCallArgs->GetNode();
GenTree* result = gtOptimizeEnumHasFlag(thisOp, flagOp);
if (result != nullptr)
assert(typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_USE_HELPER);
// Emit a call to a runtime helper
- GenTreeArgList* helperArgs = gtNewArgList(op1, op2);
- GenTree* ret = gtNewHelperCallNode(CORINFO_HELP_ARE_TYPES_EQUIVALENT, TYP_INT, helperArgs);
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(op1, op2);
+ GenTree* ret = gtNewHelperCallNode(CORINFO_HELP_ARE_TYPES_EQUIVALENT, TYP_INT, helperArgs);
if (oper == GT_EQ)
{
ret = gtNewOperNode(GT_NE, TYP_INT, ret, gtNewIconNode(0, TYP_INT));
if (op1IsFromHandle && op2IsFromHandle)
{
JITDUMP("Optimizing compare of types-from-handles to instead compare handles\n");
- GenTree* op1ClassFromHandle = tree->gtOp.gtOp1->gtCall.gtCallArgs->gtOp.gtOp1;
- GenTree* op2ClassFromHandle = tree->gtOp.gtOp2->gtCall.gtCallArgs->gtOp.gtOp1;
+ GenTree* op1ClassFromHandle = tree->AsOp()->gtOp1->AsCall()->gtCallArgs->GetNode();
+ GenTree* op2ClassFromHandle = tree->AsOp()->gtOp2->AsCall()->gtCallArgs->GetNode();
GenTree* op1TunneledHandle = nullptr;
GenTree* op2TunneledHandle = nullptr;
CORINFO_CLASS_HANDLE cls1Hnd = NO_CLASS_HANDLE;
GenTree* const opOther = op1IsFromHandle ? op2 : op1;
// Tunnel through the handle operand to get at the class handle involved.
- GenTree* const opHandleArgument = opHandle->gtCall.gtCallArgs->gtOp.gtOp1;
+ GenTree* const opHandleArgument = opHandle->AsCall()->gtCallArgs->GetNode();
CORINFO_CLASS_HANDLE clsHnd = gtGetHelperArgClassHandle(opHandleArgument);
// If we couldn't find the class handle, give up.
}
else if (asgSrcOper == GT_CALL)
{
- GenTreeCall* newobjCall = asgSrc->AsCall();
- GenTree* newobjArgs = newobjCall->gtCallArgs;
+ GenTreeCall* newobjCall = asgSrc->AsCall();
+ GenTreeCall::Use* newobjArgs = newobjCall->gtCallArgs;
// In R2R expansions the handle may not be an explicit operand to the helper,
// so we can't remove the box.
return nullptr;
}
- boxTypeHandle = newobjArgs->AsArgList()->Current();
+ boxTypeHandle = newobjArgs->GetNode();
}
else
{
op2 = op1;
op1 = gtNewHelperCallNode(CORINFO_HELP_OVERFLOW, TYP_VOID,
- gtNewArgList(gtNewIconNode(compCurBB->bbTryIndex)));
+ gtNewCallArgs(gtNewIconNode(compCurBB->bbTryIndex)));
// op1 is a call to the JIT helper that throws an Overflow exception
// attach the ExcSet for VNF_OverflowExc(Void) to this call
pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_ADDR_HELPER);
/* If we can't access it directly, we need to call a helper function */
- GenTreeArgList* args = nullptr;
- var_types helperType = TYP_BYREF;
+ GenTreeCall::Use* args = nullptr;
+ var_types helperType = TYP_BYREF;
if (pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_HELPER)
{
assg = gtNewCastNode(TYP_FLOAT, assg, false, TYP_FLOAT);
}
- args = gtNewArgList(assg);
+ args = gtNewCallArgs(assg);
helperType = TYP_VOID;
}
else if (access & CORINFO_ACCESS_GET)
if (pFieldInfo->helper == CORINFO_HELP_GETFIELDSTRUCT || pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT)
{
assert(pFieldInfo->structType != nullptr);
- args = gtNewListNode(gtNewIconEmbClsHndNode(pFieldInfo->structType), args);
+ args = gtPrependNewCallArg(gtNewIconEmbClsHndNode(pFieldInfo->structType), args);
}
GenTree* fieldHnd = impTokenToHandle(pResolvedToken);
return nullptr;
}
- args = gtNewListNode(fieldHnd, args);
+ args = gtPrependNewCallArg(fieldHnd, args);
// If it's a static field, we shouldn't have an object node
// If it's an instance field, we have an object node
if (objPtr != nullptr)
{
- args = gtNewListNode(objPtr, args);
+ args = gtPrependNewCallArg(objPtr, args);
}
GenTreeCall* call = gtNewHelperCallNode(pFieldInfo->helper, genActualType(helperType), args);
if (!call->HasSideEffects(this, ignoreExceptions, ignoreCctors))
{
// If this call is otherwise side effect free, check its arguments.
- for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
- if (gtTreeHasSideEffects(args->Current(), flags))
+ if (gtTreeHasSideEffects(use.GetNode(), flags))
{
return true;
}
}
// I'm a little worried that args that assign to temps that are late args will look like
// side effects...but better to be conservative for now.
- for (GenTreeArgList* args = call->gtCallLateArgs; args != nullptr; args = args->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- if (gtTreeHasSideEffects(args->Current(), flags))
+ if (gtTreeHasSideEffects(use.GetNode(), flags))
{
return true;
}
case CORINFO_HELP_ISINSTANCEOFANY:
{
// Fetch the class handle from the helper call arglist
- GenTreeArgList* args = call->gtCallArgs;
- GenTree* typeArg = args->Current();
+ GenTreeCall::Use* args = call->gtCallArgs;
+ GenTree* typeArg = args->GetNode();
CORINFO_CLASS_HANDLE castHnd = gtGetHelperArgClassHandle(typeArg);
// We generally assume the type being cast to is the best type
// type from the value being cast instead.
if (castHnd == nullptr)
{
- GenTree* valueArg = args->Rest()->Current();
+ GenTree* valueArg = args->GetNext()->GetNode();
castHnd = gtGetClassHandle(valueArg, pIsExact, pIsNonNull);
}
AdvanceFn m_advance;
GenTree* m_node;
GenTree** m_edge;
- // Pointer sized state storage, GenTreeArgList* or GenTreePhi::Use* currently.
+ // Pointer sized state storage, GenTreeArgList* or GenTreePhi::Use* or GenTreeCall::Use* currently.
void* m_statePtr;
// Integer sized state storage, usually the operand index for non-list based nodes.
int m_state;
struct GenTreeCall final : public GenTree
{
- GenTree* gtCallObjp; // The instance argument ('this' pointer)
- GenTreeArgList* gtCallArgs; // The list of arguments in original evaluation order
- GenTreeArgList* gtCallLateArgs; // On x86: The register arguments in an optimal order
- // On ARM/x64: - also includes any outgoing arg space arguments
- // - that were evaluated into a temp LclVar
+ class Use
+ {
+ GenTree* m_node;
+ Use* m_next;
+
+ public:
+ Use(GenTree* node, Use* next = nullptr) : m_node(node), m_next(next)
+ {
+ }
+
+ GenTree*& NodeRef()
+ {
+ return m_node;
+ }
+
+ GenTree* GetNode() const
+ {
+ return m_node;
+ }
+
+ void SetNode(GenTree* node)
+ {
+ assert(node != nullptr);
+ m_node = node;
+ }
+
+ Use*& NextRef()
+ {
+ return m_next;
+ }
+
+ Use* GetNext() const
+ {
+ return m_next;
+ }
+
+ void SetNext(Use* next)
+ {
+ m_next = next;
+ }
+ };
+
+ class UseIterator
+ {
+ Use* m_use;
+
+ public:
+ UseIterator(Use* use) : m_use(use)
+ {
+ }
+
+ Use& operator*() const
+ {
+ return *m_use;
+ }
+
+ Use* operator->() const
+ {
+ return m_use;
+ }
+
+ UseIterator& operator++()
+ {
+ m_use = m_use->GetNext();
+ return *this;
+ }
+
+ bool operator==(const UseIterator& i) const
+ {
+ return m_use == i.m_use;
+ }
+
+ bool operator!=(const UseIterator& i) const
+ {
+ return m_use != i.m_use;
+ }
+ };
+
+ class UseList
+ {
+ Use* m_uses;
+
+ public:
+ UseList(Use* uses) : m_uses(uses)
+ {
+ }
+
+ UseIterator begin() const
+ {
+ return UseIterator(m_uses);
+ }
+
+ UseIterator end() const
+ {
+ return UseIterator(nullptr);
+ }
+ };
+
+ GenTree* gtCallObjp; // The instance argument ('this' pointer)
+ Use* gtCallArgs; // The list of arguments in original evaluation order
+ Use* gtCallLateArgs; // On x86: The register arguments in an optimal order
+ // On ARM/x64: - also includes any outgoing arg space arguments
+ // - that were evaluated into a temp LclVar
fgArgInfo* fgArgInfo;
+ UseList Args()
+ {
+ return UseList(gtCallArgs);
+ }
+
+ UseList LateArgs()
+ {
+ return UseList(gtCallLateArgs);
+ }
+
#if !FEATURE_FIXED_OUT_ARGS
int regArgListCount;
regList regArgList;
comp->fgWalkTreePre(&tree->gtCall.gtCallObjp, gsMarkPtrsAndAssignGroups, (void*)&newState);
}
- for (GenTreeArgList* args = tree->gtCall.gtCallArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : tree->AsCall()->Args())
{
- comp->fgWalkTreePre(&args->Current(), gsMarkPtrsAndAssignGroups, (void*)&newState);
+ comp->fgWalkTreePre(&use.NodeRef(), gsMarkPtrsAndAssignGroups, (void*)&newState);
}
- for (GenTreeArgList* args = tree->gtCall.gtCallLateArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : tree->AsCall()->LateArgs())
{
- comp->fgWalkTreePre(&args->Current(), gsMarkPtrsAndAssignGroups, (void*)&newState);
+ comp->fgWalkTreePre(&use.NodeRef(), gsMarkPtrsAndAssignGroups, (void*)&newState);
}
if (tree->gtCall.gtCallType == CT_INDIRECT)
* prefixTree at the head of the list.
*/
-GenTreeArgList* Compiler::impPopList(unsigned count, CORINFO_SIG_INFO* sig, GenTreeArgList* prefixTree)
+GenTreeCall::Use* Compiler::impPopCallArgs(unsigned count, CORINFO_SIG_INFO* sig, GenTreeCall::Use* prefixArgs)
{
assert(sig == nullptr || count == sig->numArgs);
CORINFO_CLASS_HANDLE structType;
- GenTreeArgList* treeList;
+ GenTreeCall::Use* argList;
if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L)
{
- treeList = nullptr;
+ argList = nullptr;
}
else
{ // ARG_ORDER_L2R
- treeList = prefixTree;
+ argList = prefixArgs;
}
while (count--)
}
/* NOTE: we defer bashing the type for I_IMPL to fgMorphArgs */
- treeList = gtNewListNode(temp, treeList);
+ argList = gtPrependNewCallArg(temp, argList);
}
if (sig != nullptr)
CORINFO_ARG_LIST_HANDLE argLst = sig->args;
CORINFO_CLASS_HANDLE argClass;
CORINFO_CLASS_HANDLE argRealClass;
- GenTreeArgList* args;
+ GenTreeCall::Use* arg;
- for (args = treeList, count = sig->numArgs; count > 0; args = args->Rest(), count--)
+ for (arg = argList, count = sig->numArgs; count > 0; arg = arg->GetNext(), count--)
{
- PREFIX_ASSUME(args != nullptr);
+ PREFIX_ASSUME(arg != nullptr);
CorInfoType corType = strip(info.compCompHnd->getArgType(sig, argLst, &argClass));
// insert implied casts (from float to double or double to float)
- if (corType == CORINFO_TYPE_DOUBLE && args->Current()->TypeGet() == TYP_FLOAT)
+ if ((corType == CORINFO_TYPE_DOUBLE) && (arg->GetNode()->TypeGet() == TYP_FLOAT))
{
- args->Current() = gtNewCastNode(TYP_DOUBLE, args->Current(), false, TYP_DOUBLE);
+ arg->SetNode(gtNewCastNode(TYP_DOUBLE, arg->GetNode(), false, TYP_DOUBLE));
}
- else if (corType == CORINFO_TYPE_FLOAT && args->Current()->TypeGet() == TYP_DOUBLE)
+ else if ((corType == CORINFO_TYPE_FLOAT) && (arg->GetNode()->TypeGet() == TYP_DOUBLE))
{
- args->Current() = gtNewCastNode(TYP_FLOAT, args->Current(), false, TYP_FLOAT);
+ arg->SetNode(gtNewCastNode(TYP_FLOAT, arg->GetNode(), false, TYP_FLOAT));
}
// insert any widening or narrowing casts for backwards compatibility
- args->Current() = impImplicitIorI4Cast(args->Current(), JITtype2varType(corType));
+ arg->SetNode(impImplicitIorI4Cast(arg->GetNode(), JITtype2varType(corType)));
if (corType != CORINFO_TYPE_CLASS && corType != CORINFO_TYPE_BYREF && corType != CORINFO_TYPE_PTR &&
corType != CORINFO_TYPE_VAR && (argRealClass = info.compCompHnd->getArgClass(sig, argLst)) != nullptr)
// primitive types.
// We will try to adjust for this case here to avoid breaking customers code (see VSW 485789 for
// details).
- if (corType == CORINFO_TYPE_VALUECLASS && !varTypeIsStruct(args->Current()))
+ if (corType == CORINFO_TYPE_VALUECLASS && !varTypeIsStruct(arg->GetNode()->TypeGet()))
{
- args->Current() = impNormStructVal(args->Current(), argRealClass, (unsigned)CHECK_SPILL_ALL, true);
+ arg->SetNode(impNormStructVal(arg->GetNode(), argRealClass, (unsigned)CHECK_SPILL_ALL, true));
}
// Make sure that all valuetypes (including enums) that we push are loaded.
// Simple in-place reversal to place treeList
// at the end of a reversed prefixTree
- while (prefixTree != nullptr)
+ while (prefixArgs != nullptr)
{
- GenTreeArgList* next = prefixTree->Rest();
- prefixTree->Rest() = treeList;
- treeList = prefixTree;
- prefixTree = next;
+ GenTreeCall::Use* next = prefixArgs->GetNext();
+ prefixArgs->SetNext(argList);
+ argList = prefixArgs;
+ prefixArgs = next;
}
}
- return treeList;
+ return argList;
}
/*****************************************************************************
* The first "skipReverseCount" items are not reversed.
*/
-GenTreeArgList* Compiler::impPopRevList(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount)
-
+GenTreeCall::Use* Compiler::impPopReverseCallArgs(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount)
{
assert(skipReverseCount <= count);
- GenTreeArgList* list = impPopList(count, sig);
+ GenTreeCall::Use* list = impPopCallArgs(count, sig);
// reverse the list
if (list == nullptr || skipReverseCount == count)
return list;
}
- GenTreeArgList* ptr = nullptr; // Initialized to the first node that needs to be reversed
- GenTreeArgList* lastSkipNode = nullptr; // Will be set to the last node that does not need to be reversed
+ GenTreeCall::Use* ptr = nullptr; // Initialized to the first node that needs to be reversed
+ GenTreeCall::Use* lastSkipNode = nullptr; // Will be set to the last node that does not need to be reversed
if (skipReverseCount == 0)
{
// Get to the first node that needs to be reversed
for (unsigned i = 0; i < skipReverseCount - 1; i++)
{
- lastSkipNode = lastSkipNode->Rest();
+ lastSkipNode = lastSkipNode->GetNext();
}
PREFIX_ASSUME(lastSkipNode != nullptr);
- ptr = lastSkipNode->Rest();
+ ptr = lastSkipNode->GetNext();
}
- GenTreeArgList* reversedList = nullptr;
+ GenTreeCall::Use* reversedList = nullptr;
do
{
- GenTreeArgList* tmp = ptr->Rest();
- ptr->Rest() = reversedList;
- reversedList = ptr;
- ptr = tmp;
+ GenTreeCall::Use* tmp = ptr->GetNext();
+ ptr->SetNext(reversedList);
+ reversedList = ptr;
+ ptr = tmp;
} while (ptr != nullptr);
if (skipReverseCount)
{
- lastSkipNode->Rest() = reversedList;
+ lastSkipNode->SetNext(reversedList);
return list;
}
else
// Case of call returning a struct via hidden retbuf arg
// insert the return value buffer into the argument list as first byref parameter
- src->gtCall.gtCallArgs = gtNewListNode(destAddr, src->gtCall.gtCallArgs);
+ src->AsCall()->gtCallArgs = gtPrependNewCallArg(destAddr, src->AsCall()->gtCallArgs);
// now returns void, not a struct
src->gtType = TYP_VOID;
if (call->HasRetBufArg())
{
// insert the return value buffer into the argument list as first byref parameter
- call->gtCallArgs = gtNewListNode(destAddr, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(destAddr, call->gtCallArgs);
// now returns void, not a struct
src->gtType = TYP_VOID;
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CorInfoHelpFunc helper,
var_types type,
- GenTreeArgList* args /* =NULL*/,
+ GenTreeCall::Use* args /* = nullptr */,
CORINFO_LOOKUP_KIND* pGenericLookupKind /* =NULL. Only used with generics */)
{
CORINFO_CONST_LOOKUP lookup;
if (opts.IsReadyToRun())
{
return impReadyToRunHelperToTree(pResolvedToken, CORINFO_HELP_READYTORUN_GENERIC_HANDLE, TYP_I_IMPL,
- gtNewArgList(ctxTree), &pLookup->lookupKind);
+ gtNewCallArgs(ctxTree), &pLookup->lookupKind);
}
#endif
GenTree* argNode =
gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_TOKEN_HDL, compileTimeHandle);
- GenTreeArgList* helperArgs = gtNewArgList(ctxTree, argNode);
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(ctxTree, argNode);
return gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, helperArgs);
}
// Call to helper
GenTree* argNode = gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_TOKEN_HDL, compileTimeHandle);
- GenTreeArgList* helperArgs = gtNewArgList(ctxTree, argNode);
- GenTree* helperCall = gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, helperArgs);
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(ctxTree, argNode);
+ GenTree* helperCall = gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, helperArgs);
// Check for null and possibly call helper
GenTree* relop = gtNewOperNode(GT_NE, TYP_INT, handle, gtNewIconNode(0, TYP_I_IMPL));
}
// Strip helper call away
- fieldTokenNode = fieldTokenNode->gtCall.gtCallArgs->Current();
+ fieldTokenNode = fieldTokenNode->AsCall()->gtCallArgs->GetNode();
if (fieldTokenNode->gtOper == GT_IND)
{
return nullptr;
}
- GenTreeArgList* tokenArg = newArrayCall->gtCall.gtCallArgs;
+ GenTreeCall::Use* tokenArg = newArrayCall->gtCall.gtCallArgs;
assert(tokenArg != nullptr);
- GenTreeArgList* numArgsArg = tokenArg->Rest();
+ GenTreeCall::Use* numArgsArg = tokenArg->GetNext();
assert(numArgsArg != nullptr);
- GenTreeArgList* argsArg = numArgsArg->Rest();
+ GenTreeCall::Use* argsArg = numArgsArg->GetNext();
assert(argsArg != nullptr);
//
// be at most 64 arguments - 32 lengths and 32 lower bounds.
//
- if ((!numArgsArg->Current()->IsCnsIntOrI()) || (numArgsArg->Current()->AsIntCon()->IconValue() < 1) ||
- (numArgsArg->Current()->AsIntCon()->IconValue() > 64))
+ if ((!numArgsArg->GetNode()->IsCnsIntOrI()) || (numArgsArg->GetNode()->AsIntCon()->IconValue() < 1) ||
+ (numArgsArg->GetNode()->AsIntCon()->IconValue() > 64))
{
return nullptr;
}
- unsigned numArgs = static_cast<unsigned>(numArgsArg->Current()->AsIntCon()->IconValue());
+ unsigned numArgs = static_cast<unsigned>(numArgsArg->GetNode()->AsIntCon()->IconValue());
bool lowerBoundsSpecified;
if (numArgs == rank * 2)
unsigned argIndex = 0;
GenTree* comma;
- for (comma = argsArg->Current(); Match::IsComma(comma); comma = comma->gtGetOp2())
+ for (comma = argsArg->GetNode(); Match::IsComma(comma); comma = comma->gtGetOp2())
{
if (lowerBoundsSpecified)
{
GenTree* arrayLengthNode;
- GenTreeArgList* args = newArrayCall->gtCall.gtCallArgs;
+ GenTreeCall::Use* args = newArrayCall->AsCall()->gtCallArgs;
#ifdef FEATURE_READYTORUN_COMPILER
if (newArrayCall->gtCall.gtCallMethHnd == eeFindHelper(CORINFO_HELP_READYTORUN_NEWARR_1))
{
// Array length is 1st argument for readytorun helper
- arrayLengthNode = args->Current();
+ arrayLengthNode = args->GetNode();
}
else
#endif
{
// Array length is 2nd argument for regular helper
- arrayLengthNode = args->Rest()->Current();
+ arrayLengthNode = args->GetNext()->GetNode();
}
//
assert(typeHandleHelper == CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL);
typeHandleHelper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL;
}
- assert(op1->gtCall.gtCallArgs->gtOp.gtOp2 == nullptr);
+ assert(op1->AsCall()->gtCallArgs->GetNext() == nullptr);
op1 = gtNewHelperCallNode(typeHandleHelper, TYP_REF, op1->gtCall.gtCallArgs);
op1->gtType = TYP_REF;
retNode = op1;
op1 = impPopStack().val;
// Get native TypeHandle argument to old helper
- op1 = op1->gtCall.gtCallArgs;
- assert(op1->OperIsList());
- assert(op1->gtOp.gtOp2 == nullptr);
- op1 = op1->gtOp.gtOp1;
+ GenTreeCall::Use* arg = op1->AsCall()->gtCallArgs;
+ assert(arg->GetNext() == nullptr);
+ op1 = arg->GetNode();
retNode = op1;
}
// Call the regular function.
// do for LDTOKEN since the return value of this operator is Type,
// not RuntimeTypeHandle.
impPopStack();
- GenTreeArgList* helperArgs = gtNewArgList(boxTypeHandle);
- GenTree* runtimeType =
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(boxTypeHandle);
+ GenTree* runtimeType =
gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, helperArgs);
retNode = runtimeType;
}
assert(compDonotInline());
return nullptr;
}
- GenTreeArgList* helperArgs = gtNewArgList(typeHandleOp);
- GenTree* runtimeType =
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(typeHandleOp);
+ GenTree* runtimeType =
gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, helperArgs);
retNode = runtimeType;
}
assert(verCurrentState.esStackDepth == 0);
GenTree* op1 =
- gtNewHelperCallNode(CORINFO_HELP_VERIFICATION, TYP_VOID, gtNewArgList(gtNewIconNode(block->bbCodeOffs)));
+ gtNewHelperCallNode(CORINFO_HELP_VERIFICATION, TYP_VOID, gtNewCallArgs(gtNewIconNode(block->bbCodeOffs)));
// verCurrentState.esStackDepth = 0;
impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
runtimeMethodHandle = gtNewIconEmbMethHndNode(pResolvedToken->hMethod);
}
return gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL,
- gtNewArgList(thisPtr, runtimeMethodHandle));
+ gtNewCallArgs(thisPtr, runtimeMethodHandle));
}
#ifdef FEATURE_READYTORUN_COMPILER
if (!pCallInfo->exactContextNeedsRuntimeLookup)
{
GenTreeCall* call =
- gtNewHelperCallNode(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, TYP_I_IMPL, gtNewArgList(thisPtr));
+ gtNewHelperCallNode(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, TYP_I_IMPL, gtNewCallArgs(thisPtr));
call->setEntryPoint(pCallInfo->codePointerLookup.constLookup);
GenTree* ctxTree = getRuntimeContextTree(pCallInfo->codePointerLookup.lookupKind.runtimeLookupKind);
return impReadyToRunHelperToTree(pResolvedToken, CORINFO_HELP_READYTORUN_GENERIC_HANDLE, TYP_I_IMPL,
- gtNewArgList(ctxTree), &pCallInfo->codePointerLookup.lookupKind);
+ gtNewCallArgs(ctxTree), &pCallInfo->codePointerLookup.lookupKind);
}
}
#endif
return nullptr;
}
- GenTreeArgList* helpArgs = gtNewArgList(exactMethodDesc);
+ GenTreeCall::Use* helpArgs = gtNewCallArgs(exactMethodDesc);
- helpArgs = gtNewListNode(exactTypeDesc, helpArgs);
+ helpArgs = gtPrependNewCallArg(exactTypeDesc, helpArgs);
- helpArgs = gtNewListNode(thisPtr, helpArgs);
+ helpArgs = gtPrependNewCallArg(thisPtr, helpArgs);
// Call helper function. This gets the target address of the final destination callsite.
return;
}
- GenTreeArgList* args = gtNewArgList(op2, impGetStructAddr(exprToBox, operCls, (unsigned)CHECK_SPILL_ALL, true));
- op1 = gtNewHelperCallNode(boxHelper, TYP_REF, args);
+ GenTreeCall::Use* args =
+ gtNewCallArgs(op2, impGetStructAddr(exprToBox, operCls, (unsigned)CHECK_SPILL_ALL, true));
+ op1 = gtNewHelperCallNode(boxHelper, TYP_REF, args);
}
/* Push the result back on the stack, */
assert(pCallInfo->sig.numArgs);
- GenTree* node;
- GenTreeArgList* args;
+ GenTree* node;
//
// There are two different JIT helpers that can be used to allocate
node = gtNewOperNode(GT_COMMA, node->TypeGet(), gtNewAssignNode(dest, arg), node);
}
- args = gtNewArgList(node);
+ GenTreeCall::Use* args = gtNewCallArgs(node);
// pass number of arguments to the helper
- args = gtNewListNode(gtNewIconNode(pCallInfo->sig.numArgs), args);
+ args = gtPrependNewCallArg(gtNewIconNode(pCallInfo->sig.numArgs), args);
- args = gtNewListNode(classHandle, args);
+ args = gtPrependNewCallArg(classHandle, args);
node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR_NONVARARG, TYP_REF, args);
}
// pushed in reverse order on the CPU stack)
//
- args = gtNewArgList(classHandle);
+ GenTreeCall::Use* args = gtNewCallArgs(classHandle);
// pass number of arguments to the helper
- args = gtNewListNode(gtNewIconNode(pCallInfo->sig.numArgs), args);
+ args = gtPrependNewCallArg(gtNewIconNode(pCallInfo->sig.numArgs), args);
unsigned argFlags = 0;
- args = impPopList(pCallInfo->sig.numArgs, &pCallInfo->sig, args);
+ args = impPopCallArgs(pCallInfo->sig.numArgs, &pCallInfo->sig, args);
node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR, TYP_REF, args);
#ifdef DEBUG
// At the present time we don't track Caller pop arguments
// that have GC references in them
- for (GenTreeArgList* temp = args; temp; temp = temp->Rest())
+ for (GenTreeCall::Use& use : GenTreeCall::UseList(args))
{
- assert(temp->Current()->gtType != TYP_REF);
+ assert(use.GetNode()->TypeGet() != TYP_REF);
}
#endif
}
- node->gtFlags |= args->gtFlags & GTF_GLOB_EFFECT;
+ for (GenTreeCall::Use& use : node->AsCall()->Args())
+ {
+ node->gtFlags |= use.GetNode()->gtFlags & GTF_GLOB_EFFECT;
+ }
+
node->gtCall.compileTimeHelperArgumentHandle = (CORINFO_GENERIC_HANDLE)pResolvedToken->hClass;
// Remember that this basic block contains 'new' of a md array
/* The argument list is now "clean" - no out-of-order side effects
* Pop the argument list in reverse order */
- GenTree* args = call->gtCall.gtCallArgs = impPopRevList(sig->numArgs, sig, sig->numArgs - argsToReverse);
+ GenTreeCall::Use* args = impPopReverseCallArgs(sig->numArgs, sig, sig->numArgs - argsToReverse);
+ call->AsCall()->gtCallArgs = args;
if (call->gtCall.gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL)
{
- GenTree* thisPtr = args->Current();
+ GenTree* thisPtr = args->GetNode();
impBashVarAddrsToI(thisPtr);
assert(thisPtr->TypeGet() == TYP_I_IMPL || thisPtr->TypeGet() == TYP_BYREF);
}
- if (args)
+ for (GenTreeCall::Use& use : GenTreeCall::UseList(args))
{
- call->gtFlags |= args->gtFlags & GTF_GLOB_EFFECT;
+ call->gtFlags |= use.GetNode()->gtFlags & GTF_GLOB_EFFECT;
}
}
if (runtimeLookup)
{
- node = gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, gtNewArgList(node));
+ node = gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, gtNewCallArgs(node));
}
else
{
break;
}
- op1 = gtNewHelperCallNode(pFieldInfo->helper, type, gtNewArgList(op1));
+ op1 = gtNewHelperCallNode(pFieldInfo->helper, type, gtNewCallArgs(op1));
FieldSeqNode* fs = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField);
op1 = gtNewOperNode(GT_ADD, type, op1,
CORINFO_LOOKUP_KIND kind = info.compCompHnd->getLocationOfThisType(info.compMethodHnd);
assert(kind.needsRuntimeLookup);
- GenTree* ctxTree = getRuntimeContextTree(kind.runtimeLookupKind);
- GenTreeArgList* args = gtNewArgList(ctxTree);
+ GenTree* ctxTree = getRuntimeContextTree(kind.runtimeLookupKind);
+ GenTreeCall::Use* args = gtNewCallArgs(ctxTree);
unsigned callFlags = 0;
void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo)
{
// Construct the argument list
- GenTreeArgList* args = nullptr;
+ GenTreeCall::Use* args = nullptr;
assert(helperInfo->helperNum != CORINFO_HELP_UNDEF);
for (unsigned i = helperInfo->numArgs; i > 0; --i)
{
default:
NO_WAY("Illegal helper arg type");
}
- args = (currentArg == nullptr) ? gtNewArgList(currentArg) : gtNewListNode(currentArg, args);
+ args = gtPrependNewCallArg(currentArg, args);
}
/* TODO-Review:
unsigned mflags = 0;
unsigned argFlags = 0;
GenTree* call = nullptr;
- GenTreeArgList* args = nullptr;
+ GenTreeCall::Use* args = nullptr;
CORINFO_THIS_TRANSFORM constraintCallThisTransform = CORINFO_NO_THIS_TRANSFORM;
CORINFO_CONTEXT_HANDLE exactContextHnd = nullptr;
bool exactContextNeedsRuntimeLookup = false;
bool checkForSmallType = opts.IsJit64Compat() || opts.IsReadyToRun();
bool bIntrinsicImported = false;
- CORINFO_SIG_INFO calliSig;
- GenTreeArgList* extraArg = nullptr;
+ CORINFO_SIG_INFO calliSig;
+ GenTreeCall::Use* extraArg = nullptr;
/*-------------------------------------------------------------------------
* First create the call node
// OK, We've been told to call via LDVIRTFTN, so just
// take the call now....
- args = impPopList(sig->numArgs, sig);
+ GenTreeCall::Use* args = impPopCallArgs(sig->numArgs, sig);
GenTree* thisPtr = impPopStack().val;
thisPtr = impTransformThis(thisPtr, pConstrainedResolvedToken, callInfo->thisTransform);
GenTree* cookie = gtNewIconEmbHndNode(varCookie, pVarCookie, GTF_ICON_VARG_HDL, sig);
assert(extraArg == nullptr);
- extraArg = gtNewArgList(cookie);
+ extraArg = gtNewCallArgs(cookie);
}
//-------------------------------------------------------------------------
}
assert(extraArg == nullptr);
- extraArg = gtNewArgList(instParam);
+ extraArg = gtNewCallArgs(instParam);
}
// Inlining may need the exact type context (exactContextHnd) if we're inlining shared generic code, in particular
//-------------------------------------------------------------------------
// The main group of arguments
- args = call->gtCall.gtCallArgs = impPopList(sig->numArgs, sig, extraArg);
+ args = impPopCallArgs(sig->numArgs, sig, extraArg);
+ call->AsCall()->gtCallArgs = args;
- if (args)
+ for (GenTreeCall::Use& use : call->AsCall()->Args())
{
- call->gtFlags |= args->gtFlags & GTF_GLOB_EFFECT;
+ call->gtFlags |= use.GetNode()->gtFlags & GTF_GLOB_EFFECT;
}
//-------------------------------------------------------------------------
assert(callObj != nullptr);
if ((call->gtCall.IsVirtual() || (call->gtFlags & GTF_CALL_NULLCHECK)) &&
- impInlineIsGuaranteedThisDerefBeforeAnySideEffects(call->gtCall.gtCallArgs, callObj,
+ impInlineIsGuaranteedThisDerefBeforeAnySideEffects(nullptr, call->AsCall()->gtCallArgs, callObj,
impInlineInfo->inlArgInfo))
{
impInlineInfo->thisDereferencedFirst = true;
//
op2->gtFlags |= GTF_DONT_CSE;
- return gtNewHelperCallNode(helper, TYP_REF, gtNewArgList(op2, op1));
+ return gtNewHelperCallNode(helper, TYP_REF, gtNewCallArgs(op2, op1));
}
JITDUMP("\nExpanding %s inline\n", isCastClass ? "castclass" : "isinst");
//
const CorInfoHelpFunc specialHelper = CORINFO_HELP_CHKCASTCLASS_SPECIAL;
- condTrue = gtNewHelperCallNode(specialHelper, TYP_REF, gtNewArgList(op2Var, gtClone(op1)));
+ condTrue = gtNewHelperCallNode(specialHelper, TYP_REF, gtNewCallArgs(op2Var, gtClone(op1)));
}
else
{
CORINFO_CLASS_HANDLE ldelemClsHnd = DUMMY_INIT(NULL);
CORINFO_CLASS_HANDLE stelemClsHnd = DUMMY_INIT(NULL);
- var_types lclTyp, ovflType = TYP_UNKNOWN;
- GenTree* op1 = DUMMY_INIT(NULL);
- GenTree* op2 = DUMMY_INIT(NULL);
- GenTreeArgList* args = nullptr; // What good do these "DUMMY_INIT"s do?
- GenTree* newObjThisPtr = DUMMY_INIT(NULL);
- bool uns = DUMMY_INIT(false);
- bool isLocal = false;
+ var_types lclTyp, ovflType = TYP_UNKNOWN;
+ GenTree* op1 = DUMMY_INIT(NULL);
+ GenTree* op2 = DUMMY_INIT(NULL);
+ GenTree* newObjThisPtr = DUMMY_INIT(NULL);
+ bool uns = DUMMY_INIT(false);
+ bool isLocal = false;
/* Get the next opcode and the size of its parameters */
return;
}
- args = gtNewArgList(op1); // Type
- args = gtNewListNode(impPopStack().val, args); // index
- args = gtNewListNode(impPopStack().val, args); // array
- op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, args);
+ {
+ GenTreeCall::Use* args = gtNewCallArgs(op1); // Type
+ args = gtPrependNewCallArg(impPopStack().val, args); // index
+ args = gtPrependNewCallArg(impPopStack().val, args); // array
+ op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, args);
+ }
impPushOnStack(op1, tiRetVal);
break;
}
/* Call a helper function to do the assignment */
- op1 = gtNewHelperCallNode(CORINFO_HELP_ARRADDR_ST, TYP_VOID, impPopList(3, nullptr));
+ op1 = gtNewHelperCallNode(CORINFO_HELP_ARRADDR_ST, TYP_VOID, impPopCallArgs(3, nullptr));
goto SPILL_APPEND;
else
{
if (compIsForInlining() &&
- impInlineIsGuaranteedThisDerefBeforeAnySideEffects(nullptr, obj,
+ impInlineIsGuaranteedThisDerefBeforeAnySideEffects(nullptr, nullptr, obj,
impInlineInfo->inlArgInfo))
{
impInlineInfo->thisDereferencedFirst = true;
}
if (compIsForInlining() &&
- impInlineIsGuaranteedThisDerefBeforeAnySideEffects(op2, obj, impInlineInfo->inlArgInfo))
+ impInlineIsGuaranteedThisDerefBeforeAnySideEffects(op2, nullptr, obj,
+ impInlineInfo->inlArgInfo))
{
impInlineInfo->thisDereferencedFirst = true;
}
if (opts.IsReadyToRun())
{
op1 = impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_NEWARR_1, TYP_REF,
- gtNewArgList(op2));
+ gtNewCallArgs(op2));
usingReadyToRunHelper = (op1 != nullptr);
if (!usingReadyToRunHelper)
if (!usingReadyToRunHelper)
#endif
{
- args = gtNewArgList(op1, op2);
+ GenTreeCall::Use* args = gtNewCallArgs(op1, op2);
/* Create a call to 'new' */
{
GenTreeCall* opLookup =
impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_ISINSTANCEOF, TYP_REF,
- gtNewArgList(op1));
+ gtNewCallArgs(op1));
usingReadyToRunHelper = (opLookup != nullptr);
op1 = (usingReadyToRunHelper ? opLookup : op1);
op1 = impNormStructVal(op1, impGetRefAnyClass(), (unsigned)CHECK_SPILL_ALL);
// Call helper GETREFANY(classHandle, op1);
- args = gtNewArgList(op2, op1);
- op1 = gtNewHelperCallNode(CORINFO_HELP_GETREFANY, TYP_BYREF, args);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_GETREFANY, TYP_BYREF, gtNewCallArgs(op2, op1));
impPushOnStack(op1, tiRetVal);
break;
// convert native TypeHandle to RuntimeTypeHandle
{
- GenTreeArgList* helperArgs = gtNewArgList(op1);
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(op1);
op1 = gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL, TYP_STRUCT,
helperArgs);
helper = CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD;
}
- GenTreeArgList* helperArgs = gtNewArgList(op1);
+ GenTreeCall::Use* helperArgs = gtNewCallArgs(op1);
op1 = gtNewHelperCallNode(helper, TYP_STRUCT, helperArgs);
{ // compDonotInline()
return;
}
- args = gtNewArgList(op2, op1);
- op1 = gtNewHelperCallNode(helper, TYP_VOID, args);
+ op1 = gtNewHelperCallNode(helper, TYP_VOID, gtNewCallArgs(op2, op1));
op1 = new (this, GT_COLON) GenTreeColon(TYP_VOID, gtNewNothingNode(), op1);
op1 = gtNewQmarkNode(TYP_VOID, condBox, op1);
canExpandInline ? "want smaller code or faster jitting" : "inline expansion not legal");
// Don't optimize, just call the helper and be done with it
- args = gtNewArgList(op2, op1);
- op1 =
- gtNewHelperCallNode(helper,
- (var_types)((helper == CORINFO_HELP_UNBOX) ? TYP_BYREF : TYP_STRUCT), args);
+ op1 = gtNewHelperCallNode(helper,
+ (var_types)((helper == CORINFO_HELP_UNBOX) ? TYP_BYREF : TYP_STRUCT),
+ gtNewCallArgs(op2, op1));
}
assert(helper == CORINFO_HELP_UNBOX && op1->gtType == TYP_BYREF || // Unbox helper returns a byref.
{
GenTreeCall* opLookup =
impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_CHKCAST, TYP_REF,
- gtNewArgList(op1));
+ gtNewCallArgs(op1));
usingReadyToRunHelper = (opLookup != nullptr);
op1 = (usingReadyToRunHelper ? opLookup : op1);
block->bbSetRunRarely(); // any block with a throw is rare
/* Pop the exception object and create the 'throw' helper call */
- op1 = gtNewHelperCallNode(CORINFO_HELP_THROW, TYP_VOID, gtNewArgList(impPopStack().val));
+ op1 = gtNewHelperCallNode(CORINFO_HELP_THROW, TYP_VOID, gtNewCallArgs(impPopStack().val));
EVAL_APPEND:
if (verCurrentState.esStackDepth > 0)
assert(op2->gtType == TYP_REF);
// confirm that the argument is a GC pointer (for debugging (GC stress))
- GenTreeArgList* args = gtNewArgList(op2);
- op2 = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_REF, args);
+ GenTreeCall::Use* args = gtNewCallArgs(op2);
+ op2 = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_REF, args);
if (verbose)
{
#endif // defined(_TARGET_ARM64_)
{
assert(iciCall->HasRetBufArg());
- GenTree* dest = gtCloneExpr(iciCall->gtCallArgs->gtOp.gtOp1);
+ GenTree* dest = gtCloneExpr(iciCall->gtCallArgs->GetNode());
// spill temp only exists if there are multiple return points
if (fgNeedReturnSpillTemp())
{
{
assert(!compIsForInlining());
- GenTree* call = pInlineInfo->iciCall;
+ GenTreeCall* call = pInlineInfo->iciCall;
CORINFO_METHOD_INFO* methInfo = &pInlineInfo->inlineCandidateInfo->methInfo;
unsigned clsAttr = pInlineInfo->inlineCandidateInfo->clsAttr;
InlArgInfo* inlArgInfo = pInlineInfo->inlArgInfo;
memset(inlArgInfo, 0, (MAX_INL_ARGS + 1) * sizeof(inlArgInfo[0]));
- /* Get hold of the 'this' pointer and the argument list proper */
-
- GenTree* thisArg = call->gtCall.gtCallObjp;
- GenTree* argList = call->gtCall.gtCallArgs;
+ GenTree* thisArg = call->gtCallObjp;
unsigned argCnt = 0; // Count of the arguments
assert((methInfo->args.hasThis()) == (thisArg != nullptr));
unsigned typeCtxtArg = methInfo->args.totalILArgs();
#endif // USER_ARGS_COME_LAST
- for (GenTree* argTmp = argList; argTmp; argTmp = argTmp->gtOp.gtOp2)
+ for (GenTreeCall::Use& use : call->Args())
{
- if (argTmp == argList && hasRetBuffArg)
+ if (hasRetBuffArg && (&use == call->gtCallArgs))
{
continue;
}
continue;
}
- assert(argTmp->gtOper == GT_LIST);
- GenTree* arg = argTmp->gtOp.gtOp1;
- GenTree* actualArg = arg->gtRetExprVal();
+ GenTree* actualArg = use.GetNode()->gtRetExprVal();
impInlineRecordArgInfo(pInlineInfo, actualArg, argCnt, inlineResult);
if (inlineResult->IsFailure())
}
//-----------------------------------------------------------------------------
-// This function checks if a dereference in the inlinee can guarantee that
-// the "this" is non-NULL.
-// If we haven't hit a branch or a side effect, and we are dereferencing
-// from 'this' to access a field or make GTF_CALL_NULLCHECK call,
-// then we can avoid a separate null pointer check.
+// impInlineIsGuaranteedThisDerefBeforeAnySideEffects: Check if a dereference in
+// the inlinee can guarantee that the "this" pointer is non-NULL.
//
-// "additionalTreesToBeEvaluatedBefore"
-// is the set of pending trees that have not yet been added to the statement list,
-// and which have been removed from verCurrentState.esStack[]
-
-BOOL Compiler::impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree* additionalTreesToBeEvaluatedBefore,
- GenTree* variableBeingDereferenced,
- InlArgInfo* inlArgInfo)
+// Arguments:
+// additionalTree - a tree to check for side effects
+// additionalCallArgs - a list of call args to check for side effects
+// dereferencedAddress - address expression being dereferenced
+// inlArgInfo - inlinee argument information
+//
+// Notes:
+// If we haven't hit a branch or a side effect, and we are dereferencing
+// from 'this' to access a field or make GTF_CALL_NULLCHECK call,
+// then we can avoid a separate null pointer check.
+//
+// The importer stack and current statement list are searched for side effects.
+// Trees that have been popped of the stack but haven't been appended to the
+// statement list and have to be checked for side effects may be provided via
+// additionalTree and additionalCallArgs.
+//
+BOOL Compiler::impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree* additionalTree,
+ GenTreeCall::Use* additionalCallArgs,
+ GenTree* dereferencedAddress,
+ InlArgInfo* inlArgInfo)
{
assert(compIsForInlining());
assert(opts.OptEnabled(CLFLG_INLINING));
return FALSE;
}
- if (!impInlineIsThis(variableBeingDereferenced, inlArgInfo))
+ if (!impInlineIsThis(dereferencedAddress, inlArgInfo))
{
return FALSE;
}
- if (additionalTreesToBeEvaluatedBefore &&
- GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(additionalTreesToBeEvaluatedBefore->gtFlags))
+ if ((additionalTree != nullptr) && GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(additionalTree->gtFlags))
{
return FALSE;
}
+ for (GenTreeCall::Use& use : GenTreeCall::UseList(additionalCallArgs))
+ {
+ if (GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(use.GetNode()->gtFlags))
+ {
+ return false;
+ }
+ }
+
for (GenTreeStmt* stmt = impStmtList; stmt != nullptr; stmt = stmt->gtNextStmt)
{
GenTree* expr = stmt->gtStmtExpr;
// Prepend for R2L arg passing or empty L2R passing
if ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (call->gtCallArgs == nullptr))
{
- call->gtCallArgs = gtNewListNode(methodTableArg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(methodTableArg, call->gtCallArgs);
}
// Append for non-empty L2R
else
{
- GenTreeArgList* beforeArg = call->gtCallArgs;
- while (beforeArg->Rest() != nullptr)
+ GenTreeCall::Use* beforeArg = call->gtCallArgs;
+ while (beforeArg->GetNext() != nullptr)
{
- beforeArg = beforeArg->Rest();
+ beforeArg = beforeArg->GetNext();
}
- beforeArg->Rest() = gtNewListNode(methodTableArg, nullptr);
+ beforeArg->SetNext(gtNewCallArgs(methodTableArg));
}
call->gtCallMethHnd = unboxedEntryMethod;
void StoreRetExprResultsInArgs(GenTreeCall* call)
{
- GenTreeArgList** pArgs = &call->gtCallArgs;
- if (*pArgs != nullptr)
+ for (GenTreeCall::Use& use : call->Args())
{
- comp->fgWalkTreePre((GenTree**)pArgs, SpillRetExprVisitor, this);
+ comp->fgWalkTreePre(&use.NodeRef(), SpillRetExprVisitor, this);
}
- GenTree** pThisArg = &call->gtCallObjp;
- if (*pThisArg != nullptr)
+ if (call->gtCallObjp != nullptr)
{
- comp->fgWalkTreePre(pThisArg, SpillRetExprVisitor, this);
+ comp->fgWalkTreePre(&call->gtCallObjp, SpillRetExprVisitor, this);
}
}
//
void AddHiddenArgument(GenTreeCall* fatCall, GenTree* hiddenArgument)
{
- GenTreeArgList* oldArgs = fatCall->gtCallArgs;
- GenTreeArgList* newArgs;
#if USER_ARGS_COME_LAST
if (fatCall->HasRetBufArg())
{
- GenTree* retBuffer = oldArgs->Current();
- GenTreeArgList* rest = oldArgs->Rest();
- newArgs = compiler->gtNewListNode(hiddenArgument, rest);
- newArgs = compiler->gtNewListNode(retBuffer, newArgs);
+ GenTreeCall::Use* retBufArg = fatCall->gtCallArgs;
+ compiler->gtInsertNewCallArgAfter(hiddenArgument, retBufArg);
}
else
{
- newArgs = compiler->gtNewListNode(hiddenArgument, oldArgs);
+ fatCall->gtCallArgs = compiler->gtPrependNewCallArg(hiddenArgument, fatCall->gtCallArgs);
}
#else
- newArgs = oldArgs;
- AddArgumentToTail(newArgs, hiddenArgument);
+ AddArgumentToTail(fatCall->gtCallArgs, hiddenArgument);
#endif
- fatCall->gtCallArgs = newArgs;
}
//------------------------------------------------------------------------
// argList - fat call node
// hiddenArgument - generic context hidden argument
//
- void AddArgumentToTail(GenTreeArgList* argList, GenTree* hiddenArgument)
+ void AddArgumentToTail(GenTreeCall::Use* argList, GenTree* hiddenArgument)
{
- GenTreeArgList* iterator = argList;
- while (iterator->Rest() != nullptr)
+ GenTreeCall::Use* iterator = argList;
+ while (iterator->GetNext() != nullptr)
{
- iterator = iterator->Rest();
+ iterator = iterator->GetNext();
}
- iterator->Rest() = compiler->gtNewArgList(hiddenArgument);
+ iterator->SetNext(compiler->gtNewCallArgs(hiddenArgument));
}
private:
LowerArg(call, &call->gtCallObjp);
}
- GenTreeArgList* args = call->gtCallArgs;
-
JITDUMP("\nargs:\n======\n");
- for (; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
- LowerArg(call, &args->Current());
+ LowerArg(call, &use.NodeRef());
}
JITDUMP("\nlate:\n======\n");
- for (args = call->gtCallLateArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- LowerArg(call, &args->Current());
+ LowerArg(call, &use.NodeRef());
}
}
{
bool paddingNeeded = false;
GenTree* firstPutArgReg = nullptr;
- for (GenTreeArgList* args = call->gtCallLateArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- GenTree* tmp = args->Current();
- if (tmp->OperGet() == GT_PUTARG_REG)
+ if (use.GetNode()->OperIs(GT_PUTARG_REG))
{
if (firstPutArgReg == nullptr)
{
- firstPutArgReg = tmp;
+ firstPutArgReg = use.GetNode();
GenTree* op1 = firstPutArgReg->gtOp.gtOp1;
if (op1->OperGet() == GT_LCL_VAR_ADDR)
if (insertionPoint == nullptr)
{
- GenTree* tmp = nullptr;
- for (GenTreeArgList* args = call->gtCallArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
- tmp = args->Current();
- assert(tmp->OperGet() != GT_PUTARG_REG); // We don't expect to see these in gtCallArgs
- if (tmp->OperGet() == GT_PUTARG_STK)
+ assert(!use.GetNode()->OperIs(GT_PUTARG_REG)); // We don't expect to see these in gtCallArgs
+
+ if (use.GetNode()->OperIs(GT_PUTARG_STK))
{
// found it
- insertionPoint = tmp;
+ insertionPoint = use.GetNode();
break;
}
}
if (insertionPoint == nullptr)
{
- for (GenTreeArgList* args = call->gtCallLateArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- tmp = args->Current();
- if ((tmp->OperGet() == GT_PUTARG_REG) || (tmp->OperGet() == GT_PUTARG_STK))
+ if (use.GetNode()->OperIs(GT_PUTARG_REG, GT_PUTARG_STK))
{
// found it
- insertionPoint = tmp;
+ insertionPoint = use.GetNode();
break;
}
}
// calls subsequently in execution order to setup other args, because the nested
// call could over-write the stack arg that is setup earlier.
GenTree* firstPutArgStk = nullptr;
- GenTreeArgList* args;
ArrayStack<GenTree*> putargs(comp->getAllocator(CMK_ArrayStack));
- for (args = call->gtCallArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* tmp = args->Current();
- if (tmp->OperGet() == GT_PUTARG_STK)
+ if (use.GetNode()->OperIs(GT_PUTARG_STK))
{
- putargs.Push(tmp);
+ putargs.Push(use.GetNode());
}
}
- for (args = call->gtCallLateArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- GenTree* tmp = args->Current();
- if (tmp->OperGet() == GT_PUTARG_STK)
+ if (use.GetNode()->OperIs(GT_PUTARG_STK))
{
- putargs.Push(tmp);
+ putargs.Push(use.GetNode());
}
}
CLANG_FORMAT_COMMENT_ANCHOR;
#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
- GenTreeArgList* argList = comp->gtNewArgList(frameAddr);
+ GenTreeCall::Use* argList = comp->gtNewCallArgs(frameAddr);
#else
- GenTreeArgList* argList = comp->gtNewArgList(frameAddr, PhysReg(REG_SECRET_STUB_PARAM));
+ GenTreeCall::Use* argList = comp->gtNewCallArgs(frameAddr, PhysReg(REG_SECRET_STUB_PARAM));
#endif
GenTree* call = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_I_IMPL, argList);
// Insert call to CORINFO_HELP_JIT_PINVOKE_BEGIN
GenTree* helperCall =
- comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_BEGIN, TYP_VOID, comp->gtNewArgList(frameAddr));
+ comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_BEGIN, TYP_VOID, comp->gtNewCallArgs(frameAddr));
comp->fgMorphTree(helperCall);
BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, helperCall));
// Insert call to CORINFO_HELP_JIT_PINVOKE_END
GenTreeCall* helperCall =
- comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_END, TYP_VOID, comp->gtNewArgList(frameAddr));
+ comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_END, TYP_VOID, comp->gtNewCallArgs(frameAddr));
comp->fgMorphTree(helperCall);
BlockRange().InsertAfter(call, LIR::SeqTree(comp, helperCall));
CheckCallArg(call->gtCallObjp);
}
- for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
- CheckCallArg(args->Current());
+ CheckCallArg(use.GetNode());
}
- for (GenTreeArgList* args = call->gtCallLateArgs; args != nullptr; args = args->Rest())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- CheckCallArg(args->Current());
+ CheckCallArg(use.GetNode());
}
}
}
}
- GenTree* args = call->gtCallArgs;
- while (args)
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* arg = args->gtOp.gtOp1;
- if (arg->gtOper == GT_PUTARG_STK)
+ if (use.GetNode()->OperIs(GT_PUTARG_STK))
{
- LowerPutArgStk(arg->AsPutArgStk());
+ LowerPutArgStk(use.GetNode()->AsPutArgStk());
}
- args = args->gtOp.gtOp2;
}
- args = call->gtCallLateArgs;
- while (args)
+
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- GenTree* arg = args->gtOp.gtOp1;
- if (arg->gtOper == GT_PUTARG_STK)
+ if (use.GetNode()->OperIs(GT_PUTARG_STK))
{
- LowerPutArgStk(arg->AsPutArgStk());
+ LowerPutArgStk(use.GetNode()->AsPutArgStk());
}
- args = args->gtOp.gtOp2;
}
}
// Each register argument corresponds to one source.
bool callHasFloatRegArgs = false;
- for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
+ for (GenTreeCall::Use& arg : call->LateArgs())
{
- assert(list->OperIsList());
-
- GenTree* argNode = list->Current();
+ GenTree* argNode = arg.GetNode();
#ifdef DEBUG
// During Build, we only use the ArgTabEntry for validation,
// as getting it is rather expensive.
fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
regNumber argReg = curArgTabEntry->regNum;
- assert(curArgTabEntry);
+ assert(curArgTabEntry != nullptr);
#endif
if (argNode->gtOper == GT_PUTARG_STK)
}
}
+#ifdef DEBUG
// Now, count stack args
// Note that these need to be computed into a register, but then
// they're just stored to the stack - so the reg doesn't
// because the code generator doesn't actually consider it live,
// so it can't be spilled.
- GenTree* args = call->gtCallArgs;
- while (args)
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* arg = args->gtGetOp1();
+ GenTree* arg = use.GetNode();
// Skip arguments that have been moved to the Late Arg list
- if (!(args->gtFlags & GTF_LATE_ARG))
+ if ((arg->gtFlags & GTF_LATE_ARG) == 0)
{
-#ifdef DEBUG
fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
- assert(curArgTabEntry);
-#endif
+ assert(curArgTabEntry != nullptr);
#if FEATURE_ARG_SPLIT
// PUTARG_SPLIT nodes must be in the gtCallLateArgs list, since they
// define registers used by the call.
assert(!arg->IsValue() || arg->IsUnusedValue());
}
}
- args = args->gtGetOp2();
}
+#endif // DEBUG
// If it is a fast tail call, it is already preferenced to use IP0.
// Therefore, no need set src candidates on call tgt again.
// First, determine internal registers.
// We will need one for any float arguments to a varArgs call.
- for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
- GenTree* argNode = list->Current();
+ GenTree* argNode = use.GetNode();
if (argNode->OperIsPutArgReg())
{
HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
}
// Now, count reg args
- for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
+ for (GenTreeCall::Use& use : call->LateArgs())
{
// By this point, lowering has ensured that all call arguments are one of the following:
// - an arg setup store
// - a put arg
//
// Note that this property is statically checked by LinearScan::CheckBlock.
- GenTree* argNode = list->Current();
+ GenTree* argNode = use.GetNode();
// Each register argument corresponds to one source.
if (argNode->OperIsPutArgReg())
#endif // DEBUG
}
+#ifdef DEBUG
// Now, count stack args
// Note that these need to be computed into a register, but then
// they're just stored to the stack - so the reg doesn't
// because the code generator doesn't actually consider it live,
// so it can't be spilled.
- GenTree* args = call->gtCallArgs;
- while (args)
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* arg = args->gtGetOp1();
+ GenTree* arg = use.GetNode();
if (!(arg->gtFlags & GTF_LATE_ARG) && !arg)
{
if (arg->IsValue() && !arg->isContained())
assert(arg->IsUnusedValue());
}
}
- args = args->gtGetOp2();
}
+#endif // DEBUG
// set reg requirements on call target represented as control sequence.
if (ctrlExpr != nullptr)
noway_assert(tree->gtCast.CastOp() == oper);
noway_assert(tree->gtOper == GT_CAST);
}
- result = fgMorphIntoHelperCall(tree, helper, gtNewArgList(oper));
+ result = fgMorphIntoHelperCall(tree, helper, gtNewCallArgs(oper));
assert(result == tree);
return result;
}
* the given argument list.
*/
-GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgList* args, bool morphArgs)
+GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall::Use* args, bool morphArgs)
{
// The helper call ought to be semantically equivalent to the original node, so preserve its VN.
tree->ChangeOper(GT_CALL, GenTree::PRESERVE_VN);
tree->gtFlags &= ~GTF_EXCEPT;
}
tree->gtFlags |= GTF_CALL;
- if (args)
+
+ for (GenTreeCall::Use& use : GenTreeCall::UseList(args))
{
- tree->gtFlags |= (args->gtFlags & GTF_ALL_EFFECT);
+ tree->gtFlags |= (use.GetNode()->gtFlags & GTF_ALL_EFFECT);
}
/* Perform the morphing */
return objRef;
}
-/*****************************************************************************
- *
- * Morph an argument list; compute the pointer argument count in the process.
- *
- * NOTE: This function can be called from any place in the JIT to perform re-morphing
- * due to graph altering modifications such as copy / constant propagation
- */
-
-unsigned UpdateGT_LISTFlags(GenTree* tree)
-{
- assert(tree->gtOper == GT_LIST);
-
- unsigned flags = 0;
- if (tree->gtOp.gtOp2)
- {
- flags |= UpdateGT_LISTFlags(tree->gtOp.gtOp2);
- }
-
- flags |= (tree->gtOp.gtOp1->gtFlags & GTF_ALL_EFFECT);
-
- tree->gtFlags &= ~GTF_ALL_EFFECT;
- tree->gtFlags |= flags;
-
- return tree->gtFlags;
-}
-
#ifdef DEBUG
void fgArgTabEntry::Dump()
{
// We create local, artificial GenTreeArgLists that includes the gtCallObjp, if that exists, as first argument,
// so we can iterate over these argument lists more uniformly.
// Need to provide a temporary non-null first arguments to these constructors: if we use them, we'll replace them
- GenTreeArgList* newArgs;
- GenTreeArgList newArgObjp(newCall, newCall->gtCallArgs);
- GenTreeArgList* oldArgs;
- GenTreeArgList oldArgObjp(oldCall, oldCall->gtCallArgs);
+ GenTreeCall::Use* newArgs;
+ GenTreeCall::Use newArgObjp(newCall, newCall->gtCallArgs);
+ GenTreeCall::Use* oldArgs;
+ GenTreeCall::Use oldArgObjp(oldCall, oldCall->gtCallArgs);
if (newCall->gtCallObjp == nullptr)
{
else
{
assert(oldCall->gtCallObjp != nullptr);
- newArgObjp.Current() = newCall->gtCallArgs;
- newArgs = &newArgObjp;
- oldArgObjp.Current() = oldCall->gtCallObjp;
- oldArgs = &oldArgObjp;
+ newArgObjp.SetNode(newCall->gtCallObjp);
+ newArgs = &newArgObjp;
+ oldArgObjp.SetNode(oldCall->gtCallObjp);
+ oldArgs = &oldArgObjp;
}
- GenTree* newCurr;
- GenTree* oldCurr;
- GenTreeArgList* newParent = nullptr;
- GenTreeArgList* oldParent = nullptr;
- fgArgTabEntry** oldArgTable = oldArgInfo->argTable;
- bool scanRegArgs = false;
+ GenTree* newCurr;
+ GenTree* oldCurr;
+ GenTreeCall::Use* newParent = nullptr;
+ GenTreeCall::Use* oldParent = nullptr;
+ fgArgTabEntry** oldArgTable = oldArgInfo->argTable;
+ bool scanRegArgs = false;
- while (newArgs)
+ while (newArgs != nullptr)
{
/* Get hold of the next argument values for the oldCall and newCall */
- newCurr = newArgs->Current();
- oldCurr = oldArgs->Current();
+ newCurr = newArgs->GetNode();
+ oldCurr = oldArgs->GetNode();
if (newArgs != &newArgObjp)
{
newParent = newArgs;
{
assert(newParent == nullptr && oldParent == nullptr);
}
- newArgs = newArgs->Rest();
- oldArgs = oldArgs->Rest();
+ newArgs = newArgs->GetNext();
+ oldArgs = oldArgs->GetNext();
fgArgTabEntry* oldArgTabEntry = nullptr;
fgArgTabEntry* newArgTabEntry = nullptr;
{
oldArgTabEntry = oldArgTable[inx];
- if (oldArgTabEntry->parent == oldParent)
+ if (oldArgTabEntry->use == oldParent)
{
assert((oldParent == nullptr) == (newParent == nullptr));
// Then update all GenTree* fields in the newArgTabEntry
//
- newArgTabEntry->parent = newParent;
+ newArgTabEntry->use = newParent;
// The node field is likely to have been updated
// to point at a node in the gtCallLateArgs list
newArgs = newCall->gtCallLateArgs;
oldArgs = oldCall->gtCallLateArgs;
- while (newArgs)
+ while (newArgs != nullptr)
{
/* Get hold of the next argument values for the oldCall and newCall */
- assert(newArgs->OperIsList());
-
- newCurr = newArgs->Current();
- newArgs = newArgs->Rest();
+ newCurr = newArgs->GetNode();
+ newArgs = newArgs->GetNext();
- assert(oldArgs->OperIsList());
-
- oldCurr = oldArgs->Current();
- oldArgs = oldArgs->Rest();
+ oldCurr = oldArgs->GetNode();
+ oldArgs = oldArgs->GetNext();
fgArgTabEntry* oldArgTabEntry = nullptr;
fgArgTabEntry* newArgTabEntry = nullptr;
argCount++;
}
-fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum,
- GenTree* node,
- GenTree* parent,
- regNumber regNum,
- unsigned numRegs,
- unsigned alignment,
- bool isStruct,
- bool isVararg /*=false*/)
+fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum,
+ GenTree* node,
+ GenTreeCall::Use* use,
+ regNumber regNum,
+ unsigned numRegs,
+ unsigned alignment,
+ bool isStruct,
+ bool isVararg /*=false*/)
{
fgArgTabEntry* curArgTabEntry = new (compiler, CMK_fgArgInfo) fgArgTabEntry;
curArgTabEntry->argNum = argNum;
curArgTabEntry->node = node;
curArgTabEntry->argType = node->TypeGet();
- curArgTabEntry->parent = parent;
+ curArgTabEntry->use = use;
curArgTabEntry->slotNum = 0;
curArgTabEntry->numRegs = numRegs;
curArgTabEntry->numSlots = 0;
#if defined(UNIX_AMD64_ABI)
fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum,
GenTree* node,
- GenTree* parent,
+ GenTreeCall::Use* use,
regNumber regNum,
unsigned numRegs,
unsigned alignment,
const unsigned structFloatRegs,
const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* const structDescPtr)
{
- fgArgTabEntry* curArgTabEntry = AddRegArg(argNum, node, parent, regNum, numRegs, alignment, isStruct, isVararg);
+ fgArgTabEntry* curArgTabEntry = AddRegArg(argNum, node, use, regNum, numRegs, alignment, isStruct, isVararg);
assert(curArgTabEntry != nullptr);
curArgTabEntry->isStruct = isStruct; // is this a struct arg
}
#endif // defined(UNIX_AMD64_ABI)
-fgArgTabEntry* fgArgInfo::AddStkArg(unsigned argNum,
- GenTree* node,
- GenTree* parent,
- unsigned numSlots,
- unsigned alignment,
- bool isStruct,
- bool isVararg /*=false*/)
+fgArgTabEntry* fgArgInfo::AddStkArg(unsigned argNum,
+ GenTree* node,
+ GenTreeCall::Use* use,
+ unsigned numSlots,
+ unsigned alignment,
+ bool isStruct,
+ bool isVararg /*=false*/)
{
fgArgTabEntry* curArgTabEntry = new (compiler, CMK_fgArgInfo) fgArgTabEntry;
curArgTabEntry->argNum = argNum;
curArgTabEntry->node = node;
curArgTabEntry->argType = node->TypeGet();
- curArgTabEntry->parent = parent;
+ curArgTabEntry->use = use;
curArgTabEntry->slotNum = nextSlotNum;
curArgTabEntry->numRegs = 0;
#if defined(UNIX_AMD64_ABI)
assert(curArgTabEntry->numRegs != 0);
- if (curArgTabEntry->parent != nullptr)
+ if (curArgTabEntry->use != nullptr)
{
- assert(curArgTabEntry->parent->OperIsList());
- assert(curArgTabEntry->parent->Current() == node);
+ assert(curArgTabEntry->use->GetNode() == node);
}
if (curArgTabEntry->node != node)
assert((isLateArg && ((node->gtFlags & GTF_LATE_ARG) != 0)) ||
(!isLateArg && ((node->gtFlags & GTF_LATE_ARG) == 0)));
- noway_assert(curArgTabEntry->parent != nullptr);
+ noway_assert(curArgTabEntry->use != nullptr);
assert((curArgTabEntry->regNum == REG_STK) || curArgTabEntry->isSplit);
- assert(curArgTabEntry->parent->OperIsList());
- assert(curArgTabEntry->parent->Current() == node);
+ assert(curArgTabEntry->use->GetNode() == node);
nextSlotNum = (unsigned)roundUp(nextSlotNum, curArgTabEntry->alignment);
assert(curArgTabEntry->slotNum == nextSlotNum);
// Traverse the late argument list to find this argument so that we can update it.
unsigned listInx = 0;
- for (GenTreeArgList *list = callTree->gtCall.gtCallLateArgs; list; list = list->Rest(), listInx++)
+ for (GenTreeCall::Use& use : callTree->AsCall()->LateArgs())
{
- argx = list->Current();
+ argx = use.GetNode();
assert(!argx->IsArgPlaceHolderNode()); // No place holders nodes are in gtCallLateArgs;
if (listInx == lateArgInx)
{
break;
}
+ listInx++;
}
assert(listInx == lateArgInx);
assert(lateArgInx == curArgTabEntry->lateArgInx);
//
void fgArgInfo::EvalToTmp(fgArgTabEntry* curArgTabEntry, unsigned tmpNum, GenTree* newNode)
{
- assert(curArgTabEntry->parent->Current() == newNode);
+ assert(curArgTabEntry->use->GetNode() == newNode);
curArgTabEntry->node = newNode;
curArgTabEntry->tmpNum = tmpNum;
unsigned regArgInx = 0;
// Now go through the argument table and perform the necessary evaluation into temps
- GenTreeArgList* tmpRegArgNext = nullptr;
+ GenTreeCall::Use* tmpRegArgNext = nullptr;
for (unsigned curInx = 0; curInx < argCount; curInx++)
{
fgArgTabEntry* curArgTabEntry = argTable[curInx];
if (setupArg != nullptr)
{
- if (curArgTabEntry->parent)
+ if (curArgTabEntry->use != nullptr)
{
- GenTree* parent = curArgTabEntry->parent;
+ GenTreeCall::Use* use = curArgTabEntry->use;
/* a normal argument from the list */
- noway_assert(parent->OperIsList());
- noway_assert(parent->gtOp.gtOp1 == argx);
-
- parent->gtFlags |= (setupArg->gtFlags & GTF_ALL_EFFECT);
+ noway_assert(use->GetNode() == argx);
- parent->gtOp.gtOp1 = setupArg;
+ use->SetNode(setupArg);
}
else
{
if (tmpRegArgNext == nullptr)
{
- tmpRegArgNext = compiler->gtNewArgList(defArg);
+ tmpRegArgNext = compiler->gtNewCallArgs(defArg);
callTree->gtCall.gtCallLateArgs = tmpRegArgNext;
}
else
{
- noway_assert(tmpRegArgNext->OperIsList());
- noway_assert(tmpRegArgNext->Current());
- tmpRegArgNext->gtOp.gtOp2 = compiler->gtNewArgList(defArg);
+ noway_assert(tmpRegArgNext->GetNode() != nullptr);
+ tmpRegArgNext->SetNext(compiler->gtNewCallArgs(defArg));
- tmpRegArgNext->gtFlags |= (defArg->gtFlags & GTF_ALL_EFFECT);
- tmpRegArgNext = tmpRegArgNext->Rest();
+ tmpRegArgNext = tmpRegArgNext->GetNext();
}
- tmpRegArgNext->gtFlags |= (defArg->gtFlags & GTF_ALL_EFFECT);
-
curArgTabEntry->node = defArg;
curArgTabEntry->lateArgInx = regArgInx++;
}
// It will be used only on the intercepted-for-host code path to copy the arguments.
int Compiler::fgEstimateCallStackSize(GenTreeCall* call)
{
-
int numArgs = 0;
- for (GenTreeArgList* args = call->gtCallArgs; args; args = args->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
numArgs++;
}
//
void Compiler::fgInitArgInfo(GenTreeCall* call)
{
- GenTree* args;
- GenTree* argx;
+ GenTreeCall::Use* args;
+ GenTree* argx;
unsigned argIndex = 0;
unsigned intArgRegNum = 0;
{
numArgs++;
}
- for (GenTree* args = call->gtCallArgs; (args != nullptr); args = args->gtOp.gtOp2)
+ for (GenTreeCall::Use& use : call->Args())
{
numArgs++;
}
// Set the argument registers correctly here.
if (call->IsHelperCall(this, CORINFO_HELP_INIT_PINVOKE_FRAME))
{
- GenTreeArgList* args = call->gtCallArgs;
- GenTree* arg1 = args->Current();
+ GenTreeCall::Use* args = call->gtCallArgs;
+ GenTree* arg1 = args->GetNode();
assert(arg1 != nullptr);
nonStandardArgs.Add(arg1, REG_PINVOKE_FRAME);
}
GenTreeAddrMode(TYP_BYREF, arg, nullptr, 0, eeGetEEInfo()->offsetOfSecureDelegateIndirectCell);
// Append newArg as the last arg
- GenTreeArgList** insertionPoint = &call->gtCallArgs;
- for (; *insertionPoint != nullptr; insertionPoint = &(*insertionPoint)->Rest())
+ GenTreeCall::Use** insertionPoint = &call->gtCallArgs;
+ for (; *insertionPoint != nullptr; insertionPoint = &((*insertionPoint)->NextRef()))
{
}
- *insertionPoint = gtNewListNode(newArg, nullptr);
+ *insertionPoint = gtNewCallArgs(newArg);
numArgs++;
nonStandardArgs.Add(newArg, virtualStubParamInfo->GetReg());
else if (call->IsHelperCall(this, CORINFO_HELP_LLSH) || call->IsHelperCall(this, CORINFO_HELP_LRSH) ||
call->IsHelperCall(this, CORINFO_HELP_LRSZ))
{
- GenTreeArgList* args = call->gtCallArgs;
- GenTree* arg1 = args->Current();
+ GenTreeCall::Use* args = call->gtCallArgs;
+ GenTree* arg1 = args->GetNode();
assert(arg1 != nullptr);
nonStandardArgs.Add(arg1, REG_LNGARG_LO);
- args = args->Rest();
- GenTree* arg2 = args->Current();
+ args = args->GetNext();
+ GenTree* arg2 = args->GetNode();
assert(arg2 != nullptr);
nonStandardArgs.Add(arg2, REG_LNGARG_HI);
}
{
args = call->gtCallArgs;
assert(args != nullptr);
- assert(args->OperIsList());
- argx = call->gtCallArgs->Current();
+ argx = call->gtCallArgs->GetNode();
// We don't increment numArgs here, since we already counted this argument above.
// It will be used only on the intercepted-for-host code path to copy the arguments.
GenTree* cns = new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, fgEstimateCallStackSize(call));
- call->gtCallArgs = gtNewListNode(cns, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(cns, call->gtCallArgs);
numArgs++;
nonStandardArgs.Add(cns, REG_PINVOKE_COOKIE_PARAM);
{
GenTree* stubAddrArg = fgGetStubAddrArg(call);
// And push the stub address onto the list of arguments
- call->gtCallArgs = gtNewListNode(stubAddrArg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(stubAddrArg, call->gtCallArgs);
numArgs++;
nonStandardArgs.Add(stubAddrArg, stubAddrArg->gtRegNum);
#if defined(_TARGET_X86_)
// x86 passes the cookie on the stack as the final argument to the call.
- GenTreeArgList** insertionPoint = &call->gtCallArgs;
- for (; *insertionPoint != nullptr; insertionPoint = &(*insertionPoint)->Rest())
+ GenTreeCall::Use** insertionPoint = &call->gtCallArgs;
+ for (; *insertionPoint != nullptr; insertionPoint = &((*insertionPoint)->NextRef()))
{
}
- *insertionPoint = gtNewListNode(arg, nullptr);
+ *insertionPoint = gtNewCallArgs(arg);
#else // !defined(_TARGET_X86_)
// All other architectures pass the cookie in a register.
- call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs);
#endif // defined(_TARGET_X86_)
nonStandardArgs.Add(arg, REG_PINVOKE_COOKIE_PARAM);
// put destination into R10/EAX
arg = gtClone(call->gtCallAddr, true);
- call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs);
numArgs++;
nonStandardArgs.Add(arg, REG_PINVOKE_TARGET_PARAM);
indirectCellAddress->gtRegNum = REG_R2R_INDIRECT_PARAM;
// Push the stub address onto the list of arguments.
- call->gtCallArgs = gtNewListNode(indirectCellAddress, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(indirectCellAddress, call->gtCallArgs);
numArgs++;
nonStandardArgs.Add(indirectCellAddress, indirectCellAddress->gtRegNum);
if (call->gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL)
{
- noway_assert(call->gtCallArgs->gtOp.gtOp1->TypeGet() == TYP_I_IMPL ||
- call->gtCallArgs->gtOp.gtOp1->TypeGet() == TYP_BYREF ||
- call->gtCallArgs->gtOp.gtOp1->gtOper ==
+ noway_assert(call->gtCallArgs->GetNode()->TypeGet() == TYP_I_IMPL ||
+ call->gtCallArgs->GetNode()->TypeGet() == TYP_BYREF ||
+ call->gtCallArgs->GetNode()->gtOper ==
GT_NOP); // the arg was already morphed to a register (fgMorph called twice)
maxRegArgs = 1;
}
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
#endif // UNIX_AMD64_ABI
- for (args = call->gtCallArgs; args; args = args->gtOp.gtOp2, argIndex++)
+ for (args = call->gtCallArgs; args != nullptr; args = args->GetNext(), argIndex++)
{
- assert(args->OperIsList());
- argx = args->Current();
+ argx = args->GetNode();
fgArgTabEntry* argEntry = nullptr;
// Change the node to TYP_I_IMPL so we don't report GC info
// We have an argument with a struct type, but it may be be a child of a GT_COMMA
GenTree* argObj = argx->gtEffectiveVal(true /*commaOnly*/);
- assert(args->OperIsList());
- assert(argx == args->Current());
+ assert(argx == args->GetNode());
unsigned originalSize = structSize;
originalSize = (originalSize == 0 ? TARGET_POINTER_SIZE : originalSize);
{
// This indicates a partial enregistration of a struct type
assert((isStructArg) || argx->OperIsFieldList() || argx->OperIsCopyBlkOp() ||
- (argx->gtOper == GT_COMMA && (args->gtFlags & GTF_ASG)));
+ (argx->gtOper == GT_COMMA && (argx->gtFlags & GTF_ASG)));
unsigned numRegsPartial = MAX_REG_ARG - intArgRegNum;
assert((unsigned char)numRegsPartial == numRegsPartial);
call->fgArgInfo->SplitArg(argIndex, numRegsPartial, size - numRegsPartial);
#endif
GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
{
- GenTree* args;
- GenTree* argx;
+ GenTreeCall::Use* args;
+ GenTree* argx;
unsigned flagsSummary = 0;
// available when we call UpdateRegArg so that we correctly update the fgArgInfo
// with the folded tree that represents the final optimized argument nodes.
//
- if (call->gtCallLateArgs != nullptr)
+ for (GenTreeCall::Use& use : call->LateArgs())
{
-
- call->gtCallLateArgs = fgMorphTree(call->gtCallLateArgs)->AsArgList();
- flagsSummary |= call->gtCallLateArgs->gtFlags;
+ use.SetNode(fgMorphTree(use.GetNode()));
+ flagsSummary |= use.GetNode()->gtFlags;
}
+
assert(call->fgArgInfo != nullptr);
}
call->fgArgInfo->RemorphReset();
// Note that this name is a bit of a misnomer - it indicates that there are struct args
// that occupy more than a single slot that are passed by value (not necessarily in regs).
bool hasMultiregStructArgs = false;
- for (args = call->gtCallArgs; args; args = args->gtOp.gtOp2, argIndex++)
+ for (args = call->gtCallArgs; args != nullptr; args = args->GetNext(), argIndex++)
{
- GenTree** parentArgx = &args->gtOp.gtOp1;
+ GenTree** parentArgx = &args->NodeRef();
fgArgTabEntry* argEntry = call->fgArgInfo->GetArgEntry(argIndex, reMorphing);
// Morph the arg node, and update the parent and argEntry pointers.
argx = *parentArgx;
argx = fgMorphTree(argx);
*parentArgx = argx;
- assert(args->OperIsList());
- assert(argx == args->Current());
+ assert(argx == args->GetNode());
unsigned argAlign = argEntry->alignment;
unsigned size = argEntry->getSize();
GenTreeFieldList(argx->gtOp.gtOp2, OFFSETOF__CORINFO_TypedReference__type, TYP_I_IMPL, fieldList);
fgArgTabEntry* fp = Compiler::gtArgEntryByNode(call, argx);
fp->node = fieldList;
- args->gtOp.gtOp1 = fieldList;
+ args->SetNode(fieldList);
#else // !_TARGET_X86_
GenTree* asg = gtNewOperNode(GT_COMMA, TYP_VOID, asgPtrSlot, asgTypeSlot);
// Change the expression to "(tmp=val)"
- args->gtOp.gtOp1 = asg;
+ args->SetNode(asg);
// EvalArgsToTemps will cause tmp to actually get loaded as the argument
call->fgArgInfo->EvalToTmp(argEntry, tmp, asg);
GenTreeFieldList(lcl, fieldVarDsc->lvFldOffset, fieldVarDsc->lvType, nullptr);
fgArgTabEntry* fp = Compiler::gtArgEntryByNode(call, argx);
fp->node = fieldList;
- args->gtOp.gtOp1 = fieldList;
+ args->SetNode(fieldList);
}
else
{
}
#endif // _TARGET_X86_
- flagsSummary |= args->Current()->gtFlags;
+ flagsSummary |= args->GetNode()->gtFlags;
} // end foreach argument loop
call->fgArgInfo->ArgsComplete();
}
- if (call->gtCallArgs)
- {
- UpdateGT_LISTFlags(call->gtCallArgs);
- }
-
/* Process the function address, if indirect call */
if (call->gtCallType == CT_INDIRECT)
call->fgArgInfo->SortArgs();
call->fgArgInfo->EvalArgsToTemps();
-
- // We may have updated the arguments
- if (call->gtCallArgs)
- {
- UpdateGT_LISTFlags(call->gtCallArgs);
- }
}
if (hasMultiregStructArgs)
assert(!"Logic error: no MultiregStructArgs for Windows X64 ABI");
#endif
- for (GenTree* args = call->gtCallArgs; args != nullptr; args = args->gtOp.gtOp2)
+ for (GenTreeCall::Use& use : call->Args())
{
// For late arguments the arg tree that is overridden is in the gtCallLateArgs list.
// For such late args the gtCallArgList contains the setup arg node (evaluating the arg.)
// The tree from the gtCallLateArgs list is passed to the callee. The fgArgEntry node contains the mapping
// between the nodes in both lists. If the arg is not a late arg, the fgArgEntry->node points to itself,
// otherwise points to the list in the late args list.
- bool isLateArg = (args->gtOp.gtOp1->gtFlags & GTF_LATE_ARG) != 0;
- fgArgTabEntry* fgEntryPtr = gtArgEntryByNode(call, args->gtOp.gtOp1);
+ bool isLateArg = (use.GetNode()->gtFlags & GTF_LATE_ARG) != 0;
+ fgArgTabEntry* fgEntryPtr = gtArgEntryByNode(call, use.GetNode());
assert(fgEntryPtr != nullptr);
- GenTree* argx = fgEntryPtr->node;
- GenTree* lateList = nullptr;
- GenTree* lateNode = nullptr;
+ GenTree* argx = fgEntryPtr->node;
+ GenTreeCall::Use* lateUse = nullptr;
+ GenTree* lateNode = nullptr;
if (isLateArg)
{
- for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
+ for (GenTreeCall::Use& lateArgUse : call->LateArgs())
{
- assert(list->OperIsList());
-
- GenTree* argNode = list->Current();
+ GenTree* argNode = lateArgUse.GetNode();
if (argx == argNode)
{
- lateList = list;
+ lateUse = &lateArgUse;
lateNode = argNode;
break;
}
}
- assert(lateList != nullptr && lateNode != nullptr);
+ assert((lateUse != nullptr) && (lateNode != nullptr));
}
- GenTree* arg = argx;
-
if (!fgEntryPtr->isStruct)
{
continue;
}
}
}
- arg = fgMorphMultiregStructArg(arg, fgEntryPtr);
+
+ GenTree* newArgx = fgMorphMultiregStructArg(argx, fgEntryPtr);
// Did we replace 'argx' with a new tree?
- if (arg != argx)
+ if (newArgx != argx)
{
- fgEntryPtr->node = arg; // Record the new value for the arg in the fgEntryPtr->node
+ fgEntryPtr->node = newArgx; // Record the new value for the arg in the fgEntryPtr->node
// link the new arg node into either the late arg list or the gtCallArgs list
if (isLateArg)
{
- lateList->gtOp.gtOp1 = arg;
+ lateUse->SetNode(newArgx);
}
else
{
- args->gtOp.gtOp1 = arg;
+ use.SetNode(newArgx);
}
}
}
// tree that computes address of the outgoing arg
//
void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call,
- GenTree* args,
+ GenTreeCall::Use* args,
unsigned argIndex,
CORINFO_CLASS_HANDLE copyBlkClass)
{
- GenTree* argx = args->Current();
+ GenTree* argx = args->GetNode();
noway_assert(argx->gtOper != GT_MKREFANY);
fgArgTabEntry* argEntry = Compiler::gtArgEntryByNode(call, argx);
assert(!call->IsTailCall());
varDsc->setLvRefCnt(0, RCS_EARLY);
- args->gtOp.gtOp1 = lcl;
- argEntry->node = lcl;
+ args->SetNode(lcl);
+ argEntry->node = lcl;
JITDUMP("did not have to make outgoing copy for V%2d", varNum);
return;
#endif // FEATURE_FIXED_OUT_ARGS
- args->gtOp.gtOp1 = arg;
+ args->SetNode(arg);
call->fgArgInfo->EvalToTmp(argEntry, tmp, arg);
return;
if (info.compRetBuffArg != BAD_VAR_NUM)
{
noway_assert(call->TypeGet() == TYP_VOID);
- GenTree* retValBuf = call->gtCallArgs->gtOp.gtOp1;
+ GenTree* retValBuf = call->gtCallArgs->GetNode();
if (retValBuf->gtOper != GT_LCL_VAR || retValBuf->gtLclVarCommon.gtLclNum != info.compRetBuffArg)
{
failTailCall("Need to copy return buffer");
call->gtFlags &= ~GTF_CALL_NULLCHECK;
}
- call->gtCallArgs = gtNewListNode(objp, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(objp, call->gtCallArgs);
}
// Add the extra VSD parameter if needed
stubAddrArg->gtRegNum = REG_NA;
// And push the stub address onto the list of arguments
- call->gtCallArgs = gtNewListNode(stubAddrArg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(stubAddrArg, call->gtCallArgs);
}
else if (call->IsVirtualVtable())
{
// Now inject a placeholder for the real call target that codegen will generate
GenTree* arg = gtNewIconNode(0, TYP_I_IMPL);
- call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs);
// Lastly inject the pointer for the copy routine
noway_assert(pfnCopyArgs != nullptr);
arg = gtNewIconHandleNode(ssize_t(pfnCopyArgs), GTF_ICON_FTN_ADDR);
- call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs);
// It is now a varargs tail call
call->gtCallMoreFlags |= GTF_CALL_M_VARARGS | GTF_CALL_M_TAILCALL | GTF_CALL_M_TAILCALL_VIA_HELPER;
// During rationalization tmp="this" and null check will
// materialize as embedded stmts in right execution order.
assert(thisPtr != nullptr);
- call->gtCallArgs = gtNewListNode(thisPtr, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(thisPtr, call->gtCallArgs);
}
#if defined(_TARGET_AMD64_)
stubAddrArg->gtRegNum = REG_NA;
// And push the stub address onto the list of arguments
- call->gtCallArgs = gtNewListNode(stubAddrArg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(stubAddrArg, call->gtCallArgs);
}
// Now inject a placeholder for the real call target that Lower phase will generate.
GenTree* arg = gtNewIconNode(0, TYP_I_IMPL);
- call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs);
// Inject the pointer for the copy routine to be used for struct copying
noway_assert(pfnCopyArgs != nullptr);
arg = gtNewIconHandleNode(ssize_t(pfnCopyArgs), GTF_ICON_FTN_ADDR);
- call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
+ call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs);
#else // !_TARGET_AMD64_
// Find the end of the argument list. ppArg will point at the last pointer; setting *ppArg will
// append to the list.
- GenTreeArgList** ppArg = &call->gtCallArgs;
- for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest())
+ GenTreeCall::Use** ppArg = &call->gtCallArgs;
+ for (GenTreeCall::Use& use : call->Args())
{
- ppArg = (GenTreeArgList**)&args->gtOp2;
+ ppArg = &use.NextRef();
}
assert(ppArg != nullptr);
assert(*ppArg == nullptr);
unsigned nOldStkArgsWords =
(compArgSize - (codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES)) / REGSIZE_BYTES;
GenTree* arg3 = gtNewIconNode((ssize_t)nOldStkArgsWords, TYP_I_IMPL);
- *ppArg = gtNewListNode(arg3, nullptr); // numberOfOldStackArgs
- ppArg = (GenTreeArgList**)&((*ppArg)->gtOp2);
+ *ppArg = gtNewCallArgs(arg3); // numberOfOldStackArgs
+ ppArg = &((*ppArg)->NextRef());
// Inject a placeholder for the count of outgoing stack arguments that the Lowering phase will generate.
// The constant will be replaced.
GenTree* arg2 = gtNewIconNode(9, TYP_I_IMPL);
- *ppArg = gtNewListNode(arg2, nullptr); // numberOfNewStackArgs
- ppArg = (GenTreeArgList**)&((*ppArg)->gtOp2);
+ *ppArg = gtNewCallArgs(arg2); // numberOfNewStackArgs
+ ppArg = &((*ppArg)->NextRef());
// Inject a placeholder for the flags.
// The constant will be replaced.
GenTree* arg1 = gtNewIconNode(8, TYP_I_IMPL);
- *ppArg = gtNewListNode(arg1, nullptr);
- ppArg = (GenTreeArgList**)&((*ppArg)->gtOp2);
+ *ppArg = gtNewCallArgs(arg1);
+ ppArg = &((*ppArg)->NextRef());
// Inject a placeholder for the real call target that the Lowering phase will generate.
// The constant will be replaced.
GenTree* arg0 = gtNewIconNode(7, TYP_I_IMPL);
- *ppArg = gtNewListNode(arg0, nullptr);
+ *ppArg = gtNewCallArgs(arg0);
#endif // !_TARGET_AMD64_
// Early args don't include 'this' arg. We need to account for that so that the call to gtArgEntryByArgNum
// below has the correct second argument.
int earlyArgIndex = (thisArg == nullptr) ? 0 : 1;
- for (GenTreeArgList* earlyArgs = recursiveTailCall->gtCallArgs; earlyArgs != nullptr;
- (earlyArgIndex++, earlyArgs = earlyArgs->Rest()))
+ for (GenTreeCall::Use& use : recursiveTailCall->Args())
{
- GenTree* earlyArg = earlyArgs->Current();
+ GenTree* earlyArg = use.GetNode();
if (!earlyArg->IsNothingNode() && !earlyArg->IsArgPlaceHolderNode())
{
if ((earlyArg->gtFlags & GTF_LATE_ARG) != 0)
}
}
}
+ earlyArgIndex++;
}
// Process late args.
int lateArgIndex = 0;
- for (GenTreeArgList* lateArgs = recursiveTailCall->gtCallLateArgs; lateArgs != nullptr;
- (lateArgIndex++, lateArgs = lateArgs->Rest()))
+ for (GenTreeCall::Use& use : recursiveTailCall->LateArgs())
{
// A late argument is an actual argument that needs to be assigned to the corresponding caller's parameter.
- GenTree* lateArg = lateArgs->Current();
+ GenTree* lateArg = use.GetNode();
fgArgTabEntry* curArgTabEntry = gtArgEntryByLateArgIndex(recursiveTailCall, lateArgIndex);
GenTreeStmt* paramAssignStmt =
fgAssignRecursiveCallArgToCallerParam(lateArg, curArgTabEntry, block, callILOffset,
// All temp assignments will happen before the first param assignment.
tmpAssignmentInsertionPoint = paramAssignStmt;
}
+ lateArgIndex++;
}
// If the method has starg.s 0 or ldarga.s 0 a special local (lvaArg0Var) is created so that
// This is call to CORINFO_HELP_VIRTUAL_FUNC_PTR with ignored result.
// Transform it into a null check.
- GenTree* thisPtr = call->gtCallArgs->gtOp.gtOp1;
+ GenTree* thisPtr = call->gtCallArgs->GetNode();
GenTree* nullCheck = gtNewOperNode(GT_IND, TYP_I_IMPL, thisPtr);
nullCheck->gtFlags |= GTF_EXCEPT;
// if we're passing the caller's ret buff arg to the callee, since the caller's caller
// will maintain the same invariant.)
- GenTree* dest = call->gtCallArgs->gtOp.gtOp1;
+ GenTree* dest = call->gtCallArgs->GetNode();
assert(dest->OperGet() != GT_ARGPLACE); // If it was, we'd be in a remorph, which we've already excluded above.
if (dest->gtType == TYP_BYREF && !(dest->OperGet() == GT_ADDR && dest->gtOp.gtOp1->OperGet() == GT_LCL_VAR))
{
}
}
- call->gtCallArgs->gtOp.gtOp1 = dest;
+ call->gtCallArgs->SetNode(dest);
}
/* Process the "normal" argument list */
// Either or both of the array and index arguments may have been spilled to temps by `fgMorphArgs`. Copy
// the spill trees as well if necessary.
GenTreeOp* argSetup = nullptr;
- for (GenTreeArgList* earlyArgs = call->gtCallArgs; earlyArgs != nullptr; earlyArgs = earlyArgs->Rest())
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* const arg = earlyArgs->Current();
+ GenTree* const arg = use.GetNode();
if (arg->OperGet() != GT_ASG)
{
continue;
info.compCompHnd->getIntrinsicID(call->gtCallMethHnd) == CORINFO_INTRINSIC_GetManagedThreadId)
{
noway_assert(origDest == nullptr);
- noway_assert(call->gtCallLateArgs->gtOp.gtOp1 != nullptr);
+ noway_assert(call->gtCallLateArgs->GetNode() != nullptr);
- GenTree* innerCall = call->gtCallLateArgs->gtOp.gtOp1;
+ GenTree* innerCall = call->gtCallLateArgs->GetNode();
if (innerCall->gtOper == GT_CALL && (innerCall->gtCall.gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) &&
info.compCompHnd->getIntrinsicID(innerCall->gtCall.gtCallMethHnd) ==
{
// For un-important blocks, we want to construct the string lazily
- GenTreeArgList* args;
+ GenTreeCall::Use* args;
if (helper == CORINFO_HELP_STRCNS_CURRENT_MODULE)
{
- args = gtNewArgList(gtNewIconNode(RidFromToken(tree->gtStrCon.gtSconCPX), TYP_INT));
+ args = gtNewCallArgs(gtNewIconNode(RidFromToken(tree->gtStrCon.gtSconCPX), TYP_INT));
}
else
{
- args = gtNewArgList(gtNewIconNode(RidFromToken(tree->gtStrCon.gtSconCPX), TYP_INT),
- gtNewIconEmbScpHndNode(tree->gtStrCon.gtScpHnd));
+ args = gtNewCallArgs(gtNewIconNode(RidFromToken(tree->gtStrCon.gtSconCPX), TYP_INT),
+ gtNewIconEmbScpHndNode(tree->gtStrCon.gtScpHnd));
}
tree = gtNewHelperCallNode(helper, TYP_REF, args);
}
// Get the nullable struct argument
- GenTree* arg = opCall->gtCall.gtCallArgs->gtOp.gtOp2->gtOp.gtOp1;
+ GenTree* arg = opCall->AsCall()->gtCallArgs->GetNext()->GetNode();
// Check for cases that are unsafe to optimize and return the unchanged tree
if (arg->IsArgPlaceHolderNode() || arg->IsNothingNode() || ((arg->gtFlags & GTF_LATE_ARG) != 0))
return fgMorphTree(tree);
}
}
- return fgMorphIntoHelperCall(tree, helper, gtNewArgList(op1, op2));
+ return fgMorphIntoHelperCall(tree, helper, gtNewCallArgs(op1, op2));
case GT_RETURN:
// normalize small integer return values
switch (tree->TypeGet())
{
case TYP_DOUBLE:
- return fgMorphIntoHelperCall(tree, CORINFO_HELP_DBLROUND, gtNewArgList(op1));
+ return fgMorphIntoHelperCall(tree, CORINFO_HELP_DBLROUND, gtNewCallArgs(op1));
case TYP_FLOAT:
- return fgMorphIntoHelperCall(tree, CORINFO_HELP_FLTROUND, gtNewArgList(op1));
+ return fgMorphIntoHelperCall(tree, CORINFO_HELP_FLTROUND, gtNewCallArgs(op1));
default:
unreached();
}
// base of the class that owns the method being compiled". If we're in this method, it means we're not
// inlining and there's no ambiguity.
return impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE, TYP_BYREF,
- gtNewArgList(ctxTree), &kind);
+ gtNewCallArgs(ctxTree), &kind);
}
#endif
vtTree->gtFlags |= GTF_EXCEPT; // Null-pointer exception
GenTree* methodHnd = gtNewIconEmbMethHndNode(info.compMethodHnd);
- return gtNewHelperCallNode(CORINFO_HELP_INITINSTCLASS, TYP_VOID, gtNewArgList(vtTree, methodHnd));
+ return gtNewHelperCallNode(CORINFO_HELP_INITINSTCLASS, TYP_VOID, gtNewCallArgs(vtTree, methodHnd));
}
case CORINFO_LOOKUP_CLASSPARAM:
{
GenTree* vtTree = gtNewLclvNode(info.compTypeCtxtArg, TYP_I_IMPL);
- return gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, gtNewArgList(vtTree));
+ return gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, gtNewCallArgs(vtTree));
}
case CORINFO_LOOKUP_METHODPARAM:
{
GenTree* methHndTree = gtNewLclvNode(info.compTypeCtxtArg, TYP_I_IMPL);
return gtNewHelperCallNode(CORINFO_HELP_INITINSTCLASS, TYP_VOID,
- gtNewArgList(gtNewIconNode(0), methHndTree));
+ gtNewCallArgs(gtNewIconNode(0), methHndTree));
}
}
}
if (lvaTable[i].TypeGet() == TYP_REF)
{
// confirm that the argument is a GC pointer (for debugging (GC stress))
- GenTree* op = gtNewLclvNode(i, TYP_REF);
- GenTreeArgList* args = gtNewArgList(op);
- op = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_VOID, args);
+ GenTree* op = gtNewLclvNode(i, TYP_REF);
+ GenTreeCall::Use* args = gtNewCallArgs(op);
+ op = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_VOID, args);
fgEnsureFirstBBisScratch();
fgNewStmtAtEnd(fgFirstBB, op);
unsigned int helper = allocObj->gtNewHelper;
bool helperHasSideEffects = allocObj->gtHelperHasSideEffects;
- GenTreeArgList* args;
+ GenTreeCall::Use* args;
#ifdef FEATURE_READYTORUN_COMPILER
CORINFO_CONST_LOOKUP entryPoint = allocObj->gtEntryPoint;
if (helper == CORINFO_HELP_READYTORUN_NEW)
else
#endif
{
- args = comp->gtNewArgList(op1);
+ args = comp->gtNewCallArgs(op1);
}
const bool morphArgs = false;
#ifdef FEATURE_READYTORUN_COMPILER
CORINFO_CONST_LOOKUP entryPoint,
#endif
- GenTreeArgList* args)
+ GenTreeCall::Use* args)
{
GenTree* const tree = *use;
GenTree* const treeFirstNode = comp->fgGetFirstNode(tree);
{
GenTreeIntrinsic* intrinsic = (*use)->AsIntrinsic();
- GenTreeArgList* args;
+ GenTreeCall::Use* args;
if (intrinsic->gtOp.gtOp2 == nullptr)
{
- args = comp->gtNewArgList(intrinsic->gtGetOp1());
+ args = comp->gtNewCallArgs(intrinsic->gtGetOp1());
}
else
{
- args = comp->gtNewArgList(intrinsic->gtGetOp1(), intrinsic->gtGetOp2());
+ args = comp->gtNewCallArgs(intrinsic->gtGetOp1(), intrinsic->gtGetOp2());
}
RewriteNodeAsCall(use, parents, intrinsic->gtMethodHandle,
#ifdef FEATURE_READYTORUN_COMPILER
CORINFO_CONST_LOOKUP entryPoint,
#endif
- GenTreeArgList* args);
+ GenTreeCall::Use* args);
void RewriteIntrinsicAsUserCall(GenTree** use, Compiler::GenTreeStack& parents);
{
unsigned nArgs = ValueNumStore::VNFuncArity(vnf);
assert(vnf != VNF_Boundary);
- GenTreeArgList* args = call->gtCallArgs;
- bool generateUniqueVN = false;
- bool useEntryPointAddrAsArg0 = false;
+ GenTreeCall::Use* args = call->gtCallArgs;
+ bool generateUniqueVN = false;
+ bool useEntryPointAddrAsArg0 = false;
switch (vnf)
{
case VNF_JitNewArr:
{
generateUniqueVN = true;
- ValueNumPair vnp1 = vnStore->VNPNormalPair(args->Rest()->Current()->gtVNPair);
+ ValueNumPair vnp1 = vnStore->VNPNormalPair(args->GetNext()->GetNode()->gtVNPair);
// The New Array helper may throw an overflow exception
vnpExc = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NewArrOverflowExc, vnp1));
case VNF_JitReadyToRunNewArr:
{
generateUniqueVN = true;
- ValueNumPair vnp1 = vnStore->VNPNormalPair(args->Current()->gtVNPair);
+ ValueNumPair vnp1 = vnStore->VNPNormalPair(args->GetNode()->gtVNPair);
// The New Array helper may throw an overflow exception
vnpExc = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NewArrOverflowExc, vnp1));
if (call->IsR2RRelativeIndir())
{
#ifdef DEBUG
- assert(args->Current()->OperGet() == GT_ARGPLACE);
+ assert(args->GetNode()->OperGet() == GT_ARGPLACE);
// Find the corresponding late arg.
GenTree* indirectCellAddress = call->fgArgInfo->GetArgNode(0);
else
{
auto getCurrentArg = [call, &args, useEntryPointAddrAsArg0](int currentIndex) {
- GenTree* arg = args->Current();
+ GenTree* arg = args->GetNode();
if ((arg->gtFlags & GTF_LATE_ARG) != 0)
{
// This arg is a setup node that moves the arg into position.
// Also include in the argument exception sets
vnpExc = vnStore->VNPExcSetUnion(vnpExc, vnp0x);
- args = args->Rest();
+ args = args->GetNext();
}
if (nArgs == 1)
{
vnStore->VNPUnpackExc(vnp1wx, &vnp1, &vnp1x);
vnpExc = vnStore->VNPExcSetUnion(vnpExc, vnp1x);
- args = args->Rest();
+ args = args->GetNext();
if (nArgs == 2)
{
if (generateUniqueVN)
vnStore->VNPUnpackExc(vnp2wx, &vnp2, &vnp2x);
vnpExc = vnStore->VNPExcSetUnion(vnpExc, vnp2x);
- args = args->Rest();
+ args = args->GetNext();
assert(nArgs == 3); // Our current maximum.
assert(args == nullptr);
if (generateUniqueVN)
{
// First: do value numbering of any argument placeholder nodes in the argument list
// (by transferring from the VN of the late arg that they are standing in for...)
- unsigned i = 0;
- GenTreeArgList* args = call->gtCallArgs;
- bool updatedArgPlace = false;
- while (args != nullptr)
+ unsigned i = 0;
+ for (GenTreeCall::Use& use : call->Args())
{
- GenTree* arg = args->Current();
+ GenTree* arg = use.GetNode();
if (arg->OperGet() == GT_ARGPLACE)
{
// Find the corresponding late arg.
GenTree* lateArg = call->fgArgInfo->GetArgNode(i);
assert(lateArg->gtVNPair.BothDefined());
- arg->gtVNPair = lateArg->gtVNPair;
- updatedArgPlace = true;
+ arg->gtVNPair = lateArg->gtVNPair;
#ifdef DEBUG
if (verbose)
{
#endif
}
i++;
- args = args->Rest();
- }
- if (updatedArgPlace)
- {
- // Now we have to update the VN's of the argument list nodes, since that will be used in determining
- // loop-invariance.
- fgUpdateArgListVNs(call->gtCallArgs);
}
if (call->gtCallType == CT_HELPER)
}
}
-void Compiler::fgUpdateArgListVNs(GenTreeArgList* args)
-{
- if (args == nullptr)
- {
- return;
- }
- // Otherwise...
- fgUpdateArgListVNs(args->Rest());
- fgValueNumberTree(args);
-}
-
VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc)
{
assert(s_helperCallProperties.IsPure(helpFunc) || s_helperCallProperties.IsAllocator(helpFunc));