Skip to content

Commit

Permalink
Fix some issues with null check handling (#84523)
Browse files Browse the repository at this point in the history
* Fix some issues with null check handling

Code assumed that, given a non-null address, it
must have no side effects. This is not correct.

Fix by centralizing the logic for no-op null check
removal in morph instead of spreading it over to
places that create null checks.

This is both a CQ and TP win.

* Add a test

* Fix early prop bug
  • Loading branch information
SingleAccretion committed Apr 21, 2023
1 parent 966d36a commit 2032a59
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 62 deletions.
3 changes: 1 addition & 2 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10312,8 +10312,7 @@ void Compiler::gtChangeOperToNullCheck(GenTree* tree, BasicBlock* block)
assert(tree->OperIs(GT_FIELD, GT_IND, GT_BLK));
tree->ChangeOper(GT_NULLCHECK);
tree->ChangeType(gtTypeForNullCheck(tree));
assert(fgAddrCouldBeNull(tree->gtGetOp1()));
tree->gtFlags |= GTF_EXCEPT;
tree->SetIndirExceptionFlags(this);
block->bbFlags |= BBF_HAS_NULLCHECK;
optMethodFlags |= OMF_HAS_NULLCHECK;
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/earlyprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ GenTree* Compiler::optEarlyPropRewriteTree(GenTree* tree, LocalNumberToNullCheck
return tree;
}

return nullptr;
return folded ? tree : nullptr;
}

//-------------------------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,9 @@ bool Compiler::fgAddrCouldBeNull(GenTree* addr)
case GT_COMMA:
return fgAddrCouldBeNull(addr->AsOp()->gtOp2);

case GT_CALL:
return !addr->IsHelperCall() || !s_helperCallProperties.NonNullReturn(addr->AsCall()->GetHelperNum());

case GT_ADD:
if (addr->AsOp()->gtOp1->gtOper == GT_CNS_INT)
{
Expand Down
32 changes: 16 additions & 16 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2391,6 +2391,18 @@ bool GenTreeCall::IsHelperCall(Compiler* compiler, unsigned helper) const
return IsHelperCall(compiler->eeFindHelper(helper));
}

//-------------------------------------------------------------------------
// GetHelperNum: Get the helper identifier for this call.
//
// Return Value:
// CORINFO_HELP_UNDEF if this call is not a helper call, appropriate
// CorInfoHelpFunc otherwise.
//
CorInfoHelpFunc GenTreeCall::GetHelperNum() const
{
return IsHelperCall() ? Compiler::eeGetHelperNum(gtCallMethHnd) : CORINFO_HELP_UNDEF;
}

