Skip to content

Commit 55d8f1a

Browse files
authored
Merge pull request #48 from Akeit0/reduce-move
Optimize: Reduce and merge unnecessary move instructions
2 parents 2070b28 + bd5ce70 commit 55d8f1a

File tree

2 files changed

+155
-3
lines changed

2 files changed

+155
-3
lines changed

src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,150 @@ public void PushInstruction(in Instruction instruction, in SourcePosition positi
110110
instructionPositions.Add(position);
111111
}
112112

113+
/// <summary>
114+
/// Push or merge the new instruction.
115+
/// </summary>
116+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
117+
public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in SourcePosition position, ref bool incrementStackPosition)
118+
{
119+
if (instructions.Length == 0)
120+
{
121+
instructions.Add(instruction);
122+
instructionPositions.Add(position);
123+
return;
124+
}
125+
ref var lastInstruction = ref instructions.AsSpan()[^1];
126+
var opcode = instruction.OpCode;
127+
switch (opcode)
128+
{
129+
case OpCode.Move:
130+
// last A is not local variable
131+
if (lastInstruction.A != lastLocal &&
132+
// available to merge
133+
lastInstruction.A == instruction.B &&
134+
// not already merged
135+
lastInstruction.A != lastInstruction.B)
136+
{
137+
switch (lastInstruction.OpCode)
138+
{
139+
case OpCode.GetTable:
140+
case OpCode.Add:
141+
case OpCode.Sub:
142+
case OpCode.Mul:
143+
case OpCode.Div:
144+
case OpCode.Mod:
145+
case OpCode.Pow:
146+
case OpCode.Concat:
147+
{
148+
lastInstruction.A = instruction.A;
149+
incrementStackPosition = false;
150+
return;
151+
}
152+
}
153+
}
154+
break;
155+
case OpCode.GetTable:
156+
{
157+
// Merge MOVE GetTable
158+
if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A)
159+
{
160+
if (lastInstruction.A == instruction.B)
161+
{
162+
lastInstruction = Instruction.GetTable(instruction.A, lastInstruction.B, instruction.C);
163+
instructionPositions[^1] = position;
164+
incrementStackPosition = false;
165+
return;
166+
}
167+
168+
}
169+
break;
170+
}
171+
case OpCode.SetTable:
172+
{
173+
// Merge MOVE SETTABLE
174+
if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A)
175+
{
176+
var lastB = lastInstruction.B;
177+
var lastA = lastInstruction.A;
178+
if (lastB < 255 && lastA == instruction.A)
179+
{
180+
// Merge MOVE MOVE SETTABLE
181+
if (instructions.Length > 2)
182+
{
183+
ref var last2Instruction = ref instructions.AsSpan()[^2];
184+
var last2A = last2Instruction.A;
185+
if (last2Instruction.OpCode == OpCode.Move && lastLocal != last2A && instruction.C == last2A)
186+
{
187+
last2Instruction = Instruction.SetTable((byte)(lastB), instruction.B, last2Instruction.B);
188+
instructions.RemoveAtSwapback(instructions.Length - 1);
189+
instructionPositions.RemoveAtSwapback(instructionPositions.Length - 1);
190+
instructionPositions[^1] = position;
191+
incrementStackPosition = false;
192+
return;
193+
}
194+
}
195+
lastInstruction = Instruction.SetTable((byte)(lastB), instruction.B, instruction.C);
196+
instructionPositions[^1] = position;
197+
incrementStackPosition = false;
198+
return;
199+
}
200+
201+
if (lastA == instruction.C)
202+
{
203+
lastInstruction = Instruction.SetTable(instruction.A, instruction.B, lastB);
204+
instructionPositions[^1] = position;
205+
incrementStackPosition = false;
206+
return;
207+
}
208+
}
209+
else if (lastInstruction.OpCode == OpCode.GetTabUp && instructions.Length >= 2)
210+
{
211+
ref var last2Instruction = ref instructions[^2];
212+
var last2OpCode = last2Instruction.OpCode;
213+
if (last2OpCode is OpCode.LoadK or OpCode.Move)
214+
{
215+
216+
var last2A = last2Instruction.A;
217+
if (last2A != lastLocal && instruction.C == last2A)
218+
{
219+
var c = last2OpCode == OpCode.LoadK ? last2Instruction.Bx + 256 : last2Instruction.B;
220+
last2Instruction = lastInstruction;
221+
lastInstruction = instruction with { C = (ushort)c };
222+
instructionPositions[^2] = instructionPositions[^1];
223+
instructionPositions[^1] = position;
224+
incrementStackPosition = false;
225+
return;
226+
}
227+
}
228+
}
229+
break;
230+
}
231+
case OpCode.Unm:
232+
case OpCode.Not:
233+
case OpCode.Len:
234+
if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A && lastInstruction.A == instruction.B)
235+
{
236+
lastInstruction = instruction with { B = lastInstruction.B }; ;
237+
instructionPositions[^1] = position;
238+
incrementStackPosition = false;
239+
return;
240+
}
241+
break;
242+
case OpCode.Return:
243+
if (lastInstruction.OpCode == OpCode.Move && instruction.B == 2 && lastInstruction.B < 256)
244+
{
245+
lastInstruction = instruction with { A = (byte)lastInstruction.B };
246+
instructionPositions[^1] = position;
247+
incrementStackPosition = false;
248+
return;
249+
}
250+
break;
251+
}
252+
253+
instructions.Add(instruction);
254+
instructionPositions.Add(position);
255+
}
256+
113257
/// <summary>
114258
/// Gets the index of the constant from the value, or if the constant is not registered it is added and its index is returned.
115259
/// </summary>

