Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ad90f7a

Browse files
author
Sergey Andreenko
authored
Fix dummy OBJ/BLK/IND nodes. (dotnet#39824)
* Add a repro. * Add a helper function `fgTryRemoveNonLocal`. * Fix the issue. * extract `gtChangeOperToNullCheck`. * update comments.
1 parent 44add55 commit ad90f7a

File tree

7 files changed

+124
-29
lines changed

7 files changed

+124
-29
lines changed

src/coreclr/src/jit/compiler.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9285,3 +9285,19 @@ bool Compiler::lvaIsOSRLocal(unsigned varNum)
92859285

92869286
return false;
92879287
}
9288+
9289+
//------------------------------------------------------------------------------
9290+
// gtChangeOperToNullCheck: helper to change tree oper to a NULLCHECK.
9291+
//
9292+
// Arguments:
9293+
// tree - the node to change;
9294+
// basicBlock - basic block of the node.
9295+
//
9296+
void Compiler::gtChangeOperToNullCheck(GenTree* tree, BasicBlock* block)
9297+
{
9298+
assert(tree->OperIs(GT_FIELD, GT_IND, GT_OBJ, GT_BLK, GT_DYN_BLK));
9299+
tree->ChangeOper(GT_NULLCHECK);
9300+
tree->ChangeType(TYP_BYTE);
9301+
block->bbFlags |= BBF_HAS_NULLCHECK;
9302+
optMethodFlags |= OMF_HAS_NULLCHECK;
9303+
}

src/coreclr/src/jit/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2701,6 +2701,8 @@ class Compiler
27012701

27022702
GenTree* gtNewNullCheck(GenTree* addr, BasicBlock* basicBlock);
27032703

2704+
void gtChangeOperToNullCheck(GenTree* tree, BasicBlock* block);
2705+
27042706
GenTreeArgList* gtNewArgList(GenTree* op);
27052707
GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2);
27062708
GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2, GenTree* op3);
@@ -4633,6 +4635,8 @@ class Compiler
46334635

46344636
void fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALARG_TP volatileVars);
46354637

