JIT: null out inline gc type locals after inline body
Inlining can sometimes end up stretching GC lifetimes for inlinee
locals past the end of the inlinee method body.
This change extends the work done for inlining methods with pinned
locals to null out all gc ref type locals. If there are any gc type
locals, the return value is copied to a temp inside the inlinee body,
and then at the end of the body the gc type locals are set to null.
In most cases these null stores end up getting removed by dead code
elimination, but if the local ends up untracked, the store remains
and limits the active GC lifetime.
This change requires more extensive use of the return value spill temp
(since we must be absolutely sure the return value expression does not
depend on any of the locals). We now use the spill temp if any local is
a gc ref type (used or not).
More widespread use of the spill temp exposed an issue in the tail call
transformation, where the side effect flags were overly pessimistic for
a post tail-call GT_COMMA statement that represented an ignored return
value from an inlined call. The root issue is that the return value
placeholder is given a GTF_CALL effect but later when the actual return
value expression is substituted in place there may be no side effect --
in particular this happens when the return value expression is the spill
temp.
So, we also update the post tail-call side-effect-free statment check to
look through the potentially pessimistic GT_COMMA flags and check for side
effects on the child nodes.
Closes dotnet/coreclr#9218.
Commit migrated from https://github.com/dotnet/coreclr/commit/
05721eebc54af7bb535f8afb7c67a557cbac5dd5