Function debug information is insufficient to associate local variables to registers?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Function debug information is insufficient to associate local variables to registers?

David W
I've been exploring the output of luac (across multiple versions), and I'm coming to the conclusion that there isn't enough information in the dump output to map registers to local variables, or at least, not via any simple algorithm. Here's a simple example:

do  -- Creates three locals in slots 1-3
  local a
  local b
  local c
end
--[[
Reuses slots 1-3, but creates three new local records.
Since it's one statement, the scope of these doesn't begin until the final
VM instruction, which makes it virtually impossible to tease out which
register each actually belongs to.
]]
local d, e, f = 42, math.sin(1) + 2, "hello " .. "world"

And here's the output of luac5.3 -l -l:

main <locals.lua:0,0> (11 instructions at 0x7f49beaffc90)
0+ params, 4 slots, 1 upvalue, 6 locals, 7 constants, 0 functions
        1       [3]     LOADNIL         0 2
        2       [13]    LOADK           0 -1    ; 42
        3       [13]    GETTABUP        1 0 -2  ; _ENV "math"
        4       [13]    GETTABLE        1 1 -3  ; "sin"
        5       [13]    LOADK           2 -4    ; 1
        6       [13]    CALL            1 2 2
        7       [13]    ADD             1 1 -5  ; - 2
        8       [13]    LOADK           2 -6    ; "hello "
        9       [13]    LOADK           3 -7    ; "world"
        10      [13]    CONCAT          2 2 3
        11      [13]    RETURN          0 1
constants (7) for 0x7f49beaffc90:
        1       42
        2       "math"
        3       "sin"
        4       1
        5       2
        6       "hello "
        7       "world"
locals (6) for 0x7f49beaffc90:
        0       a       2       2
        1       b       2       2
        2       c       2       2
        3       d       11      12
        4       e       11      12
        5       f       11      12
upvalues (1) for 0x7f49beaffc90:
        0       _ENV    1       0


The thing to note here is the range on the variables. Locals a, b, and c are shown as starting their lifetime at instruction 2 and having a 0-length scope, which is basically correct. (You can quibble about whether their lifetime should start with instruction 2 or 1.) But d, e and f are all shown as starting at 11 and ending at 11, which is wrong for everything except f.

I don't think that with just this set of ranges and these opcodes, you can algorithmically deduce that (say) local e is assigned to register 2. There *is* still enough information to see when locals are going out of scope, but since the scope of temporary registers isn't part of the debug info, you can't use that to help reconstruct the numbering either.

--
=D ave
Reply | Threaded
Open this post in threaded view
|

Re: Function debug information is insufficient to associate local variables to registers?

Egor Skriptunoff-2
On Sun, Nov 15, 2020 at 12:30 PM David W wrote:
Locals a, b, and c are shown as starting their lifetime at instruction 2 and having a 0-length scope, which is basically correct. (You can quibble about whether their lifetime should start with instruction 2 or 1.)

A local variable has a "definition" and a "lifetime".
"Definition" is the instruction where an initial value has been assigned to the variable.
"Lifetime" is the range of instructions inside the scope of a local variable, that is, where it can be used in RHS expressions.
Luac output shows only "lifetime".
For variable "a" the "definition" is the instruction #1, and "lifetime" is the empty range of instructions starting from instruction #2.

 
But d, e and f are all shown as starting at 11 and ending at 11, which is wrong for everything except f.

The Lua manual says:
"The scope of a local variable begins at the first statement after its declaration"
This means that "lifetime" of a local variable starts from the first instruction of the next Lua statement after the local variable definition.
The instructions #2-10 belong to "definition" (Lua statement "local d, e, f = ...."), and "lifetime" starts from instruction #11.

 
I don't think that with just this set of ranges and these opcodes, you can algorithmically deduce that (say) local e is assigned to register 2.

The algorithm:
at the start of "lifetime" of new local variables their VM registers are numbered sequentially starting from the first unused VM register.
At the instruction #11 (the start of "lifetime" of variables d,e,f) the VM registers 0-2 are already unused because the previous variables holding these registers (a,b,c) are out of scope (their "lifetime" has ended at instruction #2).
Reply | Threaded
Open this post in threaded view
|

Re: Function debug information is insufficient to associate local variables to registers?

David W
On Sun, Nov 15, 2020, 03:31 Egor Skriptunoff <[hidden email]> wrote:
On Sun, Nov 15, 2020 at 12:30 PM David W wrote:
I don't think that with just this set of ranges and these opcodes, you can algorithmically deduce that (say) local e is assigned to register 2.

The algorithm:
at the start of "lifetime" of new local variables their VM registers are numbered sequentially starting from the first unused VM register.
At the instruction #11 (the start of "lifetime" of variables d,e,f) the VM registers 0-2 are already unused because the previous variables holding these registers (a,b,c) are out of scope (their "lifetime" has ended at instruction #2).

I see! This relies on the simplicity of Lua's code generation, specifically that temporary registers never have a lifetime longer than a single expression. It requires an O(N) loop to basically redo the register assignment in the debugger/decompiler, without ever looking at the instructions.

I think there may actually be a constant-ish time algorithm, now that I've thought about it some more: the register for a local is the destination register of the instruction that assigns it, which is usually immediately before the start of its lifetime. However, if the next N locals share the same startpc, then you have to subtract N from your calculated register. And function params are a slight special case, since there is no instruction that assigns them.