mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 22:35:43 +08:00
9c588be16d
# What's changed? * Check interrupt handler inside the pattern match engine to eliminate potential for programs to hang during string library function execution. * Allow iteration over table properties to pass the old type solver. ### Native Code Generation * Use in-place memory operands for math library operations on x64. * Replace opaque bools with separate enum classes in IrDump to improve code maintainability. * Translate operations on inferred vectors to IR. * Enable support for debugging native-compiled functions in Roblox Studio. ### New Type Solver * Rework type inference for boolean and string literals to introduce bounded free types (bounded below by the singleton type, and above by the primitive type) and reworked primitive type constraint to decide which is the appropriate type for the literal. * Introduce `FunctionCheckConstraint` to handle bidirectional typechecking for function calls, pushing the expected parameter types from the function onto the arguments. * Introduce `union` and `intersect` type families to compute deferred simplified unions and intersections to be employed by the constraint generation logic in the new solver. * Implement support for expanding the domain of local types in `Unifier2`. * Rework type inference for iteration variables bound by for in loops to use local types. * Change constraint blocking logic to use a set to prevent accidental re-blocking. * Add logic to detect missing return statements in functions. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
232 lines
7.1 KiB
C++
232 lines
7.1 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#pragma once
|
|
|
|
#include "Luau/Common.h"
|
|
#include "Luau/IrData.h"
|
|
|
|
namespace Luau
|
|
{
|
|
namespace CodeGen
|
|
{
|
|
|
|
template<typename T>
|
|
static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& inst)
|
|
{
|
|
// For correct analysis, all instruction uses must be handled before handling the definitions
|
|
switch (inst.cmd)
|
|
{
|
|
case IrCmd::LOAD_TAG:
|
|
case IrCmd::LOAD_POINTER:
|
|
case IrCmd::LOAD_DOUBLE:
|
|
case IrCmd::LOAD_INT:
|
|
case IrCmd::LOAD_FLOAT:
|
|
case IrCmd::LOAD_TVALUE:
|
|
visitor.maybeUse(inst.a); // Argument can also be a VmConst
|
|
break;
|
|
case IrCmd::STORE_TAG:
|
|
case IrCmd::STORE_EXTRA:
|
|
case IrCmd::STORE_POINTER:
|
|
case IrCmd::STORE_DOUBLE:
|
|
case IrCmd::STORE_INT:
|
|
case IrCmd::STORE_VECTOR:
|
|
case IrCmd::STORE_TVALUE:
|
|
case IrCmd::STORE_SPLIT_TVALUE:
|
|
visitor.maybeDef(inst.a); // Argument can also be a pointer value
|
|
break;
|
|
case IrCmd::CMP_ANY:
|
|
visitor.use(inst.a);
|
|
visitor.use(inst.b);
|
|
break;
|
|
case IrCmd::JUMP_IF_TRUTHY:
|
|
case IrCmd::JUMP_IF_FALSY:
|
|
visitor.use(inst.a);
|
|
break;
|
|
// A <- B, C
|
|
case IrCmd::DO_ARITH:
|
|
visitor.maybeUse(inst.b); // Argument can also be a VmConst
|
|
visitor.maybeUse(inst.c); // Argument can also be a VmConst
|
|
|
|
visitor.def(inst.a);
|
|
break;
|
|
case IrCmd::GET_TABLE:
|
|
visitor.use(inst.b);
|
|
visitor.maybeUse(inst.c); // Argument can also be a VmConst
|
|
|
|
visitor.def(inst.a);
|
|
break;
|
|
case IrCmd::SET_TABLE:
|
|
visitor.use(inst.a);
|
|
visitor.use(inst.b);
|
|
visitor.maybeUse(inst.c); // Argument can also be a VmConst
|
|
break;
|
|
// A <- B
|
|
case IrCmd::DO_LEN:
|
|
visitor.use(inst.b);
|
|
|
|
visitor.def(inst.a);
|
|
break;
|
|
case IrCmd::GET_IMPORT:
|
|
visitor.def(inst.a);
|
|
break;
|
|
case IrCmd::CONCAT:
|
|
visitor.useRange(vmRegOp(inst.a), function.uintOp(inst.b));
|
|
|
|
visitor.defRange(vmRegOp(inst.a), function.uintOp(inst.b));
|
|
break;
|
|
case IrCmd::GET_UPVALUE:
|
|
visitor.def(inst.a);
|
|
break;
|
|
case IrCmd::SET_UPVALUE:
|
|
visitor.use(inst.b);
|
|
break;
|
|
case IrCmd::INTERRUPT:
|
|
break;
|
|
case IrCmd::BARRIER_OBJ:
|
|
case IrCmd::BARRIER_TABLE_FORWARD:
|
|
visitor.maybeUse(inst.b);
|
|
break;
|
|
case IrCmd::CLOSE_UPVALS:
|
|
// Closing an upvalue should be counted as a register use (it copies the fresh register value)
|
|
// But we lack the required information about the specific set of registers that are affected
|
|
// Because we don't plan to optimize captured registers atm, we skip full dataflow analysis for them right now
|
|
break;
|
|
case IrCmd::CAPTURE:
|
|
visitor.maybeUse(inst.a);
|
|
|
|
if (function.uintOp(inst.b) == 1)
|
|
visitor.capture(vmRegOp(inst.a));
|
|
break;
|
|
case IrCmd::SETLIST:
|
|
visitor.use(inst.b);
|
|
visitor.useRange(vmRegOp(inst.c), function.intOp(inst.d));
|
|
break;
|
|
case IrCmd::CALL:
|
|
visitor.use(inst.a);
|
|
visitor.useRange(vmRegOp(inst.a) + 1, function.intOp(inst.b));
|
|
|
|
visitor.defRange(vmRegOp(inst.a), function.intOp(inst.c));
|
|
break;
|
|
case IrCmd::RETURN:
|
|
visitor.useRange(vmRegOp(inst.a), function.intOp(inst.b));
|
|
break;
|
|
|
|
// TODO: FASTCALL is more restrictive than INVOKE_FASTCALL; we should either determine the exact semantics, or rework it
|
|
case IrCmd::FASTCALL:
|
|
case IrCmd::INVOKE_FASTCALL:
|
|
if (int count = function.intOp(inst.e); count != -1)
|
|
{
|
|
if (count >= 3)
|
|
{
|
|
LUAU_ASSERT(inst.d.kind == IrOpKind::VmReg && vmRegOp(inst.d) == vmRegOp(inst.c) + 1);
|
|
|
|
visitor.useRange(vmRegOp(inst.c), count);
|
|
}
|
|
else
|
|
{
|
|
if (count >= 1)
|
|
visitor.use(inst.c);
|
|
|
|
if (count >= 2)
|
|
visitor.maybeUse(inst.d); // Argument can also be a VmConst
|
|
}
|
|
}
|
|
else
|
|
{
|
|
visitor.useVarargs(vmRegOp(inst.c));
|
|
}
|
|
|
|
// Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG
|
|
if (int count = function.intOp(inst.f); count != -1)
|
|
visitor.defRange(vmRegOp(inst.b), count);
|
|
break;
|
|
case IrCmd::FORGLOOP:
|
|
// First register is not used by instruction, we check that it's still 'nil' with CHECK_TAG
|
|
visitor.use(inst.a, 1);
|
|
visitor.use(inst.a, 2);
|
|
|
|
visitor.def(inst.a, 2);
|
|
visitor.defRange(vmRegOp(inst.a) + 3, function.intOp(inst.b));
|
|
break;
|
|
case IrCmd::FORGLOOP_FALLBACK:
|
|
visitor.useRange(vmRegOp(inst.a), 3);
|
|
|
|
visitor.def(inst.a, 2);
|
|
visitor.defRange(vmRegOp(inst.a) + 3, uint8_t(function.intOp(inst.b))); // ignore most significant bit
|
|
break;
|
|
case IrCmd::FORGPREP_XNEXT_FALLBACK:
|
|
visitor.use(inst.b);
|
|
break;
|
|
case IrCmd::FALLBACK_GETGLOBAL:
|
|
visitor.def(inst.b);
|
|
break;
|
|
case IrCmd::FALLBACK_SETGLOBAL:
|
|
visitor.use(inst.b);
|
|
break;
|
|
case IrCmd::FALLBACK_GETTABLEKS:
|
|
visitor.use(inst.c);
|
|
|
|
visitor.def(inst.b);
|
|
break;
|
|
case IrCmd::FALLBACK_SETTABLEKS:
|
|
visitor.use(inst.b);
|
|
visitor.use(inst.c);
|
|
break;
|
|
case IrCmd::FALLBACK_NAMECALL:
|
|
visitor.use(inst.c);
|
|
|
|
visitor.defRange(vmRegOp(inst.b), 2);
|
|
break;
|
|
case IrCmd::FALLBACK_PREPVARARGS:
|
|
// No effect on explicitly referenced registers
|
|
break;
|
|
case IrCmd::FALLBACK_GETVARARGS:
|
|
visitor.defRange(vmRegOp(inst.b), function.intOp(inst.c));
|
|
break;
|
|
case IrCmd::FALLBACK_DUPCLOSURE:
|
|
visitor.def(inst.b);
|
|
break;
|
|
case IrCmd::FALLBACK_FORGPREP:
|
|
visitor.use(inst.b);
|
|
|
|
visitor.defRange(vmRegOp(inst.b), 3);
|
|
break;
|
|
case IrCmd::ADJUST_STACK_TO_REG:
|
|
visitor.defRange(vmRegOp(inst.a), -1);
|
|
break;
|
|
case IrCmd::ADJUST_STACK_TO_TOP:
|
|
// While this can be considered to be a vararg consumer, it is already handled in fastcall instructions
|
|
break;
|
|
case IrCmd::GET_TYPEOF:
|
|
visitor.use(inst.a);
|
|
break;
|
|
|
|
case IrCmd::FINDUPVAL:
|
|
visitor.use(inst.a);
|
|
break;
|
|
|
|
default:
|
|
// All instructions which reference registers have to be handled explicitly
|
|
LUAU_ASSERT(inst.a.kind != IrOpKind::VmReg);
|
|
LUAU_ASSERT(inst.b.kind != IrOpKind::VmReg);
|
|
LUAU_ASSERT(inst.c.kind != IrOpKind::VmReg);
|
|
LUAU_ASSERT(inst.d.kind != IrOpKind::VmReg);
|
|
LUAU_ASSERT(inst.e.kind != IrOpKind::VmReg);
|
|
LUAU_ASSERT(inst.f.kind != IrOpKind::VmReg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrBlock& block)
|
|
{
|
|
for (uint32_t instIdx = block.start; instIdx <= block.finish; instIdx++)
|
|
{
|
|
IrInst& inst = function.instructions[instIdx];
|
|
|
|
visitVmRegDefsUses(visitor, function, inst);
|
|
}
|
|
}
|
|
|
|
} // namespace CodeGen
|
|
} // namespace Luau
|