src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public static void Return(ScopeCompilationContext context)
3333
readonly Dictionary<ReadOnlyMemory<char>, LocalVariableDescription> localVariables = new(256, Utf16StringMemoryComparer.Default);
3434
readonly Dictionary<ReadOnlyMemory<char>, LabelDescription> labels = new(32, Utf16StringMemoryComparer.Default);
3535

36+
byte lastLocalVariableIndex;
37+
3638
public byte StackStartPosition { get; private set; }
3739
public byte StackPosition { get; set; }
3840

@@ -72,8 +74,11 @@ public FunctionCompilationContext CreateChildFunction()
7274
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7375
public void PushInstruction(in Instruction instruction, SourcePosition position, bool incrementStackPosition = false)
7476
{
75-
Function.PushInstruction(instruction, position);
76-
if (incrementStackPosition) StackPosition++;
77+
Function.PushOrMergeInstruction(lastLocalVariableIndex, instruction, position, ref incrementStackPosition);
78+
if (incrementStackPosition)
79+
{
80+
StackPosition++;
81+
}
7782
}
7883

7984
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -89,11 +94,13 @@ public void TryPushCloseUpValue(byte top, SourcePosition position)
8994
/// Add new local variable.
9095
/// </summary>
9196
[MethodImpl(MethodImplOptions.AggressiveInlining)]
92-
public void AddLocalVariable(ReadOnlyMemory<char> name, LocalVariableDescription description)
97+
public void AddLocalVariable(ReadOnlyMemory<char> name, LocalVariableDescription description, bool markAsLastLocalVariable = true)
9398
{
9499
localVariables[name] = description;
100+
lastLocalVariableIndex = description.RegisterIndex;
95101
}
96102

103+
97104
/// <summary>
98105
/// Gets the local variable in scope.
99106
/// </summary>
@@ -157,6 +164,7 @@ public void Reset()
157164
HasCapturedLocalVariables = false;
158165
localVariables.Clear();
159166
labels.Clear();
167+
lastLocalVariableIndex = 0;
160168
}
161169

162170
/// <summary>

0 commit comments

Comments
 (0)