Skip to content

Commit

Permalink
Import KEEPALIVE(BOX) as KEEPALIVE(ADDR(TEMP)) (dotnet#54412)
Browse files Browse the repository at this point in the history
* Prototype removing boxes from GC.KeepAlive

Instead, import KEEPALIVE(BOX) as a sequence of
KEEPALIVE for object fields (flattened).

* Clean things up

Move the importation to its own method.
Add debug output.
Rename a few things.

* Add a test

Basic verification of the new optimization.

* Fix formatting

* Disable the test on Mono

It relies on a precise GC.

* Add a class field scenario to the test

* Fix a test

Running diffs for win-x64, there was exactly one result,
in this test. GC.KeepAlive here is used as a generic
"make sure that there is a use for the variable
beyond an assigment" function. The new optimization
eliminates this use as the struct in question has
no GC fields. Fix the test by using an actual generic
"use me" function.

Will be broken by advanced interprocediral analysis
that is apparently not as far away as one would expect,
but will do for now.

* Use advanced DEBUG features for even nicer dumps

* Add two simple EH scenarios to the test

* Fix the test

It is possible for a GC to occur just after
the KeepAlive calls and for CheckSuccess to
wrongly conclude the objects weren't kept alive.

* Only elide copies for GT_LCL_VARs

If the source is a GT_LCL_FLD, then we would only want
too keep alive the fields it encloses (or, since TYP_STRUCT
local fields are currently not supported, nothing).

* Use the address of the whole struct for KEEPALIVE

The field-by-field approach results in the struct getting
address-exposed anyway, so there is no good reason to bloat
the IR. Just have the address escape directly.

Co-authored-by: Jeremy Koritzinsky <[email protected]>
  • Loading branch information
SingleAccretion and jkoritzinsky authored Sep 3, 2021
1 parent 87ac0d5 commit 4173838
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3103,6 +3103,8 @@ class Compiler

GenTree* gtUnusedValNode(GenTree* expr);

GenTree* gtNewKeepAliveNode(GenTree* op);

GenTreeCast* gtNewCastNode(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType);

GenTreeCast* gtNewCastNodeL(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType);
Expand Down Expand Up @@ -4232,6 +4234,8 @@ class Compiler
CorInfoIntrinsics intrinsicID);
GenTree* impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig);

GenTree* impKeepAliveIntrinsic(GenTree* objToKeepAlive);

GenTree* impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo);

GenTree* impTransformThis(GenTree* thisPtr,
Expand Down
14 changes: 14 additions & 0 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,20 @@ inline void GenTree::SetOper(genTreeOps oper, ValueNumberUpdate vnUpdate)
}
}

inline GenTree* Compiler::gtNewKeepAliveNode(GenTree* op)
{
GenTree* keepalive = gtNewOperNode(GT_KEEPALIVE, TYP_VOID, op);

// Prevent both reordering and removal. Invalid optimizations of GC.KeepAlive are
// very subtle and hard to observe. Thus we are conservatively marking it with both
// GTF_CALL and GTF_GLOB_REF side-effects even though it may be more than strictly
// necessary. The conservative side-effects are unlikely to have negative impact
// on code quality in this case.
keepalive->gtFlags |= (GTF_CALL | GTF_GLOB_REF);

return keepalive;
}

inline GenTreeCast* Compiler::gtNewCastNode(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType)
{
GenTreeCast* cast = new (this, GT_CAST) GenTreeCast(typ, op1, fromUnsigned, castType);
Expand Down
70 changes: 61 additions & 9 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4532,15 +4532,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,

case NI_System_GC_KeepAlive:
{
retNode = gtNewOperNode(GT_KEEPALIVE, TYP_VOID, impPopStack().val);

// Prevent both reordering and removal. Invalid optimizations of GC.KeepAlive are
// very subtle and hard to observe. Thus we are conservatively marking it with both
// GTF_CALL and GTF_GLOB_REF side-effects even though it may be more than strictly
// necessary. The conservative side-effects are unlikely to have negative impact
// on code quality in this case.
retNode->gtFlags |= (GTF_CALL | GTF_GLOB_REF);

retNode = impKeepAliveIntrinsic(impPopStack().val);
break;
}

Expand Down Expand Up @@ -5277,6 +5269,66 @@ GenTree* Compiler::impArrayAccessIntrinsic(
}
}

//------------------------------------------------------------------------
// impKeepAliveIntrinsic: Import the GC.KeepAlive intrinsic call
//
// Imports the intrinsic as a GT_KEEPALIVE node, and, as an optimization,
// if the object to keep alive is a GT_BOX, removes its side effects and
// uses the address of a local (copied from the box's source if needed)
// as the operand for GT_KEEPALIVE. For the BOX optimization, if the class
// of the box has no GC fields, a GT_NOP is returned.
//
// Arguments:
// objToKeepAlive - the intrinisic call's argument
//
// Return Value:
// The imported GT_KEEPALIVE or GT_NOP - see description.
//
GenTree* Compiler::impKeepAliveIntrinsic(GenTree* objToKeepAlive)
{
assert(objToKeepAlive->TypeIs(TYP_REF));

if (opts.OptimizationEnabled() && objToKeepAlive->IsBoxedValue())
{
CORINFO_CLASS_HANDLE boxedClass = lvaGetDesc(objToKeepAlive->AsBox()->BoxOp()->AsLclVar())->lvClassHnd;
ClassLayout* layout = typGetObjLayout(boxedClass);

if (!layout->HasGCPtr())
{
gtTryRemoveBoxUpstreamEffects(objToKeepAlive, BR_REMOVE_AND_NARROW);
JITDUMP("\nBOX class has no GC fields, KEEPALIVE is a NOP");

return gtNewNothingNode();
}

GenTree* boxSrc = gtTryRemoveBoxUpstreamEffects(objToKeepAlive, BR_REMOVE_BUT_NOT_NARROW);
if (boxSrc != nullptr)
{
unsigned boxTempNum;
if (boxSrc->OperIs(GT_LCL_VAR))
{
boxTempNum = boxSrc->AsLclVarCommon()->GetLclNum();
}
else
{
boxTempNum = lvaGrabTemp(true DEBUGARG("Temp for the box source"));
GenTree* boxTempAsg = gtNewTempAssign(boxTempNum, boxSrc);
Statement* boxAsgStmt = objToKeepAlive->AsBox()->gtCopyStmtWhenInlinedBoxValue;
boxAsgStmt->SetRootNode(boxTempAsg);
}

JITDUMP("\nImporting KEEPALIVE(BOX) as KEEPALIVE(ADDR(LCL_VAR V%02u))", boxTempNum);

GenTree* boxTemp = gtNewLclvNode(boxTempNum, boxSrc->TypeGet());
GenTree* boxTempAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, boxTemp);

return gtNewKeepAliveNode(boxTempAddr);
}
}

return gtNewKeepAliveNode(objToKeepAlive);
}

bool Compiler::verMergeEntryStates(BasicBlock* block, bool* changed)
{
unsigned i;
Expand Down
Loading

0 comments on commit 4173838

Please sign in to comment.