Skip to content

Commit 878f932

Browse files
committed
Always unroll GT_STORE_BLK for blocks with gc refs on heap
1 parent 3e55167 commit 878f932

File tree

7 files changed

+85
-8
lines changed

7 files changed

+85
-8
lines changed

src/coreclr/jit/gentree.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7337,12 +7337,15 @@ struct GenTreeBlk : public GenTreeIndir
73377337
bool gtBlkOpGcUnsafe;
73387338
#endif
73397339

7340-
#ifdef TARGET_XARCH
73417340
bool IsOnHeapAndContainsReferences()
73427341
{
73437342
return (m_layout != nullptr) && m_layout->HasGCPtr() && !Addr()->OperIs(GT_LCL_ADDR);
73447343
}
7345-
#endif
7344+
7345+
bool ContainsReferences()
7346+
{
7347+
return (m_layout != nullptr) && m_layout->HasGCPtr();
7348+
}
73467349

73477350
GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, ClassLayout* layout)
73487351
: GenTreeIndir(oper, type, addr, nullptr)
@@ -7353,6 +7356,10 @@ struct GenTreeBlk : public GenTreeIndir
73537356
GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, GenTree* data, ClassLayout* layout)
73547357
: GenTreeIndir(oper, type, addr, data)
73557358
{
7359+
if (data->IsIntegralConst(0))
7360+
{
7361+
data->gtFlags |= GTF_DONT_CSE;
7362+
}
73567363
Initialize(layout);
73577364
}
73587365

src/coreclr/jit/lower.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8867,6 +8867,13 @@ void Lowering::LowerBlockStoreCommon(GenTreeBlk* blkNode)
88678867
{
88688868
assert(blkNode->OperIs(GT_STORE_BLK, GT_STORE_DYN_BLK));
88698869

8870+
if (blkNode->OperIs(GT_STORE_DYN_BLK))
8871+
{
8872+
// CI Test: make sure we don't use GT_STORE_DYN_BLK
8873+
// for blocks with GC references.
8874+
assert(!blkNode->ContainsReferences());
8875+
}
8876+
88708877
// Lose the type information stored in the source - we no longer need it.
88718878
if (blkNode->Data()->OperIs(GT_BLK))
88728879
{

src/coreclr/jit/lowerarmarch.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,9 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
570570
src = src->AsUnOp()->gtGetOp1();
571571
}
572572

573-
if (!blkNode->OperIs(GT_STORE_DYN_BLK) && (size <= comp->getUnrollThreshold(Compiler::UnrollKind::Memset)) &&
574-
src->OperIs(GT_CNS_INT))
573+
const bool shouldBeUnrolled = blkNode->IsOnHeapAndContainsReferences() ||
574+
(size <= comp->getUnrollThreshold(Compiler::UnrollKind::Memset));
575+
if (!blkNode->OperIs(GT_STORE_DYN_BLK) && shouldBeUnrolled && src->OperIs(GT_CNS_INT))
575576
{
576577
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
577578

@@ -584,6 +585,9 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
584585

585586
ssize_t fill = src->AsIntCon()->IconValue() & 0xFF;
586587

588+
// CI Test: it's a bad idea to use non-zero here
589+
assert((fill == 0) || !blkNode->ContainsReferences());
590+
587591
if (fill == 0)
588592
{
589593
#ifdef TARGET_ARM64
@@ -610,6 +614,8 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
610614
}
611615
else
612616
{
617+
// CI Test
618+
assert(!blkNode->ContainsReferences());
613619
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
614620
}
615621
}

src/coreclr/jit/lowerloongarch64.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,9 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
296296
src = src->AsUnOp()->gtGetOp1();
297297
}
298298

