diff options
author | Andy Ayers <andya@microsoft.com> | 2019-01-04 11:05:43 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-04 11:05:43 -0800 |
commit | df4a1854aa42c1e97874b00fe49339e216e30af9 (patch) | |
tree | 23a04ba3e574733f398a758b5b3e7bcc7711df7b /src/jit/morph.cpp | |
parent | f0c969a072c05c0d602d16d68bc6e130fd17be88 (diff) | |
download | coreclr-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.cpp | 77 |
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; + } } } } |