This page follows Lua 5.4’s ldump.c, lundump.c,
lundump.h and lopcodes.h (5.4.6). The format is not
documented by the Lua authors. Compared with 5.3, sizes and counts became
variable-length numbers, the header no longer records int and
size_t sizes, booleans became two constant tags, line information became
compact, and the instruction set was redesigned with a 7-bit opcode.
Header
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 4 | signature | 1B 4C 75 61. |
4 | 1 | version | 0x54. |
5 | 1 | format | 0, the official format. |
6 | 6 | LUAC_DATA | 19 93 0D 0A 1A 0A. |
12 | 1 | sizeof(Instruction) | Bytes in an instruction. |
13 | 1 | sizeof(lua_Integer) | Bytes in an integer. |
14 | 1 | sizeof(lua_Number) | Bytes in a float. |
15 | 8* | LUAC_INT | 0x5678 as a lua_Integer: shows the byte order. |
23* | 8* | LUAC_NUM | 370.5 as a lua_Number: shows the float format. |
31* | 1 | upvalues | Number of upvalues of the main closure. |
* With 8-byte integers and floats.
Basic types
| Type | Encoding |
|---|---|
| byte | One byte. |
| varint | Used for every size, count and int. Seven bits per byte, most significant group first; the last byte has its top bit set. 300 is 02 AC; 5 is 85. |
| integer | sizeof(lua_Integer) bytes, native byte order. |
| number | sizeof(lua_Number) bytes, native representation. |
| string | A varint holding the length plus one (0 means no string), then the bytes with no terminating zero. |
| list of T | A varint count, then that many T. |
Function prototype
| Field | Type | Meaning |
|---|---|---|
| source | string | Source name; when absent (no string), the parent function’s source is used. |
| linedefined | varint | First line. |
| lastlinedefined | varint | Last line. |
| numparams | byte | Fixed parameters. |
| is_vararg | byte | Whether the function takes .... |
| maxstacksize | byte | Registers needed. |
| code | list of Instruction | 4 bytes each, native byte order. |
| constants | list of constant | Below. |
| upvalues | list of (byte, byte, byte) | instack, idx and kind (below), one byte each. |
| protos | list of function | Nested prototypes. |
| lineinfo | list of signed byte | For each instruction, the change in line number from the previous instruction. |
| abslineinfo | list of (varint, varint) | Absolute positions: an instruction index and its line. At most MAXIWTHABS (128) instructions in a row go without one. |
| locvars | list of (string, varint, varint) | Name, start pc, end pc. |
| upvalue names | list of string | One per upvalue; with a count of 0 none are read. |
A lineinfo entry of −128 (ABSLINEINFO, byte 80)
marks an instruction whose line must be taken from abslineinfo (from
Lua’s ldebug.h).
Constants
| Tag byte | Value | Then |
|---|---|---|
0x00 | nil (LUA_VNIL) | nothing |
0x01 | false (LUA_VFALSE) | nothing |
0x11 | true (LUA_VTRUE) | nothing |
0x03 | integer (LUA_VNUMINT) | an integer |
0x13 | float (LUA_VNUMFLT) | a number |
0x04 | short string (LUA_VSHRSTR) | a string |
0x14 | long string (LUA_VLNGSTR) | a string |
Note that 5.4 swapped the integer and float variants of 5.3: 0x03 is a
float in 5.3 and an integer in 5.4.
Upvalue kinds
| Kind | Name | Variable is |
|---|---|---|
0 | VDKREG | a regular variable |
1 | RDKCONST | a constant |
2 | RDKTOCLOSE | to be closed |
3 | RDKCTC | a compile-time constant |
Instructions
| Mode | Bits 31–24 | Bits 23–16 | Bit 15 | Bits 14–7 | Bits 6–0 |
|---|---|---|---|---|---|
iABC | C (8) | B (8) | k | A (8) | opcode (7) |
iABx | Bx (17), unsigned | A (8) | opcode (7) | ||
iAsBx | sBx (17), stored + 65,535 | A (8) | opcode (7) | ||
iAx | Ax (25) | opcode (7) | |||
isJ | sJ (25), stored + 16,777,215 | opcode (7) | |||
R[x] is a register, K[x] a constant and
RK(x) is K[x] when the k bit is set, otherwise
R[x]. sB and sC are signed 8-bit values stored
+ 127. In comparisons k gives the expected result: the next instruction
(normally a JMP) is skipped when the result differs.
| Op | Name | Mode | Action |
|---|---|---|---|
| 0 | MOVE | A B | R[A] := R[B] |
| 1 | LOADI | A sBx | R[A] := sBx |
| 2 | LOADF | A sBx | R[A] := (float)sBx |
| 3 | LOADK | A Bx | R[A] := K[Bx] |
| 4 | LOADKX | A | R[A] := K[extra arg] |
| 5 | LOADFALSE | A | R[A] := false |
| 6 | LFALSESKIP | A | R[A] := false; skip next |
| 7 | LOADTRUE | A | R[A] := true |
| 8 | LOADNIL | A B | R[A], …, R[A+B] := nil |
| 9 | GETUPVAL | A B | R[A] := UpValue[B] |
| 10 | SETUPVAL | A B | UpValue[B] := R[A] |
| 11 | GETTABUP | A B C | R[A] := UpValue[B][K[C]:string] |
| 12 | GETTABLE | A B C | R[A] := R[B][R[C]] |
| 13 | GETI | A B C | R[A] := R[B][C] |
| 14 | GETFIELD | A B C | R[A] := R[B][K[C]:string] |
| 15 | SETTABUP | A B C | UpValue[A][K[B]:string] := RK(C) |
| 16 | SETTABLE | A B C | R[A][R[B]] := RK(C) |
| 17 | SETI | A B C | R[A][B] := RK(C) |
| 18 | SETFIELD | A B C | R[A][K[B]:string] := RK(C) |
| 19 | NEWTABLE | A B C k | R[A] := {}; B is log₂ of the hash size (0 for none), C the array size, extended by the following EXTRAARG when k is set |
| 20 | SELF | A B C | R[A+1] := R[B]; R[A] := R[B][RK(C):string] |
| 21 | ADDI | A B sC | R[A] := R[B] + sC |
| 22 | ADDK | A B C | R[A] := R[B] + K[C]:number |
| 23 | SUBK | A B C | R[A] := R[B] − K[C]:number |
| 24 | MULK | A B C | R[A] := R[B] × K[C]:number |
| 25 | MODK | A B C | R[A] := R[B] % K[C]:number |
| 26 | POWK | A B C | R[A] := R[B] ^ K[C]:number |
| 27 | DIVK | A B C | R[A] := R[B] / K[C]:number |
| 28 | IDIVK | A B C | R[A] := R[B] // K[C]:number |
| 29 | BANDK | A B C | R[A] := R[B] & K[C]:integer |
| 30 | BORK | A B C | R[A] := R[B] | K[C]:integer |
| 31 | BXORK | A B C | R[A] := R[B] ~ K[C]:integer |
| 32 | SHRI | A B sC | R[A] := R[B] >> sC |
| 33 | SHLI | A B sC | R[A] := sC << R[B] |
| 34 | ADD | A B C | R[A] := R[B] + R[C] |
| 35 | SUB | A B C | R[A] := R[B] − R[C] |
| 36 | MUL | A B C | R[A] := R[B] × R[C] |
| 37 | MOD | A B C | R[A] := R[B] % R[C] |
| 38 | POW | A B C | R[A] := R[B] ^ R[C] |
| 39 | DIV | A B C | R[A] := R[B] / R[C] |
| 40 | IDIV | A B C | R[A] := R[B] // R[C] |
| 41 | BAND | A B C | R[A] := R[B] & R[C] |
| 42 | BOR | A B C | R[A] := R[B] | R[C] |
| 43 | BXOR | A B C | R[A] := R[B] ~ R[C] |
| 44 | SHL | A B C | R[A] := R[B] << R[C] |
| 45 | SHR | A B C | R[A] := R[B] >> R[C] |
| 46 | MMBIN | A B C | Call the metamethod C over R[A] and R[B] (after a failed arithmetic instruction) |
| 47 | MMBINI | A sB C k | Call metamethod C over R[A] and sB |
| 48 | MMBINK | A B C k | Call metamethod C over R[A] and K[B] |
| 49 | UNM | A B | R[A] := −R[B] |
| 50 | BNOT | A B | R[A] := ~R[B] |
| 51 | NOT | A B | R[A] := not R[B] |
| 52 | LEN | A B | R[A] := #R[B] |
| 53 | CONCAT | A B | R[A] := R[A] .. … .. R[A+B−1] |
| 54 | CLOSE | A | Close upvalues and to-be-closed variables at or above R[A] |
| 55 | TBC | A | Mark R[A] as to-be-closed |
| 56 | JMP | sJ | pc += sJ |
| 57 | EQ | A B k | if ((R[A] == R[B]) ~= k) then skip next |
| 58 | LT | A B k | if ((R[A] < R[B]) ~= k) then skip next |
| 59 | LE | A B k | if ((R[A] <= R[B]) ~= k) then skip next |
| 60 | EQK | A B k | if ((R[A] == K[B]) ~= k) then skip next |
| 61 | EQI | A sB k | if ((R[A] == sB) ~= k) then skip next |
| 62 | LTI | A sB k | if ((R[A] < sB) ~= k) then skip next |
| 63 | LEI | A sB k | if ((R[A] <= sB) ~= k) then skip next |
| 64 | GTI | A sB k | if ((R[A] > sB) ~= k) then skip next |
| 65 | GEI | A sB k | if ((R[A] >= sB) ~= k) then skip next |
| 66 | TEST | A k | if (not R[A] == k) then skip next |
| 67 | TESTSET | A B k | if (not R[B] == k) then skip next else R[A] := R[B] |
| 68 | CALL | A B C | R[A], …, R[A+C−2] := R[A](R[A+1], …, R[A+B−1]) |
| 69 | TAILCALL | A B C k | return R[A](R[A+1], …, R[A+B−1]) |
| 70 | RETURN | A B C k | return R[A], …, R[A+B−2] |
| 71 | RETURN0 | return with no values | |
| 72 | RETURN1 | A | return R[A] |
| 73 | FORLOOP | A Bx | Update the numeric loop counters; if the loop continues, pc −= Bx |
| 74 | FORPREP | A Bx | Check and prepare the counters; if the loop does not run, pc += Bx + 1 |
| 75 | TFORPREP | A Bx | Create an upvalue for R[A+3]; pc += Bx |
| 76 | TFORCALL | A C | R[A+4], …, R[A+3+C] := R[A](R[A+1], R[A+2]) |
| 77 | TFORLOOP | A Bx | if R[A+2] ~= nil then { R[A] := R[A+2]; pc −= Bx } |
| 78 | SETLIST | A B C k | R[A][C+i] := R[A+i], 1 ≤ i ≤ B; B = 0 means up to the top; k changes how C is read (an EXTRAARG follows) |
| 79 | CLOSURE | A Bx | R[A] := closure(KPROTO[Bx]) |
| 80 | VARARG | A C | R[A], …, R[A+C−2] := vararg |
| 81 | VARARGPREP | A | Adjust the vararg parameters on entry |
| 82 | EXTRAARG | Ax | A larger argument for the previous instruction |
Notes from the header: in RETURN and TAILCALL,
k means the function created upvalues that may need closing, and a
C above 0 marks a vararg function. In CALL, B = 0 takes the
arguments up to the top. In MMBINI and MMBINK,
k means the operands were swapped. LOADKX and
NEWTABLE are always followed by an EXTRAARG. Every skip
assumes the next instruction is a jump.
Example
1B 4C 75 61 54 00 signature, version 5.4, format 0
19 93 0D 0A 1A 0A LUAC_DATA
04 08 08 sizes: Instruction, lua_Integer, lua_Number
78 56 00 00 00 00 00 00 LUAC_INT
00 00 00 00 00 28 77 40 LUAC_NUM 370.5
01 main closure has 1 upvalue (_ENV)
8B 40 68 65 6C 6C 6F 2E 6C 75 61 source "@hello.lua": varint 11 (8B), then 10 bytes
80 linedefined 0
80 lastlinedefined 0
00 01 02 numparams 0, is_vararg 1, maxstacksize 2
The values after the header are illustrative. The instruction
VARARGPREP 0 is 51 00 00 00 (opcode 81 = 0x51,
A = 0).
Sources
Lua 5.4.6 source (lua.org and the
lua/lua repository):
lundump.c (checkHeader, loadUnsigned,
loadStringN, loadFunction, constants, upvalues, debug
information), lundump.h (LUAC_DATA, LUAC_INT,
LUAC_NUM, LUAC_VERSION, LUAC_FORMAT),
lobject.h (variant tags), lparser.h (variable kinds),
ldebug.h (ABSLINEINFO, MAXIWTHABS) and
lopcodes.h (instruction formats, signed offsets, every opcode, comments
and notes).