4638+
bool fgTryRemoveNonLocal(GenTree* node, LIR::Range* blockRange);
4639+
46364640
bool fgRemoveDeadStore(GenTree** pTree,
46374641
LclVarDsc* varDsc,
46384642
VARSET_VALARG_TP life,

src/coreclr/src/jit/gentree.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13782,16 +13782,13 @@ GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions
1378213782
// For struct types read the first byte of the
1378313783
// source struct; there's no need to read the
1378413784
// entire thing, and no place to put it.
13785-
assert(copySrc->gtOper == GT_OBJ || copySrc->gtOper == GT_IND || copySrc->gtOper == GT_FIELD);
13785+
assert(copySrc->OperIs(GT_OBJ, GT_IND, GT_FIELD));
1378613786
copyStmt->SetRootNode(copySrc);
1378713787

1378813788
if (options == BR_REMOVE_AND_NARROW || options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE)
1378913789
{
1379013790
JITDUMP(" to read first byte of struct via modified [%06u]\n", dspTreeID(copySrc));
13791-
copySrc->ChangeOper(GT_NULLCHECK);
13792-
copySrc->gtType = TYP_BYTE;
13793-
compCurBB->bbFlags |= BBF_HAS_NULLCHECK;
13794-
optMethodFlags |= OMF_HAS_NULLCHECK;
13791+
gtChangeOperToNullCheck(copySrc, compCurBB);
1379513792
}
1379613793
else
1379713794
{
@@ -15970,6 +15967,11 @@ void Compiler::gtExtractSideEffList(GenTree* expr,
1597015967
if (m_compiler->gtNodeHasSideEffects(node, m_flags))
1597115968
{
1597215969
m_sideEffects.Push(node);
15970+
if (node->OperIsBlk() && !node->OperIsStoreBlk())
15971+
{
15972+
JITDUMP("Replace an unused OBJ/BLK node [%06d] with a NULLCHECK\n", dspTreeID(node));
15973+
m_compiler->gtChangeOperToNullCheck(node, m_compiler->compCurBB);
15974+
}
1597315975
return Compiler::WALK_SKIP_SUBTREES;
1597415976
}
1597515977

src/coreclr/src/jit/importer.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13247,10 +13247,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
1324713247
// via an underlying address, just null check the address.
1324813248
if (op1->OperIs(GT_FIELD, GT_IND, GT_OBJ))
1324913249
{
13250-
op1->ChangeOper(GT_NULLCHECK);
13251-
block->bbFlags |= BBF_HAS_NULLCHECK;
13252-
optMethodFlags |= OMF_HAS_NULLCHECK;
13253-
op1->gtType = TYP_BYTE;
13250+
gtChangeOperToNullCheck(op1, block);
1325413251
}
1325513252
else
1325613253
{

src/coreclr/src/jit/liveness.cpp

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2109,40 +2109,77 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR
21092109
break;
21102110

21112111
case GT_NOP:
2112+
{
21122113
// NOTE: we need to keep some NOPs around because they are referenced by calls. See the dead store
21132114
// removal code above (case GT_STORE_LCL_VAR) for more explanation.
21142115
if ((node->gtFlags & GTF_ORDER_SIDEEFF) != 0)
21152116
{
21162117
break;
21172118
}
2118-
__fallthrough;
2119+
fgTryRemoveNonLocal(node, &blockRange);
2120+
}
2121+
break;
21192122

2120-
default:
2121-
assert(!node->OperIsLocal());
2122-
if (!node->IsValue() || node->IsUnusedValue())
2123+
case GT_BLK:
2124+
case GT_OBJ:
2125+
case GT_DYN_BLK:
2126+
{
2127+
bool removed = fgTryRemoveNonLocal(node, &blockRange);
2128+
if (!removed && node->IsUnusedValue())
21232129
{
2124-
// We are only interested in avoiding the removal of nodes with direct side-effects
2125-
// (as opposed to side effects of their children).
2126-
// This default case should never include calls or assignments.
2127-
assert(!node->OperRequiresAsgFlag() && !node->OperIs(GT_CALL));
2128-
if (!node->gtSetFlags() && !node->OperMayThrow(this))
2129-
{
2130-
JITDUMP("Removing dead node:\n");
2131-
DISPNODE(node);
2132-
2133-
node->VisitOperands([](GenTree* operand) -> GenTree::VisitResult {
2134-
operand->SetUnusedValue();
2135-
return GenTree::VisitResult::Continue;
2136-
});
2137-
2138-
blockRange.Remove(node);
2139-
}
2130+
// IR doesn't expect dummy uses of `GT_OBJ/BLK/DYN_BLK`.
2131+
JITDUMP("Replace an unused OBJ/BLK node [%06d] with a NULLCHECK\n", dspTreeID(node));
2132+
gtChangeOperToNullCheck(node, block);
21402133
}
2134+
}
2135+
break;
2136+
2137+
default:
2138+
fgTryRemoveNonLocal(node, &blockRange);
21412139
break;
21422140
}
21432141
}
21442142
}
21452143

2144+
//---------------------------------------------------------------------
2145+
// fgTryRemoveNonLocal - try to remove a node if it is unused and has no direct
2146+
// side effects.
2147+
//
2148+
// Arguments
2149+
// node - the non-local node to try;
2150+
// blockRange - the block range that contains the node.
2151+
//
2152+
// Return value:
2153+
// None
2154+
//
2155+
// Notes: local nodes are processed independently and are not expected in this function.
2156+
//
2157+
bool Compiler::fgTryRemoveNonLocal(GenTree* node, LIR::Range* blockRange)
2158+
{
2159+
assert(!node->OperIsLocal());
2160+
if (!node->IsValue() || node->IsUnusedValue())
2161+
{
2162+
// We are only interested in avoiding the removal of nodes with direct side effects
2163+
// (as opposed to side effects of their children).
2164+
// This default case should never include calls or assignments.
2165+
assert(!node->OperRequiresAsgFlag() && !node->OperIs(GT_CALL));
2166+
if (!node->gtSetFlags() && !node->OperMayThrow(this))
2167+
{
2168+
JITDUMP("Removing dead node:\n");
2169+
DISPNODE(node);
2170+
2171+
node->VisitOperands([](GenTree* operand) -> GenTree::VisitResult {
2172+
operand->SetUnusedValue();
2173+
return GenTree::VisitResult::Continue;
2174+
});
2175+
2176+
blockRange->Remove(node);
2177+
return true;
2178+
}
2179+
}
2180+
return false;
2181+
}
2182+
21462183
// fgRemoveDeadStore - remove a store to a local which has no exposed uses.
21472184
//
21482185
// pTree - GenTree** to local, including store-form local or local addr (post-rationalize)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
// The test was deleting the hardware intrinsic leaving unconsumed GT_OBJ on top of the stack
5+
// that was leading to an assert failure.
6+
7+
using System.Runtime.Intrinsics;
8+
using System.Runtime.Intrinsics.X86;
9+
using System;
10+
11+
class Runtime_39403
12+
{
13+
public static int Main()
14+
{
15+
if (Sse41.IsSupported)
16+
{
17+
Vector128<int> left = Vector128.Create(1);
18+
Vector128<int> right = Vector128.Create(2);
19+
ref var rightRef = ref right;
20+
Vector128<int> mask = Vector128.Create(3);
21+
Sse41.BlendVariable(left, rightRef, mask);
22+
}
23+
return 100;
24+
}
25+
}
26+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
</PropertyGroup>
5+
<PropertyGroup>
6+
<DebugType />
7+
<Optimize>True</Optimize>
8+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<Compile Include="$(MSBuildProjectName).cs" />
12+
</ItemGroup>
13+
</Project>

0 commit comments

Comments
 (0)