Skip to content

Commit

Permalink
Refactor the PeepholeOptimizer into arbitrary passes (#2069)
Browse files Browse the repository at this point in the history
Co-authored-by: ike709 <[email protected]>
  • Loading branch information
ike709 and ike709 authored Nov 21, 2024
1 parent 1f067c8 commit 12b9f6e
Show file tree
Hide file tree
Showing 4 changed files with 537 additions and 428 deletions.
1 change: 1 addition & 0 deletions DMCompiler/Optimizer/BytecodeOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal void Optimize(DMCompiler compiler, List<IAnnotatedBytecode> input) {
RemoveUnreferencedLabels(input);
JoinAndForwardLabels(input);
RemoveUnreferencedLabels(input);

PeepholeOptimizer.RunPeephole(compiler, input);
}

Expand Down
378 changes: 378 additions & 0 deletions DMCompiler/Optimizer/CompactorOptimizations.cs
Original file line number Diff line number Diff line change
@@ -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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushString,
DreamProcOpcode.PushString
];
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> input, int index) {
int count = 0;
int stackDelta = 0;

while (index + count < input.Count &&
input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushString }) {
count++;
}

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushFloat,
DreamProcOpcode.PushFloat
];
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> input, int index) {
int count = 0;
int stackDelta = 0;

while (index + count < input.Count &&
input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushFloat }) {
count++;
}

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushReferenceValue,
DreamProcOpcode.PushReferenceValue
];
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> input, int index) {
int count = 0;
int stackDelta = 0;

while (index + count < input.Count &&
input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushReferenceValue }) {
count++;
}

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushString,
DreamProcOpcode.PushFloat
];
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> 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<AnnotatedBytecodeString>(0);
AnnotatedBytecodeFloat pushVal2 = secondInstruction.GetArg<AnnotatedBytecodeFloat>(0);

input.RemoveRange(index, 2);
input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushStringFloat, [pushVal1, pushVal2]));
return;
}

// Otherwise, replace with PushNOfStringFloat

int stackDelta = 0;
List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<AnnotatedBytecodeString>(0));
args.Add(floatInstruction.GetArg<AnnotatedBytecodeFloat>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushResource,
DreamProcOpcode.PushResource
];
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> input, int index) {
int count = 0;
int stackDelta = 0;
while (index + count < input.Count &&
input[index + count] is AnnotatedBytecodeInstruction { Opcode: DreamProcOpcode.PushResource }) {
count++;
}

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushNFloats,
DreamProcOpcode.CreateList
];
}

public bool CheckPreconditions(List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;
int pushVal2 = secondInstruction.GetArg<AnnotatedBytecodeListSize>(0).Size;

return pushVal1 == pushVal2;
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushNStrings,
DreamProcOpcode.CreateList
];
}

public bool CheckPreconditions(List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;
int pushVal2 = secondInstruction.GetArg<AnnotatedBytecodeListSize>(0).Size;

return pushVal1 == pushVal2;
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushNResources,
DreamProcOpcode.CreateList
];
}

public bool CheckPreconditions(List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;
int pushVal2 = secondInstruction.GetArg<AnnotatedBytecodeListSize>(0).Size;

return pushVal1 == pushVal2;
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushNRefs,
DreamProcOpcode.CreateList
];
}

public bool CheckPreconditions(List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;
int pushVal2 = ((AnnotatedBytecodeInstruction)input[index + 1]).GetArg<AnnotatedBytecodeListSize>(0).Size;

return pushVal1 == pushVal2;
}

public void Apply(DMCompiler compiler, List<IAnnotatedBytecode> 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<AnnotatedBytecodeInteger>(0).Value;

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(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
Loading

0 comments on commit 12b9f6e

Please sign in to comment.