Skip to content

Commit 2e1763c

Browse files
committed
Optimize code builder variable/function lookups
1 parent 36ebeba commit 2e1763c

File tree

3 files changed

+98
-22
lines changed

3 files changed

+98
-22
lines changed

UndertaleModLib/Compiler/CodeBuilder.cs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using Underanalyzer;
34
using Underanalyzer.Compiler;
45
using Underanalyzer.Compiler.Bytecode;
@@ -16,9 +17,34 @@ internal class CodeBuilder : ICodeBuilder
1617
// Data being used with this code builder.
1718
private readonly GlobalDecompileContext _globalContext;
1819

20+
// Lookups for variables and functions.
21+
private readonly Dictionary<(UndertaleString, UndertaleInstruction.InstanceType), UndertaleVariable> _variableLookup;
22+
private readonly Dictionary<string, UndertaleFunction> _functionLookup;
23+
1924
public CodeBuilder(GlobalDecompileContext globalContext)
2025
{
2126
_globalContext = globalContext;
27+
28+
// Make variable and function lookups (to not rely on O(n) searches)
29+
IList<UndertaleVariable> variables = _globalContext.Data.Variables;
30+
_variableLookup = new(variables.Count);
31+
foreach (UndertaleVariable v in variables)
32+
{
33+
if (v is null)
34+
{
35+
continue;
36+
}
37+
_variableLookup.TryAdd((v.Name, v.InstanceType), v);
38+
}
39+
IList<UndertaleFunction> functions = _globalContext.Data.Functions;
40+
_functionLookup = new(functions.Count);
41+
foreach (UndertaleFunction f in functions)
42+
{
43+
if (f?.Name?.Content is string name)
44+
{
45+
_functionLookup.TryAdd(name, f);
46+
}
47+
}
2248
}
2349

2450
/// <summary>
@@ -262,8 +288,18 @@ public void PatchInstruction(IGMInstruction instruction, string variableName, In
262288
// Register/define non-local variable, and update variable on instruction immediately
263289
_globalContext.CurrentCompileGroup.RegisterNonLocalVariable(variableName);
264290
UndertaleString nameString = _globalContext.CurrentCompileGroup.MakeString(variableName, out int nameStringId);
265-
utInstruction.ValueVariable = _globalContext.Data.Variables.EnsureDefined(
266-
nameString, nameStringId, (UndertaleInstruction.InstanceType)variableInstanceType, isBuiltin, _globalContext.Data);
291+
UndertaleInstruction.InstanceType actualInstType =
292+
_globalContext.Data.Variables.CalculateInstType((UndertaleInstruction.InstanceType)variableInstanceType, isBuiltin, _globalContext.Data);
293+
if (_variableLookup.TryGetValue((nameString, actualInstType), out UndertaleVariable existingVariable))
294+
{
295+
utInstruction.ValueVariable = existingVariable;
296+
}
297+
else
298+
{
299+
UndertaleVariable newVariable = _globalContext.Data.Variables.Define(nameString, nameStringId, actualInstType, isBuiltin, _globalContext.Data);
300+
_variableLookup.Add((nameString, actualInstType), newVariable);
301+
utInstruction.ValueVariable = newVariable;
302+
}
267303
}
268304

