2023-01-13 22:10:01 +00:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#pragma once
|
|
|
|
|
2023-12-02 07:46:57 +00:00
|
|
|
#include "Luau/Bytecode.h"
|
2023-03-10 20:21:07 +00:00
|
|
|
#include "Luau/IrAnalysis.h"
|
2023-01-13 22:10:01 +00:00
|
|
|
#include "Luau/Label.h"
|
|
|
|
#include "Luau/RegisterX64.h"
|
|
|
|
#include "Luau/RegisterA64.h"
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
#include <optional>
|
2023-01-13 22:10:01 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <stdint.h>
|
2023-04-28 20:55:13 +01:00
|
|
|
#include <string.h>
|
2023-01-13 22:10:01 +00:00
|
|
|
|
2023-01-20 20:27:03 +00:00
|
|
|
struct Proto;
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
namespace CodeGen
|
|
|
|
{
|
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
// IR extensions to LuauBuiltinFunction enum (these only exist inside IR, and start from 256 to avoid collisions)
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
LBF_IR_MATH_LOG2 = 256,
|
|
|
|
};
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// IR instruction command.
|
|
|
|
// In the command description, following abbreviations are used:
|
|
|
|
// * Rn - VM stack register slot, n in 0..254
|
|
|
|
// * Kn - VM proto constant slot, n in 0..2^23-1
|
2023-03-17 19:20:37 +00:00
|
|
|
// * UPn - VM function upvalue slot, n in 0..199
|
2024-06-14 21:21:20 +01:00
|
|
|
// * A, B, C, D, E, F, G are instruction arguments
|
2023-01-13 22:10:01 +00:00
|
|
|
enum class IrCmd : uint8_t
|
|
|
|
{
|
|
|
|
NOP,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Load a tag from TValue
|
|
|
|
// A: Rn or Kn
|
2023-01-13 22:10:01 +00:00
|
|
|
LOAD_TAG,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load a pointer (*) from TValue
|
|
|
|
// A: Rn or Kn
|
2023-01-13 22:10:01 +00:00
|
|
|
LOAD_POINTER,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load a double number from TValue
|
|
|
|
// A: Rn or Kn
|
2023-01-13 22:10:01 +00:00
|
|
|
LOAD_DOUBLE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load an int from TValue
|
|
|
|
// A: Rn
|
2023-01-13 22:10:01 +00:00
|
|
|
LOAD_INT,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2024-01-27 03:20:56 +00:00
|
|
|
// Load a float field from vector as a double number
|
|
|
|
// A: Rn or Kn
|
|
|
|
// B: int (offset from the start of TValue)
|
|
|
|
LOAD_FLOAT,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Load a TValue from memory
|
|
|
|
// A: Rn or Kn or pointer (TValue)
|
CodeGen: Preserve known tags for LOAD_TVALUE synthesized from LOADK (#1201)
When lowering LOADK for booleans/numbers/nils, we deconstruct the
operation using STORE_TAG which informs the rest of the optimization
pipeline about the tag of the value. This is helpful to remove various
tag checks.
When the constant is a string or a vector, we just use
LOAD_TVALUE/STORE_TVALUE. For strings, this could be replaced by pointer
load/store, but for vectors there's no great alternative using current
IR ops; in either case, the optimization needs to be carefully examined
for profitability as simply copying constants into registers for
function calls could become more expensive.
However, there are cases where it's still valuable to preserve the tag.
For vectors, doing any math with vector constants contains tag checks
that could be removed. For both strings and vectors, storing them into a
table has a barrier that for vectors could be elided, and for strings
could be simplified as there's no need to confirm the tag.
With this change we now carry the optional tag of the value with
LOAD_TVALUE. This has no performance effect on existing benchmarks but
does reduce the generated code for benchmarks by ~0.1%, and it makes
vector code more efficient (~5% lift on X64 log1p approximation).
2024-03-15 16:49:00 +00:00
|
|
|
// B: int/none (optional 'A' pointer offset)
|
|
|
|
// C: tag/none (tag of the value being loaded)
|
2023-01-13 22:10:01 +00:00
|
|
|
LOAD_TVALUE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load current environment table
|
2023-01-13 22:10:01 +00:00
|
|
|
LOAD_ENV,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Get pointer (TValue) to table array at index
|
|
|
|
// A: pointer (Table)
|
2023-02-17 23:41:51 +00:00
|
|
|
// B: int
|
2023-01-13 22:10:01 +00:00
|
|
|
GET_ARR_ADDR,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Get pointer (LuaNode) to table node element at the active cached slot index
|
|
|
|
// A: pointer (Table)
|
2023-04-07 22:01:29 +01:00
|
|
|
// B: unsigned int (pcpos)
|
2023-09-01 18:58:27 +01:00
|
|
|
// C: Kn
|
2023-01-13 22:10:01 +00:00
|
|
|
GET_SLOT_NODE_ADDR,
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
// Get pointer (LuaNode) to table node element at the main position of the specified key hash
|
|
|
|
// A: pointer (Table)
|
2023-04-07 22:01:29 +01:00
|
|
|
// B: unsigned int (hash)
|
2023-03-17 19:20:37 +00:00
|
|
|
GET_HASH_NODE_ADDR,
|
|
|
|
|
2023-07-28 16:13:53 +01:00
|
|
|
// Get pointer (TValue) to Closure upvalue.
|
|
|
|
// A: pointer or undef (Closure)
|
|
|
|
// B: UPn
|
|
|
|
// When undef is specified, uses current function Closure.
|
|
|
|
GET_CLOSURE_UPVAL_ADDR,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Store a tag into TValue
|
|
|
|
// A: Rn
|
|
|
|
// B: tag
|
2023-01-13 22:10:01 +00:00
|
|
|
STORE_TAG,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2024-01-12 22:25:27 +00:00
|
|
|
// Store an integer into the extra field of the TValue
|
|
|
|
// A: Rn
|
|
|
|
// B: int
|
|
|
|
STORE_EXTRA,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Store a pointer (*) into TValue
|
|
|
|
// A: Rn
|
|
|
|
// B: pointer
|
2023-01-13 22:10:01 +00:00
|
|
|
STORE_POINTER,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Store a double number into TValue
|
|
|
|
// A: Rn
|
|
|
|
// B: double
|
2023-01-13 22:10:01 +00:00
|
|
|
STORE_DOUBLE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Store an int into TValue
|
|
|
|
// A: Rn
|
|
|
|
// B: int
|
2023-01-13 22:10:01 +00:00
|
|
|
STORE_INT,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-04-07 22:01:29 +01:00
|
|
|
// Store a vector into TValue
|
|
|
|
// A: Rn
|
|
|
|
// B: double (x)
|
|
|
|
// C: double (y)
|
|
|
|
// D: double (z)
|
|
|
|
STORE_VECTOR,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Store a TValue into memory
|
|
|
|
// A: Rn or pointer (TValue)
|
|
|
|
// B: TValue
|
2023-08-18 19:15:41 +01:00
|
|
|
// C: int (optional 'A' pointer offset)
|
2023-01-13 22:10:01 +00:00
|
|
|
STORE_TVALUE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-08-18 19:15:41 +01:00
|
|
|
// Store a pair of tag and value into memory
|
|
|
|
// A: Rn or pointer (TValue)
|
|
|
|
// B: tag (must be a constant)
|
|
|
|
// C: int/double/pointer
|
|
|
|
// D: int (optional 'A' pointer offset)
|
|
|
|
STORE_SPLIT_TVALUE,
|
2023-01-13 22:10:01 +00:00
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Add/Sub two integers together
|
|
|
|
// A, B: int
|
2023-01-13 22:10:01 +00:00
|
|
|
ADD_INT,
|
|
|
|
SUB_INT,
|
|
|
|
|
2024-01-19 18:04:46 +00:00
|
|
|
// Add/Sub/Mul/Div/Idiv/Mod two double numbers
|
2023-02-10 19:40:38 +00:00
|
|
|
// A, B: double
|
|
|
|
// In final x64 lowering, B can also be Rn or Kn
|
2023-01-13 22:10:01 +00:00
|
|
|
ADD_NUM,
|
|
|
|
SUB_NUM,
|
|
|
|
MUL_NUM,
|
|
|
|
DIV_NUM,
|
2023-09-01 18:58:27 +01:00
|
|
|
IDIV_NUM,
|
2023-01-13 22:10:01 +00:00
|
|
|
MOD_NUM,
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// Get the minimum/maximum of two numbers
|
|
|
|
// If one of the values is NaN, 'B' is returned as the result
|
|
|
|
// A, B: double
|
|
|
|
// In final x64 lowering, B can also be Rn or Kn
|
|
|
|
MIN_NUM,
|
|
|
|
MAX_NUM,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Negate a double number
|
|
|
|
// A: double
|
2023-01-13 22:10:01 +00:00
|
|
|
UNM_NUM,
|
|
|
|
|
2023-03-31 19:42:49 +01:00
|
|
|
// Round number to negative infinity (math.floor)
|
|
|
|
// A: double
|
|
|
|
FLOOR_NUM,
|
|
|
|
|
|
|
|
// Round number to positive infinity (math.ceil)
|
|
|
|
// A: double
|
|
|
|
CEIL_NUM,
|
|
|
|
|
|
|
|
// Round number to nearest integer number, rounding half-way cases away from zero (math.round)
|
|
|
|
// A: double
|
|
|
|
ROUND_NUM,
|
|
|
|
|
|
|
|
// Get square root of the argument (math.sqrt)
|
|
|
|
// A: double
|
|
|
|
SQRT_NUM,
|
|
|
|
|
|
|
|
// Get absolute value of the argument (math.abs)
|
|
|
|
// A: double
|
|
|
|
ABS_NUM,
|
|
|
|
|
2024-06-29 01:34:49 +01:00
|
|
|
// Get the sign of the argument (math.sign)
|
|
|
|
// A: double
|
|
|
|
SIGN_NUM,
|
|
|
|
|
2024-01-27 03:20:56 +00:00
|
|
|
// Add/Sub/Mul/Div/Idiv two vectors
|
|
|
|
// A, B: TValue
|
|
|
|
ADD_VEC,
|
|
|
|
SUB_VEC,
|
|
|
|
MUL_VEC,
|
|
|
|
DIV_VEC,
|
|
|
|
|
|
|
|
// Negate a vector
|
|
|
|
// A: TValue
|
|
|
|
UNM_VEC,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Compute Luau 'not' operation on destructured TValue
|
|
|
|
// A: tag
|
2023-04-14 19:06:22 +01:00
|
|
|
// B: int (value)
|
2023-08-04 20:18:54 +01:00
|
|
|
NOT_ANY,
|
|
|
|
|
|
|
|
// Perform a TValue comparison, supported conditions are LessEqual, Less and Equal
|
|
|
|
// A, B: Rn
|
|
|
|
// C: condition
|
|
|
|
CMP_ANY,
|
2023-01-13 22:10:01 +00:00
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Unconditional jump
|
2023-08-11 15:42:37 +01:00
|
|
|
// A: block/vmexit/undef
|
2023-01-13 22:10:01 +00:00
|
|
|
JUMP,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Jump if TValue is truthy
|
|
|
|
// A: Rn
|
|
|
|
// B: block (if true)
|
|
|
|
// C: block (if false)
|
2023-01-13 22:10:01 +00:00
|
|
|
JUMP_IF_TRUTHY,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Jump if TValue is falsy
|
|
|
|
// A: Rn
|
|
|
|
// B: block (if true)
|
|
|
|
// C: block (if false)
|
2023-01-13 22:10:01 +00:00
|
|
|
JUMP_IF_FALSY,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Jump if tags are equal
|
|
|
|
// A, B: tag
|
|
|
|
// C: block (if true)
|
|
|
|
// D: block (if false)
|
2023-01-13 22:10:01 +00:00
|
|
|
JUMP_EQ_TAG,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-09-08 01:13:49 +01:00
|
|
|
// Perform a conditional jump based on the result of integer comparison
|
2023-04-28 20:55:13 +01:00
|
|
|
// A, B: int
|
2023-04-21 23:14:26 +01:00
|
|
|
// C: condition
|
|
|
|
// D: block (if true)
|
|
|
|
// E: block (if false)
|
2023-09-08 01:13:49 +01:00
|
|
|
JUMP_CMP_INT,
|
2023-04-21 23:14:26 +01:00
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Jump if pointers are equal
|
|
|
|
// A, B: pointer (*)
|
|
|
|
// C: block (if true)
|
|
|
|
// D: block (if false)
|
2023-01-13 22:10:01 +00:00
|
|
|
JUMP_EQ_POINTER,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Perform a conditional jump based on the result of double comparison
|
|
|
|
// A, B: double
|
|
|
|
// C: condition
|
|
|
|
// D: block (if true)
|
|
|
|
// E: block (if false)
|
2023-01-13 22:10:01 +00:00
|
|
|
JUMP_CMP_NUM,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-10-21 02:10:30 +01:00
|
|
|
// Perform jump based on a numerical loop condition (step > 0 ? idx <= limit : limit <= idx)
|
|
|
|
// A: double (index)
|
|
|
|
// B: double (limit)
|
|
|
|
// C: double (step)
|
|
|
|
// D: block (if true)
|
|
|
|
// E: block (if false)
|
|
|
|
JUMP_FORN_LOOP_COND,
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
// Perform a conditional jump based on cached table node slot matching the actual table node slot for a key
|
|
|
|
// A: pointer (LuaNode)
|
|
|
|
// B: Kn
|
|
|
|
// C: block (if matches)
|
|
|
|
// D: block (if it doesn't)
|
|
|
|
JUMP_SLOT_MATCH,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Get table length
|
|
|
|
// A: pointer (Table)
|
2023-01-13 22:10:01 +00:00
|
|
|
TABLE_LEN,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-07-07 21:10:48 +01:00
|
|
|
// Get string length
|
|
|
|
// A: pointer (string)
|
|
|
|
STRING_LEN,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Allocate new table
|
2023-08-25 18:23:55 +01:00
|
|
|
// A: unsigned int (array element count)
|
|
|
|
// B: unsigned int (node element count)
|
2023-01-13 22:10:01 +00:00
|
|
|
NEW_TABLE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Duplicate a table
|
|
|
|
// A: pointer (Table)
|
2023-01-13 22:10:01 +00:00
|
|
|
DUP_TABLE,
|
|
|
|
|
2023-11-10 21:10:07 +00:00
|
|
|
// Insert an integer key into a table and return the pointer to inserted value (TValue)
|
2023-09-01 18:58:27 +01:00
|
|
|
// A: pointer (Table)
|
|
|
|
// B: int (key)
|
|
|
|
TABLE_SETNUM,
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
// Try to convert a double number into a table index (int) or jump if it's not an integer
|
2023-02-10 19:40:38 +00:00
|
|
|
// A: double
|
|
|
|
// B: block
|
2023-03-17 19:20:37 +00:00
|
|
|
TRY_NUM_TO_INDEX,
|
|
|
|
|
|
|
|
// Try to get pointer to tag method TValue inside the table's metatable or jump if there is no such value or metatable
|
|
|
|
// A: table
|
2023-04-14 19:06:22 +01:00
|
|
|
// B: int (TMS enum)
|
2023-03-17 19:20:37 +00:00
|
|
|
// C: block
|
|
|
|
TRY_CALL_FASTGETTM,
|
2023-01-13 22:10:01 +00:00
|
|
|
|
2024-06-07 18:51:12 +01:00
|
|
|
// Create new tagged userdata
|
|
|
|
// A: int (size)
|
|
|
|
// B: int (tag)
|
|
|
|
NEW_USERDATA,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Convert integer into a double number
|
|
|
|
// A: int
|
|
|
|
INT_TO_NUM,
|
2023-04-21 23:14:26 +01:00
|
|
|
UINT_TO_NUM,
|
|
|
|
|
|
|
|
// Converts a double number to an integer. 'A' may be any representable integer in a double.
|
|
|
|
// A: double
|
|
|
|
NUM_TO_INT,
|
|
|
|
|
|
|
|
// Converts a double number to an unsigned integer. For out-of-range values of 'A', the result is arch-specific.
|
|
|
|
// A: double
|
|
|
|
NUM_TO_UINT,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2024-01-27 03:20:56 +00:00
|
|
|
// Converts a double number to a vector with the value in X/Y/Z
|
|
|
|
// A: double
|
2024-02-21 15:06:11 +00:00
|
|
|
NUM_TO_VEC,
|
|
|
|
|
|
|
|
// Adds VECTOR type tag to a vector, preserving X/Y/Z components
|
|
|
|
// A: TValue
|
|
|
|
TAG_VECTOR,
|
2024-01-27 03:20:56 +00:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
// Adjust stack top (L->top) to point at 'B' TValues *after* the specified register
|
2023-11-10 21:10:07 +00:00
|
|
|
// This is used to return multiple values
|
2023-02-24 21:49:38 +00:00
|
|
|
// A: Rn
|
|
|
|
// B: int (offset)
|
|
|
|
ADJUST_STACK_TO_REG,
|
|
|
|
|
|
|
|
// Restore stack top (L->top) to point to the function stack top (L->ci->top)
|
|
|
|
// This is used to recover after calling a variadic function
|
|
|
|
ADJUST_STACK_TO_TOP,
|
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
// Execute fastcall builtin function with 1 argument in-place
|
|
|
|
// This is used for a few builtins that can have more than 1 result and cannot be represented as a regular instruction
|
2024-03-15 23:37:39 +00:00
|
|
|
// A: unsigned int (builtin id)
|
2023-03-03 20:21:14 +00:00
|
|
|
// B: Rn (result start)
|
2024-06-21 00:37:55 +01:00
|
|
|
// C: Rn (first argument)
|
|
|
|
// D: int (result count)
|
2023-03-03 20:21:14 +00:00
|
|
|
FASTCALL,
|
|
|
|
|
|
|
|
// Call the fastcall builtin function
|
2024-03-15 23:37:39 +00:00
|
|
|
// A: unsigned int (builtin id)
|
2023-03-03 20:21:14 +00:00
|
|
|
// B: Rn (result start)
|
|
|
|
// C: Rn (argument start)
|
2023-05-25 22:36:34 +01:00
|
|
|
// D: Rn or Kn or undef (optional second argument)
|
2024-06-21 00:37:55 +01:00
|
|
|
// E: Rn or Kn or undef (optional third argument)
|
|
|
|
// F: int (argument count or -1 to use all arguments up to stack top)
|
|
|
|
// G: int (result count or -1 to preserve all results and adjust stack top)
|
2023-03-03 20:21:14 +00:00
|
|
|
INVOKE_FASTCALL,
|
|
|
|
|
|
|
|
// Check that fastcall builtin function invocation was successful (negative result count jumps to fallback)
|
|
|
|
// A: int (result count)
|
|
|
|
// B: block (fallback)
|
|
|
|
CHECK_FASTCALL_RES,
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
// Fallback functions
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Perform an arithmetic operation on TValues of any type
|
|
|
|
// A: Rn (where to store the result)
|
|
|
|
// B: Rn (lhs)
|
|
|
|
// C: Rn or Kn (rhs)
|
2023-03-31 19:42:49 +01:00
|
|
|
// D: int (TMS enum with arithmetic type)
|
2023-01-13 22:10:01 +00:00
|
|
|
DO_ARITH,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Get length of a TValue of any type
|
|
|
|
// A: Rn (where to store the result)
|
|
|
|
// B: Rn
|
2023-01-13 22:10:01 +00:00
|
|
|
DO_LEN,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Lookup a value in TValue of any type using a key of any type
|
|
|
|
// A: Rn (where to store the result)
|
|
|
|
// B: Rn
|
|
|
|
// C: Rn or unsigned int (key)
|
2023-01-13 22:10:01 +00:00
|
|
|
GET_TABLE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Store a value into TValue of any type using a key of any type
|
|
|
|
// A: Rn (value to store)
|
|
|
|
// B: Rn
|
|
|
|
// C: Rn or unsigned int (key)
|
2023-01-13 22:10:01 +00:00
|
|
|
SET_TABLE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Lookup a value in the environment
|
|
|
|
// A: Rn (where to store the result)
|
|
|
|
// B: unsigned int (import path)
|
2023-01-13 22:10:01 +00:00
|
|
|
GET_IMPORT,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
// Concatenate multiple TValues into a string
|
|
|
|
// A: Rn (value start)
|
|
|
|
// B: unsigned int (number of registers to go over)
|
|
|
|
// Note: result is stored in the register specified in 'A'
|
2023-03-10 20:21:07 +00:00
|
|
|
// Note: all referenced registers might be modified in the operation
|
2023-01-13 22:10:01 +00:00
|
|
|
CONCAT,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load function upvalue into stack slot
|
|
|
|
// A: Rn
|
|
|
|
// B: UPn
|
2023-01-13 22:10:01 +00:00
|
|
|
GET_UPVALUE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Store TValue from stack slot into a function upvalue
|
|
|
|
// A: UPn
|
|
|
|
// B: Rn
|
2023-08-18 19:15:41 +01:00
|
|
|
// C: tag/undef (tag of the value that was written)
|
2023-01-13 22:10:01 +00:00
|
|
|
SET_UPVALUE,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Guards and checks (these instructions are not block terminators even though they jump to fallback)
|
|
|
|
|
|
|
|
// Guard against tag mismatch
|
|
|
|
// A, B: tag
|
2023-07-14 19:08:53 +01:00
|
|
|
// C: block/vmexit/undef
|
2023-02-10 19:40:38 +00:00
|
|
|
// In final x64 lowering, A can also be Rn
|
2024-03-09 00:47:53 +00:00
|
|
|
// When DebugLuauAbortingChecks flag is enabled, A can also be Rn
|
2023-08-11 15:42:37 +01:00
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
2023-01-13 22:10:01 +00:00
|
|
|
CHECK_TAG,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-08-04 20:18:54 +01:00
|
|
|
// Guard against a falsy tag+value
|
|
|
|
// A: tag
|
|
|
|
// B: value
|
|
|
|
// C: block/vmexit/undef
|
|
|
|
CHECK_TRUTHY,
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// Guard against readonly table
|
|
|
|
// A: pointer (Table)
|
2023-08-04 20:18:54 +01:00
|
|
|
// B: block/vmexit/undef
|
2023-05-25 22:36:34 +01:00
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
2023-01-13 22:10:01 +00:00
|
|
|
CHECK_READONLY,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Guard against table having a metatable
|
|
|
|
// A: pointer (Table)
|
2023-08-04 20:18:54 +01:00
|
|
|
// B: block/vmexit/undef
|
2023-05-25 22:36:34 +01:00
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
2023-01-13 22:10:01 +00:00
|
|
|
CHECK_NO_METATABLE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-07-07 21:10:48 +01:00
|
|
|
// Guard against executing in unsafe environment, exits to VM on check failure
|
2023-08-04 20:18:54 +01:00
|
|
|
// A: vmexit/vmexit/undef
|
2023-07-07 21:10:48 +01:00
|
|
|
// When undef is specified, execution is aborted on check failure
|
2023-01-13 22:10:01 +00:00
|
|
|
CHECK_SAFE_ENV,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Guard against index overflowing the table array size
|
|
|
|
// A: pointer (Table)
|
2023-02-17 23:41:51 +00:00
|
|
|
// B: int (index)
|
2023-08-04 20:18:54 +01:00
|
|
|
// C: block/vmexit/undef
|
2023-05-25 22:36:34 +01:00
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
2023-01-13 22:10:01 +00:00
|
|
|
CHECK_ARRAY_SIZE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Guard against cached table node slot not matching the actual table node slot for a key
|
|
|
|
// A: pointer (LuaNode)
|
|
|
|
// B: Kn
|
2023-05-25 22:36:34 +01:00
|
|
|
// C: block/undef
|
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
2023-01-13 22:10:01 +00:00
|
|
|
CHECK_SLOT_MATCH,
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
// Guard against table node with a linked next node to ensure that our lookup hits the main position of the key
|
|
|
|
// A: pointer (LuaNode)
|
2023-08-04 20:18:54 +01:00
|
|
|
// B: block/vmexit/undef
|
2023-05-25 22:36:34 +01:00
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
2023-03-17 19:20:37 +00:00
|
|
|
CHECK_NODE_NO_NEXT,
|
|
|
|
|
2023-09-01 18:58:27 +01:00
|
|
|
// Guard against table node with 'nil' value
|
|
|
|
// A: pointer (LuaNode)
|
|
|
|
// B: block/vmexit/undef
|
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
|
|
|
CHECK_NODE_VALUE,
|
|
|
|
|
2023-11-10 21:10:07 +00:00
|
|
|
// Guard against access at specified offset/size overflowing the buffer length
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
// C: int (size)
|
|
|
|
// D: block/vmexit/undef
|
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
|
|
|
CHECK_BUFFER_LEN,
|
|
|
|
|
2024-06-07 18:51:12 +01:00
|
|
|
// Guard against userdata tag mismatch
|
|
|
|
// A: pointer (userdata)
|
|
|
|
// B: int (tag)
|
|
|
|
// C: block/vmexit/undef
|
|
|
|
// When undef is specified instead of a block, execution is aborted on check failure
|
|
|
|
CHECK_USERDATA_TAG,
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
// Special operations
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Check interrupt handler
|
|
|
|
// A: unsigned int (pcpos)
|
2023-01-13 22:10:01 +00:00
|
|
|
INTERRUPT,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Check and run GC assist if necessary
|
2023-01-13 22:10:01 +00:00
|
|
|
CHECK_GC,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Handle GC write barrier (forward)
|
|
|
|
// A: pointer (GCObject)
|
|
|
|
// B: Rn (TValue that was written to the object)
|
2023-06-24 07:19:39 +01:00
|
|
|
// C: tag/undef (tag of the value that was written)
|
2023-01-13 22:10:01 +00:00
|
|
|
BARRIER_OBJ,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Handle GC write barrier (backwards) for a write into a table
|
|
|
|
// A: pointer (Table)
|
2023-01-13 22:10:01 +00:00
|
|
|
BARRIER_TABLE_BACK,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Handle GC write barrier (forward) for a write into a table
|
|
|
|
// A: pointer (Table)
|
|
|
|
// B: Rn (TValue that was written to the object)
|
2023-06-24 07:19:39 +01:00
|
|
|
// C: tag/undef (tag of the value that was written)
|
2023-01-13 22:10:01 +00:00
|
|
|
BARRIER_TABLE_FORWARD,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Update savedpc value
|
|
|
|
// A: unsigned int (pcpos)
|
2023-01-13 22:10:01 +00:00
|
|
|
SET_SAVEDPC,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Close open upvalues for registers at specified index or higher
|
|
|
|
// A: Rn (starting register index)
|
2023-01-13 22:10:01 +00:00
|
|
|
CLOSE_UPVALS,
|
|
|
|
|
|
|
|
// While capture is a no-op right now, it might be useful to track register/upvalue lifetimes
|
2023-02-10 19:40:38 +00:00
|
|
|
// A: Rn or UPn
|
2023-05-25 22:36:34 +01:00
|
|
|
// B: unsigned int (1 for reference capture, 0 for value capture)
|
2023-01-13 22:10:01 +00:00
|
|
|
CAPTURE,
|
|
|
|
|
|
|
|
// Operations that don't have an IR representation yet
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Set a list of values to table in target register
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (target)
|
|
|
|
// C: Rn (source start)
|
|
|
|
// D: int (count or -1 to assign values up to stack top)
|
|
|
|
// E: unsigned int (table index to start from)
|
2023-08-25 18:23:55 +01:00
|
|
|
// F: undef/unsigned int (target table known size)
|
2023-03-31 19:42:49 +01:00
|
|
|
SETLIST,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Call specified function
|
2023-03-24 18:03:04 +00:00
|
|
|
// A: Rn (function, followed by arguments)
|
|
|
|
// B: int (argument count or -1 to use all arguments up to stack top)
|
|
|
|
// C: int (result count or -1 to preserve all results and adjust stack top)
|
|
|
|
// Note: return values are placed starting from Rn specified in 'A'
|
2023-03-31 19:42:49 +01:00
|
|
|
CALL,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Return specified values from the function
|
2023-03-24 18:03:04 +00:00
|
|
|
// A: Rn (value start)
|
|
|
|
// B: int (result count or -1 to return all values up to stack top)
|
2023-03-31 19:42:49 +01:00
|
|
|
RETURN,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// Adjust loop variables for one iteration of a generic for loop, jump back to the loop header if loop needs to continue
|
2023-03-10 20:21:07 +00:00
|
|
|
// A: Rn (loop variable start, updates Rn+2 and 'B' number of registers starting from Rn+3)
|
|
|
|
// B: int (loop variable count, if more than 2, registers starting from Rn+5 are set to nil)
|
2023-03-03 20:21:14 +00:00
|
|
|
// C: block (repeat)
|
|
|
|
// D: block (exit)
|
2023-03-31 19:42:49 +01:00
|
|
|
FORGLOOP,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// Handle LOP_FORGLOOP fallback when variable being iterated is not a table
|
2023-03-31 19:42:49 +01:00
|
|
|
// A: Rn (loop state start, updates Rn+2 and 'B' number of registers starting from Rn+3)
|
|
|
|
// B: int (loop variable count and a MSB set when it's an ipairs-like iteration loop)
|
|
|
|
// C: block (repeat)
|
|
|
|
// D: block (exit)
|
|
|
|
FORGLOOP_FALLBACK,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// Fallback for generic for loop preparation when iterating over builtin pairs/ipairs
|
|
|
|
// It raises an error if 'B' register is not a function
|
2023-02-10 19:40:38 +00:00
|
|
|
// A: unsigned int (bytecode instruction index)
|
2023-03-03 20:21:14 +00:00
|
|
|
// B: Rn
|
|
|
|
// C: block (forgloop location)
|
2023-03-31 19:42:49 +01:00
|
|
|
FORGPREP_XNEXT_FALLBACK,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Increment coverage data (saturating 24 bit add)
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
2023-03-31 19:42:49 +01:00
|
|
|
COVERAGE,
|
2023-01-13 22:10:01 +00:00
|
|
|
|
|
|
|
// Operations that have a translation, but use a full instruction fallback
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load a value from global table at specified key
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (dest)
|
|
|
|
// C: Kn (key)
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_GETGLOBAL,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Store a value into global table at specified key
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (value)
|
|
|
|
// C: Kn (key)
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_SETGLOBAL,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load a value from table at specified key
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (dest)
|
|
|
|
// C: Rn (table)
|
|
|
|
// D: Kn (key)
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_GETTABLEKS,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Store a value into a table at specified key
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (value)
|
|
|
|
// C: Rn (table)
|
|
|
|
// D: Kn (key)
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_SETTABLEKS,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Load function from source register using name into target register and copying source register into target register + 1
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (target)
|
|
|
|
// C: Rn (source)
|
|
|
|
// D: Kn (name)
|
2023-01-20 20:27:03 +00:00
|
|
|
FALLBACK_NAMECALL,
|
2023-01-13 22:10:01 +00:00
|
|
|
|
|
|
|
// Operations that don't have assembly lowering at all
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Prepare stack for variadic functions so that GETVARARGS works correctly
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: int (numparams)
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_PREPVARARGS,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Copy variables into the target registers from vararg storage for current function
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (dest start)
|
|
|
|
// C: int (count)
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_GETVARARGS,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Create closure from a child proto
|
2023-07-28 16:13:53 +01:00
|
|
|
// A: unsigned int (nups)
|
|
|
|
// B: pointer (table)
|
2023-02-10 19:40:38 +00:00
|
|
|
// C: unsigned int (protoid)
|
2023-07-28 16:13:53 +01:00
|
|
|
NEWCLOSURE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Create closure from a pre-created function object (reusing it unless environments diverge)
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
|
|
|
// B: Rn (dest)
|
|
|
|
// C: Kn (prototype)
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_DUPCLOSURE,
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
// Prepare loop variables for a generic for loop, jump to the loop backedge unconditionally
|
|
|
|
// A: unsigned int (bytecode instruction index)
|
2023-03-03 20:21:14 +00:00
|
|
|
// B: Rn (loop state start, updates Rn Rn+1 Rn+2)
|
2023-02-17 23:41:51 +00:00
|
|
|
// C: block
|
2023-01-13 22:10:01 +00:00
|
|
|
FALLBACK_FORGPREP,
|
2023-02-17 23:41:51 +00:00
|
|
|
|
|
|
|
// Instruction that passes value through, it is produced by constant folding and users substitute it with the value
|
|
|
|
SUBSTITUTE,
|
|
|
|
// A: operand of any type
|
2023-04-21 23:14:26 +01:00
|
|
|
|
|
|
|
// Performs bitwise and/xor/or on two unsigned integers
|
2023-04-28 20:55:13 +01:00
|
|
|
// A, B: int
|
2023-04-21 23:14:26 +01:00
|
|
|
BITAND_UINT,
|
|
|
|
BITXOR_UINT,
|
|
|
|
BITOR_UINT,
|
|
|
|
|
|
|
|
// Performs bitwise not on an unsigned integer
|
2023-04-28 20:55:13 +01:00
|
|
|
// A: int
|
2023-04-21 23:14:26 +01:00
|
|
|
BITNOT_UINT,
|
|
|
|
|
|
|
|
// Performs bitwise shift/rotate on an unsigned integer
|
2023-04-28 20:55:13 +01:00
|
|
|
// A: int (source)
|
2023-04-21 23:14:26 +01:00
|
|
|
// B: int (shift amount)
|
|
|
|
BITLSHIFT_UINT,
|
|
|
|
BITRSHIFT_UINT,
|
|
|
|
BITARSHIFT_UINT,
|
|
|
|
BITLROTATE_UINT,
|
|
|
|
BITRROTATE_UINT,
|
|
|
|
|
|
|
|
// Returns the number of consecutive zero bits in A starting from the left-most (most significant) bit.
|
2023-04-28 20:55:13 +01:00
|
|
|
// A: int
|
2023-04-21 23:14:26 +01:00
|
|
|
BITCOUNTLZ_UINT,
|
|
|
|
BITCOUNTRZ_UINT,
|
|
|
|
|
2023-11-03 23:45:04 +00:00
|
|
|
// Swap byte order in A
|
|
|
|
// A: int
|
|
|
|
BYTESWAP_UINT,
|
|
|
|
|
2023-04-21 23:14:26 +01:00
|
|
|
// Calls native libm function with 1 or 2 arguments
|
|
|
|
// A: builtin function ID
|
|
|
|
// B: double
|
2023-05-05 22:52:49 +01:00
|
|
|
// C: double/int (optional, 2nd argument)
|
2023-04-21 23:14:26 +01:00
|
|
|
INVOKE_LIBM,
|
2023-06-24 07:19:39 +01:00
|
|
|
|
|
|
|
// Returns the string name of a type based on tag, alternative for type(x)
|
|
|
|
// A: tag
|
|
|
|
GET_TYPE,
|
|
|
|
|
|
|
|
// Returns the string name of a type either from a __type metatable field or just based on the tag, alternative for typeof(x)
|
|
|
|
// A: Rn
|
|
|
|
GET_TYPEOF,
|
2023-07-28 16:13:53 +01:00
|
|
|
|
|
|
|
// Find or create an upval at the given level
|
|
|
|
// A: Rn (level)
|
|
|
|
FINDUPVAL,
|
2023-11-10 21:10:07 +00:00
|
|
|
|
|
|
|
// Read i8 (sign-extended to int) from buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
BUFFER_READI8,
|
|
|
|
|
|
|
|
// Read u8 (zero-extended to int) from buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
BUFFER_READU8,
|
|
|
|
|
|
|
|
// Write i8/u8 value (int argument is truncated) to buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
// C: int (value)
|
|
|
|
BUFFER_WRITEI8,
|
|
|
|
|
|
|
|
// Read i16 (sign-extended to int) from buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
BUFFER_READI16,
|
|
|
|
|
|
|
|
// Read u16 (zero-extended to int) from buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
BUFFER_READU16,
|
|
|
|
|
|
|
|
// Write i16/u16 value (int argument is truncated) to buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
// C: int (value)
|
|
|
|
BUFFER_WRITEI16,
|
|
|
|
|
|
|
|
// Read i32 value from buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
BUFFER_READI32,
|
|
|
|
|
|
|
|
// Write i32/u32 value to buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
// C: int (value)
|
|
|
|
BUFFER_WRITEI32,
|
|
|
|
|
|
|
|
// Read float value (converted to double) from buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
BUFFER_READF32,
|
|
|
|
|
|
|
|
// Write float value (converted from double) to buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
// C: double (value)
|
|
|
|
BUFFER_WRITEF32,
|
|
|
|
|
|
|
|
// Read double value from buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
BUFFER_READF64,
|
|
|
|
|
|
|
|
// Write double value to buffer storage at specified offset
|
|
|
|
// A: pointer (buffer)
|
|
|
|
// B: int (offset)
|
|
|
|
// C: double (value)
|
|
|
|
BUFFER_WRITEF64,
|
2023-01-13 22:10:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum class IrConstKind : uint8_t
|
|
|
|
{
|
|
|
|
Int,
|
|
|
|
Uint,
|
|
|
|
Double,
|
|
|
|
Tag,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IrConst
|
|
|
|
{
|
|
|
|
IrConstKind kind;
|
|
|
|
|
|
|
|
union
|
|
|
|
{
|
|
|
|
int valueInt;
|
|
|
|
unsigned valueUint;
|
|
|
|
double valueDouble;
|
|
|
|
uint8_t valueTag;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class IrCondition : uint8_t
|
|
|
|
{
|
|
|
|
Equal,
|
|
|
|
NotEqual,
|
|
|
|
Less,
|
|
|
|
NotLess,
|
|
|
|
LessEqual,
|
|
|
|
NotLessEqual,
|
|
|
|
Greater,
|
|
|
|
NotGreater,
|
|
|
|
GreaterEqual,
|
|
|
|
NotGreaterEqual,
|
|
|
|
|
|
|
|
UnsignedLess,
|
|
|
|
UnsignedLessEqual,
|
|
|
|
UnsignedGreater,
|
|
|
|
UnsignedGreaterEqual,
|
|
|
|
|
|
|
|
Count
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class IrOpKind : uint32_t
|
|
|
|
{
|
|
|
|
None,
|
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
Undef,
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
// To reference a constant value
|
|
|
|
Constant,
|
|
|
|
|
|
|
|
// To specify a condition code
|
|
|
|
Condition,
|
|
|
|
|
|
|
|
// To reference a result of a previous instruction
|
|
|
|
Inst,
|
|
|
|
|
|
|
|
// To reference a basic block in control flow
|
|
|
|
Block,
|
|
|
|
|
|
|
|
// To reference a VM register
|
|
|
|
VmReg,
|
|
|
|
|
|
|
|
// To reference a VM constant
|
|
|
|
VmConst,
|
|
|
|
|
|
|
|
// To reference a VM upvalue
|
|
|
|
VmUpvalue,
|
2023-07-14 19:08:53 +01:00
|
|
|
|
|
|
|
// To reference an exit to VM at specific PC pos
|
|
|
|
VmExit,
|
2023-01-13 22:10:01 +00:00
|
|
|
};
|
|
|
|
|
2023-08-11 15:42:37 +01:00
|
|
|
// VmExit uses a special value to indicate that pcpos update should be skipped
|
|
|
|
// This is only used during type checking at function entry
|
|
|
|
constexpr uint32_t kVmExitEntryGuardPc = (1u << 28) - 1;
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
struct IrOp
|
|
|
|
{
|
|
|
|
IrOpKind kind : 4;
|
|
|
|
uint32_t index : 28;
|
|
|
|
|
|
|
|
IrOp()
|
|
|
|
: kind(IrOpKind::None)
|
|
|
|
, index(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
IrOp(IrOpKind kind, uint32_t index)
|
|
|
|
: kind(kind)
|
|
|
|
, index(index)
|
|
|
|
{
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
bool operator==(const IrOp& rhs) const
|
|
|
|
{
|
|
|
|
return kind == rhs.kind && index == rhs.index;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const IrOp& rhs) const
|
|
|
|
{
|
|
|
|
return !(*this == rhs);
|
|
|
|
}
|
2023-01-13 22:10:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static_assert(sizeof(IrOp) == 4);
|
|
|
|
|
2023-04-07 22:01:29 +01:00
|
|
|
enum class IrValueKind : uint8_t
|
|
|
|
{
|
|
|
|
Unknown, // Used by SUBSTITUTE, argument has to be checked to get type
|
|
|
|
None,
|
|
|
|
Tag,
|
|
|
|
Int,
|
|
|
|
Pointer,
|
|
|
|
Double,
|
|
|
|
Tvalue,
|
|
|
|
};
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
struct IrInst
|
|
|
|
{
|
|
|
|
IrCmd cmd;
|
|
|
|
|
|
|
|
// Operands
|
|
|
|
IrOp a;
|
|
|
|
IrOp b;
|
|
|
|
IrOp c;
|
|
|
|
IrOp d;
|
|
|
|
IrOp e;
|
2023-02-24 21:49:38 +00:00
|
|
|
IrOp f;
|
2024-06-14 21:21:20 +01:00
|
|
|
IrOp g;
|
2023-01-13 22:10:01 +00:00
|
|
|
|
|
|
|
uint32_t lastUse = 0;
|
|
|
|
uint16_t useCount = 0;
|
|
|
|
|
|
|
|
// Location of the result (optional)
|
2023-03-03 20:21:14 +00:00
|
|
|
X64::RegisterX64 regX64 = X64::noreg;
|
|
|
|
A64::RegisterA64 regA64 = A64::noreg;
|
2023-01-13 22:10:01 +00:00
|
|
|
bool reusedReg = false;
|
2023-04-07 22:01:29 +01:00
|
|
|
bool spilled = false;
|
2023-04-21 23:14:26 +01:00
|
|
|
bool needsReload = false;
|
2023-01-13 22:10:01 +00:00
|
|
|
};
|
|
|
|
|
2023-04-07 22:01:29 +01:00
|
|
|
// When IrInst operands are used, current instruction index is often required to track lifetime
|
|
|
|
constexpr uint32_t kInvalidInstIdx = ~0u;
|
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
struct IrInstHash
|
|
|
|
{
|
|
|
|
static const uint32_t m = 0x5bd1e995;
|
|
|
|
static const int r = 24;
|
|
|
|
|
|
|
|
static uint32_t mix(uint32_t h, uint32_t k)
|
|
|
|
{
|
|
|
|
// MurmurHash2 step
|
|
|
|
k *= m;
|
|
|
|
k ^= k >> r;
|
|
|
|
k *= m;
|
|
|
|
|
|
|
|
h *= m;
|
|
|
|
h ^= k;
|
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t mix(uint32_t h, IrOp op)
|
|
|
|
{
|
|
|
|
static_assert(sizeof(op) == sizeof(uint32_t));
|
|
|
|
uint32_t k;
|
|
|
|
memcpy(&k, &op, sizeof(op));
|
|
|
|
|
|
|
|
return mix(h, k);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t operator()(const IrInst& key) const
|
|
|
|
{
|
|
|
|
// MurmurHash2 unrolled
|
|
|
|
uint32_t h = 25;
|
|
|
|
|
|
|
|
h = mix(h, uint32_t(key.cmd));
|
|
|
|
h = mix(h, key.a);
|
|
|
|
h = mix(h, key.b);
|
|
|
|
h = mix(h, key.c);
|
|
|
|
h = mix(h, key.d);
|
|
|
|
h = mix(h, key.e);
|
|
|
|
h = mix(h, key.f);
|
2024-06-14 21:21:20 +01:00
|
|
|
h = mix(h, key.g);
|
2023-04-28 20:55:13 +01:00
|
|
|
|
|
|
|
// MurmurHash2 tail
|
|
|
|
h ^= h >> 13;
|
|
|
|
h *= m;
|
|
|
|
h ^= h >> 15;
|
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IrInstEq
|
|
|
|
{
|
|
|
|
bool operator()(const IrInst& a, const IrInst& b) const
|
|
|
|
{
|
2024-06-14 21:21:20 +01:00
|
|
|
return a.cmd == b.cmd && a.a == b.a && a.b == b.b && a.c == b.c && a.d == b.d && a.e == b.e && a.f == b.f && a.g == b.g;
|
2023-04-28 20:55:13 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
enum class IrBlockKind : uint8_t
|
|
|
|
{
|
|
|
|
Bytecode,
|
|
|
|
Fallback,
|
|
|
|
Internal,
|
2023-03-03 20:21:14 +00:00
|
|
|
Linearized,
|
2023-02-10 19:40:38 +00:00
|
|
|
Dead,
|
2023-01-13 22:10:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct IrBlock
|
|
|
|
{
|
|
|
|
IrBlockKind kind;
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
uint16_t useCount = 0;
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
// 'start' and 'finish' define an inclusive range of instructions which belong to this block inside the function
|
|
|
|
// When block has been constructed, 'finish' always points to the first and only terminating instruction
|
2023-02-10 19:40:38 +00:00
|
|
|
uint32_t start = ~0u;
|
2023-02-24 21:49:38 +00:00
|
|
|
uint32_t finish = ~0u;
|
2023-01-13 22:10:01 +00:00
|
|
|
|
2023-06-16 18:35:18 +01:00
|
|
|
uint32_t sortkey = ~0u;
|
2023-09-01 18:58:27 +01:00
|
|
|
uint32_t chainkey = 0;
|
|
|
|
uint32_t expectedNextBlock = ~0u;
|
2023-06-16 18:35:18 +01:00
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
Label label;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BytecodeMapping
|
|
|
|
{
|
|
|
|
uint32_t irLocation;
|
|
|
|
uint32_t asmLocation;
|
|
|
|
};
|
|
|
|
|
2023-12-02 07:46:57 +00:00
|
|
|
struct BytecodeBlock
|
|
|
|
{
|
|
|
|
// 'start' and 'finish' define an inclusive range of instructions which belong to the block
|
|
|
|
int startpc = -1;
|
|
|
|
int finishpc = -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BytecodeTypes
|
|
|
|
{
|
|
|
|
uint8_t result = LBC_TYPE_ANY;
|
|
|
|
uint8_t a = LBC_TYPE_ANY;
|
|
|
|
uint8_t b = LBC_TYPE_ANY;
|
|
|
|
uint8_t c = LBC_TYPE_ANY;
|
|
|
|
};
|
|
|
|
|
2024-04-25 23:26:09 +01:00
|
|
|
struct BytecodeRegTypeInfo
|
|
|
|
{
|
|
|
|
uint8_t type = LBC_TYPE_ANY;
|
|
|
|
uint8_t reg = 0; // Register slot where variable is stored
|
|
|
|
int startpc = 0; // First point where variable is alive (could be before variable has been assigned a value)
|
|
|
|
int endpc = 0; // First point where variable is dead
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BytecodeTypeInfo
|
|
|
|
{
|
|
|
|
std::vector<uint8_t> argumentTypes;
|
|
|
|
std::vector<BytecodeRegTypeInfo> regTypes;
|
|
|
|
std::vector<uint8_t> upvalueTypes;
|
|
|
|
|
|
|
|
// Offsets into regTypes for each individual register
|
|
|
|
// One extra element at the end contains the vector size for easier arr[Rn], arr[Rn + 1] range access
|
|
|
|
std::vector<uint32_t> regTypeOffsets;
|
|
|
|
};
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
struct IrFunction
|
|
|
|
{
|
|
|
|
std::vector<IrBlock> blocks;
|
|
|
|
std::vector<IrInst> instructions;
|
|
|
|
std::vector<IrConst> constants;
|
|
|
|
|
2023-12-02 07:46:57 +00:00
|
|
|
std::vector<BytecodeBlock> bcBlocks;
|
|
|
|
std::vector<BytecodeTypes> bcTypes;
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
std::vector<BytecodeMapping> bcMapping;
|
2023-08-11 15:42:37 +01:00
|
|
|
uint32_t entryBlock = 0;
|
|
|
|
uint32_t entryLocation = 0;
|
2023-01-20 20:27:03 +00:00
|
|
|
|
2023-04-21 23:14:26 +01:00
|
|
|
// For each instruction, an operand that can be used to recompute the value
|
2023-04-14 19:06:22 +01:00
|
|
|
std::vector<IrOp> valueRestoreOps;
|
2023-12-02 07:46:57 +00:00
|
|
|
std::vector<uint32_t> validRestoreOpBlocks;
|
2023-04-14 19:06:22 +01:00
|
|
|
|
2024-04-25 23:26:09 +01:00
|
|
|
BytecodeTypeInfo bcTypeInfo;
|
|
|
|
|
2023-01-20 20:27:03 +00:00
|
|
|
Proto* proto = nullptr;
|
2023-06-09 18:08:00 +01:00
|
|
|
bool variadic = false;
|
2023-03-10 20:21:07 +00:00
|
|
|
|
|
|
|
CfgInfo cfg;
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
IrBlock& blockOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::Block);
|
2023-02-10 19:40:38 +00:00
|
|
|
return blocks[op.index];
|
|
|
|
}
|
|
|
|
|
|
|
|
IrInst& instOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::Inst);
|
2023-02-10 19:40:38 +00:00
|
|
|
return instructions[op.index];
|
|
|
|
}
|
|
|
|
|
2023-03-31 19:42:49 +01:00
|
|
|
IrInst* asInstOp(IrOp op)
|
|
|
|
{
|
|
|
|
if (op.kind == IrOpKind::Inst)
|
|
|
|
return &instructions[op.index];
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
IrConst& constOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::Constant);
|
2023-02-10 19:40:38 +00:00
|
|
|
return constants[op.index];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t tagOp(IrOp op)
|
|
|
|
{
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(value.kind == IrConstKind::Tag);
|
2023-02-10 19:40:38 +00:00
|
|
|
return value.valueTag;
|
|
|
|
}
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
std::optional<uint8_t> asTagOp(IrOp op)
|
|
|
|
{
|
|
|
|
if (op.kind != IrOpKind::Constant)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
|
|
|
if (value.kind != IrConstKind::Tag)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
return value.valueTag;
|
|
|
|
}
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
int intOp(IrOp op)
|
|
|
|
{
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(value.kind == IrConstKind::Int);
|
2023-02-10 19:40:38 +00:00
|
|
|
return value.valueInt;
|
|
|
|
}
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
std::optional<int> asIntOp(IrOp op)
|
|
|
|
{
|
|
|
|
if (op.kind != IrOpKind::Constant)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
|
|
|
if (value.kind != IrConstKind::Int)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
return value.valueInt;
|
|
|
|
}
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
unsigned uintOp(IrOp op)
|
|
|
|
{
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(value.kind == IrConstKind::Uint);
|
2023-02-10 19:40:38 +00:00
|
|
|
return value.valueUint;
|
|
|
|
}
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
std::optional<unsigned> asUintOp(IrOp op)
|
|
|
|
{
|
|
|
|
if (op.kind != IrOpKind::Constant)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
|
|
|
if (value.kind != IrConstKind::Uint)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
return value.valueUint;
|
|
|
|
}
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
double doubleOp(IrOp op)
|
|
|
|
{
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(value.kind == IrConstKind::Double);
|
2023-02-10 19:40:38 +00:00
|
|
|
return value.valueDouble;
|
|
|
|
}
|
2023-02-17 23:41:51 +00:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
std::optional<double> asDoubleOp(IrOp op)
|
|
|
|
{
|
|
|
|
if (op.kind != IrOpKind::Constant)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
IrConst& value = constOp(op);
|
|
|
|
|
|
|
|
if (value.kind != IrConstKind::Double)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
return value.valueDouble;
|
|
|
|
}
|
|
|
|
|
2023-04-14 19:06:22 +01:00
|
|
|
uint32_t getBlockIndex(const IrBlock& block) const
|
2023-02-24 21:49:38 +00:00
|
|
|
{
|
|
|
|
// Can only be called with blocks from our vector
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(&block >= blocks.data() && &block <= blocks.data() + blocks.size());
|
2023-02-24 21:49:38 +00:00
|
|
|
return uint32_t(&block - blocks.data());
|
|
|
|
}
|
2023-04-07 22:01:29 +01:00
|
|
|
|
2023-04-14 19:06:22 +01:00
|
|
|
uint32_t getInstIndex(const IrInst& inst) const
|
2023-04-07 22:01:29 +01:00
|
|
|
{
|
|
|
|
// Can only be called with instructions from our vector
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(&inst >= instructions.data() && &inst <= instructions.data() + instructions.size());
|
2023-04-07 22:01:29 +01:00
|
|
|
return uint32_t(&inst - instructions.data());
|
|
|
|
}
|
2023-04-14 19:06:22 +01:00
|
|
|
|
|
|
|
void recordRestoreOp(uint32_t instIdx, IrOp location)
|
|
|
|
{
|
|
|
|
if (instIdx >= valueRestoreOps.size())
|
|
|
|
valueRestoreOps.resize(instIdx + 1);
|
|
|
|
|
|
|
|
valueRestoreOps[instIdx] = location;
|
|
|
|
}
|
|
|
|
|
2023-09-01 18:58:27 +01:00
|
|
|
IrOp findRestoreOp(uint32_t instIdx, bool limitToCurrentBlock) const
|
2023-04-14 19:06:22 +01:00
|
|
|
{
|
|
|
|
if (instIdx >= valueRestoreOps.size())
|
|
|
|
return {};
|
|
|
|
|
2024-01-12 22:25:27 +00:00
|
|
|
// When spilled, values can only reference restore operands in the current block chain
|
|
|
|
if (limitToCurrentBlock)
|
2023-09-01 18:58:27 +01:00
|
|
|
{
|
2024-01-12 22:25:27 +00:00
|
|
|
for (uint32_t blockIdx : validRestoreOpBlocks)
|
2023-12-02 07:46:57 +00:00
|
|
|
{
|
2024-01-12 22:25:27 +00:00
|
|
|
const IrBlock& block = blocks[blockIdx];
|
2023-12-02 07:46:57 +00:00
|
|
|
|
2024-01-12 22:25:27 +00:00
|
|
|
if (instIdx >= block.start && instIdx <= block.finish)
|
|
|
|
return valueRestoreOps[instIdx];
|
2023-12-02 07:46:57 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 22:25:27 +00:00
|
|
|
return {};
|
2023-09-01 18:58:27 +01:00
|
|
|
}
|
2023-04-21 23:14:26 +01:00
|
|
|
|
2024-01-12 22:25:27 +00:00
|
|
|
return valueRestoreOps[instIdx];
|
2023-04-14 19:06:22 +01:00
|
|
|
}
|
|
|
|
|
2023-09-01 18:58:27 +01:00
|
|
|
IrOp findRestoreOp(const IrInst& inst, bool limitToCurrentBlock) const
|
2023-04-14 19:06:22 +01:00
|
|
|
{
|
2023-09-01 18:58:27 +01:00
|
|
|
return findRestoreOp(getInstIndex(inst), limitToCurrentBlock);
|
2023-04-14 19:06:22 +01:00
|
|
|
}
|
2023-12-02 07:46:57 +00:00
|
|
|
|
|
|
|
BytecodeTypes getBytecodeTypesAt(int pcpos) const
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(pcpos >= 0);
|
2023-12-02 07:46:57 +00:00
|
|
|
|
|
|
|
if (size_t(pcpos) < bcTypes.size())
|
|
|
|
return bcTypes[pcpos];
|
|
|
|
|
|
|
|
return BytecodeTypes();
|
|
|
|
}
|
2023-01-13 22:10:01 +00:00
|
|
|
};
|
|
|
|
|
2023-03-24 18:03:04 +00:00
|
|
|
inline IrCondition conditionOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::Condition);
|
2023-03-24 18:03:04 +00:00
|
|
|
return IrCondition(op.index);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int vmRegOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::VmReg);
|
2023-03-24 18:03:04 +00:00
|
|
|
return op.index;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int vmConstOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::VmConst);
|
2023-03-24 18:03:04 +00:00
|
|
|
return op.index;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int vmUpvalueOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::VmUpvalue);
|
2023-03-24 18:03:04 +00:00
|
|
|
return op.index;
|
|
|
|
}
|
|
|
|
|
2023-08-11 15:42:37 +01:00
|
|
|
inline uint32_t vmExitOp(IrOp op)
|
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(op.kind == IrOpKind::VmExit);
|
2023-08-11 15:42:37 +01:00
|
|
|
return op.index;
|
|
|
|
}
|
|
|
|
|
2023-01-13 22:10:01 +00:00
|
|
|
} // namespace CodeGen
|
|
|
|
} // namespace Luau
|