From 4282223db15e7ff0f0af38126a67a578d63e86df Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:10:54 +0000 Subject: [PATCH 1/3] Initial changes for DUPCLOSURE support --- Source.lua | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Source.lua b/Source.lua index 1bf635f..5acf90b 100644 --- a/Source.lua +++ b/Source.lua @@ -161,6 +161,7 @@ local function luau_newsettings() generalizedIteration = true, allowProxyErrors = false, useImportConstants = false, + reuseClosures = false, staticEnvironment = {}, decodeOp = function(op) return op end } @@ -179,6 +180,7 @@ local function luau_validatesettings(luau_settings) assert(type(luau_settings.allowProxyErrors) == "boolean", "luau_settings.allowProxyErrors should be a boolean") assert(type(luau_settings.staticEnvironment) == "table", "luau_settings.staticEnvironment should be a table") assert(type(luau_settings.useImportConstants) == "boolean", "luau_settings.useImportConstants should be a boolean") + assert(type(luau_settings.reuseClosures) == "boolean", "luau_settings.reuseClosures should be a boolean") assert(type(luau_settings.decodeOp) == "function", "luau_settings.decodeOp should be a function") end @@ -704,7 +706,7 @@ local function luau_load(module, env, luau_settings) --// Do nothing elseif op == 1 then --[[ BREAK ]] if breakHook then - local results = table.pack(breakHook(stack, debugging, proto, module, upvals)) + local results = table_pack(breakHook(stack, debugging, proto, module, upvals)) if results[1] then return table_unpack(results, 2, #results) @@ -1192,8 +1194,22 @@ local function luau_load(module, env, luau_settings) table_move(varargs.list, 1, b, A, stack) elseif op == 64 then --[[ DUPCLOSURE ]] - local newPrototype = protolist[inst.K + 1] --// correct behavior would be to reuse the prototype if possible but it would not be useful here + local K = inst.K + + local deduplicated = false + local originalClosure + local originalUpvalues + + if luau_settings.reuseClosures then + originalClosure = K.Closure + if originalClosure then + deduplicated = true + originalUpvalues = K.Upvalues + end + end + local newPrototype = protolist[K.Index + 1] --// correct behavior would be to reuse the prototype if possible but it would not be useful here + local nups = newPrototype.nups local upvalues = table_create(nups) stack[inst.A] = luau_wrapclosure(module, newPrototype, upvalues) From a7e32da32788a6c57488b602dc6f32a756e25a16 Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:04:30 +0000 Subject: [PATCH 2/3] Add closure deduplication --- Source.lua | 98 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/Source.lua b/Source.lua index 5acf90b..b7efdf4 100644 --- a/Source.lua +++ b/Source.lua @@ -4,6 +4,7 @@ local pcall = pcall local error = error local tonumber = tonumber local assert = assert +local rawequal = rawequal local setmetatable = setmetatable local string_format = string.format @@ -58,6 +59,9 @@ local ttisfunction = function(v) return type(v) == "function" end -- 4 = AUX import -- 5 = AUX boolean low 1 bit -- 6 = AUX number low 24 bits +-- 7 = B +-- 8 = AUX number low 16 bits +-- 9 = CLOSURE -- // HAS_AUX boolean specifies whether the instruction is followed up with an AUX word, which may be used to execute the instruction. local opList = { @@ -125,7 +129,7 @@ local opList = { { "FORGPREP_NEXT", 4, 0, false }, { "DEP_FORGLOOP_NEXT", 0, 0, false }, { "GETVARARGS", 2, 0, false }, - { "DUPCLOSURE", 4, 3, false }, + { "DUPCLOSURE", 4, 9, false }, { "PREPVARARGS", 1, 0, false }, { "LOADKX", 1, 1, true }, { "JUMPX", 5, 0, false }, @@ -419,6 +423,8 @@ local function luau_deserialize(bytecode, luau_settings) inst.K = k[inst.B + 1] elseif kmode == 8 then --// AUX number low 16 bits inst.K = bit32_band(inst.aux, 0xf) + elseif kmode == 9 then --// CLOSURE + inst.K = { Index = k[inst.D + 1] } end end @@ -1194,6 +1200,7 @@ local function luau_load(module, env, luau_settings) table_move(varargs.list, 1, b, A, stack) elseif op == 64 then --[[ DUPCLOSURE ]] + local A = inst.A local K = inst.K local deduplicated = false @@ -1208,29 +1215,82 @@ local function luau_load(module, env, luau_settings) end end - local newPrototype = protolist[K.Index + 1] --// correct behavior would be to reuse the prototype if possible but it would not be useful here - + local newPrototype = protolist[K.Index + 1] local nups = newPrototype.nups - local upvalues = table_create(nups) - stack[inst.A] = luau_wrapclosure(module, newPrototype, upvalues) - for i = 1, nups do - local pseudo = code[pc] - pc += 1 + local fallback = true - local type = pseudo.A - if type == 0 then --// value - local upvalue = { - value = stack[pseudo.B], - index = "value",--// self reference - } - upvalue.store = upvalue + if deduplicated then + stack[inst.A] = originalClosure - upvalues[i] = upvalue + local temporaryUpvalues = { } + local tpc = pc - --// references dont get handled by DUPCLOSURE - elseif type == 2 then --// upvalue - upvalues[i] = upvals[pseudo.B + 1] + for i = 1, nups do + local pseudo = code[tpc] + tpc += 1 + + local type = pseudo.A + if type == 0 then --// value + local upvalue = { + value = stack[pseudo.B], + index = "value",--// self reference + } + upvalue.store = upvalue + + temporaryUpvalues[i] = upvalue + + --// references dont get handled by DUPCLOSURE + elseif type == 2 then --// upvalue + temporaryUpvalues[i] = upvals[pseudo.B + 1] + end + end + + local isequal = true + + for i, upv in originalUpvalues do + local tuv = temporaryUpvalues[i] + if rawequal(upv.store[upv.index], tuv.store[tuv.index]) then + isequal = false + break + end + end + + if isequal then + fallback = false + pc = tpc + end + end + + if fallback then + local upvalues = table_create(nups) + local closure = luau_wrapclosure(module, newPrototype, upvalues) + + stack[A] = closure + + if luau_settings.reuseClosures then + K.Closure = closure + K.Upvalues = upvalues + end + + for i = 1, nups do + local pseudo = code[pc] + pc += 1 + + local type = pseudo.A + if type == 0 then --// value + local upvalue = { + value = stack[pseudo.B], + index = "value",--// self reference + } + upvalue.store = upvalue + + upvalues[i] = upvalue + + --// references dont get handled by DUPCLOSURE + elseif type == 2 then --// upvalue + upvalues[i] = upvals[pseudo.B + 1] + end end end elseif op == 65 then --[[ PREPVARARGS ]] From 4a3727d34934d45010a6225891345a986d5c8a55 Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:08:05 +0000 Subject: [PATCH 3/3] Small changes --- Source.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Source.lua b/Source.lua index b7efdf4..0b43c1d 100644 --- a/Source.lua +++ b/Source.lua @@ -1206,8 +1206,10 @@ local function luau_load(module, env, luau_settings) local deduplicated = false local originalClosure local originalUpvalues - - if luau_settings.reuseClosures then + + local reuseClosures = luau_settings.reuseClosures + + if reuseClosures then originalClosure = K.Closure if originalClosure then deduplicated = true @@ -1221,7 +1223,7 @@ local function luau_load(module, env, luau_settings) local fallback = true if deduplicated then - stack[inst.A] = originalClosure + stack[A] = originalClosure local temporaryUpvalues = { } local tpc = pc @@ -1265,10 +1267,10 @@ local function luau_load(module, env, luau_settings) if fallback then local upvalues = table_create(nups) local closure = luau_wrapclosure(module, newPrototype, upvalues) - + stack[A] = closure - if luau_settings.reuseClosures then + if reuseClosures then K.Closure = closure K.Upvalues = upvalues end