A question about TFORCALL and locals

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

A question about TFORCALL and locals

Bas Groothedde

Hello list,

I'm working on a website similar to Godbolt.org for Lua, and I'm trying to get as much information in the decompilation as possible to make the instructions as clear as possible. I'm now looking at the TFORCALL instruction in Lua 5.4 and Lua 5.3, which has me a bit confused. I might be missing something obvious.

Consider the next code and luac -p -l -l output:

 Input:

local t = {};

for key, value in pairs(t) do
        print(key, value);
end

for test1, test2, test3, test4 in somepairs(t) do
        print(test1, test2, test3, test4);
end

Output:

main <TFORCALL.lua:0,0> (28 instructions at 0x559fb6d9aca0)
0+ params, 14 slots, 1 upvalue, 15 locals, 3 constants, 0 functions
        1       [1]     VARARGPREP      0
        2       [1]     NEWTABLE        0 0 0
        3       [1]     EXTRAARG        0
        4       [3]     GETTABUP        1 0 0   ; _ENV "pairs"
        5       [3]     MOVE            2 0
        6       [3]     CALL            1 2 5   ; 1 in 4 out
        7       [3]     TFORPREP        1 4     ; to 12
        8       [4]     GETTABUP        7 0 1   ; _ENV "print"
        9       [4]     MOVE            8 5
        10      [4]     MOVE            9 6
        11      [4]     CALL            7 3 1   ; 2 in 0 out
        12      [3]     TFORCALL        1 2
        13      [3]     TFORLOOP        1 6     ; to 8
        14      [5]     CLOSE           1
        15      [7]     GETTABUP        1 0 2   ; _ENV "somepairs"
        16      [7]     MOVE            2 0
        17      [7]     CALL            1 2 5   ; 1 in 4 out
        18      [7]     TFORPREP        1 6     ; to 25
        19      [8]     GETTABUP        9 0 1   ; _ENV "print"
        20      [8]     MOVE            10 5
        21      [8]     MOVE            11 6
        22      [8]     MOVE            12 7
        23      [8]     MOVE            13 8
        24      [8]     CALL            9 5 1   ; 4 in 0 out
        25      [7]     TFORCALL        1 4
        26      [7]     TFORLOOP        1 8     ; to 19
        27      [9]     CLOSE           1
        28      [9]     RETURN          1 1 1   ; 0 out
constants (3) for 0x559fb6d9aca0:
        0       S       "pairs"
        1       S       "print"
        2       S       "somepairs"
locals (15) for 0x559fb6d9aca0:
        0       t               4       29
        1       (for state)     7       15
        2       (for state)     7       15
        3       (for state)     7       15
        4       (for state)     7       15
        5       key             8       12
        6       value           8       12
        7       (for state)     18      28
        8       (for state)     18      28
        9       (for state)     18      28
        10      (for state)     18      28
        11      test1           19      25
        12      test2           19      25
        13      test3           19      25
        14      test4           19      25
upvalues (1) for 0x559fb6d9aca0:
        0       _ENV            1       0


For the case of Lua 5.4, in TFORCALL R(A) holds the generator, R(A + 1) the state, R(A + 2) the control variable and R(A + 3) the to be closed variable.
R(A + 4) through R(A + 3 + C) will hold the local values specified in the for loop, returned by the generator. Am I correct about this?

If so, usually when a register is referring to a local then the local can easily be resolved by looking up the value of A in the locals list for that function. Am
I correct that in the case of TFORCALL, A is relative to the first local that the PC of TFORCALL falls within? (TFORCALL PC >= local startpc && TFORCALL PC <= local endpc)

Should this always be the approach of resolving a local referenced in a register? Please correct me if I'm horribly wrong, I want to learn about Lua bytecode.

Thanks,
Bas
Reply | Threaded
Open this post in threaded view
|

Re: A question about TFORCALL and locals

