Skip to content

Commit

Permalink
Cleaning up write barrier code (#68124)
Browse files Browse the repository at this point in the history
* Write barrier code cleanup

1) Delete leftovers from legacy codegen, including the "tgt" naming.
2) Do not second-guess the write barrier form when selecting a helper,
   always return "known" WBF from "gcIsWriteBarrierCandidate" instead.

* Delete GTF_IND_TGT_ANYWHERE

And create GTF_IND_TGT_HEAP instead, its logical opposite.

For reference, the situation before this change was as follows:

// Type of the ("simplified"/"base") address:
//
//  - TYP_I_IMPL: checked write barrier
//  - TYP_REF:    unchecked write barrier
//  - TYP_BYREF:
//    1) No flags:     unchecked write barrier
//    2) TGT_ANYWHERE: checked write barrier

The TYP_BYREF case will now look like this:
//
//    1) No flags: checked write barrier
//    2) TGT_HEAP: unchecked write barrier

The main benefit is that we get the default state (no flags) to
be the conservatively correct one.

* Add a quirk to avoid diffs on x86
  • Loading branch information
SingleAccretion committed Apr 22, 2022
1 parent 12c06c0 commit 10012c4
Show file tree
Hide file tree
Showing 25 changed files with 272 additions and 356 deletions.
2 changes: 1 addition & 1 deletion src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ class CodeGen final : public CodeGenInterface
protected:
void genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTarget = REG_NA);

void genGCWriteBarrier(GenTree* tgt, GCInfo::WriteBarrierForm wbf);
void genGCWriteBarrier(GenTreeStoreInd* store, GCInfo::WriteBarrierForm wbf);

BasicBlock* genCreateTempLabel();

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/codegenarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1329,7 +1329,7 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)

assert(!varTypeIsFloating(type) || (type == data->TypeGet()));

GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree, data);
GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree);
if (writeBarrierForm != GCInfo::WBF_NoBarrier)
{
// data and addr must be in registers.
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3830,7 +3830,7 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
GenTree* data = tree->Data();
GenTree* addr = tree->Addr();

GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree, data);
GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree);
if (writeBarrierForm != GCInfo::WBF_NoBarrier)
{
// data and addr must be in registers.
Expand Down Expand Up @@ -4956,7 +4956,7 @@ void CodeGen::genStoreIndTypeSIMD12(GenTree* treeNode)

#ifdef DEBUG
// Should not require a write barrier
GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(treeNode, data);
GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(treeNode->AsStoreInd());
assert(writeBarrierForm == GCInfo::WBF_NoBarrier);
#endif

Expand Down
148 changes: 55 additions & 93 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2698,19 +2698,18 @@ bool CodeGenInterface::genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf
// determining what the required write barrier form is, if possible.
//
// Arguments:
// tgt - target tree of write (e.g., GT_STOREIND)
// assignVal - tree with value to write
// store - the GT_STOREIND node
//
// Return Value:
// true if an optimized write barrier helper should be used, false otherwise.
// Note: only x86 implements register-specific source optimized write
// barriers currently.
//
bool CodeGenInterface::genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assignVal)
bool CodeGenInterface::genUseOptimizedWriteBarriers(GenTreeStoreInd* store)
{
#if defined(TARGET_X86) && NOGC_WRITE_BARRIERS
#ifdef DEBUG
GCInfo::WriteBarrierForm wbf = compiler->codeGen->gcInfo.gcIsWriteBarrierCandidate(tgt, assignVal);
GCInfo::WriteBarrierForm wbf = compiler->codeGen->gcInfo.gcIsWriteBarrierCandidate(store);
return (wbf != GCInfo::WBF_NoBarrier_CheckNotHeapInDebug); // This one is always a call to a C++ method.
#else
return true;
Expand All @@ -2721,158 +2720,121 @@ bool CodeGenInterface::genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assig
}

