You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
498 lines
27 KiB
498 lines
27 KiB
=========================================
|
|
Lua Parsing and Code Generation Internals
|
|
=========================================
|
|
|
|
Stack and Registers
|
|
===================
|
|
Lua employs two stacks.
|
|
The ``Callinfo`` stack tracks activation frames.
|
|
There is the secondary stack ``L->stack`` that is an array of ``TValue`` objects. The ``Callinfo`` objects index into this array. Registers are basically slots in the ``L->stack`` array.
|
|
|
|
When a function is called - the stack is setup as follows::
|
|
|
|
stack
|
|
| function reference
|
|
| base-> parameter 1
|
|
| ...
|
|
| parameter n
|
|
| local 1
|
|
| ...
|
|
| local n
|
|
| top->
|
|
|
|
|
V
|
|
|
|
So top is just past the registers needed by the function.
|
|
The number of registers is determined based on locals and temporaries.
|
|
|
|
The base of the stack is set to just past the function reference - i.e. on the first parameter or register.
|
|
All register addressing is done as offset from base - so ``R(0)`` is at ``base+0`` on the stack.
|
|
|
|
.. figure:: Drawing_Lua_Stack.jpg
|
|
:alt: Drawing of Lua Stack
|
|
|
|
The figure shows how the stack is related to other Lua objects.
|
|
|
|
|
|
A description of the stack and registers from Mike Pall on Lua mailing list is reproduced below.
|
|
|
|
Sliding Register Window - by Mike Pall
|
|
--------------------------------------
|
|
Note: this is a reformatted version of a post on Lua mailing list (see MP6 link below).
|
|
|
|
The Lua 5 VM employs a sliding register window on top of a stack. Frames
|
|
(named CallInfo aka 'ci' in the source) occupy different (overlapping)
|
|
ranges on the stack. Successive frames are positioned exactly over the
|
|
passed arguments (luaD_precall). The compiler ensures that there are no
|
|
live variables after the arguments for a call. Return values need to be
|
|
copied down (with truncate/extend) to the slot holding the function object
|
|
(luaD_poscall). This is because the compiler has no idea how many values
|
|
another function may return -- only how many need to be stored.
|
|
|
|
|
|
Example::
|
|
|
|
function f2(arg1, arg2, ..., argN)
|
|
local local1, local2, ...
|
|
...
|
|
return ret1, ret2, ..., retO
|
|
end
|
|
|
|
function f1(arg1, arg2, ..., argM)
|
|
local local1, local2, ...
|
|
...
|
|
local ret1, ret2, ..., retP = f2(arg1, arg2, ..., argN)
|
|
...
|
|
end
|
|
|
|
Simplified stack diagram::
|
|
|
|
stack
|
|
|
|
|
| time: >>>> call >>>>>>>>>>>>>>>>>> call ~~~~~~~~~~~~~~~~~~ return >>>>>
|
|
|
|
|
| ciX.func-> f1 f1 f1 f1
|
|
| ciX.base-> arg1 arg1 arg1 arg1
|
|
| arg2 arg2 arg2 arg2
|
|
| ... ... ... ...
|
|
| argM argM argM argM
|
|
| ciX.topC-> local1 local1 local11
|
|
| local2 local2 local2
|
|
| local3 local3 local3
|
|
| ... ... ...
|
|
| f2 ciY.func-> f2 f2 ret1
|
|
| arg1 ciY.base-> arg1 arg1 ret2
|
|
| arg2 arg2 arg2 ...
|
|
| ... ... ... retP
|
|
| argN argN argN
|
|
| ciX.topL-> ------ ------ ------ ciY.topC-> local1 local1
|
|
| local2 local2
|
|
| ... ...
|
|
| ret1
|
|
| ret2
|
|
| ...
|
|
| retO
|
|
| ciY.topL-> ------ ------
|
|
V
|
|
|
|
Note that there is only a single 'top' for each frame:
|
|
|
|
For Lua functions the top (tagged topL in the diagram) is set to the base
|
|
plus the maximum number of slots used. The compiler knows this and stores
|
|
it in the function prototype. The top pointer is used only temporarily
|
|
for handling variable length argument and return value lists.
|
|
|
|
For C functions the top (tagged topC in the diagram) is initially set to
|
|
the base plus the number of passed arguments. C functions can access their
|
|
part of the stack via Lua API calls which in turn change the stack top.
|
|
C functions return an integer that indicates the number of return values
|
|
relative to the stack top.
|
|
|
|
In reality things are a bit more complex due to overlapped locals, block
|
|
scopes, varargs, coroutines and a few other things. But this should get
|
|
you the basic idea.
|
|
|
|
|
|
Parsing and Code Generation
|
|
===========================
|
|
* The parser is in `lparser.c <http://www.lua.org/source/5.3/lparser.c.html>`_.
|
|
* The code generator is in both above and `lcode.c <http://www.lua.org/source/5.3/lcode.c.html>`_.
|
|
|
|
The parser and code generator are arguably the most complex piece in the whole of Lua. The parser is one-pass - and generates code as it parses. That is, there is no AST build phase. This is primarily for efficiency it seems. The parser uses data structures on the stack - there are no heap allocated structures. Where needed the C stack itself is used to build structures - for example, as the assignment statement is parsed, there is recursion, and a stack based structure is built that links to structures in the call stack.
|
|
|
|
The main object used by the parser is the ``struct expdesc``::
|
|
|
|
typedef struct expdesc {
|
|
expkind k;
|
|
union {
|
|
struct { /* for indexed variables (VINDEXED) */
|
|
short idx; /* index (R/K) */
|
|
lu_byte t; /* table (register or upvalue) */
|
|
lu_byte vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */
|
|
} ind;
|
|
int info; /* for generic use */
|
|
lua_Number nval; /* for VKFLT */
|
|
lua_Integer ival; /* for VKINT */
|
|
} u;
|
|
int t; /* patch list of 'exit when true' */
|
|
int f; /* patch list of 'exit when false' */
|
|
int ravi_type; /* RAVI change: type of the expression if known, else LUA_TNONE */
|
|
} expdesc;
|
|
|
|
The code is somewhat hard to follow as the ``expdesc`` objects go through various states and are also reused when needed.
|
|
|
|
As the parser generates code while parsing it needs to go back and patch the generated instructions when it has more information. For example when a function call is parsed the parser assumes that only 1 value is expected to be returned - but later this is patched when more information is available. The most common example is when the register where the value will be stored (operand A) is not known - in this case the parser later on updates this operand in the instruction. I believe jump statements have similar mechanics - however I have not yet gone through the details of these instructions.
|
|
|
|
Handling of Stack during parsing
|
|
--------------------------------
|
|
Functions have a register window on the stack.
|
|
The stack is represented in ``LexState->dyd.actvar`` (Dyndata)
|
|
structure (see llex.h). The register window of the function
|
|
starts from ``LexState->dyd.actvar.arr[firstlocal]``.
|
|
|
|
The 'active' local variables
|
|
of the function extend up to ``LexState->dyd.actvar.arr[nactvar-1]``. Note that
|
|
when parsing a ``local`` declaration statement the ``nactvar`` is adjusted at the end of
|
|
the statement so that during parsing of the statement the ``nactvar``
|
|
covers locals up to the start of the statement. This means that
|
|
local variables come into scope (become 'active') after the local statement ends.
|
|
However, if the local statement defines a function then the variable becomes 'active'
|
|
before the function body is parsed.
|
|
|
|
A tricky thing to note is that while ``nactvar`` is adjusted at the end of the
|
|
statement - the 'stack' as represented by ``LexState->dyd.actvar.arr`` is extended to the required
|
|
size as the local variables are created by ``new_localvar()``.
|
|
|
|
When a function is the topmost function being parsed, the
|
|
registers between ``LexState->dyd.actvar.arr[nactvar]`` and ``LexState->dyd.actvar.arr[freereg-1]``
|
|
are used by the parser for evaluating expressions - i.e. these are part of the
|
|
local registers available to the function
|
|
|
|
Note that function parameters are handled as locals.
|
|
|
|
Example of what all this mean. Let's say we are parsing following chunk of code::
|
|
|
|
function testfunc()
|
|
-- at this stage 'nactvar' is 0 (no active variables)
|
|
-- 'firstlocal' is set to current top of the variables stack
|
|
-- LexState->dyd.actvar.n (i.e. excluding registers used for expression evaluation)
|
|
-- LexState->dyd.actvar.n = 0 at this stage
|
|
local function tryme()
|
|
-- Since we are inside the local statement and 'tryme' is a local variable,
|
|
-- the LexState->dyd.actvar.n goes to 1. As this is a function definition
|
|
-- the local variable declaration is deemed to end here, so 'nactvar' for testfunc()
|
|
-- is gets set to 1 (making 'tryme' an active variable).
|
|
-- A new FuncState is created for 'tryme' function.
|
|
-- The new tryme() FunState has 'firstlocal' set to value of LexState->dyd.actvar.n, i.e., 1
|
|
local i,j = 5,6
|
|
-- After 'i' is parsed, LexState->dyd.actvar.n = 2, but 'nactvar' = 0 for tryme()
|
|
-- After 'j' is parsed, LexState->dyd.actvar.n = 3, but 'nactvar' = 0 for tryme()
|
|
-- Only after the full statement above is parsed, 'nactvar' for tryme() is set to '2'
|
|
-- This is done by adjustlocalvar().
|
|
return i,j
|
|
end
|
|
-- Here two things happen
|
|
-- Firstly the FuncState for tryme() is popped so that
|
|
-- FuncState for testfunc() is now at top
|
|
-- As part of this popping, leaveblock() calls removevars()
|
|
-- to adjust the LexState->dyd.actvar.n down to 1 where it was
|
|
-- at before parsing the tryme() function body.
|
|
local i, j = tryme()
|
|
-- After 'i' is parsed, LexState->dyd.actvar.n = 2, but 'nactvar' = 1 still
|
|
-- After 'j' is parsed, LexState->dyd.actvar.n = 3, but 'nactvar' = 1 still
|
|
-- At the end of the statement 'nactvar' is set to 3.
|
|
return i+j
|
|
end
|
|
-- As before the leaveblock() calls removevars() which resets
|
|
-- LexState->dyd.actvar.n to 0 (the value before testfunc() was parsed)
|
|
|
|
A rough debug trace of the above gives::
|
|
|
|
function testfunc()
|
|
-- open_func -> fs->firstlocal set to 0 (ls->dyd->actvar.n), and fs->nactvar reset to 0
|
|
local function tryme()
|
|
-- new_localvar -> registering var tryme fs->f->locvars[0] at ls->dyd->actvar.arr[0]
|
|
-- new_localvar -> ls->dyd->actvar.n set to 1
|
|
-- adjustlocalvars -> set fs->nactvar to 1
|
|
-- open_func -> fs->firstlocal set to 1 (ls->dyd->actvar.n), and fs->nactvar reset to 0
|
|
-- adjustlocalvars -> set fs->nactvar to 0 (no parameters)
|
|
local i,j = 5,6
|
|
-- new_localvar -> registering var i fs->f->locvars[0] at ls->dyd->actvar.arr[1]
|
|
-- new_localvar -> ls->dyd->actvar.n set to 2
|
|
-- new_localvar -> registering var j fs->f->locvars[1] at ls->dyd->actvar.arr[2]
|
|
-- new_localvar -> ls->dyd->actvar.n set to 3
|
|
-- adjustlocalvars -> set fs->nactvar to 2
|
|
return i,j
|
|
-- removevars -> reset fs->nactvar to 0
|
|
end
|
|
local i, j = tryme()
|
|
-- new_localvar -> registering var i fs->f->locvars[1] at ls->dyd->actvar.arr[1]
|
|
-- new_localvar -> ls->dyd->actvar.n set to 2
|
|
-- new_localvar -> registering var j fs->f->locvars[2] at ls->dyd->actvar.arr[2]
|
|
-- new_localvar -> ls->dyd->actvar.n set to 3
|
|
-- adjustlocalvars -> set fs->nactvar to 3
|
|
return i+j
|
|
-- removevars -> reset fs->nactvar to 0
|
|
end
|
|
|
|
Notes on Parser by Sven Olsen
|
|
-----------------------------
|
|
|
|
"discharging" expressions
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
"discharging" takes an expression of arbitrary type, and
|
|
converts it to one having particular properties.
|
|
|
|
the lowest-level discharge function is ``discharge2vars ()``,
|
|
which converts an expression into one of the two "result"
|
|
types; either a ``VNONRELOC`` or a ``VRELOCABLE``.
|
|
|
|
if the variable in question is a ``VLOCAL``, ``discharge2vars``
|
|
will simply change the stored type to ``VNONRELOC``.
|
|
|
|
much of lcode.c assumes that the it will be working with
|
|
discharged expressions. in particular, it assumes that if
|
|
it encounters a ``VNONRELOC`` expression, and ``e->info < nactvar``,
|
|
then the register referenced is a local, and therefore
|
|
shouldn't be implicitly freed after use.
|
|
|
|
local variables
|
|
~~~~~~~~~~~~~~~
|
|
|
|
however, the relationship between ``nactvar`` and locals is
|
|
actually somewhat more complex -- as each local variable
|
|
appearing in the code has a collection of data attached to
|
|
it, data that's being accumulated and changed as the lexer
|
|
moves through the source.
|
|
|
|
``fs->nlocvars`` stores the total number of named locals inside
|
|
the function -- recall that different local variables are
|
|
allowed to overlap the same register, depending on which
|
|
are in-scope at any particular time.
|
|
|
|
the list of locals that are active at any given time is
|
|
stored in ``ls->dyd`` -- a vector of stack references that grows
|
|
or shrinks as locals enter or leave scope.
|
|
|
|
managing the lifetime of local variables involves several
|
|
steps. first, new locals are declared using ``new_localvar``.
|
|
this sets their names and creates new references in ``dyd``.
|
|
soon thereafter, the parser is expected to call
|
|
``adjustlocalvar(ls,nvars)``, with ``nvars`` set to the number of
|
|
new locals. ``adjustlocalvar`` increments ``fs->nactvar`` by ``nvars``,
|
|
and marks the startpc's of all the locals.
|
|
|
|
note that neither ``new_localvar`` or ``adjustlocalvar`` ensures
|
|
that anything is actually inside the registers being labeled
|
|
as locals. failing to initialize said registers is an easy
|
|
way to write memory access bugs (peter's original table
|
|
unpack patch includes one such).
|
|
|
|
after ``adjustlocalvar`` is called, ``luaK_exp2nextreg()`` will no
|
|
longer place new data inside the local's registers -- as
|
|
they're no longer part of the temporary register stack.
|
|
|
|
when the time comes to deactivate locals, that's done via
|
|
``removevars(tolevel)``. ``tolevel`` is assumed to contain ``nactvars``
|
|
as it existed prior to entering the previous block. thus,
|
|
the number of locals to remove should simply be
|
|
``fs->nactvar-tolevel``. ``removevars(tolevel)`` will decrement
|
|
``nactvars`` down to ``tolevel``. it also shrinks the ``dyd`` vector,
|
|
and marks the endpc's of all the removed locals.
|
|
|
|
except in between ``new_localvar`` and ``adjustlocalvar`` calls, i
|
|
believe that::
|
|
|
|
fs->ls->dyd->actvar.n - fs->firstlocal == fs->nactvar
|
|
|
|
temporary registers
|
|
~~~~~~~~~~~~~~~~~~~
|
|
``freereg`` is used to manage the temporary register stack --
|
|
registers between [``fs->nactvars,fs->freereg``) are assumed to
|
|
belong to expressions currently being stored by the parser.
|
|
|
|
``fs->freereg`` is incremented explicitly by calls to
|
|
``luaK_reserveregs``, or implicitly, inside ``luaK_exp2nextreg``.
|
|
it's decremented whenever a ``freereg(r)`` is called on a
|
|
register in the temporary stack (i.e., a register for which
|
|
``r >= fs->nactvar``).
|
|
|
|
the temporary register stack is cleared when ``leaveblock()`` is
|
|
called, by setting ``fs->freereg=fs->nactvar``. it's also
|
|
partially cleared in other places -- for example, inside
|
|
the evaluation of table constructors.
|
|
|
|
note that ``freereg`` just pops the top of the stack if r does
|
|
not appear to be a local -- thus it doesn't necessarily,
|
|
free r. one of the important sanity checks that you'll get
|
|
by enabling ``lua_assert()`` checks that the register being
|
|
freed is also the top of the stack.
|
|
|
|
when writing parser patches, it's your job to ensure that
|
|
the registers that you've reserved are freed in an
|
|
appropriate order.
|
|
|
|
when a ``VINDEXED`` expression is discharged, ``freereg()`` will be
|
|
called on both the table and the index register. otherwise,
|
|
``freereg`` is only called from ``freeexp()`` -- which gets
|
|
triggered anytime an expression has been "used up";
|
|
typically, anytime it's been transformed into another
|
|
expression.
|
|
|
|
State Transitions
|
|
-----------------
|
|
The state transitions for ``expdesc`` structure are as follows:
|
|
|
|
+------------------+----------------------------------------+------------------------------------+
|
|
| expkind | Description | State Transitions |
|
|
+==================+========================================+====================================+
|
|
|``VVOID`` | This is used to indicate the lack of | None |
|
|
| | value - e.g. function call with no | |
|
|
| | arguments, the rhs of local variable | |
|
|
| | declaration, and empty table | |
|
|
| | constructor | |
|
|
+------------------+----------------------------------------+------------------------------------+
|
|
|``VRELOCABLE`` | This is used to indicate that the | In terms of transitions the |
|
|
| | result from expression needs to be set | following expression kinds convert |
|
|
| | to a register. The operation that | to ``VRELOCABLE``: ``VVARARG`` |
|
|
| | created the expression is referenced | ``VUPVAL`` (``OP_GETUPVAL`` |
|
|
| | by the ``u.info`` parameter which | ``VINDEXED`` (``OP_GETTABUP`` or |
|
|
| | contains an offset into the ``code`` | ``OP_GETTABLE`` And following |
|
|
| | of the function that is being compiled | expression states can result from |
|
|
| | So you can access this instruction by | a ``VRELOCABLE`` expression: |
|
|
| | calling | ``VNONRELOC`` which |
|
|
| | ``getcode(FuncState *, expdesc *)`` | means that the result register in |
|
|
| | The operations that result in a | the instruction operand A has been |
|
|
| | ``VRELOCABLE`` object include | set. |
|
|
| | ``OP_CLOSURE`` ``OP_NEWTABLE`` | |
|
|
| | ``OP_GETUPVAL`` ``OP_GETTABUP`` | |
|
|
| | ``OP_GETTABLE`` ``OP_NOT`` and code | |
|
|
| | for binary and unary expressions that | |
|
|
| | produce values (arithmetic operations, | |
|
|
| | bitwise operations, concat, length). | |
|
|
| | The associated code instruction has | |
|
|
| | operand ``A`` unset (defaulted to 0) - | |
|
|
| | this the ``VRELOCABLE`` expression | |
|
|
| | must be later transitioned to | |
|
|
| | ``VNONRELOC`` state when the register | |
|
|
| | is set. | |
|
|
+------------------+----------------------------------------+------------------------------------+
|
|
|``VNONRELOC`` | This state indicates that the output | As for transitions, the |
|
|
| | or result register has been set. The | ``VNONELOC`` state results from |
|
|
| | register is referenced in ``u.info`` | ``VRELOCABLE`` after a register |
|
|
| | parameter. Once set the register | is assigned to the operation |
|
|
| | cannot be changed for this expression; | referenced by ``VRELOCABLE``. |
|
|
| | subsequent operations involving this | Also a ``VCALL`` expression |
|
|
| | expression can refer to the register | transitions to ``VNONRELOC`` |
|
|
| | to obtain the result value. | expression - ``u.info`` is set to |
|
|
| | | the operand ``A`` in the |
|
|
| | | call instruction. ``VLOCAL`` |
|
|
| | | ``VNIL`` ``VTRUE`` ``VFALSE`` |
|
|
| | | ``VK`` ``VKINT`` ``VKFLT`` and |
|
|
| | | ``VJMP`` expressions transition to |
|
|
| | | ``VNONRELOC``. |
|
|
+------------------+----------------------------------------+------------------------------------+
|
|
|``VLOCAL`` | This is used when referencing local | The ``VLOCAL`` expression may |
|
|
| | variables. ``u.info`` is set to the | transition to ``VNONRELOC`` |
|
|
| | local variable's register. | although this doesn't change the |
|
|
| | | ``u.info`` parameter. |
|
|
+------------------+----------------------------------------+------------------------------------+
|
|
|``VCALL`` | This results from a function call. The | In terms of transitions, the |
|
|
| | ``OP_CALL`` instruction is referenced | ``VCALL`` expression transitions |
|
|
| | by ``u.info`` parameter and may be | to ``VNONRELOC`` When this happens |
|
|
| | retrieved by calling | the result register in |
|
|
| | ``getcode(FuncState *, expdesc *)``. | ``VNONRELOC`` (``u.info`` is set |
|
|
| | The ``OP_CALL`` instruction gets | to the operand ``A`` in the |
|
|
| | changed to ``OP_TAILCALL`` if the | ``OP_CALL`` instruction. |
|
|
| | function call expression is the value | |
|
|
| | of a ``RETURN`` statement. | |
|
|
| | The instructions operand ``C`` | |
|
|
| | gets updated when it is known the | |
|
|
| | number of expected results from the | |
|
|
| | function call. | |
|
|
+------------------+----------------------------------------+------------------------------------+
|
|
|``VINDEXED`` | This expression represents a table | The ``VINDEXED`` expression |
|
|
| | access. The ``u.ind.t`` parameter is | transitions to ``VRELOCABLE`` |
|
|
| | set to the register or upvalue? that | When this happens the ``u.info`` |
|
|
| | holds the table, the ``u.ind.idx`` is | is set to the offset of the code |
|
|
| | set to the register or constant that | that contains the opcode |
|
|
| | is the key, and ``u.ind.vt`` is either | ``OP_GETTABUP`` if ``u.ind.vt`` |
|
|
| | ``VLOCAL`` or ``VUPVAL`` | was ``VUPVAL`` or ``OP_GETTABLE`` |
|
|
| | | if ``u.ind.vt`` was ``VLOCAL`` |
|
|
+------------------+----------------------------------------+------------------------------------+
|
|
|
|
|
|
Examples of Parsing
|
|
-------------------
|
|
example 1
|
|
~~~~~~~~~
|
|
|
|
We investigate the simple code chunk below::
|
|
|
|
local i,j; j = i*j+i
|
|
|
|
The compiler allocates following local registers, constants and upvalues::
|
|
|
|
constants (0) for 0000007428FED950:
|
|
locals (2) for 0000007428FED950:
|
|
0 i 2 5
|
|
1 j 2 5
|
|
upvalues (1) for 0000007428FED950:
|
|
0 _ENV 1 0
|
|
|
|
Some of the parse steps are highlighted below.
|
|
|
|
Reference to variable ``i`` which is located in register ``0``. The ``p`` here is the pointer address of ``expdesc`` object so you can see how the same object evolves::
|
|
|
|
{p=0000007428E1F170, k=VLOCAL, register=0}
|
|
|
|
Reference to variable ``j`` located in register ``1``::
|
|
|
|
{p=0000007428E1F078, k=VLOCAL, register=1}
|
|
|
|
Now the MUL operator is applied so we get following. Note that the previously ``VLOCAL`` expression for ``i`` is now ``VNONRELOC``::
|
|
|
|
{p=0000007428E1F170, k=VNONRELOC, register=0} MUL {p=0000007428E1F078, k=VLOCAL, register=1}
|
|
|
|
Next code gets generated for the ``MUL`` operator and we can see that first expression is replaced by a ``VRELOCABLE`` expression. Note also that the ``MUL`` operator is encoded in the ``VRELOCABLE`` expression as instruction ``1`` which is decoded below::
|
|
|
|
{p=0000007428E1F170, k=VRELOCABLE, pc=1, instruction=(MUL A=0 B=0 C=1)}
|
|
|
|
Now a reference to ``i`` is again required::
|
|
|
|
{p=0000007428E1F078, k=VLOCAL, register=0}
|
|
|
|
And the ``ADD`` operator must be applied to the result of the ``MUL`` operator and above. Notice that a temporary register ``2`` has been allocated to hold the result of the ``MUL`` operator, and also notice that as a result the ``VRELOCABLE`` has now changed to ``VNONRELOC``::
|
|
|
|
{p=0000007428E1F170, k=VNONRELOC, register=2} ADD {p=0000007428E1F078, k=VLOCAL, register=0}
|
|
|
|
Next the result of the ``ADD`` expression gets encoded similarly to ``MUL`` earlier. As this is a ``VRELOCABLE`` expression it will be later on assigned a result register::
|
|
|
|
{p=0000007428E1F170, k=VRELOCABLE, pc=2, instruction=(ADD A=0 B=2 C=0)}
|
|
|
|
Eventually above gets assigned a result register and becomes ``VNONRELOC`` (not shown here) - and so the final generated code looks like below::
|
|
|
|
main <(string):0,0> (4 instructions at 0000007428FED950)
|
|
0+ params, 3 slots, 1 upvalue, 2 locals, 0 constants, 0 functions
|
|
1 [1] LOADNIL 0 1
|
|
2 [1] MUL 2 0 1
|
|
3 [1] ADD 1 2 0
|
|
4 [1] RETURN 0 1
|
|
|
|
Links
|
|
=====
|
|
* `(MP1) Lua Code Reading Order <http://www.reddit.com/comments/63hth/ask_reddit_which_oss_codebases_out_there_are_so/c02pxbpC>`_
|
|
* `(RL1) Registers allocation and GC <http://lua-users.org/lists/lua-l/2013-02/msg00075.html>`_
|
|
* `(MP2) LuaJIT interpreter optmisations <http://www.reddit.com/r/programming/comments/badl2/luajit_2_beta_3_is_out_support_both_x32_x64/c0lrus0>`_
|
|
* `(MP3) Performance of Switch Based Dispatch <http://lua-users.org/lists/lua-l/2011-02/msg00742.html>`_
|
|
* `(MP4) Challenges for static compilation of dynamic langauges <http://lua-users.org/lists/lua-l/2009-06/msg00071.html>`_
|
|
* `(MP5) VM Internals (bytecode format) <http://lua-users.org/lists/lua-l/2008-07/msg00651.html>`_
|
|
* `(RL2) Upvalues in closures <http://lua-users.org/lists/lua-l/2008-09/msg00076.html>`_
|
|
* `(LHF) Lua bytecode dump format <http://lua-users.org/lists/lua-l/2006-06/msg00205.html>`_
|
|
* `(MP6) Register VM and sliding stack window <http://lua-users.org/lists/lua-l/2005-01/msg00628.html>`_
|
|
* `(SO1) Sven Olsen's notes on registers <http://lua-users.org/files/wiki_insecure/power_patches/5.2/svenshacks-5.2.2.patch>`_ from `Sven Olsen's Lua Users Wiki page <http://lua-users.org/wiki/SvenOlsen>`_
|
|
* `(KHM) No Frills Introduction to Lua 5.1 VM Instructions <http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf>`_
|
|
* `(MP7) LuaJIT Roadmap 2008 <http://lua-users.org/lists/lua-l/2008-02/msg00051.html>`_
|
|
* `(MP8) LuaJIT Roadmap 2011 <http://lua-users.org/lists/lua-l/2011-01/msg01238.html>`_
|