Roberto Ierusalimschy
> For the case of Lua 5.4, in TFORCALL R(A) holds the generator, R(A + 1)
> the state, R(A + 2) the control variable and R(A + 3) the to be closed
> variable.
> R(A + 4) through R(A + 3 + C) will hold the local values specified in
> the for loop, returned by the generator. Am I correct about this?

Yes.


> If so, usually when a register is referring to a local then the local
> can easily be resolved by looking up the value of A in the locals list
> for that function. Am
> I correct that in the case of TFORCALL, A is relative to the first local
> that the PC of TFORCALL falls within? (TFORCALL PC >= local startpc &&
> TFORCALL PC <= local endpc)

I did not understand your question here.

-- Roberto

Reply | Threaded
Open this post in threaded view
|

Re: A question about TFORCALL and locals

Bas Groothedde

On 18-12-2019 18:53, Roberto Ierusalimschy wrote:

For the case of Lua 5.4, in TFORCALL R(A) holds the generator, R(A + 1)
the state, R(A + 2) the control variable and R(A + 3) the to be closed
variable.
R(A + 4) through R(A + 3 + C) will hold the local values specified in
the for loop, returned by the generator. Am I correct about this?

Yes.

Thank you.



If so, usually when a register is referring to a local then the local
can easily be resolved by looking up the value of A in the locals list
for that function. Am
I correct that in the case of TFORCALL, A is relative to the first local
that the PC of TFORCALL falls within? (TFORCALL PC >= local startpc &&
TFORCALL PC <= local endpc)

I did not understand your question here.

-- Roberto

I worded it wrong - if for example (in general) a register (i.e. R(A)) contains a number that is also 
an index in the list of locals that are valid in the PC of that instruction, can I then assume that local 
is referenced? 

i.e.

pc    line    opcode  operands   comment
28    [52]    SELF    2 1 1k     ; "push"

and 

# name   pc start  pc end
0 stack   3        106
1 stack1 25        106
2 t1     51        106

In this case, R(B) of the SELF instruction is `stack1`, however R(A) of the SELF instruction is not `t1` as that instruction's PC is not in the pc start to pc end range of that local. This seems logical to me, however I would like to be sure about this. 

Thank you.


Reply | Threaded
Open this post in threaded view
|

Re: A question about TFORCALL and locals

Roberto Ierusalimschy
> I worded it wrong - if for example (in general) a register (i.e. R(A))
> contains a number that is also
> an index in the list of locals that are valid in the PC of that
> instruction, can I then assume that local
> is referenced?  

The indices of locals in the list of locals are not equal to their
indices. Consider the next example:

  do
    local a, b, c
  end
  do
    local x, y, z
  end

Because registers are reused, local 'x' has index 0, but it is not
in position 0 in the list of locals: it is in position 3 (after 'a',
'b', and 'c'). To find a local at a given index and given PC, you
must do what luaF_getlocalname does, counting the variables that
are active at the given PC until you find the one with the index you
are looking for. (Have a look at luaF_getlocalname in file 'lfunc.c'.)

-- Roberto

Reply | Threaded
Open this post in threaded view
|

Re: A question about TFORCALL and locals

Bas Groothedde

On 18-12-2019 20:46, Roberto Ierusalimschy wrote:

The indices of locals in the list of locals are not equal to their
indices. Consider the next example:

  do
    local a, b, c
  end
  do
    local x, y, z
  end

Because registers are reused, local 'x' has index 0, but it is not
in position 0 in the list of locals: it is in position 3 (after 'a',
'b', and 'c'). To find a local at a given index and given PC, you
must do what luaF_getlocalname does, counting the variables that
are active at the given PC until you find the one with the index you
are looking for. (Have a look at luaF_getlocalname in file 'lfunc.c'.)

-- Roberto

Ah, thank you. That confirms how I currently try to get the local name for an index. I will have a look at luaF_getlocalname.

Bas