//----------------------------------------------------------------------
// genWriteBarrierHelperForWriteBarrierForm: Given a write node requiring a write
// barrier, and the write barrier form required, determine the helper to call.
// genWriteBarrierHelperForWriteBarrierForm: Given a write barrier form
// return the corresponding helper.
//
// Arguments:
// tgt - target tree of write (e.g., GT_STOREIND)
// wbf - already computed write barrier form to use
// wbf - the write barrier form
//
// Return Value:
// Write barrier helper to use.
//
// Note: do not call this function to get an optimized write barrier helper (e.g.,
// for x86).
//
CorInfoHelpFunc CodeGenInterface::genWriteBarrierHelperForWriteBarrierForm(GenTree* tgt, GCInfo::WriteBarrierForm wbf)
CorInfoHelpFunc CodeGenInterface::genWriteBarrierHelperForWriteBarrierForm(GCInfo::WriteBarrierForm wbf)
{
noway_assert(tgt->gtOper == GT_STOREIND);
switch (wbf)
{
case GCInfo::WBF_BarrierChecked:
return CORINFO_HELP_CHECKED_ASSIGN_REF;

CorInfoHelpFunc helper = CORINFO_HELP_ASSIGN_REF;
case GCInfo::WBF_BarrierUnchecked:
return CORINFO_HELP_ASSIGN_REF;

#ifdef DEBUG
if (wbf == GCInfo::WBF_NoBarrier_CheckNotHeapInDebug)
{
helper = CORINFO_HELP_ASSIGN_REF_ENSURE_NONHEAP;
}
else
#endif
if (tgt->gtOper != GT_CLS_VAR)
{
if (wbf != GCInfo::WBF_BarrierUnchecked) // This overrides the tests below.
{
if (tgt->gtFlags & GTF_IND_TGTANYWHERE)
{
helper = CORINFO_HELP_CHECKED_ASSIGN_REF;
}
else if (tgt->AsOp()->gtOp1->TypeGet() == TYP_I_IMPL)
{
helper = CORINFO_HELP_CHECKED_ASSIGN_REF;
}
}
}
assert(((helper == CORINFO_HELP_ASSIGN_REF_ENSURE_NONHEAP) && (wbf == GCInfo::WBF_NoBarrier_CheckNotHeapInDebug)) ||
((helper == CORINFO_HELP_CHECKED_ASSIGN_REF) &&
(wbf == GCInfo::WBF_BarrierChecked || wbf == GCInfo::WBF_BarrierUnknown)) ||
((helper == CORINFO_HELP_ASSIGN_REF) &&
(wbf == GCInfo::WBF_BarrierUnchecked || wbf == GCInfo::WBF_BarrierUnknown)));
case GCInfo::WBF_NoBarrier_CheckNotHeapInDebug:
return CORINFO_HELP_ASSIGN_REF_ENSURE_NONHEAP;
#endif // DEBUG

return helper;
default:
unreached();
}
}

