summaryrefslogtreecommitdiff
path: root/src/jit/morph.cpp
diff options
context:
space:
mode:
authorAndy Ayers <andya@microsoft.com>2019-01-04 11:05:43 -0800
committerGitHub <noreply@github.com>2019-01-04 11:05:43 -0800
commitdf4a1854aa42c1e97874b00fe49339e216e30af9 (patch)
tree23a04ba3e574733f398a758b5b3e7bcc7711df7b /src/jit/morph.cpp
parentf0c969a072c05c0d602d16d68bc6e130fd17be88 (diff)
downloadcoreclr-df4a1854aa42c1e97874b00fe49339e216e30af9.tar.gz
coreclr-df4a1854aa42c1e97874b00fe49339e216e30af9.tar.bz2
coreclr-df4a1854aa42c1e97874b00fe49339e216e30af9.zip
JIT: don't optimize struct copies for call args in debug or minopts (#21792)
The jit will opportunistically optimize away copies for some struct call arguments, if that argument is a "last use" for the struct (and some other conditions apply). In cases like #21544 this leads to confusing debug experiences as inputs to a call appear to be modified by the call. Also we really should not be optimizing the code this way in debug or in minopts codegen modes. So, block this optimization for debug and minopts. Closes #21544.
Diffstat (limited to 'src/jit/morph.cpp')
-rw-r--r--src/jit/morph.cpp77
1 files changed, 48 insertions, 29 deletions
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 09c6fc7258..df95c9344b 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -5027,8 +5027,19 @@ GenTreeFieldList* Compiler::fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl)
return newArg;
}
-// Make a copy of a struct variable if necessary, to pass to a callee.
-// returns: tree that computes address of the outgoing arg
+//------------------------------------------------------------------------
+// fgMakeOutgoingStructArgCopy: make a copy of a struct variable if necessary,
+// to pass to a callee.
+//
+// Arguments:
+// call - call being processed
+// args - args for the call
+/// argIndex - arg being processed
+// copyBlkClass - class handle for the struct
+//
+// Return value:
+// tree that computes address of the outgoing arg
+//
void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call,
GenTree* args,
unsigned argIndex,
@@ -5036,38 +5047,46 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call,
{
GenTree* argx = args->Current();
noway_assert(argx->gtOper != GT_MKREFANY);
- fgArgTabEntry* argEntry = Compiler::gtArgEntryByNode(call, argx);
- GenTreeLclVarCommon* lcl = nullptr;
+ fgArgTabEntry* argEntry = Compiler::gtArgEntryByNode(call, argx);
- // See if we need to insert a copy at all
- // Case 1: don't need a copy if it is the last use of a local. We can't determine that all of the time
- // but if there is only one use and no loops, the use must be last.
- if (argx->OperIsLocal())
- {
- lcl = argx->AsLclVarCommon();
- }
- else if ((argx->OperGet() == GT_OBJ) && argx->AsIndir()->Addr()->OperIsLocal())
- {
- lcl = argx->AsObj()->Addr()->AsLclVarCommon();
- }
- if (lcl != nullptr)
+ // If we're optimizing, see if we can avoid making a copy.
+ //
+ // We don't need a copy if this is the last use of an implicit by-ref local.
+ //
+ // We can't determine that all of the time, but if there is only
+ // one use and the method has no loops, then this use must be the last.
+ if (!(opts.compDbgCode || opts.MinOpts()))
{
- unsigned varNum = lcl->AsLclVarCommon()->GetLclNum();
- if (lvaIsImplicitByRefLocal(varNum))
+ GenTreeLclVarCommon* lcl = nullptr;
+
+ if (argx->OperIsLocal())
{
- LclVarDsc* varDsc = &lvaTable[varNum];
- // JIT_TailCall helper has an implicit assumption that all tail call arguments live
- // on the caller's frame. If an argument lives on the caller caller's frame, it may get
- // overwritten if that frame is reused for the tail call. Therefore, we should always copy
- // struct parameters if they are passed as arguments to a tail call.
- if (!call->IsTailCallViaHelper() && (varDsc->lvRefCnt(RCS_EARLY) == 1) && !fgMightHaveLoop())
+ lcl = argx->AsLclVarCommon();
+ }
+ else if ((argx->OperGet() == GT_OBJ) && argx->AsIndir()->Addr()->OperIsLocal())
+ {
+ lcl = argx->AsObj()->Addr()->AsLclVarCommon();
+ }
+
+ if (lcl != nullptr)
+ {
+ unsigned varNum = lcl->AsLclVarCommon()->GetLclNum();
+ if (lvaIsImplicitByRefLocal(varNum))
{
- varDsc->setLvRefCnt(0, RCS_EARLY);
- args->gtOp.gtOp1 = lcl;
- argEntry->node = lcl;
+ LclVarDsc* varDsc = &lvaTable[varNum];
+ // JIT_TailCall helper has an implicit assumption that all tail call arguments live
+ // on the caller's frame. If an argument lives on the caller caller's frame, it may get
+ // overwritten if that frame is reused for the tail call. Therefore, we should always copy
+ // struct parameters if they are passed as arguments to a tail call.
+ if (!call->IsTailCallViaHelper() && (varDsc->lvRefCnt(RCS_EARLY) == 1) && !fgMightHaveLoop())
+ {
+ varDsc->setLvRefCnt(0, RCS_EARLY);
+ args->gtOp.gtOp1 = lcl;
+ argEntry->node = lcl;
- JITDUMP("did not have to make outgoing copy for V%2d", varNum);
- return;
+ JITDUMP("did not have to make outgoing copy for V%2d", varNum);
+ return;
+ }
}
}
}