From 76d2b30e4b8ce201d4b90bc04e02bc20f5845770 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Wed, 23 Apr 2025 16:39:24 -0700 Subject: [PATCH 1/4] Support generating simple 'switch' statements --- Fuzzlyn/Fuzzlyn.csproj | 2 +- Fuzzlyn/FuzzlynOptions.cs | 3 +- Fuzzlyn/Methods/FuncBodyGenerator.cs | 56 +++++++++++++++++++++++++++- Fuzzlyn/Reduction/Reducer.cs | 20 ++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/Fuzzlyn/Fuzzlyn.csproj b/Fuzzlyn/Fuzzlyn.csproj index f24cb9cc..f0dd52b8 100644 --- a/Fuzzlyn/Fuzzlyn.csproj +++ b/Fuzzlyn/Fuzzlyn.csproj @@ -3,7 +3,7 @@ Exe net8.0 - 2.8 + 2.9 false win-x64;win-x86;linux-arm64;linux-arm;osx-arm64 preview diff --git a/Fuzzlyn/FuzzlynOptions.cs b/Fuzzlyn/FuzzlynOptions.cs index 2fc8c565..507ce100 100644 --- a/Fuzzlyn/FuzzlynOptions.cs +++ b/Fuzzlyn/FuzzlynOptions.cs @@ -63,7 +63,8 @@ internal class FuzzlynOptions = new TableDistribution(new Dictionary { [(int)StatementKind.Assignment] = 0.57, - [(int)StatementKind.If] = 0.14, + [(int)StatementKind.If] = 0.135, + [(int)StatementKind.Switch] = 0.005, [(int)StatementKind.Block] = 0.1, [(int)StatementKind.Call] = 0.1, [(int)StatementKind.Throw] = 0.005, diff --git a/Fuzzlyn/Methods/FuncBodyGenerator.cs b/Fuzzlyn/Methods/FuncBodyGenerator.cs index 238b9be6..ea000fc7 100644 --- a/Fuzzlyn/Methods/FuncBodyGenerator.cs +++ b/Fuzzlyn/Methods/FuncBodyGenerator.cs @@ -61,7 +61,12 @@ private StatementSyntax GenStatement(bool allowReturn = true) StatementKind kind = (StatementKind)Options.StatementTypeDist.Sample(_random.Rng); - if ((kind == StatementKind.Block || kind == StatementKind.If || kind == StatementKind.TryCatch || kind == StatementKind.TryFinally || kind == StatementKind.Loop) && + if ((kind == StatementKind.Block || + kind == StatementKind.If || + kind == StatementKind.Switch || + kind == StatementKind.TryCatch || + kind == StatementKind.TryFinally || + kind == StatementKind.Loop) && ShouldRejectRecursion()) continue; @@ -81,6 +86,8 @@ private StatementSyntax GenStatement(bool allowReturn = true) return GenCallStatement(tryExisting: ShouldRejectRecursion()); case StatementKind.If: return GenIf(); + case StatementKind.Switch: + return GenSwitch(); case StatementKind.Throw: return GenThrow(); case StatementKind.TryCatch: @@ -333,6 +340,52 @@ private StatementSyntax GenIf() return gen; } + private StatementSyntax GenSwitch() + { + ExpressionSyntax guard; + int attempts = 0; + do + { + guard = GenExpression(new PrimitiveType(SyntaxKind.IntKeyword)); + } while (IsLiteralOrCastOfLiteral(guard) && attempts++ < 20); + + // C# has a rich 'switch' syntax. Here, for now, generate only a simple switch on int type with dense + // numeric cases, to attempt to get Roslyn to generate a 'switch' IL instruction. + + List switchSections = new(); + int caseCount = _random.Next(5, 25); + for (int caseNumber = 0; caseNumber < caseCount; caseNumber++) + { + ExpressionSyntax caseValue = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(caseNumber)); + switchSections.Add( + SwitchSection() + .WithLabels( + SingletonList( + CaseSwitchLabel(caseValue))) + .WithStatements( + List( + new StatementSyntax[] { + GenBlock(), + BreakStatement() }))); + } + + // Add a default case (which is mostly likely what will get executed when we run). + switchSections.Add( + SwitchSection() + .WithLabels( + SingletonList( + DefaultSwitchLabel())) + .WithStatements( + List( + new StatementSyntax[] { + GenBlock(), + BreakStatement() }))); + + SyntaxList switchSectionSyntax = switchSections.ToSyntaxList(); + StatementSyntax gen = SwitchStatement(guard, switchSectionSyntax); + return gen; + } + private StatementSyntax GenThrow() { Debug.Assert(_tryCatchCount > 0); @@ -1341,6 +1394,7 @@ internal enum StatementKind Assignment, Call, If, + Switch, Return, Throw, TryCatch, diff --git a/Fuzzlyn/Reduction/Reducer.cs b/Fuzzlyn/Reduction/Reducer.cs index 9035a4a4..a00a637a 100644 --- a/Fuzzlyn/Reduction/Reducer.cs +++ b/Fuzzlyn/Reduction/Reducer.cs @@ -1323,6 +1323,26 @@ private IEnumerable SimplifyIf(SyntaxNode node) } } + [Simplifier] + private IEnumerable SimplifySwitch(SyntaxNode node) + { + if (node is not SwitchStatementSyntax @switch) + yield break; + + foreach (var section in @switch.Sections) + { + // Remove the 'break;' statement + if (section.Statements.Count > 0) + { + var lastStmt = section.Statements.Last(); + if (lastStmt is BreakStatementSyntax) + yield return Block(section.Statements.RemoveAt(section.Statements.Count - 1)); + else + yield return Block(section.Statements); + } + } + } + [Simplifier] private IEnumerable SimplifyFor(SyntaxNode node) { From 5eff5d0c7e8381e1fe5361c4fd2aef53768f640b Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 28 Apr 2025 11:42:34 -0700 Subject: [PATCH 2/4] Use geometric distribution for case count --- Fuzzlyn/FuzzlynOptions.cs | 2 ++ Fuzzlyn/Methods/FuncBodyGenerator.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Fuzzlyn/FuzzlynOptions.cs b/Fuzzlyn/FuzzlynOptions.cs index 507ce100..81f8ae70 100644 --- a/Fuzzlyn/FuzzlynOptions.cs +++ b/Fuzzlyn/FuzzlynOptions.cs @@ -246,6 +246,8 @@ internal class FuzzlynOptions [(int)VectorCreationKind.CreateBroadcast] = 0.44, [(int)VectorCreationKind.CreateScalar] = 0.44, }); + + public ProbabilityDistribution SwitchCaseCountDist { get; set; } = new GeometricDistribution(0.2, 5); } internal enum AggregateFieldKind diff --git a/Fuzzlyn/Methods/FuncBodyGenerator.cs b/Fuzzlyn/Methods/FuncBodyGenerator.cs index ea000fc7..73bb4d9c 100644 --- a/Fuzzlyn/Methods/FuncBodyGenerator.cs +++ b/Fuzzlyn/Methods/FuncBodyGenerator.cs @@ -353,7 +353,7 @@ private StatementSyntax GenSwitch() // numeric cases, to attempt to get Roslyn to generate a 'switch' IL instruction. List switchSections = new(); - int caseCount = _random.Next(5, 25); + int caseCount = Options.SwitchCaseCountDist.Sample(_random.Rng); for (int caseNumber = 0; caseNumber < caseCount; caseNumber++) { ExpressionSyntax caseValue = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(caseNumber)); From c2aaa6a7f610ae37aea8ffa3a1d348560c387b0a Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 28 Apr 2025 11:42:59 -0700 Subject: [PATCH 3/4] Fix `--stats` for use with unspecified extensions list --- Fuzzlyn/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Fuzzlyn/Program.cs b/Fuzzlyn/Program.cs index 00bde32f..c47ca8d6 100644 --- a/Fuzzlyn/Program.cs +++ b/Fuzzlyn/Program.cs @@ -191,6 +191,9 @@ private static void Main(string[] args) } else if (options.Stats) { + if (options.GenExtensions == null) + options.GenExtensions = []; // No extension by default with --stats + GenerateProgramsAndGetStats(options); } else From 3b76afe3b8fb1eb2311a79d94a5796523395c139 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 28 Apr 2025 16:31:35 -0700 Subject: [PATCH 4/4] Encourage loop cloning A small constant loop iteration count is easy for the JIT to detect, and leads to the JIT unrolling the loop, if other profitability conditions occur. It never leads to loop cloning. Sometimes choosing larger loop bounds does lead to cloning, though in the cases I tried, less than 0.5% of generated programs ended up with cloned loops. --- Fuzzlyn/FuzzlynOptions.cs | 1 + Fuzzlyn/LiteralGenerator.cs | 2 +- Fuzzlyn/Types/PrimitiveType.cs | 97 ++++++++++++++++++---------------- 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/Fuzzlyn/FuzzlynOptions.cs b/Fuzzlyn/FuzzlynOptions.cs index 81f8ae70..8e5bfe1e 100644 --- a/Fuzzlyn/FuzzlynOptions.cs +++ b/Fuzzlyn/FuzzlynOptions.cs @@ -110,6 +110,7 @@ internal class FuzzlynOptions public double PickLiteralFromTableProb { get; set; } = 0.5; public double ForLoopProb { get; set; } = 0.8; public double UpCountedLoopProb { get; set; } = 0.5; + public double SmallLoopIterationCountProb { get; set; } = 0.25; public ProbabilityDistribution SignedIntegerTypicalLiteralDist { get; set; } = new TableDistribution(new Dictionary diff --git a/Fuzzlyn/LiteralGenerator.cs b/Fuzzlyn/LiteralGenerator.cs index 9a6bcfe7..1d139e0c 100644 --- a/Fuzzlyn/LiteralGenerator.cs +++ b/Fuzzlyn/LiteralGenerator.cs @@ -75,7 +75,7 @@ public static (ExpressionSyntax, ExpressionSyntax) GenPrimitiveLiteralLoopBounds kind = (TypicalLiteralKind)random.Options.SignedIntegerTypicalLiteralDist.Sample(random.Rng); } - return primType.Info.GenTypicalLiteralLoopBounds(kind); + return primType.Info.GenTypicalLiteralLoopBounds(kind, random); } private static List GenArrayDimensions(Randomizer random, ArrayType at) diff --git a/Fuzzlyn/Types/PrimitiveType.cs b/Fuzzlyn/Types/PrimitiveType.cs index 21579d7b..f3e51898 100644 --- a/Fuzzlyn/Types/PrimitiveType.cs +++ b/Fuzzlyn/Types/PrimitiveType.cs @@ -92,9 +92,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => CreateLiteralWithSuffix(rng.NextUInt64(), null, "UL", (val, str) => Literal(val, str)), GenTypicalLiteral = literal => CreateLiteralWithSuffix(FromTypicalLiteral(literal), null, "UL", (val, str) => Literal(val, str)), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal), null, "UL", (val, str) => Literal(val, str)), - CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal), null, "UL", (val, str) => Literal(val, str))), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal, random), null, "UL", (val, str) => Literal(val, str)), + CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal, random), null, "UL", (val, str) => Literal(val, str))), GenInRange = (min, max, rng) => CreateLiteralWithSuffix(RandomMinMax(min, max, rng), null, "UL", (val, str) => Literal(val, str)), IsUnsigned = true, IsNumeric = true, @@ -105,9 +105,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => CreateLiteralWithSuffix((long)rng.NextUInt64(), null, "L", (val, str) => Literal(val, str)), GenTypicalLiteral = literal => CreateLiteralWithSuffix(FromTypicalLiteral(literal), null, "L", (val, str) => Literal(val, str)), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal), null, "L", (val, str) => Literal(val, str)), - CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal), null, "L", (val, str) => Literal(val, str))), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal, random), null, "L", (val, str) => Literal(val, str)), + CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal, random), null, "L", (val, str) => Literal(val, str))), GenInRange = (min, max, rng) => CreateLiteralWithSuffix(RandomMinMax(min, max, rng), null, "L", (val, str) => Literal(val, str)), IsNumeric = true, Size = sizeof(long), @@ -117,9 +117,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => CreateLiteralWithSuffix((uint)rng.NextUInt64(), null, "U", (val, str) => Literal(val, str)), GenTypicalLiteral = literal => CreateLiteralWithSuffix(FromTypicalLiteral(literal), null, "U", (val, str) => Literal(val, str)), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal), null, "U", (val, str) => Literal(val, str)), - CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal), null, "U", (val, str) => Literal(val, str))), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal, random), null, "U", (val, str) => Literal(val, str)), + CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal, random), null, "U", (val, str) => Literal(val, str))), GenInRange = (min, max, rng) => CreateLiteralWithSuffix(RandomMinMax(min, max, rng), null, "U", (val, str) => Literal(val, str)), IsUnsigned = true, IsNumeric = true, @@ -130,9 +130,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((int)rng.NextUInt64())), GenTypicalLiteral = literal => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(FromTypicalLiteral(literal))), - GenTypicalLiteralLoopBounds = literal => ( - LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(FromTypicalLiteralLoopLowerBound(literal))), - LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(FromTypicalLiteralLoopUpperBound(literal)))), + GenTypicalLiteralLoopBounds = (literal, random) => ( + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(FromTypicalLiteralLoopLowerBound(literal, random))), + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(FromTypicalLiteralLoopUpperBound(literal, random)))), GenInRange = (min, max, rng) => LiteralExpression(SyntaxKind.NumericLiteralToken, Literal(RandomMinMax(min, max, rng))), IsNumeric = true, Size = sizeof(int), @@ -142,9 +142,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => CreateLiteralWithCast(Literal((ushort)rng.NextUInt64()), SyntaxKind.UShortKeyword), GenTypicalLiteral = literal => CreateLiteralWithCast(Literal(FromTypicalLiteral(literal)), SyntaxKind.UShortKeyword), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal)), SyntaxKind.UShortKeyword), - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal)), SyntaxKind.UShortKeyword)), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal, random)), SyntaxKind.UShortKeyword), + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal, random)), SyntaxKind.UShortKeyword)), GenInRange = (min, max, rng) => CreateLiteralWithCast(Literal(RandomMinMax(min, max, rng)), SyntaxKind.UShortKeyword), IsUnsigned = true, IsNumeric = true, @@ -155,9 +155,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => CreateLiteralWithCast(Literal((short)rng.NextUInt64()), SyntaxKind.ShortKeyword), GenTypicalLiteral = literal => CreateLiteralWithCast(Literal(FromTypicalLiteral(literal)), SyntaxKind.ShortKeyword), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal)), SyntaxKind.ShortKeyword), - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal)), SyntaxKind.ShortKeyword)), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal, random)), SyntaxKind.ShortKeyword), + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal, random)), SyntaxKind.ShortKeyword)), GenInRange = (min, max, rng) => CreateLiteralWithCast(Literal(RandomMinMax(min, max, rng)), SyntaxKind.ShortKeyword), IsNumeric = true, Size = sizeof(short), @@ -167,9 +167,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => CreateLiteralWithCast(Literal((byte)rng.NextUInt64()), SyntaxKind.ByteKeyword), GenTypicalLiteral = literal => CreateLiteralWithCast(Literal(FromTypicalLiteral(literal)), SyntaxKind.ByteKeyword), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal)), SyntaxKind.ByteKeyword), - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal)), SyntaxKind.ByteKeyword)), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal, random)), SyntaxKind.ByteKeyword), + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal, random)), SyntaxKind.ByteKeyword)), GenInRange = (min, max, rng) => CreateLiteralWithCast(Literal(RandomMinMax(min, max, rng)), SyntaxKind.ByteKeyword), IsUnsigned = true, IsNumeric = true, @@ -180,9 +180,9 @@ static PrimitiveType() AllowedAdditionalAssignments = intAssigns, GenRandomLiteral = rng => CreateLiteralWithCast(Literal((sbyte)rng.NextUInt64()), SyntaxKind.SByteKeyword), GenTypicalLiteral = literal => CreateLiteralWithCast(Literal(FromTypicalLiteral(literal)), SyntaxKind.SByteKeyword), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal)), SyntaxKind.SByteKeyword), - CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal)), SyntaxKind.SByteKeyword)), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopLowerBound(literal, random)), SyntaxKind.SByteKeyword), + CreateLiteralWithCast(Literal(FromTypicalLiteralLoopUpperBound(literal, random)), SyntaxKind.SByteKeyword)), GenInRange = (min, max, rng) => CreateLiteralWithCast(Literal(RandomMinMax(min, max, rng)), SyntaxKind.SByteKeyword), IsNumeric = true, Size = sizeof(sbyte), @@ -192,9 +192,9 @@ static PrimitiveType() AllowedAdditionalAssignments = floatAssigns, GenRandomLiteral = rng => CreateLiteralWithSuffix((float)((rng.NextDouble() - 0.5) * 1000), "R", "f", (val, str) => Literal(val, str)), GenTypicalLiteral = literal => CreateLiteralWithSuffix(FromTypicalLiteral(literal), "R", "f", (val, str) => Literal(val, str)), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal), "R", "f", (val, str) => Literal(val, str)), - CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal), "R", "f", (val, str) => Literal(val, str))), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal, random), "R", "f", (val, str) => Literal(val, str)), + CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal, random), "R", "f", (val, str) => Literal(val, str))), GenInRange = (min, max, rng) => throw new InvalidOperationException(), IsNumeric = true, IsFloat = true, @@ -205,9 +205,9 @@ static PrimitiveType() AllowedAdditionalAssignments = floatAssigns, GenRandomLiteral = rng => CreateLiteralWithSuffix((double)((rng.NextDouble() - 0.5) * 10000), "R", "d", (val, str) => Literal(val, str)), GenTypicalLiteral = literal => CreateLiteralWithSuffix(FromTypicalLiteral(literal), "R", "d", (val, str) => Literal(val, str)), - GenTypicalLiteralLoopBounds = literal => ( - CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal), "R", "d", (val, str) => Literal(val, str)), - CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal), "R", "d", (val, str) => Literal(val, str))), + GenTypicalLiteralLoopBounds = (literal, random) => ( + CreateLiteralWithSuffix(FromTypicalLiteralLoopLowerBound(literal, random), "R", "d", (val, str) => Literal(val, str)), + CreateLiteralWithSuffix(FromTypicalLiteralLoopUpperBound(literal, random), "R", "d", (val, str) => Literal(val, str))), GenInRange = (min, max, rng) => throw new InvalidOperationException(), IsNumeric = true, IsFloat = true, @@ -218,7 +218,7 @@ static PrimitiveType() AllowedAdditionalAssignments = boolAssigns, GenRandomLiteral = rng => LiteralExpression(rng.Next(2) == 0 ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression), GenTypicalLiteral = literal => throw new InvalidOperationException(), - GenTypicalLiteralLoopBounds = literal => throw new InvalidOperationException(), + GenTypicalLiteralLoopBounds = (literal, random) => throw new InvalidOperationException(), GenInRange = (min, max, rng) => throw new InvalidOperationException(), Size = sizeof(bool), }, @@ -257,7 +257,7 @@ private static T FromTypicalLiteral(TypicalLiteralKind literal) where T : INu }; } - private static T FromTypicalLiteralLoopLowerBound(TypicalLiteralKind literal) where T : INumber, IMinMaxValue + private static T FromTypicalLiteralLoopLowerBound(TypicalLiteralKind literal, Randomizer random) where T : INumber, IMinMaxValue { return literal switch { @@ -271,27 +271,30 @@ private static T FromTypicalLiteralLoopLowerBound(TypicalLiteralKind literal) TypicalLiteralKind.Two => T.CreateChecked(2), TypicalLiteralKind.Ten => T.CreateChecked(10), TypicalLiteralKind.MaxValueMinusOne => T.MaxValue - T.CreateChecked(100), // This is a bit misnamed for loop bounds - TypicalLiteralKind.MaxValue => T.MaxValue - T.CreateChecked(5), // This is a bit misnamed for loop bounds, since we need space after it + TypicalLiteralKind.MaxValue => T.MaxValue - T.CreateChecked(50), // This is a bit misnamed for loop bounds, since we need space after it _ => throw new ArgumentOutOfRangeException(nameof(literal)), }; } - // TODO: handle varying loop iteration counts - private static T FromTypicalLiteralLoopUpperBound(TypicalLiteralKind literal) where T : INumber, IMinMaxValue + private static T FromTypicalLiteralLoopUpperBound(TypicalLiteralKind literal, Randomizer random) where T : INumber, IMinMaxValue { + // An iteration count of 2 leads to JIT loop unrolling (if size contraints apply). An iteration count of 40 is larger + // than the JIT allowed constant loop iteration unroll count, and leads to loop cloning. + T iterationCount = random.FlipCoin(random.Options.SmallLoopIterationCountProb) ? T.CreateChecked(2) : T.CreateChecked(40); + return literal switch { - TypicalLiteralKind.MinValue => T.MinValue + T.CreateChecked(2), - TypicalLiteralKind.MinValuePlusOne => T.MinValue + T.One + T.CreateChecked(2), - TypicalLiteralKind.MinusTen => T.CreateChecked(-10) + T.CreateChecked(2), - TypicalLiteralKind.MinusTwo => T.CreateChecked(-2) + T.CreateChecked(2), - TypicalLiteralKind.MinusOne => T.CreateChecked(-1) + T.CreateChecked(2), - TypicalLiteralKind.Zero => T.Zero + T.CreateChecked(2), - TypicalLiteralKind.One => T.One + T.CreateChecked(2), - TypicalLiteralKind.Two => T.CreateChecked(2) + T.CreateChecked(2), - TypicalLiteralKind.Ten => T.CreateChecked(10) + T.CreateChecked(2), - TypicalLiteralKind.MaxValueMinusOne => T.MaxValue - T.CreateChecked(100) + T.CreateChecked(2), - TypicalLiteralKind.MaxValue => T.MaxValue - T.CreateChecked(5) + T.CreateChecked(2), + TypicalLiteralKind.MinValue => T.MinValue + iterationCount, + TypicalLiteralKind.MinValuePlusOne => T.MinValue + T.One + iterationCount, + TypicalLiteralKind.MinusTen => T.CreateChecked(-10) + iterationCount, + TypicalLiteralKind.MinusTwo => T.CreateChecked(-2) + iterationCount, + TypicalLiteralKind.MinusOne => T.CreateChecked(-1) + iterationCount, + TypicalLiteralKind.Zero => T.Zero + iterationCount, + TypicalLiteralKind.One => T.One + iterationCount, + TypicalLiteralKind.Two => T.CreateChecked(2) + iterationCount, + TypicalLiteralKind.Ten => T.CreateChecked(10) + iterationCount, + TypicalLiteralKind.MaxValueMinusOne => T.MaxValue - T.CreateChecked(100) + iterationCount, + TypicalLiteralKind.MaxValue => T.MaxValue - T.CreateChecked(50) + iterationCount, _ => throw new ArgumentOutOfRangeException(nameof(literal)), }; } @@ -334,7 +337,7 @@ internal class PrimitiveTypeInfo public SyntaxKind[] AllowedAdditionalAssignments { get; set; } public Func GenRandomLiteral { get; set; } public Func GenTypicalLiteral { get; set; } - public Func GenTypicalLiteralLoopBounds { get; set; } + public Func GenTypicalLiteralLoopBounds { get; set; } public Func GenInRange { get; set; } public bool IsUnsigned { get; set; } public bool IsNumeric { get; set; }