//----------------------------------------------------------------------
// genGCWriteBarrier: Generate a write barrier for a node.
//
// Arguments:
// tgt - target tree of write (e.g., GT_STOREIND)
// wbf - already computed write barrier form to use
// store - the GT_STOREIND node
// wbf - already computed write barrier form to use
//
void CodeGen::genGCWriteBarrier(GenTree* tgt, GCInfo::WriteBarrierForm wbf)
void CodeGen::genGCWriteBarrier(GenTreeStoreInd* store, GCInfo::WriteBarrierForm wbf)
{
CorInfoHelpFunc helper = genWriteBarrierHelperForWriteBarrierForm(tgt, wbf);
CorInfoHelpFunc helper = genWriteBarrierHelperForWriteBarrierForm(wbf);

#ifdef FEATURE_COUNT_GC_WRITE_BARRIERS
// We classify the "tgt" trees as follows:
// If "tgt" is of the form (where [ x ] indicates an optional x, and { x1, ..., xn } means "one of the x_i forms"):
// IND [-> ADDR -> IND] -> { GT_LCL_VAR, ADD({GT_LCL_VAR}, X), ADD(X, (GT_LCL_VAR)) }
// then let "v" be the GT_LCL_VAR.
// * If "v" is the return buffer argument, classify as CWBKind_RetBuf.
// * If "v" is another by-ref argument, classify as CWBKind_ByRefArg.
// * Otherwise, classify as CWBKind_OtherByRefLocal.
// If "tgt" is of the form IND -> ADDR -> GT_LCL_VAR, clasify as CWBKind_AddrOfLocal.
// Otherwise, classify as CWBKind_Unclassified.

CheckedWriteBarrierKinds wbKind = CWBKind_Unclassified;
if (tgt->gtOper == GT_IND)
// Under FEATURE_COUNT_GC_WRITE_BARRIERS, we will add an extra argument to the
// checked write barrier call denoting the kind of address being written to.
//
if (helper == CORINFO_HELP_CHECKED_ASSIGN_REF)
{
GenTree* lcl = NULL;
CheckedWriteBarrierKinds wbKind = CWBKind_Unclassified;
GenTree* tgtAddr = store->Addr();

GenTree* indArg = tgt->AsOp()->gtOp1;
if (indArg->gtOper == GT_ADDR && indArg->AsOp()->gtOp1->gtOper == GT_IND)
{
indArg = indArg->AsOp()->gtOp1->AsOp()->gtOp1;
}
if (indArg->gtOper == GT_LCL_VAR)
while (tgtAddr->OperIs(GT_ADD, GT_LEA))
{
lcl = indArg;
}
else if (indArg->gtOper == GT_ADD)
{
if (indArg->AsOp()->gtOp1->gtOper == GT_LCL_VAR)
if (tgtAddr->OperIs(GT_LEA) && tgtAddr->AsAddrMode()->HasBase())
{
tgtAddr = tgtAddr->AsAddrMode()->Base();
}
else if (tgtAddr->OperIs(GT_ADD) && tgtAddr->AsOp()->gtGetOp2()->IsCnsIntOrI())
{
lcl = indArg->AsOp()->gtOp1;
tgtAddr = tgtAddr->AsOp()->gtGetOp1();
}
else if (indArg->AsOp()->gtOp2->gtOper == GT_LCL_VAR)
else
{
lcl = indArg->AsOp()->gtOp2;
break;
}
}
if (lcl != NULL)

if (tgtAddr->OperIs(GT_LCL_VAR))
{
wbKind = CWBKind_OtherByRefLocal; // Unclassified local variable.
unsigned lclNum = lcl->AsLclVar()->GetLclNum();
unsigned lclNum = tgtAddr->AsLclVar()->GetLclNum();
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
if (lclNum == compiler->info.compRetBuffArg)
{
wbKind = CWBKind_RetBuf; // Ret buff. Can happen if the struct exceeds the size limit.
wbKind = CWBKind_RetBuf
}
else
else if (varDsc->TypeGet() == TYP_BYREF)
{
const LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
if (varDsc->lvIsParam && varDsc->lvType == TYP_BYREF)
{
wbKind = CWBKind_ByRefArg; // Out (or in/out) arg
}
wbKind = varDsc->lvIsParam ? CWBKind_ByRefArg : CWBKind_OtherByRefLocal;
}
}
else
else if (tgtAddr->OperIsLocalAddr())
{
// We should have eliminated the barrier for this case.
assert(!(indArg->gtOper == GT_ADDR && indArg->AsOp()->gtOp1->gtOper == GT_LCL_VAR));
// Ideally, we should have eliminated the barrier for this case.
wbKind = CWBKind_AddrOfLocal;
}
}

if (helper == CORINFO_HELP_CHECKED_ASSIGN_REF)
{
#if 0
#ifdef DEBUG
// Enable this to sample the unclassified trees.
static int unclassifiedBarrierSite = 0;
if (wbKind == CWBKind_Unclassified)
{
unclassifiedBarrierSite++;
printf("unclassifiedBarrierSite = %d:\n", unclassifiedBarrierSite); compiler->gtDispTree(tgt); printf(""); printf("\n");
printf("unclassifiedBarrierSite = %d:\n", unclassifiedBarrierSite);
compiler->gtDispTree(store);
printf(""); // Flush.
printf("\n");
}
#endif // DEBUG
#endif // 0

AddStackLevel(4);
inst_IV(INS_push, wbKind);
genEmitHelperCall(helper,
4, // argSize
EA_PTRSIZE); // retSize
SubtractStackLevel(4);
return;
}
else
{
genEmitHelperCall(helper,
0, // argSize
EA_PTRSIZE); // retSize
}
#endif // FEATURE_COUNT_GC_WRITE_BARRIERS

#else // !FEATURE_COUNT_GC_WRITE_BARRIERS
genEmitHelperCall(helper,
0, // argSize
EA_PTRSIZE); // retSize
#endif // !FEATURE_COUNT_GC_WRITE_BARRIERS
}

