diff --git a/DMCompiler/Optimizer/BytecodeOptimizer.cs b/DMCompiler/Optimizer/BytecodeOptimizer.cs index be97db7c19..89389db81b 100644 --- a/DMCompiler/Optimizer/BytecodeOptimizer.cs +++ b/DMCompiler/Optimizer/BytecodeOptimizer.cs @@ -10,6 +10,7 @@ internal void Optimize(DMCompiler compiler, List input) { RemoveUnreferencedLabels(input); JoinAndForwardLabels(input); RemoveUnreferencedLabels(input); + PeepholeOptimizer.RunPeephole(compiler, input); } diff --git a/DMCompiler/Optimizer/CompactorOptimizations.cs b/DMCompiler/Optimizer/CompactorOptimizations.cs new file mode 100644 index 0000000000..9a6525e2fa --- /dev/null +++ b/DMCompiler/Optimizer/CompactorOptimizations.cs @@ -0,0 +1,378 @@ +using DMCompiler.Bytecode; + +// ReSharper disable UnusedType.Global + +namespace DMCompiler.Optimizer; + +#region BytecodeCompactors + +// PushString [string] +// ... +// PushString [string] +// -> PushNStrings [count] [string] ... [string] +internal sealed class PushNStrings : IOptimization { + public OptPass OptimizationPass => OptPass.BytecodeCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushString, + DreamProcOpcode.PushString + ]; + } + + public void Apply(DMCompiler compiler, List input, int index) { + int count = 0; + int stackDelta = 0; + + while (index + count < input.Count && + input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushString }) { + count++; + } + + List args = new List(count + 1) { new AnnotatedBytecodeInteger(count, new Location()) }; + + for (int i = 0; i < count; i++) { + AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); + args.Add(instruction.GetArg(0)); + stackDelta++; + } + + input.RemoveRange(index, count); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNStrings, stackDelta, args)); + } +} + +// PushFloat [float] +// ... +// PushFloat [float] +// -> PushNFloats [count] [float] ... [float] +internal sealed class PushNFloats : IOptimization { + public OptPass OptimizationPass => OptPass.BytecodeCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushFloat, + DreamProcOpcode.PushFloat + ]; + } + + public void Apply(DMCompiler compiler, List input, int index) { + int count = 0; + int stackDelta = 0; + + while (index + count < input.Count && + input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushFloat }) { + count++; + } + + List args = new List(count + 1) { new AnnotatedBytecodeInteger(count, new Location()) }; + + for (int i = 0; i < count; i++) { + AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); + args.Add(instruction.GetArg(0)); + stackDelta++; + } + + input.RemoveRange(index, count); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNFloats, stackDelta, args)); + } +} + +// PushReferenceValue [ref] +// ... +// PushReferenceValue [ref] +// -> PushNRef [count] [ref] ... [ref] +internal sealed class PushNRef : IOptimization { + public OptPass OptimizationPass => OptPass.BytecodeCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushReferenceValue, + DreamProcOpcode.PushReferenceValue + ]; + } + + public void Apply(DMCompiler compiler, List input, int index) { + int count = 0; + int stackDelta = 0; + + while (index + count < input.Count && + input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushReferenceValue }) { + count++; + } + + List args = new List(count + 1) { new AnnotatedBytecodeInteger(count, new Location()) }; + + for (int i = 0; i < count; i++) { + AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); + args.Add(instruction.GetArg(0)); + stackDelta++; + } + + input.RemoveRange(index, count); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNRefs, stackDelta, args)); + } +} + +// PushString [string] +// PushFloat [float] +// -> PushStringFloat [string] [float] +// or if there's multiple +// -> PushNOfStringFloat [count] [string] [float] ... [string] [float] +internal sealed class PushStringFloat : IOptimization { + public OptPass OptimizationPass => OptPass.BytecodeCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushString, + DreamProcOpcode.PushFloat + ]; + } + + public void Apply(DMCompiler compiler, List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); + } + + int count = 0; + while (index + count*2 + 1 < input.Count && + input[index + count * 2] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushString } && input[index + count * 2 + 1] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushFloat }) { + count++; + } + + // If the pattern only occurs once, replace with PushStringFloat and return + if (count == 1) { + AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); + AnnotatedBytecodeString pushVal1 = firstInstruction.GetArg(0); + AnnotatedBytecodeFloat pushVal2 = secondInstruction.GetArg(0); + + input.RemoveRange(index, 2); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushStringFloat, [pushVal1, pushVal2])); + return; + } + + // Otherwise, replace with PushNOfStringFloat + + int stackDelta = 0; + List args = new List(2 * count + 1) { new AnnotatedBytecodeInteger(count, input[index].GetLocation()) }; + + for (int i = 0; i < count; i++) { + AnnotatedBytecodeInstruction stringInstruction = (AnnotatedBytecodeInstruction)(input[index + i*2]); + AnnotatedBytecodeInstruction floatInstruction = (AnnotatedBytecodeInstruction)(input[index + i*2 + 1]); + args.Add(stringInstruction.GetArg(0)); + args.Add(floatInstruction.GetArg(0)); + stackDelta += 2; + } + + input.RemoveRange(index, count * 2); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNOfStringFloats, stackDelta, args)); + } +} + +// PushResource [resource] +// ... +// PushResource [resource] +// -> PushNResources [count] [resource] ... [resource] +internal sealed class PushNResources : IOptimization { + public OptPass OptimizationPass => OptPass.BytecodeCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushResource, + DreamProcOpcode.PushResource + ]; + } + + public void Apply(DMCompiler compiler, List input, int index) { + int count = 0; + int stackDelta = 0; + while (index + count < input.Count && + input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushResource }) { + count++; + } + + List args = new List(count + 1) { new AnnotatedBytecodeInteger(count, new Location()) }; + + for (int i = 0; i < count; i++) { + AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); + args.Add(instruction.GetArg(0)); + stackDelta++; + } + + input.RemoveRange(index, count); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNResources, stackDelta, args)); + } +} + +#endregion + +#region ListCompactors + +// PushNFloats [count] [float] ... [float] +// CreateList [count] +// -> CreateListNFloats [count] [float] ... [float] +internal sealed class CreateListNFloats : IOptimization { + public OptPass OptimizationPass => OptPass.ListCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushNFloats, + DreamProcOpcode.CreateList + ]; + } + + public bool CheckPreconditions(List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); + } + + AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); + int pushVal1 = firstInstruction.GetArg(0).Value; + int pushVal2 = secondInstruction.GetArg(0).Size; + + return pushVal1 == pushVal2; + } + + public void Apply(DMCompiler compiler, List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); + } + + AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + int pushVal1 = firstInstruction.GetArg(0).Value; + + List args = new List(pushVal1 + 1) { new AnnotatedBytecodeInteger(pushVal1, new Location()) }; + args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); + + input.RemoveRange(index, 2); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNFloats, 1, args)); + } +} + +// PushNStrings [count] [string] ... [string] +// CreateList [count] +// -> CreateListNStrings [count] [string] ... [string] +internal sealed class CreateListNStrings : IOptimization { + public OptPass OptimizationPass => OptPass.ListCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushNStrings, + DreamProcOpcode.CreateList + ]; + } + + public bool CheckPreconditions(List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); + } + + AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); + int pushVal1 = firstInstruction.GetArg(0).Value; + int pushVal2 = secondInstruction.GetArg(0).Size; + + return pushVal1 == pushVal2; + } + + public void Apply(DMCompiler compiler, List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); + } + + AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + int pushVal1 = firstInstruction.GetArg(0).Value; + + List args = new List(pushVal1 + 1) { new AnnotatedBytecodeInteger(pushVal1, new Location()) }; + args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); + + input.RemoveRange(index, 2); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNStrings, 1, args)); + } +} + +// PushNResources [count] [resource] ... [resource] +// CreateList [count] +// -> CreateListNResources [count] [resource] ... [resource] +internal sealed class CreateListNResources : IOptimization { + public OptPass OptimizationPass => OptPass.ListCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushNResources, + DreamProcOpcode.CreateList + ]; + } + + public bool CheckPreconditions(List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); + } + + AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); + int pushVal1 = firstInstruction.GetArg(0).Value; + int pushVal2 = secondInstruction.GetArg(0).Size; + + return pushVal1 == pushVal2; + } + + public void Apply(DMCompiler compiler, List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); + } + + AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + int pushVal1 = firstInstruction.GetArg(0).Value; + + List args = new List(pushVal1 + 1) { new AnnotatedBytecodeInteger(pushVal1, new Location()) }; + args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); + + input.RemoveRange(index, 2); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNResources, 1, args)); + } +} + +// PushNRefs [count] [ref] ... [ref] +// CreateList [count] +// -> CreateListNRefs [count] [ref] ... [ref] +internal sealed class CreateListNRefs : IOptimization { + public OptPass OptimizationPass => OptPass.ListCompactor; + + public ReadOnlySpan GetOpcodes() { + return [ + DreamProcOpcode.PushNRefs, + DreamProcOpcode.CreateList + ]; + } + + public bool CheckPreconditions(List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index),"Bytecode index is outside the bounds of the input list."); + } + + int pushVal1 = ((AnnotatedBytecodeInstruction)input[index]).GetArg(0).Value; + int pushVal2 = ((AnnotatedBytecodeInstruction)input[index + 1]).GetArg(0).Size; + + return pushVal1 == pushVal2; + } + + public void Apply(DMCompiler compiler, List input, int index) { + if (index + 1 >= input.Count) { + throw new ArgumentOutOfRangeException(nameof(index), "Bytecode index is outside the bounds of the input list."); + } + + var firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); + int pushVal1 = firstInstruction.GetArg(0).Value; + + List args = new List(1 + pushVal1) { new AnnotatedBytecodeInteger(pushVal1, new Location()) }; + args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); + + input.RemoveRange(index, 2); + input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNRefs, 1, args)); + } +} + +#endregion diff --git a/DMCompiler/Optimizer/PeepholeOptimizations.cs b/DMCompiler/Optimizer/PeepholeOptimizations.cs index a80d48f9d3..56f6f4577f 100644 --- a/DMCompiler/Optimizer/PeepholeOptimizations.cs +++ b/DMCompiler/Optimizer/PeepholeOptimizations.cs @@ -1,11 +1,15 @@ using DMCompiler.Bytecode; +// ReSharper disable UnusedType.Global + namespace DMCompiler.Optimizer; // Append [ref] // Pop // -> AppendNoPush [ref] -internal sealed class AppendNoPush : IPeepholeOptimization { +internal sealed class AppendNoPush : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.Append, @@ -29,7 +33,9 @@ public void Apply(DMCompiler compiler, List input, int index // Assign [ref] // Pop // -> AssignNoPush [ref] -internal sealed class AssignNoPush : IPeepholeOptimization { +internal sealed class AssignNoPush : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.Assign, @@ -53,7 +59,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushNull // AssignNoPush [ref] // -> AssignNull [ref] -internal sealed class AssignNull : IPeepholeOptimization { +internal sealed class AssignNull : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushNull, @@ -82,7 +90,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushReferenceValue [ref] // DereferenceField [field] // -> PushRefAndDereferenceField [ref, field] -internal sealed class PushField : IPeepholeOptimization { +internal sealed class PushField : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushReferenceValue, @@ -109,7 +119,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushReferenceValue [ref] // Return // -> ReturnReferenceValue [ref] -internal class ReturnReferenceValue : IPeepholeOptimization { +internal class ReturnReferenceValue : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushReferenceValue, @@ -128,7 +140,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [float] // Return // -> ReturnFloat [float] -internal class ReturnFloat : IPeepholeOptimization { +internal class ReturnFloat : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -137,8 +151,8 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal); - IPeepholeOptimization.ReplaceInstructions(input, index, 2, + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal); + IOptimization.ReplaceInstructions(input, index, 2, new AnnotatedBytecodeInstruction(DreamProcOpcode.ReturnFloat, [new AnnotatedBytecodeFloat(pushVal, firstInstruction.Location)])); } } @@ -146,7 +160,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushReferenceValue [ref] // JumpIfFalse [label] // -> JumpIfReferenceFalse [ref] [label] -internal sealed class JumpIfReferenceFalse : IPeepholeOptimization { +internal sealed class JumpIfReferenceFalse : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushReferenceValue, @@ -170,169 +186,12 @@ public void Apply(DMCompiler compiler, List input, int index } } -// PushString [string] -// ... -// PushString [string] -// -> PushNStrings [count] [string] ... [string] -internal sealed class PushNStrings : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushString, - DreamProcOpcode.PushString - ]; - } - - public void Apply(DMCompiler compiler, List input, int index) { - int count = 0; - int stackDelta = 0; - - while (index + count < input.Count && - input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushString }) { - count++; - } - - List args = new List(count + 1); - args.Add(new AnnotatedBytecodeInteger(count, new Location())); - - for (int i = 0; i < count; i++) { - AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); - args.Add(instruction.GetArg(0)); - stackDelta++; - } - - input.RemoveRange(index, count); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNStrings, stackDelta, args)); - } -} - -// PushFloat [float] -// ... -// PushFloat [float] -// -> PushNFloats [count] [float] ... [float] -internal sealed class PushNFloats : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushFloat, - DreamProcOpcode.PushFloat - ]; - } - - public void Apply(DMCompiler compiler, List input, int index) { - int count = 0; - int stackDelta = 0; - - while (index + count < input.Count && - input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushFloat }) { - count++; - } - - List args = new List(count + 1); - args.Add(new AnnotatedBytecodeInteger(count, new Location())); - - for (int i = 0; i < count; i++) { - AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); - args.Add(instruction.GetArg(0)); - stackDelta++; - } - - input.RemoveRange(index, count); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNFloats, stackDelta, args)); - } -} - -// PushReferenceValue [ref] -// ... -// PushReferenceValue [ref] -// -> PushNRef [count] [ref] ... [ref] -internal sealed class PushNRef : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushReferenceValue, - DreamProcOpcode.PushReferenceValue - ]; - } - - public void Apply(DMCompiler compiler, List input, int index) { - int count = 0; - int stackDelta = 0; - - while (index + count < input.Count && - input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushReferenceValue }) { - count++; - } - - List args = new List(count + 1); - args.Add(new AnnotatedBytecodeInteger(count, new Location())); - - for (int i = 0; i < count; i++) { - AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); - args.Add(instruction.GetArg(0)); - stackDelta++; - } - - input.RemoveRange(index, count); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNRefs, stackDelta, args)); - } -} - -// PushString [string] -// PushFloat [float] -// -> PushStringFloat [string] [float] -// or if there's multiple -// -> PushNOfStringFloat [count] [string] [float] ... [string] [float] -internal sealed class PushStringFloat : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushString, - DreamProcOpcode.PushFloat - ]; - } - - public void Apply(DMCompiler compiler, List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); - } - - int count = 0; - while (index + count*2 + 1 < input.Count && - input[index + count * 2] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushString } && input[index + count * 2 + 1] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushFloat }) { - count++; - } - - // If the pattern only occurs once, replace with PushStringFloat and return - if (count == 1) { - AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); - AnnotatedBytecodeString pushVal1 = firstInstruction.GetArg(0); - AnnotatedBytecodeFloat pushVal2 = secondInstruction.GetArg(0); - - input.RemoveRange(index, 2); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushStringFloat, [pushVal1, pushVal2])); - return; - } - - // Otherwise, replace with PushNOfStringFloat - - int stackDelta = 0; - List args = new List(2 * count + 1) { new AnnotatedBytecodeInteger(count, input[index].GetLocation()) }; - - for (int i = 0; i < count; i++) { - AnnotatedBytecodeInstruction stringInstruction = (AnnotatedBytecodeInstruction)(input[index + i*2]); - AnnotatedBytecodeInstruction floatInstruction = (AnnotatedBytecodeInstruction)(input[index + i*2 + 1]); - args.Add(stringInstruction.GetArg(0)); - args.Add(floatInstruction.GetArg(0)); - stackDelta += 2; - } - - input.RemoveRange(index, count * 2); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNOfStringFloats, stackDelta, args)); - } -} - // PushFloat [float] // SwitchCase [label] // -> SwitchOnFloat [float] [label] -internal sealed class SwitchOnFloat : IPeepholeOptimization { +internal sealed class SwitchOnFloat : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -358,7 +217,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushString [string] // SwitchCase [label] // -> SwitchOnString [string] [label] -internal sealed class SwitchOnString : IPeepholeOptimization { +internal sealed class SwitchOnString : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushString, @@ -381,206 +242,12 @@ public void Apply(DMCompiler compiler, List input, int index } } -// PushResource [resource] -// ... -// PushResource [resource] -// -> PushNResources [count] [resource] ... [resource] -internal sealed class PushNResources : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushResource, - DreamProcOpcode.PushResource - ]; - } - - public void Apply(DMCompiler compiler, List input, int index) { - int count = 0; - int stackDelta = 0; - while (index + count < input.Count && - input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushResource }) { - count++; - } - - List args = new List(count + 1); - args.Add(new AnnotatedBytecodeInteger(count, new Location())); - - for (int i = 0; i < count; i++) { - AnnotatedBytecodeInstruction instruction = (AnnotatedBytecodeInstruction)(input[index + i]); - args.Add(instruction.GetArg(0)); - stackDelta++; - } - - input.RemoveRange(index, count); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNResources, stackDelta, args)); - } -} - -// PushNFloats [count] [float] ... [float] -// CreateList [count] -// -> CreateListNFloats [count] [float] ... [float] -internal sealed class CreateListNFloats : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushNFloats, - DreamProcOpcode.CreateList - ]; - } - - public bool CheckPreconditions(List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); - } - - AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); - int pushVal1 = firstInstruction.GetArg(0).Value; - int pushVal2 = secondInstruction.GetArg(0).Size; - - return pushVal1 == pushVal2; - } - - public void Apply(DMCompiler compiler, List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); - } - - AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - int pushVal1 = firstInstruction.GetArg(0).Value; - - List args = new List(pushVal1 + 1); - args.Add(new AnnotatedBytecodeInteger(pushVal1, new Location())); - args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); - - input.RemoveRange(index, 2); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNFloats, 1, args)); - } -} - -// PushNStrings [count] [string] ... [string] -// CreateList [count] -// -> CreateListNStrings [count] [string] ... [string] -internal sealed class CreateListNStrings : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushNStrings, - DreamProcOpcode.CreateList - ]; - } - - public bool CheckPreconditions(List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); - } - - AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); - int pushVal1 = firstInstruction.GetArg(0).Value; - int pushVal2 = secondInstruction.GetArg(0).Size; - - return pushVal1 == pushVal2; - } - - public void Apply(DMCompiler compiler, List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); - } - - AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - int pushVal1 = firstInstruction.GetArg(0).Value; - - List args = new List(pushVal1 + 1); - args.Add(new AnnotatedBytecodeInteger(pushVal1, new Location())); - args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); - - input.RemoveRange(index, 2); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNStrings, 1, args)); - } -} - -// PushNResources [count] [resource] ... [resource] -// CreateList [count] -// -> CreateListNResources [count] [resource] ... [resource] -internal sealed class CreateListNResources : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushNResources, - DreamProcOpcode.CreateList - ]; - } - - public bool CheckPreconditions(List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); - } - - AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - AnnotatedBytecodeInstruction secondInstruction = (AnnotatedBytecodeInstruction)(input[index + 1]); - int pushVal1 = firstInstruction.GetArg(0).Value; - int pushVal2 = secondInstruction.GetArg(0).Size; - - return pushVal1 == pushVal2; - } - - public void Apply(DMCompiler compiler, List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Index plus one is outside the bounds of the input list."); - } - - AnnotatedBytecodeInstruction firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - int pushVal1 = firstInstruction.GetArg(0).Value; - - List args = new List(pushVal1 + 1); - args.Add(new AnnotatedBytecodeInteger(pushVal1, new Location())); - args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); - - input.RemoveRange(index, 2); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNResources, 1, args)); - } -} - -// PushNRefs [count] [ref] ... [ref] -// CreateList [count] -// -> CreateListNRefs [count] [ref] ... [ref] -internal sealed class CreateListNRefs : IPeepholeOptimization { - public ReadOnlySpan GetOpcodes() { - return [ - DreamProcOpcode.PushNRefs, - DreamProcOpcode.CreateList - ]; - } - - public bool CheckPreconditions(List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index),"Bytecode index is outside the bounds of the input list."); - } - - int pushVal1 = ((AnnotatedBytecodeInstruction)input[index]).GetArg(0).Value; - int pushVal2 = ((AnnotatedBytecodeInstruction)input[index + 1]).GetArg(0).Size; - - return pushVal1 == pushVal2; - } - - public void Apply(DMCompiler compiler, List input, int index) { - if (index + 1 >= input.Count) { - throw new ArgumentOutOfRangeException(nameof(index), "Bytecode index is outside the bounds of the input list."); - } - - var firstInstruction = (AnnotatedBytecodeInstruction)(input[index]); - int pushVal1 = firstInstruction.GetArg(0).Value; - - List args = new List(1 + pushVal1); - args.Add(new AnnotatedBytecodeInteger(pushVal1, new Location())); - args.AddRange(firstInstruction.GetArgs()[1..(pushVal1+1)]); - - input.RemoveRange(index, 2); - input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.CreateListNRefs, 1, args)); - } -} - // Jump [label1] // Jump [label2] <- Dead code // -> Jump [label1] -internal sealed class RemoveJumpFollowedByJump : IPeepholeOptimization { +internal sealed class RemoveJumpFollowedByJump : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.Jump, @@ -596,7 +263,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushType [type] // IsType // -> IsTypeDirect [type] -internal sealed class IsTypeDirect : IPeepholeOptimization { +internal sealed class IsTypeDirect : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushType, @@ -622,7 +291,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // BitNot // -> PushFloat [result] -internal sealed class ConstFoldBitNot : IPeepholeOptimization { +internal sealed class ConstFoldBitNot : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -631,11 +302,11 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); var args = new List(1) {new AnnotatedBytecodeFloat(((~(int)pushVal1) & 0xFFFFFF), firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 2, + IOptimization.ReplaceInstructions(input, index, 2, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -644,7 +315,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // BitOr // -> PushFloat [result] -internal sealed class ConstFoldBitOr : IPeepholeOptimization { +internal sealed class ConstFoldBitOr : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -654,13 +327,13 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); var args = new List(1) {new AnnotatedBytecodeFloat(((int)pushVal1 | (int)pushVal2), firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -669,7 +342,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // BitAnd // -> PushFloat [result] -internal sealed class ConstFoldBitAnd : IPeepholeOptimization { +internal sealed class ConstFoldBitAnd : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -679,13 +354,13 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); var args = new List(1) {new AnnotatedBytecodeFloat(((int)pushVal1 & (int)pushVal2), firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -694,7 +369,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // Multiply // -> PushFloat [result] -internal sealed class ConstFoldMultiply : IPeepholeOptimization { +internal sealed class ConstFoldMultiply : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -704,13 +381,13 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); var args = new List(1) {new AnnotatedBytecodeFloat(pushVal1 * pushVal2, firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -719,7 +396,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // Divide // -> PushFloat [result] -internal sealed class ConstFoldDivide : IPeepholeOptimization { +internal sealed class ConstFoldDivide : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -729,15 +408,15 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); // At runtime, given "A / B" we pop B then A // In the peephole optimizer, index is "A", index+1 is "B" var args = new List(1) {new AnnotatedBytecodeFloat(pushVal1 / pushVal2, firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -746,7 +425,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // Add // -> PushFloat [result] -internal sealed class ConstFoldAdd : IPeepholeOptimization { +internal sealed class ConstFoldAdd : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -756,13 +437,13 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); var args = new List(1) {new AnnotatedBytecodeFloat(pushVal1 + pushVal2, firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -771,7 +452,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushString [constant] // Add // -> PushString [result] -internal sealed class ConstFoldAddStrings : IPeepholeOptimization { +internal sealed class ConstFoldAddStrings : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushString, @@ -789,7 +472,7 @@ public void Apply(DMCompiler compiler, List input, int index var args = new List(1) {new AnnotatedBytecodeString(combinedId, firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushString, 1, args)); } } @@ -798,7 +481,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // Subtract // -> PushFloat [result] -internal sealed class ConstFoldSubtract : IPeepholeOptimization { +internal sealed class ConstFoldSubtract : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -808,15 +493,15 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); // At runtime, given "A - B" we pop B then A // In the peephole optimizer, index is "A", index+1 is "B" var args = new List(1) {new AnnotatedBytecodeFloat(pushVal1 - pushVal2, firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -825,7 +510,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // Modulus // -> PushFloat [result] -internal sealed class ConstFoldModulus : IPeepholeOptimization { +internal sealed class ConstFoldModulus : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -835,15 +522,15 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); // At runtime, given "A % B" we pop B then A // In the peephole optimizer, index is "A", index+1 is "B" var args = new List(1) {new AnnotatedBytecodeFloat((int)pushVal1 % (int)pushVal2, firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -852,7 +539,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // Power // -> PushFloat [result] -internal sealed class ConstFoldPower : IPeepholeOptimization { +internal sealed class ConstFoldPower : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -862,15 +551,15 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); // At runtime, given "A ** B" we pop B then A // In the peephole optimizer, index is "A", index+1 is "B" var args = new List(1) {new AnnotatedBytecodeFloat(MathF.Pow(pushVal1, pushVal2), firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -879,7 +568,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushReferenceValue [ref] // -> Assign [ref] // These opcodes can be reduced to a single Assign as long as the [ref]s are the same -internal sealed class AssignAndPushReferenceValue : IPeepholeOptimization { +internal sealed class AssignAndPushReferenceValue : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.AssignNoPush, @@ -919,7 +610,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushReferenceValue [ref] // -> Append [ref] // These opcodes can be reduced to a single Append as long as the [ref]s are the same -internal sealed class AppendAndPushReferenceValue : IPeepholeOptimization { +internal sealed class AppendAndPushReferenceValue : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.AppendNoPush, @@ -959,7 +652,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // BitshiftLeft // -> PushFloat [result] -internal sealed class ConstFoldBitshiftLeft : IPeepholeOptimization { +internal sealed class ConstFoldBitshiftLeft : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -969,14 +664,14 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); // At runtime, given "A << B" we pop B then A // In the peephole optimizer, index is "A", index+1 is "B" var args = new List(1) {new AnnotatedBytecodeFloat(((int)pushVal1 << (int)pushVal2), firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } @@ -985,7 +680,9 @@ public void Apply(DMCompiler compiler, List input, int index // PushFloat [constant] // BitshiftRight // -> PushFloat [result] -internal sealed class ConstFoldBitshiftRight : IPeepholeOptimization { +internal sealed class ConstFoldBitshiftRight : IOptimization { + public OptPass OptimizationPass => OptPass.PeepholeOptimization; + public ReadOnlySpan GetOpcodes() { return [ DreamProcOpcode.PushFloat, @@ -995,14 +692,14 @@ public ReadOnlySpan GetOpcodes() { } public void Apply(DMCompiler compiler, List input, int index) { - var firstInstruction = IPeepholeOptimization.GetInstructionAndValue(input[index], out var pushVal1); - IPeepholeOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); + var firstInstruction = IOptimization.GetInstructionAndValue(input[index], out var pushVal1); + IOptimization.GetInstructionAndValue(input[index + 1], out var pushVal2); // At runtime, given "A >> B" we pop B then A // In the peephole optimizer, index is "A", index+1 is "B" var args = new List(1) {new AnnotatedBytecodeFloat(((int)pushVal1 >> (int)pushVal2), firstInstruction.Location)}; - IPeepholeOptimization.ReplaceInstructions(input, index, 3, + IOptimization.ReplaceInstructions(input, index, 3, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushFloat, 1, args)); } } diff --git a/DMCompiler/Optimizer/PeepholeOptimizer.cs b/DMCompiler/Optimizer/PeepholeOptimizer.cs index e49852bae0..aad7cd7287 100644 --- a/DMCompiler/Optimizer/PeepholeOptimizer.cs +++ b/DMCompiler/Optimizer/PeepholeOptimizer.cs @@ -3,7 +3,11 @@ namespace DMCompiler.Optimizer; -internal interface IPeepholeOptimization { +/// +/// A single peephole optimization (e.g. const fold an operator) +/// +internal interface IOptimization { + public OptPass OptimizationPass { get; } public ReadOnlySpan GetOpcodes(); public void Apply(DMCompiler compiler, List input, int index); @@ -25,44 +29,66 @@ public static void ReplaceInstructions(List input, int index } } +/// +/// The list of peephole optimizer passes in the order that they should run +/// +internal enum OptPass : byte { + PeepholeOptimization = 0, // First-pass peephole optimizations (e.g. const folding) + BytecodeCompactor = 1, // Next-pass bytecode compacting (e.g. PushNFloats and other PushN opcodes) + ListCompactor = 2 // Final-pass list compacting (e.g. PushNFloats & CreateList -> CreateListNFloats) +} + +// ReSharper disable once ClassNeverInstantiated.Global internal sealed class PeepholeOptimizer { private class OptimizationTreeEntry { - public IPeepholeOptimization? Optimization; + public IOptimization? Optimization; public Dictionary? Children; } + /// + /// The optimization passes in the order that they run + /// + private static readonly OptPass[] Passes; + /// /// Trees matching chains of opcodes to peephole optimizations /// - private static readonly Dictionary OptimizationTrees = new(); + private static readonly Dictionary[] OptimizationTrees; - /// Setup - private PeepholeOptimizer(DMCompiler compiler) { - var possibleTypes = typeof(PeepholeOptimizer).Assembly.GetTypes(); + static PeepholeOptimizer() { + Passes = (OptPass[])Enum.GetValues(typeof(OptPass)); + OptimizationTrees = new Dictionary[Passes.Length]; + for (int i = 0; i < OptimizationTrees.Length; i++) { + OptimizationTrees[i] = new Dictionary(); + } + } + + /// Setup for each + private static void GetOptimizations(DMCompiler compiler) { + var possibleTypes = typeof(IOptimization).Assembly.GetTypes(); var optimizationTypes = new List(possibleTypes.Length); + foreach (var type in possibleTypes) { - if (typeof(IPeepholeOptimization).IsAssignableFrom(type)) { + if (typeof(IOptimization).IsAssignableFrom(type) && type is { IsClass: true, IsAbstract: false }) { optimizationTypes.Add(type); } } foreach (var optType in optimizationTypes) { - if (optType.IsInterface || optType.IsAbstract) - continue; + var opt = (IOptimization)(Activator.CreateInstance(optType)!); - var opt = (IPeepholeOptimization)(Activator.CreateInstance(optType))!; var opcodes = opt.GetOpcodes(); if (opcodes.Length < 2) { compiler.ForcedError(Location.Internal, $"Peephole optimization {optType} must have at least 2 opcodes"); continue; } - if (!OptimizationTrees.TryGetValue(opcodes[0], out var treeEntry)) { + if (!OptimizationTrees[(byte)opt.OptimizationPass].TryGetValue(opcodes[0], out var treeEntry)) { treeEntry = new() { Children = new() }; - OptimizationTrees.Add(opcodes[0], treeEntry); + OptimizationTrees[(byte)opt.OptimizationPass].Add(opcodes[0], treeEntry); } for (int i = 1; i < opcodes.Length; i++) { @@ -82,6 +108,13 @@ private PeepholeOptimizer(DMCompiler compiler) { } public static void RunPeephole(DMCompiler compiler, List input) { + GetOptimizations(compiler); + foreach (var optPass in Passes) { + RunPass(compiler, (byte)optPass, input); + } + } + + private static void RunPass(DMCompiler compiler, byte pass, List input) { OptimizationTreeEntry? currentOpt = null; int optSize = 0; @@ -116,7 +149,7 @@ int AttemptCurrentOpt(int i) { if (currentOpt == null) { optSize = 1; - OptimizationTrees.TryGetValue(opcode, out currentOpt); + OptimizationTrees[pass].TryGetValue(opcode, out currentOpt); continue; }