//--------------------------------------------------------------------------
// Equals: Check if 2 CALL nodes are equal.
//
Expand Down Expand Up @@ -16556,23 +16568,11 @@ void Compiler::gtExtractSideEffList(GenTree* expr,
{
if (node->OperIsBlk() && !node->OperIsStoreBlk())
{
// Check for a guaranteed non-faulting IND, and create a NOP node instead of a NULLCHECK in that
// case.
if (m_compiler->fgAddrCouldBeNull(node->AsBlk()->Addr()))
{
Append(node);
JITDUMP("Replace an unused OBJ/BLK node [%06d] with a NULLCHECK\n", dspTreeID(node));
m_compiler->gtChangeOperToNullCheck(node, m_compiler->compCurBB);
}
else
{
JITDUMP("Dropping non-faulting OBJ/BLK node [%06d]\n", dspTreeID(node));
}
}
else
{
Append(node);
JITDUMP("Replace an unused BLK node [%06d] with a NULLCHECK\n", dspTreeID(node));
m_compiler->gtChangeOperToNullCheck(node, m_compiler->compCurBB);
}

Append(node);
return Compiler::WALK_SKIP_SUBTREES;
}

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -5561,6 +5561,8 @@ struct GenTreeCall final : public GenTree

bool IsHelperCall(Compiler* compiler, unsigned helper) const;

CorInfoHelpFunc GetHelperNum() const;

bool AreArgsComplete() const;

CorInfoCallConvExtension GetUnmanagedCallConv() const
Expand Down
10 changes: 1 addition & 9 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8336,15 +8336,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// via an underlying address, just null check the address.
if (op1->OperIs(GT_FIELD, GT_IND, GT_BLK))
{
GenTree* addr = op1->gtGetOp1();
if ((addr != nullptr) && fgAddrCouldBeNull(addr))
{
gtChangeOperToNullCheck(op1, block);
}
else
{
op1 = gtNewNothingNode();
}
gtChangeOperToNullCheck(op1, block);
}
else
{
Expand Down
20 changes: 4 additions & 16 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7957,16 +7957,13 @@ void Lowering::LowerIndir(GenTreeIndir* ind)
#if defined(TARGET_ARM64)
// Verify containment safety before creating an LEA that must be contained.
//
const bool isContainable = (ind->Addr() != nullptr) && IsInvariantInRange(ind->Addr(), ind);
const bool isContainable = IsInvariantInRange(ind->Addr(), ind);
#else
const bool isContainable = true;
#endif

if (!ind->OperIs(GT_NOP))
{
TryCreateAddrMode(ind->Addr(), isContainable, ind);
ContainCheckIndir(ind);
}
TryCreateAddrMode(ind->Addr(), isContainable, ind);
ContainCheckIndir(ind);

#ifdef TARGET_XARCH
if (ind->OperIs(GT_NULLCHECK) || ind->IsUnusedValue())
Expand Down Expand Up @@ -8014,23 +8011,14 @@ void Lowering::TransformUnusedIndirection(GenTreeIndir* ind, Compiler* comp, Bas
//
assert(ind->OperIs(GT_NULLCHECK, GT_IND, GT_BLK));

GenTree* const addr = ind->Addr();
if (!comp->fgAddrCouldBeNull(addr))
{
addr->SetUnusedValue();
ind->gtBashToNOP();
JITDUMP("bash an unused indir [%06u] to NOP.\n", comp->dspTreeID(ind));
return;
}

ind->ChangeType(comp->gtTypeForNullCheck(ind));

#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
bool useNullCheck = true;
#elif defined(TARGET_ARM)
bool useNullCheck = false;
#else // TARGET_XARCH
bool useNullCheck = !addr->isContained();
bool useNullCheck = !ind->Addr()->isContained();
ind->ClearDontExtend();
#endif // !TARGET_XARCH

Expand Down
35 changes: 17 additions & 18 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9096,24 +9096,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA
break;
#endif

case GT_NULLCHECK:
{
op1 = tree->AsUnOp()->gtGetOp1();
if (op1->IsCall())
{
GenTreeCall* const call = op1->AsCall();
if (call->IsHelperCall() && s_helperCallProperties.NonNullReturn(eeGetHelperNum(call->gtCallMethHnd)))
{
JITDUMP("\nNULLCHECK on [%06u] will always succeed\n", dspTreeID(call));

// TODO: Can we also remove the call?
//
return fgMorphTree(call);
}
}
}
break;

default:
break;
}
Expand Down Expand Up @@ -9923,6 +9905,23 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA
}
break;

case GT_NULLCHECK:
if (opts.OptimizationEnabled() && !optValnumCSE_phase && !tree->OperMayThrow(this))
{
JITDUMP("\nNULLCHECK on [%06u] will always succeed\n", dspTreeID(op1));
if ((op1->gtFlags & GTF_SIDE_EFFECT) != 0)
{
tree = gtUnusedValNode(op1);
INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
}
else
{
tree->gtBashToNOP();
}
return tree;
}
break;

case GT_COLON:
if (fgGlobalMorph)
{
Expand Down
52 changes: 52 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_84522/Runtime_84522.il
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

.assembly extern System.Runtime { }
.assembly extern xunit.core { }
.assembly Runtime_84522 { }

.class sealed StructWithInt extends [System.Runtime]System.ValueType
{
.field public int32 Value
}

.class Runtime_84522 extends [System.Runtime]System.Object
{
.method public static int32 Main()
{
.custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = (
01 00 00 00
)
.entrypoint

.try
{
ldc.i4 0
newarr StructWithInt
call void .this::UnusedLdelem(valuetype StructWithInt[])
leave FAIL
}
catch [System.Runtime]System.IndexOutOfRangeException
{
pop
leave SUCCESS
}

FAIL:
ldc.i4 0
ret

SUCCESS:
ldc.i4 100
ret
}

.method private static void UnusedLdelem(valuetype StructWithInt[] arr) noinlining
{
ldarg arr
ldc.i4 0
ldelem valuetype StructWithInt
pop
ret
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).il" />
</ItemGroup>
</Project>

0 comments on commit 2032a59

Please sign in to comment.