mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-09 17:44:48 +09:00
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.
This commit is contained in:
parent
af70c369a7
commit
ee1da26f85
3 changed files with 180 additions and 0 deletions
|
@ -15,6 +15,8 @@ namespace ILLink.Shared.DataFlow
|
|||
public readonly struct ValueSet<TValue> : IEquatable<ValueSet<TValue>>, IEnumerable<TValue>, IDeepCopyValue<ValueSet<TValue>>
|
||||
where TValue : notnull
|
||||
{
|
||||
const int MaxValuesInSet = 256;
|
||||
|
||||
// Since we're going to do lot of type checks for this class a lot, it is much more efficient
|
||||
// if the class is sealed (as then the runtime can do a simple method table pointer comparison)
|
||||
private sealed class EnumerableValues : HashSet<TValue>
|
||||
|
@ -194,6 +196,10 @@ namespace ILLink.Shared.DataFlow
|
|||
|
||||
var values = new EnumerableValues (left.DeepCopy ());
|
||||
values.UnionWith (right.DeepCopy ());
|
||||
// Limit the number of values we track, to prevent hangs in case of patterns that
|
||||
// create exponentially many possible values. This will result in analysis holes.
|
||||
if (values.Count > MaxValuesInSet)
|
||||
return default;
|
||||
return new ValueSet<TValue> (values);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,12 @@ namespace ILLink.RoslynAnalyzer.Tests
|
|||
public sealed partial class DataFlowTests : LinkerTestBase
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public Task ExponentialDataFlow ()
|
||||
{
|
||||
return RunTest (allowMissingWarnings: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GenericParameterDataFlowMarking ()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Mono.Linker.Tests.Cases.Expectations.Assertions;
|
||||
using Mono.Linker.Tests.Cases.Expectations.Helpers;
|
||||
|
||||
namespace Mono.Linker.Tests.Cases.DataFlow
|
||||
{
|
||||
[ExpectedNoWarnings]
|
||||
[SkipKeptItemsValidation]
|
||||
public class ExponentialDataFlow
|
||||
{
|
||||
public static void Main ()
|
||||
{
|
||||
ExponentialArrayStates.Test ();
|
||||
ExponentialArrayStatesDataFlow.Test<int> ();
|
||||
ArrayStatesDataFlow.Test<int> ();
|
||||
}
|
||||
|
||||
class ExponentialArrayStates
|
||||
{
|
||||
public static void Test ()
|
||||
{
|
||||
object[] data = new object[20];
|
||||
if (true) data[0] = new object ();
|
||||
if (true) data[1] = new object ();
|
||||
if (true) data[2] = new object ();
|
||||
if (true) data[3] = new object ();
|
||||
if (true) data[4] = new object ();
|
||||
if (true) data[5] = new object ();
|
||||
if (true) data[6] = new object ();
|
||||
if (true) data[7] = new object ();
|
||||
if (true) data[8] = new object ();
|
||||
if (true) data[9] = new object ();
|
||||
if (true) data[10] = new object ();
|
||||
if (true) data[11] = new object ();
|
||||
if (true) data[12] = new object ();
|
||||
if (true) data[13] = new object ();
|
||||
if (true) data[14] = new object ();
|
||||
if (true) data[15] = new object ();
|
||||
if (true) data[16] = new object ();
|
||||
if (true) data[17] = new object ();
|
||||
if (true) data[18] = new object ();
|
||||
if (true) data[19] = new object ();
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayStatesDataFlow
|
||||
{
|
||||
class GenericTypeWithRequires<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T>
|
||||
{
|
||||
}
|
||||
|
||||
[ExpectedWarning ("IL3050", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'")]
|
||||
public static void Test<T> ()
|
||||
{
|
||||
Type[] types = new Type[1] { typeof (int) };
|
||||
if (true) types[0] = typeof (T);
|
||||
typeof (GenericTypeWithRequires<>).MakeGenericType (types);
|
||||
}
|
||||
}
|
||||
|
||||
class ExponentialArrayStatesDataFlow
|
||||
{
|
||||
class GenericTypeWithRequires<
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T0,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T1,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T2,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T3,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T4,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T5,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T6,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T7,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T8,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T9,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T10,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T11,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T12,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T13,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T14,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T15,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T16,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T17,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T18,
|
||||
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T19>
|
||||
{
|
||||
}
|
||||
|
||||
[ExpectedWarning ("IL3050", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
|
||||
// The way we track arrays causes the analyzer to track exponentially many
|
||||
// ArrayValues in the ValueSet for the pattern in this method, hitting the limit.
|
||||
// When this happens, we replace the ValueSet wit a TopValue, which doesn't
|
||||
// produce a warning in this case.
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
[ExpectedWarning ("IL2090", "'T'", ProducedBy = Tool.Trimmer | Tool.NativeAot)]
|
||||
public static void Test<T> ()
|
||||
{
|
||||
Type[] types = new Type[20] {
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int),
|
||||
typeof (int)
|
||||
};
|
||||
if (Condition) types[0] = typeof (T);
|
||||
if (Condition) types[1] = typeof (T);
|
||||
if (Condition) types[2] = typeof (T);
|
||||
if (Condition) types[3] = typeof (T);
|
||||
if (Condition) types[4] = typeof (T);
|
||||
if (Condition) types[5] = typeof (T);
|
||||
if (Condition) types[6] = typeof (T);
|
||||
if (Condition) types[7] = typeof (T);
|
||||
if (Condition) types[8] = typeof (T);
|
||||
if (Condition) types[9] = typeof (T);
|
||||
if (Condition) types[10] = typeof (T);
|
||||
if (Condition) types[11] = typeof (T);
|
||||
if (Condition) types[12] = typeof (T);
|
||||
if (Condition) types[13] = typeof (T);
|
||||
if (Condition) types[14] = typeof (T);
|
||||
if (Condition) types[15] = typeof (T);
|
||||
if (Condition) types[16] = typeof (T);
|
||||
if (Condition) types[17] = typeof (T);
|
||||
if (Condition) types[18] = typeof (T);
|
||||
if (Condition) types[19] = typeof (T);
|
||||
|
||||
typeof (GenericTypeWithRequires<,,,,,,,,,,,,,,,,,,,>).MakeGenericType (types);
|
||||
}
|
||||
|
||||
static bool Condition => Random.Shared.Next (2) == 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue