Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Peephole optimizer & proc decoder fixes #2054

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 34 additions & 41 deletions DMCompiler/Optimizer/PeepholeOptimizations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ public void Apply(List<IAnnotatedBytecode> input, int index) {
// 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<DreamProcOpcode> GetOpcodes() {
return [
Expand All @@ -267,13 +269,39 @@ public void Apply(List<IAnnotatedBytecode> input, int index) {
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]);
AnnotatedBytecodeString pushVal1 = firstInstruction.GetArg<AnnotatedBytecodeString>(0);
AnnotatedBytecodeFloat pushVal2 = secondInstruction.GetArg<AnnotatedBytecodeFloat>(0);
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++;
}

input.RemoveRange(index, 2);
input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushStringFloat, [pushVal1, pushVal2]));
// 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));
}
}

Expand Down Expand Up @@ -329,41 +357,6 @@ public void Apply(List<IAnnotatedBytecode> input, int index) {
}
}

// PushStringFloat [string] [float]
// ...
// PushStringFloat [string] [float]
// -> PushArbitraryNOfStringFloat [count] [string] [float] ... [string] [float]
internal sealed class PushNOfStringFloat : IPeepholeOptimization {
public ReadOnlySpan<DreamProcOpcode> GetOpcodes() {
return [
DreamProcOpcode.PushStringFloat,
DreamProcOpcode.PushStringFloat
];
}

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

List<IAnnotatedBytecode> args = new List<IAnnotatedBytecode>(2 * 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));
args.Add(instruction.GetArg(1));
stackDelta += 2;
}

input.RemoveRange(index, count);
input.Insert(index, new AnnotatedBytecodeInstruction(DreamProcOpcode.PushNOfStringFloats, stackDelta, args));
}
}

// PushResource [resource]
// ...
// PushResource [resource]
Expand Down
4 changes: 3 additions & 1 deletion DMCompiler/Optimizer/PeepholeOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ int AttemptCurrentOpt(int i) {

if (currentOpt.Optimization?.CheckPreconditions(input, i - optSize) is true) {
currentOpt.Optimization.Apply(input, i - optSize);
offset = (optSize + 1); // Run over the new opcodes for potential further optimization
offset = (optSize + 2); // Run over the new opcodes for potential further optimization
} else {
// This chain of opcodes did not lead to a valid optimization.
// Start again from the opcode after the first.
Expand All @@ -108,6 +108,7 @@ int AttemptCurrentOpt(int i) {
var bytecode = input[i];
if (bytecode is not AnnotatedBytecodeInstruction instruction) {
i -= AttemptCurrentOpt(i);
i = Math.Max(i, 0);
continue;
}

Expand All @@ -126,6 +127,7 @@ int AttemptCurrentOpt(int i) {
}

i -= AttemptCurrentOpt(i);
i = Math.Max(i, 0);
}

AttemptCurrentOpt(input.Count);
Expand Down
27 changes: 27 additions & 0 deletions OpenDreamRuntime/Procs/ProcDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public ITuple DecodeInstruction() {
case DreamProcOpcode.OutputReference:
case DreamProcOpcode.PushReferenceValue:
case DreamProcOpcode.PopReference:
case DreamProcOpcode.NullRef:
case DreamProcOpcode.AssignNoPush:
case DreamProcOpcode.ReturnReferenceValue:
return (opcode, ReadReference());
Expand Down Expand Up @@ -202,6 +203,19 @@ public ITuple DecodeInstruction() {
return (opcode, values);
}

case DreamProcOpcode.PushNOfStringFloats: {
var count = ReadInt();
var strings = new string[count];
var floats = new float[count];

for (int i = 0; i < count; i++) {
strings[i] = ReadString();
floats[i] = ReadFloat();
}

return (opcode, strings, floats);
}

case DreamProcOpcode.CreateListNResources:
case DreamProcOpcode.PushNResources: {
var count = ReadInt();
Expand Down Expand Up @@ -332,6 +346,19 @@ or DreamProcOpcode.JumpIfTrueReference
break;
}

case (DreamProcOpcode.PushNOfStringFloats, string[] strings, float[] floats): {
// The length of both arrays are equal
for (var index = 0; index < strings.Length; index++) {
text.Append($"\"{strings[index]}\"");
text.Append(' ');
text.Append(floats[index]);
if(index + 1 < strings.Length) // Don't leave a trailing space
text.Append(' ');
}

break;
}

default:
for (int i = 1; i < instruction.Length; ++i) {
var arg = instruction[i];
Expand Down
Loading