Bytecode format

Lua 5.2 and 5.3 have an identical bytecode format other than a few extra instructions.

file:  
    1b 4C 75 61       | Lua bytecode signature
    [u8 version]      | Version number (0x52 for Lua 5.2, etc)
    [u8 impl]         | Implementation (0 for reference impl)
    [u8 endian]       | Big-endian flag
    [u8 intsize]      | Size of integers (usually 4)
    [u8 size_t]       | Size of pointers
    [u8 instsize]     | Size of instructions (always 4)
    [u8 numsize]      | Size of Lua numbers (usually 8)
    [u8 use_int]      | Use integers instead of floats (usually for embedded)
    19 93 0D 0A 1A 0A | Lua magic (used to detect presence of EOL conversion)
    [func main]
string:  
    [size_t size]
    ... data
    00
func:  
    [int line_start] | debug info
    [int line_end]   | debug info
    [u8 nparams]
    [u8 varargflags]
    [u8 nregisters]
    [int ninstructions]
    ... instructions:
        [instsize instruction]
    [int nconsts]
    ... consts:
        [u8 type]
        type 0: | nil
        type 1: | bool
            [u8 value]
        type 3: | number
            [numsize value]
        type 4: | string
            [string value]
    [int nprimitives]
    ... primitives:
        [func primitive]
    [int nupvals]
    ... upvals:
        [u8 stack]
        [u8 register]
    [string source] | debug info
    [int nlines]
    ... lines:
        [int line]
    [int nlocals]
    ... locals:
        [string name] | debug info
        [int startpc]
        [int endpc]
    [int nupvalnames]
    ... upvalnames:
        [string name] | debug info
Instruction type31..23 (9 bits)24..14 (9 bits)13..6 (8 bits)5..0 (6 bits)
iABCBCAopcode
iABxBxAopcode
iAsBxBx (signed)Aopcode
iAxAopcode

Opcodes

Lua 5.2:
IdInstructionParameters
0MOVEiABC
1LOADKiABx
2LOADKXiABx
3LOADBOOLiABC
4LOADNILiABC
5GETUPVALiABC
6GETTABUPiABC
7GETTABLEiABC
8SETTABUPiABC
9SETUPVALiABC
10SETTABLEiABC
11NEWTABLEiABC
12SELFiABC
13ADDiABC
14SUBiABC
15MULiABC
16DIViABC
17MODiABC
18POWiABC
19UNMiABC
20NOTiABC
21LENiABC
22CONCATiABC
23JMPiAsBx
24EQiABC
25LTiABC
26LEiABC
27TESTiABC
28TESTSETiABC
29CALLiABC
30TAILCALLiABC
31RETURNiABC
32FORLOOPiAsBx
33FORPREPiAsBx
34TFORCALLiABC
35TFORLOOPiAsBx
36SETLISTiABC
37CLOSUREiABx
38VARARGiABC
39EXTRAARGiAx
Lua 5.3:
IdInstructionParameters
0MOVEiABC
1LOADKiABx
2LOADKXiABx
3LOADBOOLiABC
4LOADNILiABC
5GETUPVALiABC
6GETTABUPiABC
7GETTABLEiABC
8SETTABUPiABC
9SETUPVALiABC
10SETTABLEiABC
11NEWTABLEiABC
12SELFiABC
13ADDiABC
14SUBiABC
15MULiABC
16MODiABC
17POWiABC
18DIViABC
19IDIViABC
20BANDiABC
21BORiABC
22BXORiABC
23SHLiABC
24SHRiABC
25UNMiABC
26BNOTiABC
27NOTiABC
28LENiABC
29CONCATiABC
30JMPiAsBx
31EQiABC
32LTiABC
33LEiABC
34TESTiABC
35TESTSETiABC
36CALLiABC
37TAILCALLiABC
38RETURNiABC
39FORLOOPiAsBx
40FORPREPiAsBx
41TFORCALLiABC
42TFORLOOPiAsBx
43SETLISTiABC
44CLOSUREiABx
45VARARGiABC
46EXTRAARGiAx

Locals

Locals point to registers on a closure's stack, you may notice in the bytecode they do not explicitly state a register but define the range of instructions its accessible from. Starting at register 0, you can infer the register from the order the locals are accessible.

    ... locals:
        [string name] | optional, for debugging
        [int startpc]
        [int endpc]

While iterating instructions you simply check if a local starts there, then point the local to the top of the stack, then when the local ends simply pop it off the stack.

Upvalues

Upvalues are locals you access from parent functions, for example:

local potato
local function walrus()
    potato = 1
end

Here walrus would call SETUPVAL on potato, upvalues are statically defined in each of their prototypes by two variables:
stack is the number of stacks above the variable is, in this case 1.
register is the register of the stack the upvalue references.

In Lua 5.2 and 5.3 upvalue 1 corresponds to _ENV.

Instructions

NameDescription
RRegister list
KConstant list
UUpvalue list
pcProgram counter aka instruction pointer
KprotoFunction prototype list
TypenameDescription
regRegister
constConstant
valueRegister but when negative its a constant
upvalueUpvalue
closureClosure
intLiteral integer
jumpRelative jump position
MOVE(reg a, reg b)
a = b;  
LOADK(reg a, const bx)
a = bx;  
LOADKX(reg a) EXTRAARG(const ax)
a = ax;  
LOADBOOL(reg a, int b, int c)
a = (bool)b;  
if (c) pc++;  
LOADNIL(reg a, reg b)
a..b = nil;  
GETUPVAL(reg a, upvalue b)
a = b;  
GETTABUP(reg a, upvalue b, value c)
a = b[c];  
GETTABLE(reg a, reg b, value c)
a = b[c];  
SETTABUP(upvalue a, value b, value c)
a[b] = c;  
SETUPVAL(upvalue a, reg b)
a = b;  
SETTABLE(reg a, value b, value c)
a[b] = c;  
NEWTABLE(reg a, int b, int c)
a = {} // preallocate b array elements and c hash elements  
SELF(reg a, reg b, value c)
a + 1 = b;  
a = b[c];  
operator(reg a, value b, value c)

Where operator is one of the following:

ADD+SUB-MUL*MOD%
POW%DIV/IDIV//BAND&
BOR|BXOR~SHL<<SHR>>
a = b <op> c;  
unary operator(reg a, value b)

Where unary operator is one of the following:

UNM-BNOT~NOTnotLEN#
a = <op> b;  
CONCAT(reg a, value b, value c)
a = b .. .. c;  
JMP(int a, jump sbx)
pc += sbx;  
if (a) /* close upvalues >= a - 1*/  
EQ(int a, value b, value c)
if ((b == c) ~= a) pc++;  
LT(int a, value b, value c)
if ((b < c) ~= a) pc++;  
LE(int a, value b, value c)
if ((b <= c) ~= a) pc++;  
TEST(int a, value b, value c)
if ((b <= c) ~= a) pc++;