/*
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/codegeninterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ class CodeGenInterface

public:
bool genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf);
bool genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assignVal);
CorInfoHelpFunc genWriteBarrierHelperForWriteBarrierForm(GenTree* tgt, GCInfo::WriteBarrierForm wbf);
bool genUseOptimizedWriteBarriers(GenTreeStoreInd* store);
CorInfoHelpFunc genWriteBarrierHelperForWriteBarrierForm(GCInfo::WriteBarrierForm wbf);

// The following property indicates whether the current method sets up
// an explicit stack frame or not.
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/codegenloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3579,7 +3579,7 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
GenTree* data = tree->Data();
GenTree* addr = tree->Addr();

GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree, data);
GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree);
if (writeBarrierForm != GCInfo::WBF_NoBarrier)
{
// data and addr must be in registers.
Expand Down
8 changes: 3 additions & 5 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5016,7 +5016,7 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)

assert(!varTypeIsFloating(targetType) || (genTypeSize(targetType) == genTypeSize(data->TypeGet())));

GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree, data);
GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree);
if (writeBarrierForm != GCInfo::WBF_NoBarrier)
{
// data and addr must be in registers.
Expand Down Expand Up @@ -5284,10 +5284,8 @@ bool CodeGen::genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarri
tgtAnywhere = 1;
}

// We might want to call a modified version of genGCWriteBarrier() to get the benefit of
// the FEATURE_COUNT_GC_WRITE_BARRIERS code there, but that code doesn't look like it works
// with rationalized RyuJIT IR. So, for now, just emit the helper call directly here.

// Here we might want to call a modified version of genGCWriteBarrier() to get the benefit
// of the FEATURE_COUNT_GC_WRITE_BARRIERS code. For now, just emit the helper call directly.
genEmitHelperCall(regToHelper[tgtAnywhere][reg],
0, // argSize
EA_PTRSIZE); // retSize
Expand Down
16 changes: 12 additions & 4 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9215,6 +9215,10 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
{
chars += printf("[FLD_VOLATILE]");
}
if (tree->gtFlags & GTF_FLD_TGT_HEAP)
{
chars += printf("[FLD_TGT_HEAP]");
}
break;

case GT_INDEX:
Expand All @@ -9238,14 +9242,14 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
{
chars += printf("[IND_VOLATILE]");
}
if (tree->gtFlags & GTF_IND_TGTANYWHERE)
{
chars += printf("[IND_TGTANYWHERE]");
}
if (tree->gtFlags & GTF_IND_TGT_NOT_HEAP)
{
chars += printf("[IND_TGT_NOT_HEAP]");
}
if (tree->gtFlags & GTF_IND_TGT_HEAP)
{
chars += printf("[IND_TGT_HEAP]");
}
if (tree->gtFlags & GTF_IND_TLS_REF)
{
chars += printf("[IND_TLS_REF]");
Expand Down Expand Up @@ -9274,6 +9278,10 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
{
chars += printf("[CLS_VAR_ASG_LHS]");
}
if (tree->gtFlags & GTF_CLS_VAR_TGT_HEAP)
{
chars += printf("[CLS_VAR_TGT_HEAP]");
}
break;

case GT_MUL:
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3033,6 +3033,8 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
// Currently we expect all indirections with constant addresses to be nonfaulting.
expectedFlags |= GTF_IND_NONFAULTING;
}

assert(((tree->gtFlags & GTF_IND_TGT_NOT_HEAP) == 0) || ((tree->gtFlags & GTF_IND_TGT_HEAP) == 0));
break;

case GT_CALL:
Expand Down
Loading

0 comments on commit 10012c4

Please sign in to comment.