#ifndef LEGACY_BACKEND
// In the future we can migrate UNIX_AMD64 to use this
// method instead of fgMorphSystemVStructArgs
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef _TARGET_ARM_
+ // Rewrite the struct args to be passed by value on stack.
+ if (hasStructArgument)
+ {
+ fgMorphStructArgs(call);
+ }
+#endif // _TARGET_ARM_
// We only build GT_FIELD_LISTs for MultiReg structs for the RyuJIT backend
if (hasMultiregStructArgs)
}
#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#ifdef _TARGET_ARM_
+//-----------------------------------------------------------------------------
+// fgMorphStructArgs: Rewrite the struct args to be passed by value on stack.
+//
+// Arguments:
+// call: a GenTreeCall node that has one or more TYP_STRUCT arguments
+//
+void Compiler::fgMorphStructArgs(GenTreeCall* call)
+{
+ unsigned flagsSummary = 0;
+ GenTreePtr args;
+
+ for (args = call->gtCallArgs; args != nullptr; args = args->gtOp.gtOp2)
+ {
+ // 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;
+ fgArgTabEntryPtr fgEntryPtr = gtArgEntryByNode(call, args->gtOp.gtOp1);
+ assert(fgEntryPtr != nullptr);
+ GenTreePtr argx = fgEntryPtr->node;
+ GenTreePtr lateList = nullptr;
+ GenTreePtr lateNode = nullptr;
+
+ if (isLateArg)
+ {
+ for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
+ {
+ assert(list->OperIsList());
+
+ GenTreePtr argNode = list->Current();
+ if (argx == argNode)
+ {
+ lateList = list;
+ lateNode = argNode;
+ break;
+ }
+ }
+ assert(lateList != nullptr && lateNode != nullptr);
+ }
+
+ GenTreePtr arg = argx;
+
+ if (arg->TypeGet() == TYP_STRUCT)
+ {
+ // If we have already processed the arg...
+ if (arg->OperGet() == GT_FIELD_LIST && varTypeIsStruct(arg))
+ {
+ continue;
+ }
+
+ // If already OBJ it is set properly already.
+ if (arg->OperGet() == GT_OBJ)
+ {
+ continue;
+ }
+
+ assert(arg->OperGet() == GT_LCL_VAR || arg->OperGet() == GT_LCL_FLD ||
+ (arg->OperGet() == GT_ADDR &&
+ (arg->gtOp.gtOp1->OperGet() == GT_LCL_FLD || arg->gtOp.gtOp1->OperGet() == GT_LCL_VAR)));
+
+ GenTreeLclVarCommon* lclCommon =
+ arg->OperGet() == GT_ADDR ? arg->gtOp.gtOp1->AsLclVarCommon() : arg->AsLclVarCommon();
+
+ // If we didn't change the type of the struct, it means
+ // its classification doesn't support to be passed directly through a
+ // register, so we need to pass a pointer to the destination where
+ // where we copied the struct to.
+ // Make sure this is an addr node.
+ if (arg->OperGet() != GT_ADDR && arg->OperGet() != GT_LCL_VAR_ADDR)
+ {
+ arg = gtNewOperNode(GT_ADDR, TYP_I_IMPL, arg);
+ }
+
+ assert(arg->OperGet() == GT_ADDR || arg->OperGet() == GT_LCL_VAR_ADDR);
+
+ // Create an Obj of the temp to use it as a call argument.
+ arg = gtNewObjNode(lvaGetStruct(lclCommon->gtLclNum), arg);
+ }
+
+ // Did we replace 'argx' with a new tree?
+ if (argx != arg)
+ {
+ fgEntryPtr->node = arg; // 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;
+ }
+ else
+ {
+ args->gtOp.gtOp1 = arg;
+ }
+ }
+ }
+
+ // Update the flags
+ call->gtFlags |= (flagsSummary & GTF_ALL_EFFECT);
+}
+#endif // _TARGET_ARM_
+
//-----------------------------------------------------------------------------
// fgMorphMultiregStructArgs: Locate the TYP_STRUCT arguments and
// call fgMorphMultiregStructArg on each of them.