Initial commit

This commit is contained in:
2026-08-23 11:11:36 +03:00
commit 0548f8de26
97 changed files with 20841 additions and 0 deletions
+731
View File
@@ -0,0 +1,731 @@
// This file contains the bytecode definition for Luau interpreter
// Creating the bytecode is outside the scope of this file and is handled by bytecode builder (BytecodeBuilder.h) and bytecode compiler (Compiler.h)
// Note that ALL enums declared in this file are order-sensitive since the values are baked into bytecode that needs to be processed by legacy clients.
// # Bytecode definitions
// Bytecode instructions are using "word code" - each instruction is one or many 32-bit words.
// The first word in the instruction is always the instruction header, and *must* contain the opcode (enum below) in the least significant byte.
//
// Instruction word can be encoded using one of the following encodings:
// ABC - least-significant byte for the opcode, followed by three bytes, A, B and C; each byte declares a register index, small index into some other table or an unsigned integral value
// AD - least-significant byte for the opcode, followed by A byte, followed by D half-word (16-bit integer). D is a signed integer that commonly specifies constant table index or jump offset
// E - least-significant byte for the opcode, followed by E (24-bit integer). E is a signed integer that commonly specifies a jump offset
//
// Instruction word is sometimes followed by one extra word, indicated as AUX - this is just a 32-bit word and is decoded according to the specification for each opcode.
// For each opcode the encoding is *static* - that is, based on the opcode you know a-priory how large the instruction is, with the exception of NEWCLOSURE
// # Bytecode indices
// Bytecode instructions commonly refer to integer values that define offsets or indices for various entities. For each type, there's a maximum encodable value.
// Note that in some cases, the compiler will set a lower limit than the maximum encodable value is to prevent fragile code into bumping against the limits whenever we change the compilation details.
// Additionally, in some specific instructions such as ANDK, the limit on the encoded value is smaller; this means that if a value is larger, a different instruction must be selected.
//
// Registers: 0-254. Registers refer to the values on the function's stack frame, including arguments.
// Upvalues: 0-199. Upvalues refer to the values stored in the closure object.
// Constants: 0-2^23-1. Constants are stored in a table allocated with each proto; to allow for future bytecode tweaks the encodable value is limited to 23 bits.
// Closures: 0-2^15-1. Closures are created from child protos via a child index; the limit is for the number of closures immediately referenced in each function.
// Jumps: -2^23..2^23. Jump offsets are specified in word increments, so jumping over an instruction may sometimes require an offset of 2 or more. Note that for jump instructions with AUX, the AUX word is included as part of the jump offset.
// # Bytecode versions
// Bytecode serialized format embeds a version number, that dictates both the serialized form as well as the allowed instructions. As long as the bytecode version falls into supported
// range (indicated by BYTECODE_MIN / BYTECODE_MAX) and was produced by Luau compiler, it should load and execute correctly.
//
// Note that Luau runtime doesn't provide indefinite bytecode compatibility: support for older versions gets removed over time. As such, bytecode isn't a durable storage format and it's expected
// that Luau users can recompile bytecode from source on Luau version upgrades if necessary.
// # Bytecode version history
//
// Note: due to limitations of the versioning scheme, some bytecode blobs that carry version 2 are using features from version 3. Starting from version 3, version should be sufficient to indicate bytecode compatibility.
//
// Version 1: Baseline version for the open-source release. Supported until 0.521.
// Version 2: Adds Proto::linedefined. Supported until 0.544.
// Version 3: Adds FORGPREP/JUMPXEQK* and enhances AUX encoding for FORGLOOP. Removes FORGLOOP_NEXT/INEXT and JUMPIFEQK/JUMPIFNOTEQK. Currently supported.
// Version 4: Adds Proto::flags, typeinfo, and floor division opcodes IDIV/IDIVK. Currently supported.
// Version 5: Adds SUBRK/DIVRK and vector constants. Currently supported.
// Version 6: Adds FASTCALL3. Currently supported.
// # Bytecode type information history
// Version 1: (from bytecode version 4) Type information for function signature. Currently supported.
// Version 2: (from bytecode version 4) Type information for arguments, upvalues, locals and some temporaries. Currently supported.
// Bytecode opcode, part of the instruction header
pub const Opcode = enum(u32) {
// NOP: noop
NOP,
// BREAK: debugger break
BREAK,
// LOADNIL: sets register to nil
// A: target register
LOADNIL,
// LOADB: sets register to boolean and jumps to a given short offset (used to compile comparison results into a boolean)
// A: target register
// B: value (0/1)
// C: jump offset
LOADB,
// LOADN: sets register to a number literal
// A: target register
// D: value (-32768..32767)
LOADN,
// LOADK: sets register to an entry from the constant table from the proto (number/vector/string)
// A: target register
// D: constant table index (0..32767)
LOADK,
// MOVE: move (copy) value from one register to another
// A: target register
// B: source register
MOVE,
// GETGLOBAL: load value from global table using constant string as a key
// A: target register
// C: predicted slot index (based on hash)
// AUX: constant table index
GETGLOBAL,
// SETGLOBAL: set value in global table using constant string as a key
// A: source register
// C: predicted slot index (based on hash)
// AUX: constant table index
SETGLOBAL,
// GETUPVAL: load upvalue from the upvalue table for the current function
// A: target register
// B: upvalue index
GETUPVAL,
// SETUPVAL: store value into the upvalue table for the current function
// A: target register
// B: upvalue index
SETUPVAL,
// CLOSEUPVALS: close (migrate to heap) all upvalues that were captured for registers >= target
// A: target register
CLOSEUPVALS,
// GETIMPORT: load imported global table global from the constant table
// A: target register
// D: constant table index (0..32767); we assume that imports are loaded into the constant table
// AUX: 3 10-bit indices of constant strings that, combined, constitute an import path; length of the path is set by the top 2 bits (1,2,3)
GETIMPORT,
// GETTABLE: load value from table into target register using key from register
// A: target register
// B: table register
// C: index register
GETTABLE,
// SETTABLE: store source register into table using key from register
// A: source register
// B: table register
// C: index register
SETTABLE,
// GETTABLEKS: load value from table into target register using constant string as a key
// A: target register
// B: table register
// C: predicted slot index (based on hash)
// AUX: constant table index
GETTABLEKS,
// SETTABLEKS: store source register into table using constant string as a key
// A: source register
// B: table register
// C: predicted slot index (based on hash)
// AUX: constant table index
SETTABLEKS,
// GETTABLEN: load value from table into target register using small integer index as a key
// A: target register
// B: table register
// C: index-1 (index is 1..256)
GETTABLEN,
// SETTABLEN: store source register into table using small integer index as a key
// A: source register
// B: table register
// C: index-1 (index is 1..256)
SETTABLEN,
// NEWCLOSURE: create closure from a child proto; followed by a CAPTURE instruction for each upvalue
// A: target register
// D: child proto index (0..32767)
NEWCLOSURE,
// NAMECALL: prepare to call specified method by name by loading function from source register using constant index into target register and copying source register into target register + 1
// A: target register
// B: source register
// C: predicted slot index (based on hash)
// AUX: constant table index
// Note that this instruction must be followed directly by CALL; it prepares the arguments
// This instruction is roughly equivalent to GETTABLEKS + MOVE pair, but we need a special instruction to support custom __namecall metamethod
NAMECALL,
// CALL: call specified function
// A: register where the function object lives, followed by arguments; results are placed starting from the same register
// B: argument count + 1, or 0 to preserve all arguments up to top (MULTRET)
// C: result count + 1, or 0 to preserve all values and adjust top (MULTRET)
CALL,
// RETURN: returns specified values from the function
// A: register where the returned values start
// B: number of returned values + 1, or 0 to return all values up to top (MULTRET)
RETURN,
// JUMP: jumps to target offset
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
JUMP,
// JUMPBACK: jumps to target offset; this is equivalent to JUMP but is used as a safepoint to be able to interrupt while/repeat loops
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
JUMPBACK,
// JUMPIF: jumps to target offset if register is not nil/false
// A: source register
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
JUMPIF,
// JUMPIFNOT: jumps to target offset if register is nil/false
// A: source register
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
JUMPIFNOT,
// JUMPIFEQ, JUMPIFLE, JUMPIFLT, JUMPIFNOTEQ, JUMPIFNOTLE, JUMPIFNOTLT: jumps to target offset if the comparison is true (or false, for NOT variants)
// A: source register 1
// D: jump offset (-32768..32767; 1 means "next instruction" aka "don't jump")
// AUX: source register 2
JUMPIFEQ,
JUMPIFLE,
JUMPIFLT,
JUMPIFNOTEQ,
JUMPIFNOTLE,
JUMPIFNOTLT,
// ADD, SUB, MUL, DIV, MOD, POW: compute arithmetic operation between two source registers and put the result into target register
// A: target register
// B: source register 1
// C: source register 2
ADD,
SUB,
MUL,
DIV,
MOD,
POW,
// ADDK, SUBK, MULK, DIVK, MODK, POWK: compute arithmetic operation between the source register and a constant and put the result into target register
// A: target register
// B: source register
// C: constant table index (0..255); must refer to a number
ADDK,
SUBK,
MULK,
DIVK,
MODK,
POWK,
// AND, OR: perform `and` or `or` operation (selecting first or second register based on whether the first one is truthy) and put the result into target register
// A: target register
// B: source register 1
// C: source register 2
AND,
OR,
// ANDK, ORK: perform `and` or `or` operation (selecting source register or constant based on whether the source register is truthy) and put the result into target register
// A: target register
// B: source register
// C: constant table index (0..255)
ANDK,
ORK,
// CONCAT: concatenate all strings between B and C (inclusive) and put the result into A
// A: target register
// B: source register start
// C: source register end
CONCAT,
// NOT, MINUS, LENGTH: compute unary operation for source register and put the result into target register
// A: target register
// B: source register
NOT,
MINUS,
LENGTH,
// NEWTABLE: create table in target register
// A: target register
// B: table size, stored as 0 for v=0 and ceil(log2(v))+1 for v!=0
// AUX: array size
NEWTABLE,
// DUPTABLE: duplicate table using the constant table template to target register
// A: target register
// D: constant table index (0..32767)
DUPTABLE,
// SETLIST: set a list of values to table in target register
// A: target register
// B: source register start
// C: value count + 1, or 0 to use all values up to top (MULTRET)
// AUX: table index to start from
SETLIST,
// FORNPREP: prepare a numeric for loop, jump over the loop if first iteration doesn't need to run
// A: target register; numeric for loops assume a register layout [limit, step, index, variable]
// D: jump offset (-32768..32767)
// limit/step are immutable, index isn't visible to user code since it's copied into variable
FORNPREP,
// FORNLOOP: adjust loop variables for one iteration, jump back to the loop header if loop needs to continue
// A: target register; see FORNPREP for register layout
// D: jump offset (-32768..32767)
FORNLOOP,
// FORGLOOP: adjust loop variables for one iteration of a generic for loop, jump back to the loop header if loop needs to continue
// A: target register; generic for loops assume a register layout [generator, state, index, variables...]
// D: jump offset (-32768..32767)
// AUX: variable count (1..255) in the low 8 bits, high bit indicates whether to use ipairs-style traversal in the fast path
// loop variables are adjusted by calling generator(state, index) and expecting it to return a tuple that's copied to the user variables
// the first variable is then copied into index; generator/state are immutable, index isn't visible to user code
FORGLOOP,
// FORGPREP_INEXT: prepare FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_inext, and jump to FORGLOOP
// A: target register (see FORGLOOP for register layout)
FORGPREP_INEXT,
// FASTCALL3: perform a fast call of a built-in function using 3 register arguments
// A: builtin function id (see LuauBuiltinFunction)
// B: source argument register
// C: jump offset to get to following CALL
// AUX: source register 2 in least-significant byte
// AUX: source register 3 in second least-significant byte
FASTCALL3,
// FORGPREP_NEXT: prepare FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_next, and jump to FORGLOOP
// A: target register (see FORGLOOP for register layout)
FORGPREP_NEXT,
// NATIVECALL: start executing new function in native code
// this is a pseudo-instruction that is never emitted by bytecode compiler, but can be constructed at runtime to accelerate native code dispatch
NATIVECALL,
// GETVARARGS: copy variables into the target register from vararg storage for current function
// A: target register
// B: variable count + 1, or 0 to copy all variables and adjust top (MULTRET)
GETVARARGS,
// DUPCLOSURE: create closure from a pre-created function object (reusing it unless environments diverge)
// A: target register
// D: constant table index (0..32767)
DUPCLOSURE,
// PREPVARARGS: prepare stack for variadic functions so that GETVARARGS works correctly
// A: number of fixed arguments
PREPVARARGS,
// LOADKX: sets register to an entry from the constant table from the proto (number/string)
// A: target register
// AUX: constant table index
LOADKX,
// JUMPX: jumps to the target offset; like JUMPBACK, supports interruption
// E: jump offset (-2^23..2^23; 0 means "next instruction" aka "don't jump")
JUMPX,
// FASTCALL: perform a fast call of a built-in function
// A: builtin function id (see LuauBuiltinFunction)
// C: jump offset to get to following CALL
// FASTCALL is followed by one of (GETIMPORT, MOVE, GETUPVAL) instructions and by CALL instruction
// This is necessary so that if FASTCALL can't perform the call inline, it can continue normal execution
// If FASTCALL *can* perform the call, it jumps over the instructions *and* over the next CALL
// Note that FASTCALL will read the actual call arguments, such as argument/result registers and counts, from the CALL instruction
FASTCALL,
// COVERAGE: update coverage information stored in the instruction
// E: hit count for the instruction (0..2^23-1)
// The hit count is incremented by VM every time the instruction is executed, and saturates at 2^23-1
COVERAGE,
// CAPTURE: capture a local or an upvalue as an upvalue into a newly created closure; only valid after NEWCLOSURE
// A: capture type, see LuauCaptureType
// B: source register (for VAL/REF) or upvalue index (for UPVAL/UPREF)
CAPTURE,
// SUBRK, DIVRK: compute arithmetic operation between the constant and a source register and put the result into target register
// A: target register
// B: constant table index (0..255); must refer to a number
// C: source register
SUBRK,
DIVRK,
// FASTCALL1: perform a fast call of a built-in function using 1 register argument
// A: builtin function id (see LuauBuiltinFunction)
// B: source argument register
// C: jump offset to get to following CALL
FASTCALL1,
// FASTCALL2: perform a fast call of a built-in function using 2 register arguments
// A: builtin function id (see LuauBuiltinFunction)
// B: source argument register
// C: jump offset to get to following CALL
// AUX: source register 2 in least-significant byte
FASTCALL2,
// FASTCALL2K: perform a fast call of a built-in function using 1 register argument and 1 constant argument
// A: builtin function id (see LuauBuiltinFunction)
// B: source argument register
// C: jump offset to get to following CALL
// AUX: constant index
FASTCALL2K,
// FORGPREP: prepare loop variables for a generic for loop, jump to the loop backedge unconditionally
// A: target register; generic for loops assume a register layout [generator, state, index, variables...]
// D: jump offset (-32768..32767)
FORGPREP,
// JUMPXEQKNIL, JUMPXEQKB: jumps to target offset if the comparison with constant is true (or false, see AUX)
// A: source register 1
// D: jump offset (-32768..32767; 1 means "next instruction" aka "don't jump")
// AUX: constant value (for boolean) in low bit, NOT flag (that flips comparison result) in high bit
JUMPXEQKNIL,
JUMPXEQKB,
// JUMPXEQKN, JUMPXEQKS: jumps to target offset if the comparison with constant is true (or false, see AUX)
// A: source register 1
// D: jump offset (-32768..32767; 1 means "next instruction" aka "don't jump")
// AUX: constant table index in low 24 bits, NOT flag (that flips comparison result) in high bit
JUMPXEQKN,
JUMPXEQKS,
// IDIV: compute floor division between two source registers and put the result into target register
// A: target register
// B: source register 1
// C: source register 2
IDIV,
// IDIVK compute floor division between the source register and a constant and put the result into target register
// A: target register
// B: source register
// C: constant table index (0..255)
IDIVK,
// Atom-based userdata field access acceleration
// These are equivalent to their GETTABLEKS/SETTABLEKS/NAMECALL counterparts, except tailored towards userdata field accesses
// If the user has registered metamethods for a userdata tag, callbacks will be called by these instructions
GETUDATAKS,
SETUDATAKS,
NAMECALLUDATA,
// NEWCLASSMEMBER: register this method on a class object.
// A: target register of class
// B: reserved
// C: initial value of this member. currently must be a function.
// AUX: The name of this member as a constant string
NEWCLASSMEMBER,
// CALLFB: call specified function with collecting runtime stats in a feedback slot
// A: register where the function object lives, followed by arguments; results are placed starting from the same register
// B: argument count + 1, or 0 to preserve all arguments up to top (MULTRET)
// C: result count + 1, or 0 to preserve all values and adjust top (MULTRET)
// AUX: feedback slot id. 0xFFFFFFFF - sealed
CALLFB,
// CMPPROTO: check if a register contains a closure with a specified Luau function proto id
// A: closure register
// D: jump offset if proto doesn't match
// AUX: proto id
CMPPROTO,
// Enum entry for number of opcodes, not a valid opcode by itself!
_COUNT,
};
// Bytecode instruction header: it's always a 32-bit integer, with low byte (first byte in little endian) containing the opcode
// Some instruction types require more data and have more 32-bit integers following the header
pub inline fn INSN_OP(insn: u32) u8 {
return (insn) & 0xff;
}
// ABC encoding: three 8-bit values, containing registers or small numbers
pub inline fn INSN_A(insn: u32) u8 {
return (((insn) >> 8) & 0xff);
}
pub inline fn INSN_B(insn: u32) u8 {
return (((insn) >> 16) & 0xff);
}
pub inline fn INSN_C(insn: u32) u8 {
return (((insn) >> 24) & 0xff);
}
// AD encoding: one 8-bit value, one signed 16-bit value
pub inline fn INSN_D(insn: u32) i16 {
return (@as(i32, @bitCast(insn)) >> 16);
}
// E encoding: one signed 24-bit value
pub inline fn INSN_E(insn: u32) i32 {
return (@as(i32, @bitCast(insn)) >> 8);
}
// Bytecode tags, used internally for bytecode encoded as a string
pub const BytecodeTag = enum(u32) {
CONSTANT_NIL = 0,
CONSTANT_BOOLEAN,
CONSTANT_NUMBER,
CONSTANT_STRING,
CONSTANT_IMPORT,
CONSTANT_TABLE,
CONSTANT_CLOSURE,
CONSTANT_VECTOR,
CONSTANT_TABLE_WITH_CONSTANTS,
CONSTANT_INTEGER,
CONSTANT_CLASS_SHAPE,
// WARNING: This must always be last.
CONSTANT__COUNT,
// Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled
// Type encoding version
// Types of constant table entries
pub const VERSION_MIN = 3;
pub const VERSION_MAX = 6;
pub const VERSION_TARGET = 6;
pub const TYPE_VERSION_MIN = 1;
pub const TYPE_VERSION_MAX = 3;
pub const TYPE_VERSION_TARGET = 3;
};
// Type table tags
pub const BytecodeType = enum(u32) {
TYPE_NIL = 0,
TYPE_BOOLEAN,
TYPE_NUMBER,
TYPE_STRING,
TYPE_TABLE,
TYPE_FUNCTION,
TYPE_THREAD,
TYPE_USERDATA,
TYPE_VECTOR,
TYPE_BUFFER,
TYPE_ANY = 15,
TYPE_TAGGED_USERDATA_BASE = 64,
TYPE_TAGGED_USERDATA_END = 64 + 32,
TYPE_OPTIONAL_BIT = 1 << 7,
TYPE_INVALID = 256,
};
// Builtin function ids, used in FASTCALL
pub const BuiltinFunction = enum(u32) {
LBF_NONE = 0,
// assert()
LBF_ASSERT,
// math.
LBF_MATH_ABS,
LBF_MATH_ACOS,
LBF_MATH_ASIN,
LBF_MATH_ATAN2,
LBF_MATH_ATAN,
LBF_MATH_CEIL,
LBF_MATH_COSH,
LBF_MATH_COS,
LBF_MATH_DEG,
LBF_MATH_EXP,
LBF_MATH_FLOOR,
LBF_MATH_FMOD,
LBF_MATH_FREXP,
LBF_MATH_LDEXP,
LBF_MATH_LOG10,
LBF_MATH_LOG,
LBF_MATH_MAX,
LBF_MATH_MIN,
LBF_MATH_MODF,
LBF_MATH_POW,
LBF_MATH_RAD,
LBF_MATH_SINH,
LBF_MATH_SIN,
LBF_MATH_SQRT,
LBF_MATH_TANH,
LBF_MATH_TAN,
// bit32.
LBF_BIT32_ARSHIFT,
LBF_BIT32_BAND,
LBF_BIT32_BNOT,
LBF_BIT32_BOR,
LBF_BIT32_BXOR,
LBF_BIT32_BTEST,
LBF_BIT32_EXTRACT,
LBF_BIT32_LROTATE,
LBF_BIT32_LSHIFT,
LBF_BIT32_REPLACE,
LBF_BIT32_RROTATE,
LBF_BIT32_RSHIFT,
// type()
LBF_TYPE,
// string.
LBF_STRING_BYTE,
LBF_STRING_CHAR,
LBF_STRING_LEN,
// typeof()
LBF_TYPEOF,
// string.
LBF_STRING_SUB,
// math.
LBF_MATH_CLAMP,
LBF_MATH_SIGN,
LBF_MATH_ROUND,
// raw*
LBF_RAWSET,
LBF_RAWGET,
LBF_RAWEQUAL,
// table.
LBF_TABLE_INSERT,
LBF_TABLE_UNPACK,
// vector ctor
LBF_VECTOR,
// bit32.count
LBF_BIT32_COUNTLZ,
LBF_BIT32_COUNTRZ,
// select(_, ...)
LBF_SELECT_VARARG,
// rawlen
LBF_RAWLEN,
// bit32.extract(_, k, k)
LBF_BIT32_EXTRACTK,
// get/setmetatable
LBF_GETMETATABLE,
LBF_SETMETATABLE,
// tonumber/tostring
LBF_TONUMBER,
LBF_TOSTRING,
// bit32.byteswap(n)
LBF_BIT32_BYTESWAP,
// buffer.
LBF_BUFFER_READI8,
LBF_BUFFER_READU8,
LBF_BUFFER_WRITEU8,
LBF_BUFFER_READI16,
LBF_BUFFER_READU16,
LBF_BUFFER_WRITEU16,
LBF_BUFFER_READI32,
LBF_BUFFER_READU32,
LBF_BUFFER_WRITEU32,
LBF_BUFFER_READF32,
LBF_BUFFER_WRITEF32,
LBF_BUFFER_READF64,
LBF_BUFFER_WRITEF64,
// vector.
LBF_VECTOR_MAGNITUDE,
LBF_VECTOR_NORMALIZE,
LBF_VECTOR_CROSS,
LBF_VECTOR_DOT,
LBF_VECTOR_FLOOR,
LBF_VECTOR_CEIL,
LBF_VECTOR_ABS,
LBF_VECTOR_SIGN,
LBF_VECTOR_CLAMP,
LBF_VECTOR_MIN,
LBF_VECTOR_MAX,
// math.lerp
LBF_MATH_LERP,
// vector.lerp
LBF_VECTOR_LERP,
// math.
LBF_MATH_ISNAN,
LBF_MATH_ISINF,
LBF_MATH_ISFINITE,
// integer
LBF_INTEGER_CREATE,
LBF_INTEGER_TONUMBER,
LBF_INTEGER_NEG,
LBF_INTEGER_ADD,
LBF_INTEGER_SUB,
LBF_INTEGER_MUL,
LBF_INTEGER_DIV,
LBF_INTEGER_MIN,
LBF_INTEGER_MAX,
LBF_INTEGER_REM,
LBF_INTEGER_IDIV,
LBF_INTEGER_UDIV,
LBF_INTEGER_UREM,
LBF_INTEGER_MOD,
LBF_INTEGER_CLAMP,
LBF_INTEGER_BAND,
LBF_INTEGER_BOR,
LBF_INTEGER_BNOT,
LBF_INTEGER_BXOR,
LBF_INTEGER_LT,
LBF_INTEGER_LE,
LBF_INTEGER_ULT,
LBF_INTEGER_ULE,
LBF_INTEGER_GT,
LBF_INTEGER_GE,
LBF_INTEGER_UGT,
LBF_INTEGER_UGE,
LBF_INTEGER_LSHIFT,
LBF_INTEGER_RSHIFT,
LBF_INTEGER_ARSHIFT,
LBF_INTEGER_LROTATE,
LBF_INTEGER_RROTATE,
LBF_INTEGER_EXTRACT,
LBF_INTEGER_BTEST,
LBF_INTEGER_COUNTRZ,
LBF_INTEGER_COUNTLZ,
LBF_INTEGER_BSWAP,
// buffer.readinteger / buffer.writeinteger (int64_t)
LBF_BUFFER_READINTEGER,
LBF_BUFFER_WRITEINTEGER,
};
// Capture type, used in CAPTURE
pub const CaptureType = enum(u32) {
LCT_VAL = 0,
LCT_REF,
LCT_UPVAL,
};
// Proto flag bitmask, stored in Proto::flags
pub const ProtoFlag = enum(u32) {
/// used to tag main proto for modules with --!native
LPF_NATIVE_MODULE = 1 << 0,
/// used to tag individual protos as not profitable to compile natively
LPF_NATIVE_COLD = 1 << 1,
/// used to tag main proto for modules that have at least one function with native attribute
LPF_NATIVE_FUNCTION = 1 << 2,
/// function can be inlined
LPF_INLINABLE = 1 << 3,
};
pub const LuauFeedbackType = enum(u32) { LFT_CALLTARGET = 0 };
// sources:
// https://github.com/luau-lang/luau/blob/a2303a6ae68c53035eccf230c4450b9f068536af/Common/include/Luau/Bytecode.h
+40
View File
@@ -0,0 +1,40 @@
const Bytecode = @import("Bytecode.zig");
pub inline fn getOpLength(op: Bytecode.Opcode) usize {
return switch (op) {
.GETGLOBAL,
.SETGLOBAL,
.GETIMPORT,
.GETTABLEKS,
.SETTABLEKS,
.NAMECALL,
.JUMPIFEQ,
.JUMPIFLE,
.JUMPIFLT,
.JUMPIFNOTEQ,
.JUMPIFNOTLE,
.JUMPIFNOTLT,
.NEWTABLE,
.SETLIST,
.FORGLOOP,
.LOADKX,
.FASTCALL2,
.FASTCALL2K,
.FASTCALL3,
.JUMPXEQKNIL,
.JUMPXEQKB,
.JUMPXEQKN,
.JUMPXEQKS,
.GETUDATAKS,
.SETUDATAKS,
.NAMECALLUDATA,
.NEWCLASSMEMBER,
.CALLFB,
.CMPPROTO,
=> 2,
else => 1,
};
}
// sources:
// https://github.com/luau-lang/luau/blob/32d52d1b2ceef46fc25d87094a2d7f201c3ea5b8/Common/include/Luau/BytecodeUtils.h
+191
View File
@@ -0,0 +1,191 @@
const std = @import("std");
const cpp_std = @import("../cpp_std.zig");
extern fn zig_new_any(size: usize) callconv(.c) *anyopaque;
extern fn zig_delete_any(*anyopaque) callconv(.c) void;
pub fn DenseHashPointer(key: *const anyopaque) usize {
// return (@intFromPtr(key) >> 4) ^ (@intFromPtr(key) >> 9);
// The idea to use this hash function was suggested here originally: https://maskray.me/blog/2026-06-07-recent-llvm-hash-table-improvements
// Hash function implementation is detailed here: https://github.com/MaskRay/llvm-project/blob/main/llvm/include/llvm/ADT/DenseMapInfo.h
// This hash produces better scattering for arena allocated types, because the pointers usually share the higher order bits.
// When inserting lots of keys, quadratic probing is not enough to save DenseHash, although it usually takes many more elements,
// before it becomes a problem
var u: u64 = @intFromPtr(key);
u *%= 0xbf58476d1ce4e5b9;
u ^= u >> 31;
// On 32-bit platforms uint64_t to size_t is a narrowing, so we need
// to static cast here.
return @truncate(u);
}
pub const detail = struct {
pub fn DenseHashTable(
comptime Key: type,
comptime Item: type,
comptime MutableItem: type,
comptime ItemInterface: type,
comptime Hasher: type,
) type {
const hash = if (@hasDecl(Hasher, "hash")) Hasher.hash else struct {
pub fn hash(e: Key) usize {
if (comptime @typeInfo(Key) == .pointer) {
return DenseHashPointer(@ptrCast(@alignCast(e)));
} else {
@compileError("Hasher must implement 'hash' function");
}
}
}.hash;
const eq = if (@hasDecl(ItemInterface, "eq")) ItemInterface.eq else struct {
pub fn eq(a: Key, b: Key) bool {
return a == b;
}
}.eq;
_ = MutableItem;
return extern struct {
data: ?[*]Item = null,
capacity: usize = 0,
count: usize = 0,
empty_key: Key,
hasher: u8 = 0,
eq: u8 = 0,
const This = @This();
pub fn init(empty_key: Key, buckets: usize) This {
var data: ?[*]Item = null;
var capacity: usize = 0;
if (buckets > 0) {
data = @ptrCast(@alignCast(zig_new_any(@sizeOf(Item) * buckets)));
capacity = buckets;
ItemInterface.fill(data.?, buckets, empty_key);
}
return .{
.data = data,
.capacity = capacity,
.count = 0,
.empty_key = empty_key,
};
}
pub fn find(self: *This, key: Key) ?*const Item {
if (self.count == 0)
return null;
if (eq(key, self.empty_key))
return null;
const hashmod = self.capacity - 1;
var bucket = hash(key) & hashmod;
for (0..hashmod) |probe| {
const probe_item = &self.data.?[bucket];
// Element exists
if (eq(ItemInterface.getKey(probe_item), key))
return probe_item;
// Element does not exist
if (eq(ItemInterface.getKey(probe_item), self.empty_key))
return null;
// Hash collision, quadratic probing
bucket = (bucket + probe + 1) & hashmod;
}
// Hash table is full - this should not happen
std.debug.assert(false);
return null;
}
pub fn size(self: This) usize {
return self.count;
}
pub fn deinit(self: *This) void {
if (self.data) |data| {
ItemInterface.destroy(data, self.capacity);
zig_delete_any(@ptrCast(@alignCast(data)));
self.data = null;
self.capacity = 0;
}
}
};
}
};
pub fn ItemInterfaceSet(comptime Key: type) type {
return struct {
pub fn getKey(item: *const Key) Key {
return item.*;
}
pub fn setKey(item: *Key, key: Key) void {
item.* = key;
}
pub fn fill(data: [*]Key, count: usize, key: Key) void {
for (0..count) |i|
data[i] = key;
}
pub fn destroy(data: [*]Key, count: usize) void {
if (@hasDecl(Key, "deinit"))
for (0..count) |i| {
Key.deinit(data[i]);
};
}
};
}
pub fn ItemInterfaceMap(comptime Key: type, comptime Value: type) type {
return struct {
pub fn getKey(item: *const cpp_std.Pair(Key, Value)) Key {
return item.first;
}
pub fn setKey(item: *cpp_std.Pair(Key, Value), key: Key) void {
item.first = key;
}
pub fn fill(data: [*]cpp_std.Pair(Key, Value), count: usize, key: Key) void {
for (0..count) |i| {
data[i].first = key;
data[i].second = .{};
}
}
pub fn destroy(data: [*]cpp_std.Pair(Key, Value), count: usize) void {
for (0..count) |i| {
const ptr = data[i];
if (@hasDecl(Key, "deinit"))
Key.deinit(&ptr.first);
if (@hasDecl(Value, "deinit"))
Value.deinit(&ptr.second);
}
}
};
}
pub fn DenseHashSet(
comptime Key: type,
comptime Hasher: type,
) type {
return detail.DenseHashTable(Key, Key, Key, ItemInterfaceSet(Key), Hasher);
}
pub fn DenseHashMap(
comptime Key: type,
comptime Value: type,
comptime Hasher: type,
) type {
return detail.DenseHashTable(Key, cpp_std.Pair(Key, Value), cpp_std.Pair(Key, Value), ItemInterfaceMap(Key, Value), Hasher);
}
// sources:
// https://github.com/luau-lang/luau/blob/a2303a6ae68c53035eccf230c4450b9f068536af/Common/include/Luau/DenseHash.h
+28
View File
@@ -0,0 +1,28 @@
const std = @import("std");
pub inline fn isAnalysisFlagExperimental(flag: []const u8) bool {
// Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final,
// or critical bugs that are found after the code has been submitted. This list is intended _only_ for flags that affect
// Luau's type checking. Flags that may change runtime behavior (e.g.: parser or VM flags) are not appropriate for this list.
const kList = [_][]const u8{
"LuauInstantiateInSubtyping", // requires some fixes to lua-apps code
"LuauFixIndexerSubtypingOrdering", // requires some small fixes to lua-apps code since this fixes a false negative
"StudioReportLuauAny2", // takes telemetry data for usage of any types
"LuauTableCloneClonesType3", // requires fixes in lua-apps code, terrifyingly
"LuauSolverV2",
"UseNewLuauTypeSolverDefaultEnabled", // This can change the default solver used in cli applications, so it also needs to be disabled. Will require fixes in lua-apps code
};
for (comptime kList) |item|
if (std.mem.eql(u8, flag, item))
return true;
return false;
}
test {
std.testing.refAllDecls(@This());
}
// sources:
// https://github.com/luau-lang/luau/blob/a2303a6ae68c53035eccf230c4450b9f068536af/Common/include/Luau/ExperimentalFlags.h
+66
View File
@@ -0,0 +1,66 @@
const std = @import("std");
pub fn Variant(comptime Ts: []const type) type {
comptime {
if (Ts.len == 0) @compileError("variant must have at least 1 type");
}
const storage_size = comptime blk: {
var max: usize = 0;
for (Ts) |T|
max = @max(max, @sizeOf(T));
break :blk max;
};
const storage_align = comptime blk: {
var max: usize = 1;
for (Ts) |T|
max = @max(max, @alignOf(T));
break :blk max;
};
const TaggedUnion = blk: {
var names: [Ts.len][]const u8 = undefined;
var field_types: [Ts.len]type = undefined;
var field_attributes: [Ts.len]std.builtin.Type.UnionField.Attributes = undefined;
inline for (Ts, 0..) |T, i| {
names[i] = std.fmt.comptimePrint("{d}", .{i}); // becomes @"0", @"1", etc.
field_types[i] = T;
field_attributes[i] = .{
.@"align" = @alignOf(T),
};
}
break :blk @Union(
.auto,
null,
&names,
&field_types,
&field_attributes,
);
};
return extern struct {
typeId: c_int,
storage: [storage_size]u8 align(storage_align),
pub const Union = TaggedUnion;
pub fn @"union"(self: *const @This()) Union {
inline for (Ts, 0..) |T, i| {
if (self.typeId == @as(c_int, @intCast(i))) {
const active_ptr: *const T = @ptrCast(@alignCast(&self.storage));
return @unionInit(
Union,
std.fmt.comptimePrint("{d}", .{i}),
active_ptr.*,
);
}
}
unreachable;
}
pub fn index(self: *const @This()) c_int {
return self.typeId;
}
};
}