luau/CodeGen/include/Luau/IrData.h

1028 lines
25 KiB
C
Raw Normal View History

2023-01-13 20:36:28 +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-03-10 19:20:04 +00:00
#include "Luau/IrAnalysis.h"
2023-01-13 20:36:28 +00:00
#include "Luau/Label.h"
#include "Luau/RegisterX64.h"
#include "Luau/RegisterA64.h"
2023-02-24 18:24:22 +00:00
#include <optional>
2023-01-13 20:36:28 +00:00
#include <vector>
#include <stdint.h>
2023-04-28 12:55:55 +01:00
#include <string.h>
2023-01-13 20:36:28 +00:00
2023-01-20 12:02:39 +00:00
struct Proto;
2023-01-13 20:36:28 +00:00
namespace Luau
{
namespace CodeGen
{
2023-04-28 12:55:55 +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 18:50:54 +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 14:59:30 +00:00
// * UPn - VM function upvalue slot, n in 0..199
2023-02-10 18:50:54 +00:00
// * A, B, C, D, E are instruction arguments
2023-01-13 20:36:28 +00:00
enum class IrCmd : uint8_t
{
NOP,
2023-02-10 18:50:54 +00:00
// Load a tag from TValue
// A: Rn or Kn
2023-01-13 20:36:28 +00:00
LOAD_TAG,
2023-02-10 18:50:54 +00:00
// Load a pointer (*) from TValue
// A: Rn or Kn
2023-01-13 20:36:28 +00:00
LOAD_POINTER,
2023-02-10 18:50:54 +00:00
// Load a double number from TValue
// A: Rn or Kn
2023-01-13 20:36:28 +00:00
LOAD_DOUBLE,
2023-02-10 18:50:54 +00:00
// Load an int from TValue
// A: Rn
2023-01-13 20:36:28 +00:00
LOAD_INT,
2023-02-10 18:50:54 +00:00
// Load a TValue from memory
// A: Rn or Kn or pointer (TValue)
2023-01-13 20:36:28 +00:00
LOAD_TVALUE,
2023-02-10 18:50:54 +00:00
// Load a TValue from table node value
// A: pointer (LuaNode)
2023-01-13 20:36:28 +00:00
LOAD_NODE_VALUE_TV, // TODO: we should find a way to generalize LOAD_TVALUE
2023-02-10 18:50:54 +00:00
// Load current environment table
2023-01-13 20:36:28 +00:00
LOAD_ENV,
2023-02-10 18:50:54 +00:00
// Get pointer (TValue) to table array at index
// A: pointer (Table)
2023-02-17 14:53:37 +00:00
// B: int
2023-01-13 20:36:28 +00:00
GET_ARR_ADDR,
2023-02-10 18:50:54 +00:00
// Get pointer (LuaNode) to table node element at the active cached slot index
// A: pointer (Table)
2023-04-07 20:56:27 +01:00
// B: unsigned int (pcpos)
2023-01-13 20:36:28 +00:00
GET_SLOT_NODE_ADDR,
2023-03-17 14:59:30 +00:00
// Get pointer (LuaNode) to table node element at the main position of the specified key hash
// A: pointer (Table)
2023-04-07 20:56:27 +01:00
// B: unsigned int (hash)
2023-03-17 14:59:30 +00:00
GET_HASH_NODE_ADDR,
2023-02-10 18:50:54 +00:00
// Store a tag into TValue
// A: Rn
// B: tag
2023-01-13 20:36:28 +00:00
STORE_TAG,
2023-02-10 18:50:54 +00:00
// Store a pointer (*) into TValue
// A: Rn
// B: pointer
2023-01-13 20:36:28 +00:00
STORE_POINTER,
2023-02-10 18:50:54 +00:00
// Store a double number into TValue
// A: Rn
// B: double
2023-01-13 20:36:28 +00:00
STORE_DOUBLE,
2023-02-10 18:50:54 +00:00
// Store an int into TValue
// A: Rn
// B: int
2023-01-13 20:36:28 +00:00
STORE_INT,
2023-02-10 18:50:54 +00:00
2023-04-07 20:56:27 +01:00
// Store a vector into TValue
// A: Rn
// B: double (x)
// C: double (y)
// D: double (z)
STORE_VECTOR,
2023-02-10 18:50:54 +00:00
// Store a TValue into memory
// A: Rn or pointer (TValue)
// B: TValue
2023-01-13 20:36:28 +00:00
STORE_TVALUE,
2023-02-10 18:50:54 +00:00
// Store a TValue into table node value
// A: pointer (LuaNode)
// B: TValue
2023-01-13 20:36:28 +00:00
STORE_NODE_VALUE_TV, // TODO: we should find a way to generalize STORE_TVALUE
2023-02-10 18:50:54 +00:00
// Add/Sub two integers together
// A, B: int
2023-01-13 20:36:28 +00:00
ADD_INT,
SUB_INT,
2023-04-28 12:55:55 +01:00
// Add/Sub/Mul/Div/Mod two double numbers
2023-02-10 18:50:54 +00:00
// A, B: double
// In final x64 lowering, B can also be Rn or Kn
2023-01-13 20:36:28 +00:00
ADD_NUM,
SUB_NUM,
MUL_NUM,
DIV_NUM,
MOD_NUM,
2023-03-03 13:45:38 +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 18:50:54 +00:00
// Negate a double number
// A: double
2023-01-13 20:36:28 +00:00
UNM_NUM,
2023-03-31 13:21:14 +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,
2023-02-10 18:50:54 +00:00
// Compute Luau 'not' operation on destructured TValue
// A: tag
2023-04-14 13:05:27 +01:00
// B: int (value)
2023-01-13 20:36:28 +00:00
NOT_ANY, // TODO: boolean specialization will be useful
2023-02-10 18:50:54 +00:00
// Unconditional jump
2023-07-14 16:57:16 +01:00
// A: block/vmexit
2023-01-13 20:36:28 +00:00
JUMP,
2023-02-10 18:50:54 +00:00
// Jump if TValue is truthy
// A: Rn
// B: block (if true)
// C: block (if false)
2023-01-13 20:36:28 +00:00
JUMP_IF_TRUTHY,
2023-02-10 18:50:54 +00:00
// Jump if TValue is falsy
// A: Rn
// B: block (if true)
// C: block (if false)
2023-01-13 20:36:28 +00:00
JUMP_IF_FALSY,
2023-02-10 18:50:54 +00:00
// Jump if tags are equal
// A, B: tag
// C: block (if true)
// D: block (if false)
2023-01-13 20:36:28 +00:00
JUMP_EQ_TAG,
2023-02-10 18:50:54 +00:00
// Jump if two int numbers are equal
// A, B: int
// C: block (if true)
// D: block (if false)
JUMP_EQ_INT,
2023-04-21 22:41:03 +01:00
// Jump if A < B
// A, B: int
// C: block (if true)
// D: block (if false)
JUMP_LT_INT,
2023-04-28 12:55:55 +01:00
// Jump if unsigned(A) >= unsigned(B)
// A, B: int
2023-04-21 22:41:03 +01:00
// C: condition
// D: block (if true)
// E: block (if false)
JUMP_GE_UINT,
2023-02-10 18:50:54 +00:00
// Jump if pointers are equal
// A, B: pointer (*)
// C: block (if true)
// D: block (if false)
2023-01-13 20:36:28 +00:00
JUMP_EQ_POINTER,
2023-02-10 18:50:54 +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 20:36:28 +00:00
JUMP_CMP_NUM,
2023-02-10 18:50:54 +00:00
// Perform a conditional jump based on the result of TValue comparison
// A, B: Rn
// C: condition
// D: block (if true)
// E: block (if false)
2023-01-13 20:36:28 +00:00
JUMP_CMP_ANY,
2023-03-17 14:59:30 +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 18:50:54 +00:00
// Get table length
// A: pointer (Table)
2023-01-13 20:36:28 +00:00
TABLE_LEN,
2023-02-10 18:50:54 +00:00
2023-07-07 18:14:35 +01:00
// Get string length
// A: pointer (string)
STRING_LEN,
2023-02-10 18:50:54 +00:00
// Allocate new table
// A: int (array element count)
// B: int (node element count)
2023-01-13 20:36:28 +00:00
NEW_TABLE,
2023-02-10 18:50:54 +00:00
// Duplicate a table
// A: pointer (Table)
2023-01-13 20:36:28 +00:00
DUP_TABLE,
2023-02-17 14:53:37 +00:00
// Try to convert a double number into a table index (int) or jump if it's not an integer
2023-02-10 18:50:54 +00:00
// A: double
// B: block
2023-03-17 14:59:30 +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 13:05:27 +01:00
// B: int (TMS enum)
2023-03-17 14:59:30 +00:00
// C: block
TRY_CALL_FASTGETTM,
2023-01-13 20:36:28 +00:00
2023-02-10 18:50:54 +00:00
// Convert integer into a double number
// A: int
INT_TO_NUM,
2023-04-21 22:41:03 +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 18:50:54 +00:00
2023-02-24 18:24:22 +00:00
// Adjust stack top (L->top) to point at 'B' TValues *after* the specified register
// This is used to return muliple values
// 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,
2023-03-03 13:45:38 +00:00
// Execute fastcall builtin function in-place
// A: builtin
// B: Rn (result start)
// C: Rn (argument start)
2023-05-25 21:46:51 +01:00
// D: Rn or Kn or undef (optional second argument)
2023-04-14 13:05:27 +01:00
// E: int (argument count)
// F: int (result count)
2023-03-03 13:45:38 +00:00
FASTCALL,
// Call the fastcall builtin function
// A: builtin
// B: Rn (result start)
// C: Rn (argument start)
2023-05-25 21:46:51 +01:00
// D: Rn or Kn or undef (optional second argument)
2023-03-03 13:45:38 +00:00
// E: int (argument count or -1 to use all arguments up to stack top)
// F: int (result count or -1 to preserve all results and adjust stack top)
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 20:36:28 +00:00
// Fallback functions
2023-02-10 18:50:54 +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 13:21:14 +01:00
// D: int (TMS enum with arithmetic type)
2023-01-13 20:36:28 +00:00
DO_ARITH,
2023-02-10 18:50:54 +00:00
// Get length of a TValue of any type
// A: Rn (where to store the result)
// B: Rn
2023-01-13 20:36:28 +00:00
DO_LEN,
2023-02-10 18:50:54 +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 20:36:28 +00:00
GET_TABLE,
2023-02-10 18:50:54 +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 20:36:28 +00:00
SET_TABLE,
2023-02-10 18:50:54 +00:00
// Lookup a value in the environment
// A: Rn (where to store the result)
// B: unsigned int (import path)
2023-01-13 20:36:28 +00:00
GET_IMPORT,
2023-02-10 18:50:54 +00:00
2023-02-17 14:53:37 +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 19:20:04 +00:00
// Note: all referenced registers might be modified in the operation
2023-01-13 20:36:28 +00:00
CONCAT,
2023-02-10 18:50:54 +00:00
// Load function upvalue into stack slot
// A: Rn
// B: UPn
2023-01-13 20:36:28 +00:00
GET_UPVALUE,
2023-02-10 18:50:54 +00:00
// Store TValue from stack slot into a function upvalue
// A: UPn
// B: Rn
2023-01-13 20:36:28 +00:00
SET_UPVALUE,
2023-02-10 18:50:54 +00:00
// Convert TValues into numbers for a numerical for loop
// A: Rn (start)
// B: Rn (end)
// C: Rn (step)
PREPARE_FORN,
// 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 16:57:16 +01:00
// C: block/vmexit/undef
2023-07-07 18:14:35 +01:00
// D: bool (finish execution in VM on failure)
2023-02-10 18:50:54 +00:00
// In final x64 lowering, A can also be Rn
2023-07-07 18:14:35 +01:00
// When undef is specified instead of a block, execution is aborted on check failure; if D is true, execution is continued in VM interpreter
// instead.
2023-01-13 20:36:28 +00:00
CHECK_TAG,
2023-02-10 18:50:54 +00:00
// Guard against readonly table
// A: pointer (Table)
2023-05-25 21:46:51 +01:00
// B: block/undef
// When undef is specified instead of a block, execution is aborted on check failure
2023-01-13 20:36:28 +00:00
CHECK_READONLY,
2023-02-10 18:50:54 +00:00
// Guard against table having a metatable
// A: pointer (Table)
2023-05-25 21:46:51 +01:00
// B: block/undef
// When undef is specified instead of a block, execution is aborted on check failure
2023-01-13 20:36:28 +00:00
CHECK_NO_METATABLE,
2023-02-10 18:50:54 +00:00
2023-07-07 18:14:35 +01:00
// Guard against executing in unsafe environment, exits to VM on check failure
2023-07-14 16:57:16 +01:00
// A: vmexit/undef
2023-07-07 18:14:35 +01:00
// When undef is specified, execution is aborted on check failure
2023-01-13 20:36:28 +00:00
CHECK_SAFE_ENV,
2023-02-10 18:50:54 +00:00
// Guard against index overflowing the table array size
// A: pointer (Table)
2023-02-17 14:53:37 +00:00
// B: int (index)
2023-05-25 21:46:51 +01:00
// C: block/undef
// When undef is specified instead of a block, execution is aborted on check failure
2023-01-13 20:36:28 +00:00
CHECK_ARRAY_SIZE,
2023-02-10 18:50:54 +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 21:46:51 +01:00
// C: block/undef
// When undef is specified instead of a block, execution is aborted on check failure
2023-01-13 20:36:28 +00:00
CHECK_SLOT_MATCH,
2023-03-17 14:59:30 +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-05-25 21:46:51 +01:00
// B: block/undef
// When undef is specified instead of a block, execution is aborted on check failure
2023-03-17 14:59:30 +00:00
CHECK_NODE_NO_NEXT,
2023-01-13 20:36:28 +00:00
// Special operations
2023-02-10 18:50:54 +00:00
// Check interrupt handler
// A: unsigned int (pcpos)
2023-01-13 20:36:28 +00:00
INTERRUPT,
2023-02-10 18:50:54 +00:00
// Check and run GC assist if necessary
2023-01-13 20:36:28 +00:00
CHECK_GC,
2023-02-10 18:50:54 +00:00
// Handle GC write barrier (forward)
// A: pointer (GCObject)
// B: Rn (TValue that was written to the object)
2023-06-24 06:33:44 +01:00
// C: tag/undef (tag of the value that was written)
2023-01-13 20:36:28 +00:00
BARRIER_OBJ,
2023-02-10 18:50:54 +00:00
// Handle GC write barrier (backwards) for a write into a table
// A: pointer (Table)
2023-01-13 20:36:28 +00:00
BARRIER_TABLE_BACK,
2023-02-10 18:50:54 +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 06:33:44 +01:00
// C: tag/undef (tag of the value that was written)
2023-01-13 20:36:28 +00:00
BARRIER_TABLE_FORWARD,
2023-02-10 18:50:54 +00:00
// Update savedpc value
// A: unsigned int (pcpos)
2023-01-13 20:36:28 +00:00
SET_SAVEDPC,
2023-02-10 18:50:54 +00:00
// Close open upvalues for registers at specified index or higher
// A: Rn (starting register index)
2023-01-13 20:36:28 +00:00
CLOSE_UPVALS,
// While capture is a no-op right now, it might be useful to track register/upvalue lifetimes
2023-02-10 18:50:54 +00:00
// A: Rn or UPn
2023-05-25 21:46:51 +01:00
// B: unsigned int (1 for reference capture, 0 for value capture)
2023-01-13 20:36:28 +00:00
CAPTURE,
// Operations that don't have an IR representation yet
2023-02-10 18:50:54 +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-03-31 13:21:14 +01:00
SETLIST,
2023-02-10 18:50:54 +00:00
// Call specified function
2023-03-24 17:34:14 +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 13:21:14 +01:00
CALL,
2023-02-10 18:50:54 +00:00
// Return specified values from the function
2023-03-24 17:34:14 +00:00
// A: Rn (value start)
// B: int (result count or -1 to return all values up to stack top)
2023-03-31 13:21:14 +01:00
RETURN,
2023-02-10 18:50:54 +00:00
2023-03-03 13:45:38 +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 19:20:04 +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 13:45:38 +00:00
// C: block (repeat)
// D: block (exit)
2023-03-31 13:21:14 +01:00
FORGLOOP,
2023-02-10 18:50:54 +00:00
2023-03-03 13:45:38 +00:00
// Handle LOP_FORGLOOP fallback when variable being iterated is not a table
2023-03-31 13:21:14 +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 18:50:54 +00:00
2023-03-03 13:45:38 +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 18:50:54 +00:00
// A: unsigned int (bytecode instruction index)
2023-03-03 13:45:38 +00:00
// B: Rn
// C: block (forgloop location)
2023-03-31 13:21:14 +01:00
FORGPREP_XNEXT_FALLBACK,
2023-02-10 18:50:54 +00:00
// Increment coverage data (saturating 24 bit add)
// A: unsigned int (bytecode instruction index)
2023-03-31 13:21:14 +01:00
COVERAGE,
2023-01-13 20:36:28 +00:00
// Operations that have a translation, but use a full instruction fallback
2023-02-10 18:50:54 +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 20:36:28 +00:00
FALLBACK_GETGLOBAL,
2023-02-10 18:50:54 +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 20:36:28 +00:00
FALLBACK_SETGLOBAL,
2023-02-10 18:50:54 +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 20:36:28 +00:00
FALLBACK_GETTABLEKS,
2023-02-10 18:50:54 +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 20:36:28 +00:00
FALLBACK_SETTABLEKS,
2023-02-10 18:50:54 +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 12:02:39 +00:00
FALLBACK_NAMECALL,
2023-01-13 20:36:28 +00:00
// Operations that don't have assembly lowering at all
2023-02-10 18:50:54 +00:00
// Prepare stack for variadic functions so that GETVARARGS works correctly
// A: unsigned int (bytecode instruction index)
// B: int (numparams)
2023-01-13 20:36:28 +00:00
FALLBACK_PREPVARARGS,
2023-02-10 18:50:54 +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 20:36:28 +00:00
FALLBACK_GETVARARGS,
2023-02-10 18:50:54 +00:00
// Create closure from a child proto
// A: unsigned int (bytecode instruction index)
// B: Rn (dest)
// C: unsigned int (protoid)
2023-01-13 20:36:28 +00:00
FALLBACK_NEWCLOSURE,
2023-02-10 18:50:54 +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 20:36:28 +00:00
FALLBACK_DUPCLOSURE,
2023-02-10 18:50:54 +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 13:45:38 +00:00
// B: Rn (loop state start, updates Rn Rn+1 Rn+2)
2023-02-17 14:53:37 +00:00
// C: block
2023-01-13 20:36:28 +00:00
FALLBACK_FORGPREP,
2023-02-17 14:53:37 +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 22:41:03 +01:00
// Performs bitwise and/xor/or on two unsigned integers
2023-04-28 12:55:55 +01:00
// A, B: int
2023-04-21 22:41:03 +01:00
BITAND_UINT,
BITXOR_UINT,
BITOR_UINT,
// Performs bitwise not on an unsigned integer
2023-04-28 12:55:55 +01:00
// A: int
2023-04-21 22:41:03 +01:00
BITNOT_UINT,
// Performs bitwise shift/rotate on an unsigned integer
2023-04-28 12:55:55 +01:00
// A: int (source)
2023-04-21 22:41:03 +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 12:55:55 +01:00
// A: int
2023-04-21 22:41:03 +01:00
BITCOUNTLZ_UINT,
BITCOUNTRZ_UINT,
// Calls native libm function with 1 or 2 arguments
// A: builtin function ID
// B: double
2023-05-05 20:57:12 +01:00
// C: double/int (optional, 2nd argument)
2023-04-21 22:41:03 +01:00
INVOKE_LIBM,
2023-06-24 06:33:44 +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-01-13 20:36:28 +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 12:55:55 +01:00
Undef,
2023-01-13 20:36:28 +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 16:57:16 +01:00
// To reference an exit to VM at specific PC pos
VmExit,
2023-01-13 20:36:28 +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 14:59:30 +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 20:36:28 +00:00
};
static_assert(sizeof(IrOp) == 4);
2023-04-07 20:56:27 +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 20:36:28 +00:00
struct IrInst
{
IrCmd cmd;
// Operands
IrOp a;
IrOp b;
IrOp c;
IrOp d;
IrOp e;
2023-02-24 18:24:22 +00:00
IrOp f;
2023-01-13 20:36:28 +00:00
uint32_t lastUse = 0;
uint16_t useCount = 0;
// Location of the result (optional)
2023-03-03 13:45:38 +00:00
X64::RegisterX64 regX64 = X64::noreg;
A64::RegisterA64 regA64 = A64::noreg;
2023-01-13 20:36:28 +00:00
bool reusedReg = false;
2023-04-07 20:56:27 +01:00
bool spilled = false;
2023-04-21 22:41:03 +01:00
bool needsReload = false;
2023-01-13 20:36:28 +00:00
};
2023-04-07 20:56:27 +01:00
// When IrInst operands are used, current instruction index is often required to track lifetime
constexpr uint32_t kInvalidInstIdx = ~0u;
2023-04-28 12:55:55 +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);
// MurmurHash2 tail
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
};
struct IrInstEq
{
bool operator()(const IrInst& a, const IrInst& b) const
{
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;
}
};
2023-01-13 20:36:28 +00:00
enum class IrBlockKind : uint8_t
{
Bytecode,
Fallback,
Internal,
2023-03-03 13:45:38 +00:00
Linearized,
2023-02-10 18:50:54 +00:00
Dead,
2023-01-13 20:36:28 +00:00
};
struct IrBlock
{
IrBlockKind kind;
2023-02-10 18:50:54 +00:00
uint16_t useCount = 0;
2023-02-24 18:24:22 +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 18:50:54 +00:00
uint32_t start = ~0u;
2023-02-24 18:24:22 +00:00
uint32_t finish = ~0u;
2023-01-13 20:36:28 +00:00
2023-06-16 18:01:18 +01:00
uint32_t sortkey = ~0u;
2023-01-13 20:36:28 +00:00
Label label;
};
struct BytecodeMapping
{
uint32_t irLocation;
uint32_t asmLocation;
};
struct IrFunction
{
std::vector<IrBlock> blocks;
std::vector<IrInst> instructions;
std::vector<IrConst> constants;
std::vector<BytecodeMapping> bcMapping;
2023-01-20 12:02:39 +00:00
2023-04-21 22:41:03 +01:00
// For each instruction, an operand that can be used to recompute the value
2023-04-14 13:05:27 +01:00
std::vector<IrOp> valueRestoreOps;
2023-04-21 22:41:03 +01:00
uint32_t validRestoreOpBlockIdx = 0;
2023-04-14 13:05:27 +01:00
2023-01-20 12:02:39 +00:00
Proto* proto = nullptr;
2023-06-09 13:20:36 +01:00
bool variadic = false;
2023-03-10 19:20:04 +00:00
CfgInfo cfg;
2023-02-10 18:50:54 +00:00
IrBlock& blockOp(IrOp op)
{
LUAU_ASSERT(op.kind == IrOpKind::Block);
return blocks[op.index];
}
IrInst& instOp(IrOp op)
{
LUAU_ASSERT(op.kind == IrOpKind::Inst);
return instructions[op.index];
}
2023-03-31 13:21:14 +01:00
IrInst* asInstOp(IrOp op)
{
if (op.kind == IrOpKind::Inst)
return &instructions[op.index];
return nullptr;
}
2023-02-10 18:50:54 +00:00
IrConst& constOp(IrOp op)
{
LUAU_ASSERT(op.kind == IrOpKind::Constant);
return constants[op.index];
}
uint8_t tagOp(IrOp op)
{
IrConst& value = constOp(op);
LUAU_ASSERT(value.kind == IrConstKind::Tag);
return value.valueTag;
}
2023-02-24 18:24:22 +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 18:50:54 +00:00
int intOp(IrOp op)
{
IrConst& value = constOp(op);
LUAU_ASSERT(value.kind == IrConstKind::Int);
return value.valueInt;
}
2023-02-24 18:24:22 +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 18:50:54 +00:00
unsigned uintOp(IrOp op)
{
IrConst& value = constOp(op);
LUAU_ASSERT(value.kind == IrConstKind::Uint);
return value.valueUint;
}
2023-02-24 18:24:22 +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 18:50:54 +00:00
double doubleOp(IrOp op)
{
IrConst& value = constOp(op);
LUAU_ASSERT(value.kind == IrConstKind::Double);
return value.valueDouble;
}
2023-02-17 14:53:37 +00:00
2023-02-24 18:24:22 +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 13:05:27 +01:00
uint32_t getBlockIndex(const IrBlock& block) const
2023-02-24 18:24:22 +00:00
{
// Can only be called with blocks from our vector
LUAU_ASSERT(&block >= blocks.data() && &block <= blocks.data() + blocks.size());
return uint32_t(&block - blocks.data());
}
2023-04-07 20:56:27 +01:00
2023-04-14 13:05:27 +01:00
uint32_t getInstIndex(const IrInst& inst) const
2023-04-07 20:56:27 +01:00
{
// Can only be called with instructions from our vector
LUAU_ASSERT(&inst >= instructions.data() && &inst <= instructions.data() + instructions.size());
return uint32_t(&inst - instructions.data());
}
2023-04-14 13:05:27 +01:00
void recordRestoreOp(uint32_t instIdx, IrOp location)
{
if (instIdx >= valueRestoreOps.size())
valueRestoreOps.resize(instIdx + 1);
valueRestoreOps[instIdx] = location;
}
IrOp findRestoreOp(uint32_t instIdx) const
{
if (instIdx >= valueRestoreOps.size())
return {};
2023-04-21 22:41:03 +01:00
const IrBlock& block = blocks[validRestoreOpBlockIdx];
// Values can only reference restore operands in the current block
if (instIdx < block.start || instIdx > block.finish)
return {};
2023-04-14 13:05:27 +01:00
return valueRestoreOps[instIdx];
}
IrOp findRestoreOp(const IrInst& inst) const
{
return findRestoreOp(getInstIndex(inst));
}
2023-01-13 20:36:28 +00:00
};
2023-03-24 17:34:14 +00:00
inline IrCondition conditionOp(IrOp op)
{
LUAU_ASSERT(op.kind == IrOpKind::Condition);
return IrCondition(op.index);
}
inline int vmRegOp(IrOp op)
{
LUAU_ASSERT(op.kind == IrOpKind::VmReg);
return op.index;
}
inline int vmConstOp(IrOp op)
{
LUAU_ASSERT(op.kind == IrOpKind::VmConst);
return op.index;
}
inline int vmUpvalueOp(IrOp op)
{
LUAU_ASSERT(op.kind == IrOpKind::VmUpvalue);
return op.index;
}
2023-01-13 20:36:28 +00:00
} // namespace CodeGen
} // namespace Luau