Skip to content

Commit 1c653e9

Browse files
authored
JIT: update layout of BLK nodes during escape analysis (#115845)
If we retype a struct local we also need to retype any BLK operations on those locals. Fixes #115832.
1 parent ed055b1 commit 1c653e9

File tree

5 files changed

+111
-10
lines changed

5 files changed

+111
-10
lines changed

src/coreclr/jit/gentree.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7867,6 +7867,13 @@ struct GenTreeBlk : public GenTreeIndir
78677867
return m_layout;
78687868
}
78697869

7870+
void SetLayout(ClassLayout* newLayout)
7871+
{
7872+
assert(newLayout != nullptr);
7873+
assert(newLayout->GetSize() == m_layout->GetSize());
7874+
m_layout = newLayout;
7875+
}
7876+
78707877
// The data to be stored (null for GT_BLK)
78717878
GenTree*& Data()
78727879
{

src/coreclr/jit/objectalloc.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,7 @@ void ObjectAllocator::AnalyzeParentStack(ArrayStack<GenTree*>* parentStack, unsi
20202020
// tree - Possibly-stack-pointing tree
20212021
// parentStack - Parent stack of the possibly-stack-pointing tree
20222022
// newType - New type of the possibly-stack-pointing tree
2023+
// newLayout - Layout for a retyped local struct
20232024
// retypeFields - Inspiring local is a retyped local struct; retype fields.
20242025
//
20252026
// Notes:
@@ -2028,10 +2029,8 @@ void ObjectAllocator::AnalyzeParentStack(ArrayStack<GenTree*>* parentStack, unsi
20282029
// In addition to updating types this method may set GTF_IND_TGT_NOT_HEAP on ancestor
20292030
// indirections to help codegen with write barrier selection.
20302031
//
2031-
void ObjectAllocator::UpdateAncestorTypes(GenTree* tree,
2032-
ArrayStack<GenTree*>* parentStack,
2033-
var_types newType,
2034-
bool retypeFields)
2032+
void ObjectAllocator::UpdateAncestorTypes(
2033+
GenTree* tree, ArrayStack<GenTree*>* parentStack, var_types newType, ClassLayout* newLayout, bool retypeFields)
20352034
{
20362035
assert(newType == TYP_BYREF || newType == TYP_I_IMPL);
20372036
assert(parentStack != nullptr);
@@ -2200,10 +2199,17 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree,
22002199

22012200
// If we are storing to a GC struct field, we may need to retype the store
22022201
//
2203-
if (parent->OperIs(GT_STOREIND) && addr->OperIs(GT_FIELD_ADDR) && varTypeIsGC(parent->TypeGet()))
2202+
if (parent->OperIs(GT_STOREIND) && varTypeIsGC(parent->TypeGet()))
22042203
{
22052204
parent->ChangeType(newType);
22062205
}
2206+
2207+
// If we are storing a struct, we may need to change the layout
2208+
//
2209+
if (retypeFields && parent->OperIs(GT_STORE_BLK))
2210+
{
2211+
parent->AsBlk()->SetLayout(newLayout);
2212+
}
22072213
}
22082214
break;
22092215
}
@@ -2216,6 +2222,12 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree,
22162222
if (retypeFields && (varTypeIsGC(parent->TypeGet())))
22172223
{
22182224
parent->ChangeType(newType);
2225+
2226+
if (parent->OperIs(GT_BLK))
2227+
{
2228+
parent->AsBlk()->SetLayout(newLayout);
2229+
}
2230+
22192231
++parentIndex;
22202232
keepChecking = true;
22212233
retypeFields = false;
@@ -2290,6 +2302,7 @@ void ObjectAllocator::RewriteUses()
22902302

22912303
unsigned int newLclNum = BAD_VAR_NUM;
22922304
var_types newType = lclVarDsc->TypeGet();
2305+
ClassLayout* newLayout = nullptr;
22932306

22942307
if (m_allocator->m_HeapLocalToStackLocalMap.TryGetValue(lclNum, &newLclNum))
22952308
{
@@ -2303,16 +2316,16 @@ void ObjectAllocator::RewriteUses()
23032316
}
23042317
else if (newType == TYP_STRUCT)
23052318
{
2306-
ClassLayout* const layout = lclVarDsc->GetLayout();
2307-
newType = layout->HasGCPtr() ? TYP_BYREF : TYP_I_IMPL;
2308-
retypeFields = true;
2319+
newLayout = lclVarDsc->GetLayout();
2320+
newType = newLayout->HasGCPtr() ? TYP_BYREF : TYP_I_IMPL;
2321+
retypeFields = true;
23092322
}
23102323
else
23112324
{
23122325
tree->ChangeType(newType);
23132326
}
23142327

2315-
m_allocator->UpdateAncestorTypes(tree, &m_ancestors, newType, retypeFields);
2328+
m_allocator->UpdateAncestorTypes(tree, &m_ancestors, newType, newLayout, retypeFields);
23162329

23172330
return Compiler::fgWalkResult::WALK_CONTINUE;
23182331
}

src/coreclr/jit/objectalloc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ class ObjectAllocator final : public Phase
237237
Statement* stmt);
238238
struct BuildConnGraphVisitorCallbackData;
239239
void AnalyzeParentStack(ArrayStack<GenTree*>* parentStack, unsigned int lclNum, BasicBlock* block);
240-
void UpdateAncestorTypes(GenTree* tree, ArrayStack<GenTree*>* parentStack, var_types newType, bool retypeFields);
240+
void UpdateAncestorTypes(
241+
GenTree* tree, ArrayStack<GenTree*>* parentStack, var_types newType, ClassLayout* newLayout, bool retypeFields);
241242
ObjectAllocationType AllocationKind(GenTree* tree);
242243

243244
// Conditionally escaping allocation support
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
// Generated by Fuzzlyn v3.0 on 2025-05-20 22:26:35
5+
// Run on X64 Windows
6+
// Seed: 457555340882575514-vectort,vector128,vector256,x86aes,x86avx,x86avx2,x86avx512bw,x86avx512bwvl,x86avx512cd,x86avx512cdvl,x86avx512dq,x86avx512dqvl,x86avx512f,x86avx512fvl,x86avx512fx64,x86bmi1,x86bmi1x64,x86bmi2,x86bmi2x64,x86fma,x86lzcnt,x86lzcntx64,x86pclmulqdq,x86popcnt,x86popcntx64,x86sse,x86ssex64,x86sse2,x86sse2x64,x86sse3,x86sse41,x86sse41x64,x86sse42,x86sse42x64,x86ssse3,x86x86base
7+
// Reduced from 154.0 KiB to 0.8 KiB in 00:06:12
8+
// Hits JIT assert in Release:
9+
// Assertion failed 'm_blockLayout->CanAssignFrom(m_src->GetLayout(m_comp))' in 'Program:Main(Fuzzlyn.ExecutionServer.IRuntime)' during 'Morph - Global' (IL size 69; hash 0xade6b36b; FullOpts)
10+
//
11+
// File: D:\a\_work\1\s\src\coreclr\jit\morphblock.cpp Line: 668
12+
//
13+
using System;
14+
using Xunit;
15+
16+
public class C0
17+
{
18+
}
19+
20+
public struct S0
21+
{
22+
public int F5;
23+
}
24+
25+
public struct S1
26+
{
27+
public C0 F2;
28+
public S0 F3;
29+
public S1(C0 f2) : this()
30+
{
31+
F2 = f2;
32+
}
33+
}
34+
35+
public struct S2
36+
{
37+
public S1 F5;
38+
public S1 F7;
39+
public S2(S1 f5) : this()
40+
{
41+
F5 = f5;
42+
}
43+
}
44+
45+
public struct S4
46+
{
47+
public S2 F1;
48+
public S4(S2 f1) : this()
49+
{
50+
F1 = f1;
51+
}
52+
}
53+
54+
public struct S5
55+
{
56+
public S4 F5;
57+
public S5(S4 f5) : this()
58+
{
59+
F5 = f5;
60+
}
61+
}
62+
63+
public class Runtime_115832
64+
{
65+
[Fact]
66+
public static void Problem()
67+
{
68+
S5 vr0 = new S5(new S4(new S2(new S1(new C0()))));
69+
System.Console.WriteLine(vr0.F5.F1.F7.F3.F5);
70+
}
71+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<DebugType>None</DebugType>
4+
<Optimize>True</Optimize>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="$(MSBuildProjectName).cs" />
8+
</ItemGroup>
9+
</Project>

0 commit comments

Comments
 (0)