299-
if (!blkNode->OperIs(GT_STORE_DYN_BLK) && (size <= comp->getUnrollThreshold(Compiler::UnrollKind::Memset)) &&
300-
src->OperIs(GT_CNS_INT))
299+
const bool shouldBeUnrolled = blkNode->IsOnHeapAndContainsReferences() ||
300+
(size <= comp->getUnrollThreshold(Compiler::UnrollKind::Memset));
301+
if (!blkNode->OperIs(GT_STORE_DYN_BLK) && shouldBeUnrolled && src->OperIs(GT_CNS_INT))
301302
{
302303
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
303304

src/coreclr/jit/lowerxarch.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,15 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
338338
src = src->AsUnOp()->gtGetOp1();
339339
}
340340

341-
if (!blkNode->OperIs(GT_STORE_DYN_BLK) && (size <= comp->getUnrollThreshold(Compiler::UnrollKind::Memset)))
341+
const bool shouldBeUnrolled = blkNode->IsOnHeapAndContainsReferences() ||
342+
(size <= comp->getUnrollThreshold(Compiler::UnrollKind::Memset));
343+
if (!blkNode->OperIs(GT_STORE_DYN_BLK) && shouldBeUnrolled)
342344
{
343345
if (!src->OperIs(GT_CNS_INT))
344346
{
347+
// CI Test: Hopefully, we won't see anything but GT_CNS_INT(0) here for blocks with GC pointers.
348+
assert(!blkNode->ContainsReferences());
349+
345350
// TODO-CQ: We could unroll even when the initialization value is not a constant
346351
// by inserting a MUL init, 0x01010101 instruction. We need to determine if the
347352
// extra latency that MUL introduces isn't worse that rep stosb. Likely not.
@@ -357,9 +362,15 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
357362
// the largest width store of the desired inline expansion.
358363

359364
ssize_t fill = src->AsIntCon()->IconValue() & 0xFF;
365+
if (blkNode->ContainsReferences())
366+
{
367+
// CI Test: it's a bad idea to use non-zero here
368+
assert(fill == 0);
369+
}
360370

361371
const bool canUseSimd = !blkNode->IsOnHeapAndContainsReferences() && comp->IsBaselineSimdIsaSupported();
362-
if (size > comp->getUnrollThreshold(Compiler::UnrollKind::Memset, canUseSimd))
372+
if (!blkNode->IsOnHeapAndContainsReferences() &&
373+
(size > comp->getUnrollThreshold(Compiler::UnrollKind::Memset, canUseSimd)))
363374
{
364375
// It turns out we can't use SIMD so the default threshold is too big
365376
goto TOO_BIG_TO_UNROLL;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
using System;
5+
using System.Runtime.CompilerServices;
6+
using Xunit;
7+
8+
public class StructWithGC_Zeroing
9+
{
10+
[Fact]
11+
public static void StructZeroingShouldNotUseMemset()
12+
{
13+
var largeStructWithGc = new LargeStructWithGC();
14+
Test(ref largeStructWithGc);
15+
}
16+
17+
[MethodImpl(MethodImplOptions.NoInlining)]
18+
private static void Test(ref LargeStructWithGC s)
19+
{
20+
// X64-NOT: CORINFO_HELP_MEMSET
21+
// ARM64-NOT: CORINFO_HELP_MEMSET
22+
s = default;
23+
}
24+
25+
struct LargeStructWithGC
26+
{
27+
public byte x;
28+
public string a;
29+
public long b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, r, s, t, u, v, w, z;
30+
}
31+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<RequiresProcessIsolation>true</RequiresProcessIsolation>
4+
</PropertyGroup>
5+
<PropertyGroup>
6+
<DebugType>None</DebugType>
7+
<Optimize>True</Optimize>
8+
</PropertyGroup>
9+
<ItemGroup>
10+
<Compile Include="$(MSBuildProjectName).cs">
11+
<HasDisasmCheck>true</HasDisasmCheck>
12+
</Compile>
13+
</ItemGroup>
14+
</Project>

0 commit comments

Comments
 (0)