Skip to content

Commit ee1da26

Browse files
authored
Limit number of tracked values in trim dataflow analysis (#87634)
Places a limit on the number of values tracked in our dataflow analysis, to prevent hangs in the illink analyzer. This avoids hangs when analyzing patterns that currently lead to an exponential number of tracked values.
1 parent af70c36 commit ee1da26

File tree

3 files changed

+180
-0
lines changed

3 files changed

+180
-0
lines changed

src/tools/illink/src/ILLink.Shared/DataFlow/ValueSet.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ namespace ILLink.Shared.DataFlow
1515
public readonly struct ValueSet<TValue> : IEquatable<ValueSet<TValue>>, IEnumerable<TValue>, IDeepCopyValue<ValueSet<TValue>>
1616
where TValue : notnull
1717
{
18+
const int MaxValuesInSet = 256;
19+
1820
// Since we're going to do lot of type checks for this class a lot, it is much more efficient
1921
// if the class is sealed (as then the runtime can do a simple method table pointer comparison)
2022
private sealed class EnumerableValues : HashSet<TValue>
@@ -194,6 +196,10 @@ internal static ValueSet<TValue> Meet (ValueSet<TValue> left, ValueSet<TValue> r
194196

195197
var values = new EnumerableValues (left.DeepCopy ());
196198
values.UnionWith (right.DeepCopy ());
199+
// Limit the number of values we track, to prevent hangs in case of patterns that
200+
// create exponentially many possible values. This will result in analysis holes.
201+
if (values.Count > MaxValuesInSet)
202+
return default;
197203
return new ValueSet<TValue> (values);
198204
}
199205

src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DataFlowTests.g.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ namespace ILLink.RoslynAnalyzer.Tests
77
public sealed partial class DataFlowTests : LinkerTestBase
88
{
99

10+
[Fact]
11+
public Task ExponentialDataFlow ()
12+
{
13+
return RunTest (allowMissingWarnings: true);
14+
}
15+
1016
[Fact]
1117
public Task GenericParameterDataFlowMarking ()
1218
{
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Diagnostics.CodeAnalysis;
6+
using Mono.Linker.Tests.Cases.Expectations.Assertions;
7+
using Mono.Linker.Tests.Cases.Expectations.Helpers;
8+
9+
namespace Mono.Linker.Tests.Cases.DataFlow
10+
{
11+
[ExpectedNoWarnings]
12+
[SkipKeptItemsValidation]
13+
public class ExponentialDataFlow
14+
{
15+
public static void Main ()
16+
{
17+
ExponentialArrayStates.Test ();
18+
ExponentialArrayStatesDataFlow.Test<int> ();
19+
ArrayStatesDataFlow.Test<int> ();
20+
}
21+
22+
class ExponentialArrayStates
23+
{
24+
public static void Test ()
25+
{
26+
object[] data = new object[20];
27+
if (true) data[0] = new object ();
28+
if (true) data[1] = new object ();
29+
if (true) data[2] = new object ();
30+
if (true) data[3] = new object ();
31+
if (true) data[4] = new object ();
32+
if (true) data[5] = new object ();
33+
if (true) data[6] = new object ();
34+
if (true) data[7] = new object ();
35+
if (true) data[8] = new object ();
36+
if (true) data[9] = new object ();
37+
if (true) data[10] = new object ();
38+
if (true) data[11] = new object ();
39+
if (true) data[12] = new object ();
40+
if (true) data[13] = new object ();
41+
if (true) data[14] = new object ();
42+
if (true) data[15] = new object ();
43+
if (true) data[16] = new object ();
44+
if (true) data[17] = new object ();
45+
if (true) data[18] = new object ();
46+
if (true) data[19] = new object ();
47+
}
48+
}
49+
50+
class ArrayStatesDataFlow
51+
{
52+
class GenericTypeWithRequires<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T>
53+
{
54+
}
55+
56+
[ExpectedWarning ("IL3050", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
57+
[ExpectedWarning ("IL2090", "'T'")]
58+
public static void Test<T> ()
59+
{
60+
Type[] types = new Type[1] { typeof (int) };
61+
if (true) types[0] = typeof (T);
62+
typeof (GenericTypeWithRequires<>).MakeGenericType (types);
63+
}
64+
}
65+
66+
class ExponentialArrayStatesDataFlow
67+
{
68+
class GenericTypeWithRequires<
69+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T0,
70+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T1,
71+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T2,
72+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T3,
73+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T4,
74+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T5,
75+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T6,
76+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T7,
77+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T8,
78+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T9,
79+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T10,
80+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T11,
81+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T12,
82+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T13,
83+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T14,
84+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T15,
85+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T16,
86+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T17,
87+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T18,
88+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T19>
89+
{
90+
}
91+
92+
[ExpectedWarning ("IL3050", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
93+
// The way we track arrays causes the analyzer to track exponentially many
94+
// ArrayValues in the ValueSet for the pattern in this method, hitting the limit.
95+
// When this happens, we replace the ValueSet wit a TopValue, which doesn't
96+
// produce a warning in this case.
97+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
98+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
99+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
100+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
101+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
102+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
103+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
104+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
105+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
106+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
107+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
108+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
109+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
110+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
111+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
112+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
113+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
114+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
115+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
116+
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
117+
public static void Test<T> ()
118+
{
119+
Type[] types = new Type[20] {
120+
typeof (int),
121+
typeof (int),
122+
typeof (int),
123+
typeof (int),
124+
typeof (int),
125+
typeof (int),
126+
typeof (int),
127+
typeof (int),
128+
typeof (int),
129+
typeof (int),
130+
typeof (int),
131+
typeof (int),
132+
typeof (int),
133+
typeof (int),
134+
typeof (int),
135+
typeof (int),
136+
typeof (int),
137+
typeof (int),
138+
typeof (int),
139+
typeof (int)
140+
};
141+
if (Condition) types[0] = typeof (T);
142+
if (Condition) types[1] = typeof (T);
143+
if (Condition) types[2] = typeof (T);
144+
if (Condition) types[3] = typeof (T);
145+
if (Condition) types[4] = typeof (T);
146+
if (Condition) types[5] = typeof (T);
147+
if (Condition) types[6] = typeof (T);
148+
if (Condition) types[7] = typeof (T);
149+
if (Condition) types[8] = typeof (T);
150+
if (Condition) types[9] = typeof (T);
151+
if (Condition) types[10] = typeof (T);
152+
if (Condition) types[11] = typeof (T);
153+
if (Condition) types[12] = typeof (T);
154+
if (Condition) types[13] = typeof (T);
155+
if (Condition) types[14] = typeof (T);
156+
if (Condition) types[15] = typeof (T);
157+
if (Condition) types[16] = typeof (T);
158+
if (Condition) types[17] = typeof (T);
159+
if (Condition) types[18] = typeof (T);
160+
if (Condition) types[19] = typeof (T);
161+
162+
typeof (GenericTypeWithRequires<,,,,,,,,,,,,,,,,,,,>).MakeGenericType (types);
163+
}
164+
165+
static bool Condition => Random.Shared.Next (2) == 0;
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)