269305
// Update other parts of instruction
@@ -288,15 +324,31 @@ public void PatchInstruction(IGMInstruction instruction, FunctionScope scope, st
288324
}
289325
else if (_globalContext.Builtins.LookupBuiltinFunction(functionName) is not null)
290326
{
291-
reference = _globalContext.Data.Functions.EnsureDefined(functionName, _globalContext.Data.Strings);
327+
if (_functionLookup.TryGetValue(functionName, out UndertaleFunction existingFunction))
328+
{
329+
reference = existingFunction;
330+
}
331+
else
332+
{
333+
reference = _globalContext.Data.Functions.Define(functionName, _globalContext.Data.Strings);
334+
_functionLookup.Add(functionName, reference);
335+
}
292336
}
293337
else if (_globalContext.GlobalFunctions.TryGetFunction(functionName, out IGMFunction function))
294338
{
295339
reference = (UndertaleFunction)function;
296340
}
297341
else if (_globalContext.GetScriptId(functionName, out int _))
298342
{
299-
reference = _globalContext.Data.Functions.EnsureDefined(functionName, _globalContext.Data.Strings);
343+
if (_functionLookup.TryGetValue(functionName, out UndertaleFunction existingFunction))
344+
{
345+
reference = existingFunction;
346+
}
347+
else
348+
{
349+
reference = _globalContext.Data.Functions.Define(functionName, _globalContext.Data.Strings);
350+
_functionLookup.Add(functionName, reference);
351+
}
300352
}
301353
else
302354
{

UndertaleModLib/Decompiler/GlobalDecompileContext.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,10 @@ public void PrepareForCompilation(bool forceReloadAssets = true)
200200
Builtins ??= (Data.BuiltinList ??= new BuiltinList(Data));
201201

202202
// Initialize code builder if not already done
203-
CodeBuilder = new CodeBuilder(this);
203+
if (CodeBuilder is null || forceReloadAssets)
204+
{
205+
CodeBuilder = new CodeBuilder(this);
206+
}
204207

205208
// Reload asset lists if necessary
206209
if (forceReloadAssets || _assetIdLookup is null || _scriptIdLookup is null)

UndertaleModLib/UndertaleDataExtensionMethods.cs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -191,23 +191,29 @@ public static UndertaleString MakeString(this IList<UndertaleString> list, strin
191191
return newString;
192192
}
193193

194+
public static UndertaleFunction Define(this IList<UndertaleFunction> list, string name, IList<UndertaleString> strg)
195+
{
196+
UndertaleString str = strg.MakeString(name, out int id);
197+
UndertaleFunction func = new()
198+
{
199+
Name = str,
200+
NameStringID = id
201+
};
202+
list.Add(func);
203+
return func;
204+
}
205+
194206
public static UndertaleFunction EnsureDefined(this IList<UndertaleFunction> list, string name, IList<UndertaleString> strg)
195207
{
196208
UndertaleFunction func = list.ByName(name);
197209
if (func is null)
198210
{
199-
var str = strg.MakeString(name, out int id);
200-
func = new UndertaleFunction()
201-
{
202-
Name = str,
203-
NameStringID = id
204-
};
205-
list.Add(func);
211+
return list.Define(name, strg);
206212
}
207213
return func;
208214
}
209215

210-
public static UndertaleVariable EnsureDefined(this IList<UndertaleVariable> list, UndertaleString nameString, int nameStringId, UndertaleInstruction.InstanceType inst, bool isBuiltin, UndertaleData data)
216+
public static UndertaleInstruction.InstanceType CalculateInstType(this IList<UndertaleVariable> list, UndertaleInstruction.InstanceType inst, bool isBuiltin, UndertaleData data)
211217
{
212218
// Local variables are defined distinctly
213219
if (inst == UndertaleInstruction.InstanceType.Local)
@@ -229,17 +235,14 @@ public static UndertaleVariable EnsureDefined(this IList<UndertaleVariable> list
229235
inst = UndertaleInstruction.InstanceType.Undefined;
230236
}
231237

232-
// Search for existing variable that can be used
233-
foreach (UndertaleVariable variable in list)
234-
{
235-
if (variable.Name == nameString && variable.InstanceType == inst)
236-
{
237-
return variable;
238-
}
239-
}
238+
return inst;
239+
}
240240

241-
// Otherwise, make a new variable. Update variables counts first.
241+
public static UndertaleVariable Define(this IList<UndertaleVariable> list, UndertaleString nameString, int nameStringId, UndertaleInstruction.InstanceType inst, bool isBuiltin, UndertaleData data)
242+
{
243+
// Update variables counts first
242244
uint oldId = data.VarCount1;
245+
bool bytecode14 = data.GeneralInfo.BytecodeVersion <= 14;
243246
if (!bytecode14)
244247
{
245248
if (data.IsVersionAtLeast(2, 3))
@@ -286,6 +289,24 @@ public static UndertaleVariable EnsureDefined(this IList<UndertaleVariable> list
286289
return newVariable;
287290
}
288291

292+
public static UndertaleVariable EnsureDefined(this IList<UndertaleVariable> list, UndertaleString nameString, int nameStringId, UndertaleInstruction.InstanceType inst, bool isBuiltin, UndertaleData data)
293+
{
294+
// Calculate correct instance type
295+
inst = list.CalculateInstType(inst, isBuiltin, data);
296+
297+
// Search for existing variable that can be used
298+
foreach (UndertaleVariable variable in list)
299+
{
300+
if (variable.Name == nameString && variable.InstanceType == inst)
301+
{
302+
return variable;
303+
}
304+
}
305+
306+
// Otherwise, make a new variable
307+
return list.Define(nameString, nameStringId, inst, isBuiltin, data);
308+
}
309+
289310
public static UndertaleVariable DefineLocal(this IList<UndertaleVariable> list, UndertaleData data, int varId, UndertaleString nameString, int nameStringId)
290311
{
291312
// In bytecode 14, look up on entire variable list for existing locals...

0 commit comments

Comments
 (0)