2022-10-14 20:48:41 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#include "Luau/CodeGen.h"
|
|
|
|
|
2023-06-24 07:19:39 +01:00
|
|
|
#include "CodeGenLower.h"
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
#include "Luau/Common.h"
|
|
|
|
#include "Luau/CodeAllocator.h"
|
|
|
|
#include "Luau/CodeBlockUnwind.h"
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
#include "Luau/IrBuilder.h"
|
2023-03-17 19:20:37 +00:00
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
#include "Luau/UnwindBuilder.h"
|
|
|
|
#include "Luau/UnwindBuilderDwarf2.h"
|
|
|
|
#include "Luau/UnwindBuilderWin.h"
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
#include "Luau/AssemblyBuilderA64.h"
|
2023-03-24 18:03:04 +00:00
|
|
|
#include "Luau/AssemblyBuilderX64.h"
|
2023-03-17 19:20:37 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
#include "CodeGenContext.h"
|
2023-03-24 18:03:04 +00:00
|
|
|
#include "NativeState.h"
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
#include "CodeGenA64.h"
|
2023-03-24 18:03:04 +00:00
|
|
|
#include "CodeGenX64.h"
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
|
|
#include "lapi.h"
|
2024-01-27 03:20:56 +00:00
|
|
|
#include "lmem.h"
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
|
|
#include <memory>
|
2023-05-19 20:37:30 +01:00
|
|
|
#include <optional>
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#include <intrin.h> // __cpuid
|
|
|
|
#else
|
|
|
|
#include <cpuid.h> // __cpuid
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2023-03-31 19:42:49 +01:00
|
|
|
#if defined(__aarch64__)
|
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt, false)
|
2023-04-21 23:14:26 +01:00
|
|
|
LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize, false)
|
2023-04-28 20:55:13 +01:00
|
|
|
LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering, false)
|
2023-12-08 21:50:16 +00:00
|
|
|
|
|
|
|
// Per-module IR instruction count limit
|
2024-02-16 02:04:39 +00:00
|
|
|
LUAU_FASTINTVARIABLE(CodegenHeuristicsInstructionLimit, 1'048'576) // 1 M
|
2023-12-08 21:50:16 +00:00
|
|
|
|
|
|
|
// Per-function IR block limit
|
|
|
|
// Current value is based on some member variables being limited to 16 bits
|
|
|
|
// Because block check is made before optimization passes and optimization can generate new blocks, limit is lowered 2x
|
|
|
|
// The limit will probably be adjusted in the future to avoid performance issues with analysis that's more complex than O(n)
|
2024-02-16 02:04:39 +00:00
|
|
|
LUAU_FASTINTVARIABLE(CodegenHeuristicsBlockLimit, 32'768) // 32 K
|
2023-12-08 21:50:16 +00:00
|
|
|
|
|
|
|
// Per-function IR instruction limit
|
|
|
|
// Current value is based on some member variables being limited to 16 bits
|
2023-10-21 02:10:30 +01:00
|
|
|
LUAU_FASTINTVARIABLE(CodegenHeuristicsBlockInstructionLimit, 65'536) // 64 K
|
2023-01-27 22:28:31 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
LUAU_FASTFLAG(LuauCodegenContext)
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
namespace CodeGen
|
|
|
|
{
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
static const Instruction kCodeEntryInsn = LOP_NATIVECALL;
|
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
void* gPerfLogContext = nullptr;
|
|
|
|
PerfLogFn gPerfLogFn = nullptr;
|
2023-05-12 18:50:47 +01:00
|
|
|
|
2024-03-22 17:47:10 +00:00
|
|
|
struct OldNativeProto
|
2023-03-17 19:20:37 +00:00
|
|
|
{
|
2023-05-19 20:37:30 +01:00
|
|
|
Proto* p;
|
|
|
|
void* execdata;
|
|
|
|
uintptr_t exectarget;
|
|
|
|
};
|
2023-03-17 19:20:37 +00:00
|
|
|
|
2024-03-15 23:37:39 +00:00
|
|
|
// Additional data attached to Proto::execdata
|
|
|
|
// Guaranteed to be aligned to 16 bytes
|
|
|
|
struct ExtraExecData
|
|
|
|
{
|
|
|
|
size_t execDataSize;
|
|
|
|
size_t codeSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int alignTo(int value, int align)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
2024-03-15 23:37:39 +00:00
|
|
|
CODEGEN_ASSERT(align > 0 && (align & (align - 1)) == 0);
|
|
|
|
return (value + (align - 1)) & ~(align - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the size of execdata required to store all code offsets and ExtraExecData structure at proper alignment
|
|
|
|
// Always a multiple of 4 bytes
|
|
|
|
static int calculateExecDataSize(Proto* proto)
|
2023-05-19 20:37:30 +01:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
2024-03-15 23:37:39 +00:00
|
|
|
int size = proto->sizecode * sizeof(uint32_t);
|
|
|
|
|
|
|
|
size = alignTo(size, 16);
|
|
|
|
size += sizeof(ExtraExecData);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns pointer to the ExtraExecData inside the Proto::execdata
|
|
|
|
// Even though 'execdata' is a field in Proto, we require it to support cases where it's not attached to Proto during construction
|
|
|
|
ExtraExecData* getExtraExecData(Proto* proto, void* execdata)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
2024-03-15 23:37:39 +00:00
|
|
|
int size = proto->sizecode * sizeof(uint32_t);
|
|
|
|
|
|
|
|
size = alignTo(size, 16);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
2024-03-15 23:37:39 +00:00
|
|
|
return reinterpret_cast<ExtraExecData*>(reinterpret_cast<char*>(execdata) + size);
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
2024-03-22 17:47:10 +00:00
|
|
|
static OldNativeProto createOldNativeProto(Proto* proto, const IrBuilder& ir)
|
2024-03-15 23:37:39 +00:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
2024-04-19 22:48:02 +01:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
int execDataSize = calculateExecDataSize(proto);
|
|
|
|
CODEGEN_ASSERT(execDataSize % 4 == 0);
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
uint32_t* execData = new uint32_t[execDataSize / 4];
|
|
|
|
uint32_t instTarget = ir.function.entryLocation;
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
for (int i = 0; i < proto->sizecode; i++)
|
2024-03-15 23:37:39 +00:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(ir.function.bcMapping[i].asmLocation >= instTarget);
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
execData[i] = ir.function.bcMapping[i].asmLocation - instTarget;
|
|
|
|
}
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
// Set first instruction offset to 0 so that entering this function still executes any generated entry code.
|
|
|
|
execData[0] = 0;
|
2023-08-11 15:42:37 +01:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
ExtraExecData* extra = getExtraExecData(proto, execData);
|
|
|
|
memset(extra, 0, sizeof(ExtraExecData));
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
extra->execDataSize = execDataSize;
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
// entry target will be relocated when assembly is finalized
|
|
|
|
return {proto, execData, instTarget};
|
2023-03-17 19:20:37 +00:00
|
|
|
}
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
static void destroyExecData(void* execdata)
|
2023-04-28 20:55:13 +01:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
delete[] static_cast<uint32_t*>(execdata);
|
2023-04-28 20:55:13 +01:00
|
|
|
}
|
|
|
|
|
2023-05-12 18:50:47 +01:00
|
|
|
static void logPerfFunction(Proto* p, uintptr_t addr, unsigned size)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(p->source);
|
2023-05-12 18:50:47 +01:00
|
|
|
|
|
|
|
const char* source = getstr(p->source);
|
|
|
|
source = (source[0] == '=' || source[0] == '@') ? source + 1 : "[string]";
|
|
|
|
|
|
|
|
char name[256];
|
|
|
|
snprintf(name, sizeof(name), "<luau> %s:%d %s", source, p->linedefined, p->debugname ? getstr(p->debugname) : "");
|
|
|
|
|
|
|
|
if (gPerfLogFn)
|
|
|
|
gPerfLogFn(gPerfLogContext, addr, size, name);
|
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
template<typename AssemblyBuilder>
|
2024-03-22 17:47:10 +00:00
|
|
|
static std::optional<OldNativeProto> createNativeFunction(
|
2024-02-16 02:04:39 +00:00
|
|
|
AssemblyBuilder& build, ModuleHelpers& helpers, Proto* proto, uint32_t& totalIrInstCount, CodeGenCompilationResult& result)
|
2023-03-17 19:20:37 +00:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
IrBuilder ir;
|
|
|
|
ir.buildFunctionIr(proto);
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2023-12-08 21:50:16 +00:00
|
|
|
unsigned instCount = unsigned(ir.function.instructions.size());
|
|
|
|
|
|
|
|
if (totalIrInstCount + instCount >= unsigned(FInt::CodegenHeuristicsInstructionLimit.value))
|
2024-02-16 02:04:39 +00:00
|
|
|
{
|
|
|
|
result = CodeGenCompilationResult::CodeGenOverflowInstructionLimit;
|
2023-12-08 21:50:16 +00:00
|
|
|
return std::nullopt;
|
2024-02-16 02:04:39 +00:00
|
|
|
}
|
2023-12-08 21:50:16 +00:00
|
|
|
totalIrInstCount += instCount;
|
|
|
|
|
2024-02-16 02:04:39 +00:00
|
|
|
if (!lowerFunction(ir, build, helpers, proto, {}, /* stats */ nullptr, result))
|
2023-05-19 20:37:30 +01:00
|
|
|
return std::nullopt;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
2024-03-22 17:47:10 +00:00
|
|
|
return createOldNativeProto(proto, ir);
|
2022-10-14 20:48:41 +01:00
|
|
|
}
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
static NativeState* getNativeState(lua_State* L)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
return static_cast<NativeState*>(L->global->ecb.context);
|
|
|
|
}
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
static void onCloseState(lua_State* L)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
delete getNativeState(L);
|
|
|
|
L->global->ecb = lua_ExecutionCallbacks();
|
2022-10-14 20:48:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void onDestroyFunction(lua_State* L, Proto* proto)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
destroyExecData(proto->execdata);
|
|
|
|
proto->execdata = nullptr;
|
|
|
|
proto->exectarget = 0;
|
2023-05-25 22:36:34 +01:00
|
|
|
proto->codeentry = proto->code;
|
2022-10-14 20:48:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int onEnter(lua_State* L, Proto* proto)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
NativeState* data = getNativeState(L);
|
2023-04-28 20:55:13 +01:00
|
|
|
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(proto->execdata);
|
|
|
|
CODEGEN_ASSERT(L->ci->savedpc >= proto->code && L->ci->savedpc < proto->code + proto->sizecode);
|
2023-05-12 18:50:47 +01:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
uintptr_t target = proto->exectarget + static_cast<uint32_t*>(proto->execdata)[L->ci->savedpc - proto->code];
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
|
|
// Returns 1 to finish the function in the VM
|
2023-05-12 18:50:47 +01:00
|
|
|
return GateFn(data->context.gateEntry)(L, proto, target, &data->context);
|
2022-10-14 20:48:41 +01:00
|
|
|
}
|
|
|
|
|
2024-03-15 23:37:39 +00:00
|
|
|
// used to disable native execution, unconditionally
|
|
|
|
static int onEnterDisabled(lua_State* L, Proto* proto)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
|
|
|
|
2024-03-15 23:37:39 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2024-01-27 03:20:56 +00:00
|
|
|
void onDisable(lua_State* L, Proto* proto)
|
|
|
|
{
|
|
|
|
// do nothing if proto already uses bytecode
|
|
|
|
if (proto->codeentry == proto->code)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// ensure that VM does not call native code for this proto
|
|
|
|
proto->codeentry = proto->code;
|
|
|
|
|
|
|
|
// prevent native code from entering proto with breakpoints
|
|
|
|
proto->exectarget = 0;
|
|
|
|
|
|
|
|
// walk all thread call stacks and clear the LUA_CALLINFO_NATIVE flag from any
|
|
|
|
// entries pointing to the current proto that has native code enabled.
|
|
|
|
luaM_visitgco(L, proto, [](void* context, lua_Page* page, GCObject* gco) {
|
|
|
|
Proto* proto = (Proto*)context;
|
|
|
|
|
|
|
|
if (gco->gch.tt != LUA_TTHREAD)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
lua_State* th = gco2th(gco);
|
|
|
|
|
|
|
|
for (CallInfo* ci = th->ci; ci > th->base_ci; ci--)
|
|
|
|
{
|
|
|
|
if (isLua(ci))
|
|
|
|
{
|
|
|
|
Proto* p = clvalue(ci->func)->l.p;
|
|
|
|
|
|
|
|
if (p == proto)
|
|
|
|
{
|
|
|
|
ci->flags &= ~LUA_CALLINFO_NATIVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-30 23:14:44 +00:00
|
|
|
static size_t getMemorySize(lua_State* L, Proto* proto)
|
2024-03-15 23:37:39 +00:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
2024-03-15 23:37:39 +00:00
|
|
|
ExtraExecData* extra = getExtraExecData(proto, proto->execdata);
|
|
|
|
|
|
|
|
// While execDataSize is exactly the size of the allocation we made and hold for 'execdata' field, the code size is approximate
|
|
|
|
// This is because code+data page is shared and owned by all Proto from a single module and each one can keep the whole region alive
|
|
|
|
// So individual Proto being freed by GC will not reflect memory use by native code correctly
|
|
|
|
return extra->execDataSize + extra->codeSize;
|
|
|
|
}
|
|
|
|
|
2023-03-31 19:42:49 +01:00
|
|
|
#if defined(__aarch64__)
|
2023-06-24 07:19:39 +01:00
|
|
|
unsigned int getCpuFeaturesA64()
|
2023-03-31 19:42:49 +01:00
|
|
|
{
|
|
|
|
unsigned int result = 0;
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
int jscvt = 0;
|
|
|
|
size_t jscvtLen = sizeof(jscvt);
|
|
|
|
if (sysctlbyname("hw.optional.arm.FEAT_JSCVT", &jscvt, &jscvtLen, nullptr, 0) == 0 && jscvt == 1)
|
|
|
|
result |= A64::Feature_JSCVT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
bool isSupported()
|
|
|
|
{
|
|
|
|
if (LUA_EXTRA_SIZE != 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (sizeof(TValue) != 16)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (sizeof(LuaNode) != 32)
|
|
|
|
return false;
|
|
|
|
|
2023-05-12 18:50:47 +01:00
|
|
|
// Windows CRT uses stack unwinding in longjmp so we have to use unwind data; on other platforms, it's only necessary for C++ EH.
|
|
|
|
#if defined(_WIN32)
|
|
|
|
if (!isUnwindSupported())
|
|
|
|
return false;
|
|
|
|
#else
|
|
|
|
if (!LUA_USE_LONGJMP && !isUnwindSupported())
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
2022-10-14 20:48:41 +01:00
|
|
|
int cpuinfo[4] = {};
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
__cpuid(cpuinfo, 1);
|
|
|
|
#else
|
|
|
|
__cpuid(1, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// We require AVX1 support for VEX encoded XMM operations
|
|
|
|
// We also requre SSE4.1 support for ROUNDSD but the AVX check below covers it
|
|
|
|
// https://en.wikipedia.org/wiki/CPUID#EAX=1:_Processor_Info_and_Feature_Bits
|
|
|
|
if ((cpuinfo[2] & (1 << 28)) == 0)
|
|
|
|
return false;
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
return true;
|
|
|
|
#elif defined(__aarch64__)
|
2023-03-31 19:42:49 +01:00
|
|
|
return true;
|
2022-10-14 20:48:41 +01:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
static void create_OLD(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext)
|
2022-10-14 20:48:41 +01:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauCodegenContext);
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(isSupported());
|
2022-10-14 20:48:41 +01:00
|
|
|
|
2023-08-11 15:42:37 +01:00
|
|
|
std::unique_ptr<NativeState> data = std::make_unique<NativeState>(allocationCallback, allocationCallbackContext);
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
|
|
#if defined(_WIN32)
|
2023-05-25 22:36:34 +01:00
|
|
|
data->unwindBuilder = std::make_unique<UnwindBuilderWin>();
|
2022-10-14 20:48:41 +01:00
|
|
|
#else
|
2023-05-25 22:36:34 +01:00
|
|
|
data->unwindBuilder = std::make_unique<UnwindBuilderDwarf2>();
|
2022-10-14 20:48:41 +01:00
|
|
|
#endif
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
data->codeAllocator.context = data->unwindBuilder.get();
|
|
|
|
data->codeAllocator.createBlockUnwindInfo = createBlockUnwindInfo;
|
|
|
|
data->codeAllocator.destroyBlockUnwindInfo = destroyBlockUnwindInfo;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
initFunctions(*data);
|
2022-10-14 20:48:41 +01:00
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
2023-05-25 22:36:34 +01:00
|
|
|
if (!X64::initHeaderFunctions(*data))
|
2022-10-14 20:48:41 +01:00
|
|
|
return;
|
2023-03-17 19:20:37 +00:00
|
|
|
#elif defined(__aarch64__)
|
2023-05-25 22:36:34 +01:00
|
|
|
if (!A64::initHeaderFunctions(*data))
|
2023-03-17 19:20:37 +00:00
|
|
|
return;
|
|
|
|
#endif
|
2022-10-14 20:48:41 +01:00
|
|
|
|
2023-05-12 18:50:47 +01:00
|
|
|
if (gPerfLogFn)
|
2023-05-25 22:36:34 +01:00
|
|
|
gPerfLogFn(gPerfLogContext, uintptr_t(data->context.gateEntry), 4096, "<luau gate>");
|
2023-05-12 18:50:47 +01:00
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
lua_ExecutionCallbacks* ecb = &L->global->ecb;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
ecb->context = data.release();
|
2022-10-14 20:48:41 +01:00
|
|
|
ecb->close = onCloseState;
|
|
|
|
ecb->destroy = onDestroyFunction;
|
|
|
|
ecb->enter = onEnter;
|
2024-02-23 20:08:34 +00:00
|
|
|
ecb->disable = onDisable;
|
2024-04-12 18:18:49 +01:00
|
|
|
ecb->getmemorysize = getMemorySize;
|
|
|
|
}
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
void create(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext)
|
|
|
|
{
|
|
|
|
if (FFlag::LuauCodegenContext)
|
|
|
|
{
|
|
|
|
create_NEW(L, allocationCallback, allocationCallbackContext);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
create_OLD(L, allocationCallback, allocationCallbackContext);
|
|
|
|
}
|
2022-10-14 20:48:41 +01:00
|
|
|
}
|
|
|
|
|
2023-08-11 15:42:37 +01:00
|
|
|
void create(lua_State* L)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
if (FFlag::LuauCodegenContext)
|
|
|
|
{
|
|
|
|
create_NEW(L);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
create(L, nullptr, nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void create(lua_State* L, SharedCodeGenContext* codeGenContext)
|
|
|
|
{
|
|
|
|
CODEGEN_ASSERT(FFlag::LuauCodegenContext);
|
|
|
|
|
|
|
|
create_NEW(L, codeGenContext);
|
2023-08-11 15:42:37 +01:00
|
|
|
}
|
|
|
|
|
2024-03-15 23:37:39 +00:00
|
|
|
[[nodiscard]] bool isNativeExecutionEnabled(lua_State* L)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
if (FFlag::LuauCodegenContext)
|
|
|
|
{
|
|
|
|
return isNativeExecutionEnabled_NEW(L);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return getNativeState(L) ? (L->global->ecb.enter == onEnter) : false;
|
|
|
|
}
|
2024-03-15 23:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void setNativeExecutionEnabled(lua_State* L, bool enabled)
|
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
if (FFlag::LuauCodegenContext)
|
|
|
|
{
|
|
|
|
setNativeExecutionEnabled_NEW(L, enabled);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (getNativeState(L))
|
|
|
|
L->global->ecb.enter = enabled ? onEnter : onEnterDisabled;
|
|
|
|
}
|
2024-03-15 23:37:39 +00:00
|
|
|
}
|
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
static CompilationResult compile_OLD(lua_State* L, int idx, unsigned int flags, CompilationStats* stats)
|
2024-03-30 23:14:44 +00:00
|
|
|
{
|
|
|
|
CompilationResult compilationResult;
|
|
|
|
|
|
|
|
CODEGEN_ASSERT(lua_isLfunction(L, idx));
|
|
|
|
const TValue* func = luaA_toobject(L, idx);
|
|
|
|
|
|
|
|
Proto* root = clvalue(func)->l.p;
|
|
|
|
|
|
|
|
if ((flags & CodeGen_OnlyNativeModules) != 0 && (root->flags & LPF_NATIVE_MODULE) == 0)
|
|
|
|
{
|
|
|
|
compilationResult.result = CodeGenCompilationResult::NotNativeModule;
|
|
|
|
return compilationResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If initialization has failed, do not compile any functions
|
|
|
|
NativeState* data = getNativeState(L);
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
compilationResult.result = CodeGenCompilationResult::CodeGenNotInitialized;
|
|
|
|
return compilationResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Proto*> protos;
|
|
|
|
gatherFunctions(protos, root, flags);
|
|
|
|
|
|
|
|
// Skip protos that have been compiled during previous invocations of CodeGen::compile
|
|
|
|
protos.erase(std::remove_if(protos.begin(), protos.end(),
|
|
|
|
[](Proto* p) {
|
|
|
|
return p == nullptr || p->execdata != nullptr;
|
|
|
|
}),
|
|
|
|
protos.end());
|
|
|
|
|
|
|
|
if (protos.empty())
|
|
|
|
{
|
|
|
|
compilationResult.result = CodeGenCompilationResult::NothingToCompile;
|
|
|
|
return compilationResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stats != nullptr)
|
|
|
|
stats->functionsTotal = uint32_t(protos.size());
|
|
|
|
|
|
|
|
#if defined(__aarch64__)
|
|
|
|
static unsigned int cpuFeatures = getCpuFeaturesA64();
|
|
|
|
A64::AssemblyBuilderA64 build(/* logText= */ false, cpuFeatures);
|
|
|
|
#else
|
|
|
|
X64::AssemblyBuilderX64 build(/* logText= */ false);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ModuleHelpers helpers;
|
|
|
|
#if defined(__aarch64__)
|
|
|
|
A64::assembleHelpers(build, helpers);
|
|
|
|
#else
|
|
|
|
X64::assembleHelpers(build, helpers);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
std::vector<OldNativeProto> results;
|
|
|
|
results.reserve(protos.size());
|
|
|
|
|
|
|
|
uint32_t totalIrInstCount = 0;
|
|
|
|
|
|
|
|
for (Proto* p : protos)
|
|
|
|
{
|
|
|
|
CodeGenCompilationResult protoResult = CodeGenCompilationResult::Success;
|
|
|
|
|
|
|
|
if (std::optional<OldNativeProto> np = createNativeFunction(build, helpers, p, totalIrInstCount, protoResult))
|
|
|
|
results.push_back(*np);
|
|
|
|
else
|
|
|
|
compilationResult.protoFailures.push_back({protoResult, p->debugname ? getstr(p->debugname) : "", p->linedefined});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Very large modules might result in overflowing a jump offset; in this case we currently abandon the entire module
|
|
|
|
if (!build.finalize())
|
|
|
|
{
|
|
|
|
for (OldNativeProto result : results)
|
|
|
|
destroyExecData(result.execdata);
|
|
|
|
|
|
|
|
compilationResult.result = CodeGenCompilationResult::CodeGenAssemblerFinalizationFailure;
|
|
|
|
return compilationResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no functions were assembled, we don't need to allocate/copy executable pages for helpers
|
|
|
|
if (results.empty())
|
|
|
|
return compilationResult;
|
|
|
|
|
|
|
|
uint8_t* nativeData = nullptr;
|
|
|
|
size_t sizeNativeData = 0;
|
|
|
|
uint8_t* codeStart = nullptr;
|
|
|
|
if (!data->codeAllocator.allocate(build.data.data(), int(build.data.size()), reinterpret_cast<const uint8_t*>(build.code.data()),
|
|
|
|
int(build.code.size() * sizeof(build.code[0])), nativeData, sizeNativeData, codeStart))
|
|
|
|
{
|
|
|
|
for (OldNativeProto result : results)
|
|
|
|
destroyExecData(result.execdata);
|
|
|
|
|
|
|
|
compilationResult.result = CodeGenCompilationResult::AllocationFailed;
|
|
|
|
return compilationResult;
|
|
|
|
}
|
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
if (gPerfLogFn && results.size() > 0)
|
|
|
|
gPerfLogFn(gPerfLogContext, uintptr_t(codeStart), uint32_t(results[0].exectarget), "<luau helpers>");
|
2024-03-30 23:14:44 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
for (size_t i = 0; i < results.size(); ++i)
|
2024-03-30 23:14:44 +00:00
|
|
|
{
|
2024-04-12 18:18:49 +01:00
|
|
|
uint32_t begin = uint32_t(results[i].exectarget);
|
|
|
|
uint32_t end = i + 1 < results.size() ? uint32_t(results[i + 1].exectarget) : uint32_t(build.code.size() * sizeof(build.code[0]));
|
|
|
|
CODEGEN_ASSERT(begin < end);
|
2024-03-30 23:14:44 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
if (gPerfLogFn)
|
|
|
|
logPerfFunction(results[i].p, uintptr_t(codeStart) + begin, end - begin);
|
2024-03-30 23:14:44 +00:00
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
ExtraExecData* extra = getExtraExecData(results[i].p, results[i].execdata);
|
|
|
|
extra->codeSize = end - begin;
|
2024-03-30 23:14:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const OldNativeProto& result : results)
|
|
|
|
{
|
|
|
|
// the memory is now managed by VM and will be freed via onDestroyFunction
|
|
|
|
result.p->execdata = result.execdata;
|
|
|
|
result.p->exectarget = uintptr_t(codeStart) + result.exectarget;
|
|
|
|
result.p->codeentry = &kCodeEntryInsn;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stats != nullptr)
|
|
|
|
{
|
|
|
|
for (const OldNativeProto& result : results)
|
|
|
|
{
|
|
|
|
stats->bytecodeSizeBytes += result.p->sizecode * sizeof(Instruction);
|
|
|
|
|
|
|
|
// Account for the native -> bytecode instruction offsets mapping:
|
|
|
|
stats->nativeMetadataSizeBytes += result.p->sizecode * sizeof(uint32_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
stats->functionsCompiled += uint32_t(results.size());
|
|
|
|
stats->nativeCodeSizeBytes += build.code.size();
|
|
|
|
stats->nativeDataSizeBytes += build.data.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
return compilationResult;
|
|
|
|
}
|
|
|
|
|
2024-04-12 18:18:49 +01:00
|
|
|
CompilationResult compile(lua_State* L, int idx, unsigned int flags, CompilationStats* stats)
|
|
|
|
{
|
|
|
|
if (FFlag::LuauCodegenContext)
|
|
|
|
{
|
|
|
|
return compile_NEW(L, idx, flags, stats);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return compile_OLD(L, idx, flags, stats);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, unsigned int flags, CompilationStats* stats)
|
|
|
|
{
|
|
|
|
CODEGEN_ASSERT(FFlag::LuauCodegenContext);
|
|
|
|
|
|
|
|
return compile_NEW(moduleId, L, idx, flags, stats);
|
|
|
|
}
|
|
|
|
|
2023-05-12 18:50:47 +01:00
|
|
|
void setPerfLog(void* context, PerfLogFn logFn)
|
|
|
|
{
|
|
|
|
gPerfLogContext = context;
|
|
|
|
gPerfLogFn = logFn;
|
|
|
|
}
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
} // namespace CodeGen
|
|
|
|
} // namespace Luau
|