2023-06-24 07:19:39 +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"
|
2024-04-25 23:26:09 +01:00
|
|
|
#include "Luau/BytecodeAnalysis.h"
|
2023-12-02 07:46:57 +00:00
|
|
|
#include "Luau/BytecodeUtils.h"
|
2024-01-12 22:25:27 +00:00
|
|
|
#include "Luau/BytecodeSummary.h"
|
2024-04-25 23:26:09 +01:00
|
|
|
#include "Luau/IrDump.h"
|
2023-06-24 07:19:39 +01:00
|
|
|
|
|
|
|
#include "CodeGenLower.h"
|
|
|
|
|
|
|
|
#include "CodeGenA64.h"
|
|
|
|
#include "CodeGenX64.h"
|
|
|
|
|
|
|
|
#include "lapi.h"
|
|
|
|
|
2024-05-31 20:18:18 +01:00
|
|
|
LUAU_FASTFLAG(LuauLoadUserdataInfo)
|
2024-06-14 21:21:20 +01:00
|
|
|
LUAU_FASTFLAG(LuauNativeAttribute)
|
2024-04-25 23:26:09 +01:00
|
|
|
|
2023-06-24 07:19:39 +01:00
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
namespace CodeGen
|
|
|
|
{
|
|
|
|
|
2024-05-03 21:17:51 +01:00
|
|
|
static const LocVar* tryFindLocal(const Proto* proto, int reg, int pcpos)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < proto->sizelocvars; i++)
|
|
|
|
{
|
|
|
|
const LocVar& local = proto->locvars[i];
|
|
|
|
|
|
|
|
if (reg == local.reg && pcpos >= local.startpc && pcpos < local.endpc)
|
|
|
|
return &local;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* tryFindLocalName(const Proto* proto, int reg, int pcpos)
|
|
|
|
{
|
|
|
|
const LocVar* var = tryFindLocal(proto, reg, pcpos);
|
|
|
|
|
|
|
|
if (var && var->varname)
|
|
|
|
return getstr(var->varname);
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* tryFindUpvalueName(const Proto* proto, int upval)
|
|
|
|
{
|
|
|
|
if (proto->upvalues)
|
|
|
|
{
|
|
|
|
CODEGEN_ASSERT(upval < proto->sizeupvalues);
|
|
|
|
|
|
|
|
if (proto->upvalues[upval])
|
|
|
|
return getstr(proto->upvalues[upval]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-06-24 07:19:39 +01:00
|
|
|
template<typename AssemblyBuilder>
|
|
|
|
static void logFunctionHeader(AssemblyBuilder& build, Proto* proto)
|
|
|
|
{
|
|
|
|
if (proto->debugname)
|
|
|
|
build.logAppend("; function %s(", getstr(proto->debugname));
|
|
|
|
else
|
|
|
|
build.logAppend("; function(");
|
|
|
|
|
|
|
|
for (int i = 0; i < proto->numparams; i++)
|
|
|
|
{
|
2024-05-31 20:18:18 +01:00
|
|
|
if (const char* name = tryFindLocalName(proto, i, 0))
|
|
|
|
build.logAppend("%s%s", i == 0 ? "" : ", ", name);
|
2023-06-24 07:19:39 +01:00
|
|
|
else
|
2024-05-31 20:18:18 +01:00
|
|
|
build.logAppend("%s$arg%d", i == 0 ? "" : ", ", i);
|
2023-06-24 07:19:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (proto->numparams != 0 && proto->is_vararg)
|
|
|
|
build.logAppend(", ...)");
|
|
|
|
else
|
|
|
|
build.logAppend(")");
|
|
|
|
|
|
|
|
if (proto->linedefined >= 0)
|
|
|
|
build.logAppend(" line %d\n", proto->linedefined);
|
|
|
|
else
|
|
|
|
build.logAppend("\n");
|
|
|
|
}
|
|
|
|
|
2024-04-25 23:26:09 +01:00
|
|
|
template<typename AssemblyBuilder>
|
2024-05-31 20:18:18 +01:00
|
|
|
static void logFunctionTypes_DEPRECATED(AssemblyBuilder& build, const IrFunction& function)
|
2024-04-25 23:26:09 +01:00
|
|
|
{
|
2024-05-31 20:18:18 +01:00
|
|
|
CODEGEN_ASSERT(!FFlag::LuauLoadUserdataInfo);
|
2024-04-25 23:26:09 +01:00
|
|
|
|
|
|
|
const BytecodeTypeInfo& typeInfo = function.bcTypeInfo;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < typeInfo.argumentTypes.size(); i++)
|
|
|
|
{
|
|
|
|
uint8_t ty = typeInfo.argumentTypes[i];
|
|
|
|
|
2024-05-31 20:18:18 +01:00
|
|
|
if (ty != LBC_TYPE_ANY)
|
2024-05-03 21:17:51 +01:00
|
|
|
{
|
2024-05-31 20:18:18 +01:00
|
|
|
if (const char* name = tryFindLocalName(function.proto, int(i), 0))
|
|
|
|
build.logAppend("; R%d: %s [argument '%s']\n", int(i), getBytecodeTypeName_DEPRECATED(ty), name);
|
|
|
|
else
|
|
|
|
build.logAppend("; R%d: %s [argument]\n", int(i), getBytecodeTypeName_DEPRECATED(ty));
|
2024-05-03 21:17:51 +01:00
|
|
|
}
|
2024-04-25 23:26:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < typeInfo.upvalueTypes.size(); i++)
|
|
|
|
{
|
|
|
|
uint8_t ty = typeInfo.upvalueTypes[i];
|
|
|
|
|
2024-05-31 20:18:18 +01:00
|
|
|
if (ty != LBC_TYPE_ANY)
|
2024-05-03 21:17:51 +01:00
|
|
|
{
|
2024-05-31 20:18:18 +01:00
|
|
|
if (const char* name = tryFindUpvalueName(function.proto, int(i)))
|
|
|
|
build.logAppend("; U%d: %s ['%s']\n", int(i), getBytecodeTypeName_DEPRECATED(ty), name);
|
|
|
|
else
|
|
|
|
build.logAppend("; U%d: %s\n", int(i), getBytecodeTypeName_DEPRECATED(ty));
|
2024-05-03 21:17:51 +01:00
|
|
|
}
|
2024-05-31 20:18:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const BytecodeRegTypeInfo& el : typeInfo.regTypes)
|
|
|
|
{
|
|
|
|
// Using last active position as the PC because 'startpc' for type info is before local is initialized
|
|
|
|
if (const char* name = tryFindLocalName(function.proto, el.reg, el.endpc - 1))
|
|
|
|
build.logAppend("; R%d: %s from %d to %d [local '%s']\n", el.reg, getBytecodeTypeName_DEPRECATED(el.type), el.startpc, el.endpc, name);
|
2024-05-03 21:17:51 +01:00
|
|
|
else
|
2024-05-31 20:18:18 +01:00
|
|
|
build.logAppend("; R%d: %s from %d to %d\n", el.reg, getBytecodeTypeName_DEPRECATED(el.type), el.startpc, el.endpc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename AssemblyBuilder>
|
|
|
|
static void logFunctionTypes(AssemblyBuilder& build, const IrFunction& function, const char* const* userdataTypes)
|
|
|
|
{
|
|
|
|
CODEGEN_ASSERT(FFlag::LuauLoadUserdataInfo);
|
|
|
|
|
|
|
|
const BytecodeTypeInfo& typeInfo = function.bcTypeInfo;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < typeInfo.argumentTypes.size(); i++)
|
|
|
|
{
|
|
|
|
uint8_t ty = typeInfo.argumentTypes[i];
|
|
|
|
|
|
|
|
const char* type = getBytecodeTypeName(ty, userdataTypes);
|
|
|
|
const char* optional = (ty & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "";
|
|
|
|
|
|
|
|
if (ty != LBC_TYPE_ANY)
|
2024-05-03 21:17:51 +01:00
|
|
|
{
|
2024-05-31 20:18:18 +01:00
|
|
|
if (const char* name = tryFindLocalName(function.proto, int(i), 0))
|
|
|
|
build.logAppend("; R%d: %s%s [argument '%s']\n", int(i), type, optional, name);
|
|
|
|
else
|
|
|
|
build.logAppend("; R%d: %s%s [argument]\n", int(i), type, optional);
|
2024-05-03 21:17:51 +01:00
|
|
|
}
|
2024-04-25 23:26:09 +01:00
|
|
|
}
|
|
|
|
|
2024-05-31 20:18:18 +01:00
|
|
|
for (size_t i = 0; i < typeInfo.upvalueTypes.size(); i++)
|
2024-04-25 23:26:09 +01:00
|
|
|
{
|
2024-05-31 20:18:18 +01:00
|
|
|
uint8_t ty = typeInfo.upvalueTypes[i];
|
|
|
|
|
|
|
|
const char* type = getBytecodeTypeName(ty, userdataTypes);
|
|
|
|
const char* optional = (ty & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "";
|
|
|
|
|
|
|
|
if (ty != LBC_TYPE_ANY)
|
2024-05-03 21:17:51 +01:00
|
|
|
{
|
2024-05-31 20:18:18 +01:00
|
|
|
if (const char* name = tryFindUpvalueName(function.proto, int(i)))
|
|
|
|
build.logAppend("; U%d: %s%s ['%s']\n", int(i), type, optional, name);
|
2024-05-03 21:17:51 +01:00
|
|
|
else
|
2024-05-31 20:18:18 +01:00
|
|
|
build.logAppend("; U%d: %s%s\n", int(i), type, optional);
|
2024-05-03 21:17:51 +01:00
|
|
|
}
|
2024-05-31 20:18:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const BytecodeRegTypeInfo& el : typeInfo.regTypes)
|
|
|
|
{
|
|
|
|
const char* type = getBytecodeTypeName(el.type, userdataTypes);
|
|
|
|
const char* optional = (el.type & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "";
|
|
|
|
|
|
|
|
// Using last active position as the PC because 'startpc' for type info is before local is initialized
|
|
|
|
if (const char* name = tryFindLocalName(function.proto, el.reg, el.endpc - 1))
|
|
|
|
build.logAppend("; R%d: %s%s from %d to %d [local '%s']\n", el.reg, type, optional, el.startpc, el.endpc, name);
|
2024-05-03 21:17:51 +01:00
|
|
|
else
|
2024-05-31 20:18:18 +01:00
|
|
|
build.logAppend("; R%d: %s%s from %d to %d\n", el.reg, type, optional, el.startpc, el.endpc);
|
2024-04-25 23:26:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-02 07:46:57 +00:00
|
|
|
unsigned getInstructionCount(const Instruction* insns, const unsigned size)
|
|
|
|
{
|
|
|
|
unsigned count = 0;
|
|
|
|
for (unsigned i = 0; i < size;)
|
|
|
|
{
|
|
|
|
++count;
|
|
|
|
i += Luau::getOpLength(LuauOpcode(LUAU_INSN_OP(insns[i])));
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2023-06-24 07:19:39 +01:00
|
|
|
template<typename AssemblyBuilder>
|
2023-09-15 18:26:59 +01:00
|
|
|
static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, AssemblyOptions options, LoweringStats* stats)
|
2023-06-24 07:19:39 +01:00
|
|
|
{
|
2023-10-21 02:10:30 +01:00
|
|
|
Proto* root = clvalue(func)->l.p;
|
|
|
|
|
2024-05-10 19:21:45 +01:00
|
|
|
if ((options.compilationOptions.flags & CodeGen_OnlyNativeModules) != 0 && (root->flags & LPF_NATIVE_MODULE) == 0)
|
2023-10-21 02:10:30 +01:00
|
|
|
return std::string();
|
|
|
|
|
2023-06-24 07:19:39 +01:00
|
|
|
std::vector<Proto*> protos;
|
2024-06-14 21:21:20 +01:00
|
|
|
if (FFlag::LuauNativeAttribute)
|
|
|
|
gatherFunctions(protos, root, options.compilationOptions.flags, root->flags & LPF_NATIVE_FUNCTION);
|
|
|
|
else
|
|
|
|
gatherFunctions_DEPRECATED(protos, root, options.compilationOptions.flags);
|
2023-10-06 20:02:32 +01:00
|
|
|
|
2023-12-02 07:46:57 +00:00
|
|
|
protos.erase(std::remove_if(protos.begin(), protos.end(),
|
|
|
|
[](Proto* p) {
|
|
|
|
return p == nullptr;
|
|
|
|
}),
|
|
|
|
protos.end());
|
2023-10-06 20:02:32 +01:00
|
|
|
|
2023-10-21 02:10:30 +01:00
|
|
|
if (stats)
|
|
|
|
stats->totalFunctions += unsigned(protos.size());
|
|
|
|
|
2023-10-06 20:02:32 +01:00
|
|
|
if (protos.empty())
|
|
|
|
{
|
|
|
|
build.finalize(); // to avoid assertion in AssemblyBuilder dtor
|
|
|
|
return std::string();
|
|
|
|
}
|
2023-06-24 07:19:39 +01:00
|
|
|
|
|
|
|
ModuleHelpers helpers;
|
|
|
|
assembleHelpers(build, helpers);
|
|
|
|
|
|
|
|
if (!options.includeOutlinedCode && options.includeAssembly)
|
|
|
|
{
|
|
|
|
build.text.clear();
|
|
|
|
build.logAppend("; skipping %u bytes of outlined helpers\n", unsigned(build.getCodeSize() * sizeof(build.code[0])));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Proto* p : protos)
|
2023-10-06 20:02:32 +01:00
|
|
|
{
|
2024-05-10 19:21:45 +01:00
|
|
|
IrBuilder ir(options.compilationOptions.hooks);
|
2023-10-06 20:02:32 +01:00
|
|
|
ir.buildFunctionIr(p);
|
2024-01-12 22:25:27 +00:00
|
|
|
unsigned asmSize = build.getCodeSize();
|
|
|
|
unsigned asmCount = build.getInstructionCount();
|
2023-06-24 07:19:39 +01:00
|
|
|
|
2023-10-06 20:02:32 +01:00
|
|
|
if (options.includeAssembly || options.includeIr)
|
|
|
|
logFunctionHeader(build, p);
|
2023-06-24 07:19:39 +01:00
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
if (options.includeIrTypes)
|
2024-05-31 20:18:18 +01:00
|
|
|
{
|
|
|
|
if (FFlag::LuauLoadUserdataInfo)
|
|
|
|
logFunctionTypes(build, ir.function, options.compilationOptions.userdataTypes);
|
|
|
|
else
|
|
|
|
logFunctionTypes_DEPRECATED(build, ir.function);
|
|
|
|
}
|
2024-04-25 23:26:09 +01:00
|
|
|
|
2024-02-16 02:04:39 +00:00
|
|
|
CodeGenCompilationResult result = CodeGenCompilationResult::Success;
|
|
|
|
|
|
|
|
if (!lowerFunction(ir, build, helpers, p, options, stats, result))
|
2023-10-06 20:02:32 +01:00
|
|
|
{
|
2023-06-24 07:19:39 +01:00
|
|
|
if (build.logText)
|
2023-10-06 20:02:32 +01:00
|
|
|
build.logAppend("; skipping (can't lower)\n");
|
2023-10-21 02:10:30 +01:00
|
|
|
|
2024-01-12 22:25:27 +00:00
|
|
|
asmSize = 0;
|
2023-12-02 07:46:57 +00:00
|
|
|
asmCount = 0;
|
|
|
|
|
2023-10-21 02:10:30 +01:00
|
|
|
if (stats)
|
|
|
|
stats->skippedFunctions += 1;
|
2023-06-24 07:19:39 +01:00
|
|
|
}
|
2023-12-02 07:46:57 +00:00
|
|
|
else
|
|
|
|
{
|
2024-01-12 22:25:27 +00:00
|
|
|
asmSize = build.getCodeSize() - asmSize;
|
|
|
|
asmCount = build.getInstructionCount() - asmCount;
|
2023-12-02 07:46:57 +00:00
|
|
|
}
|
|
|
|
|
2024-01-12 22:25:27 +00:00
|
|
|
if (stats && (stats->functionStatsFlags & FunctionStats_Enable))
|
2023-12-02 07:46:57 +00:00
|
|
|
{
|
2024-01-12 22:25:27 +00:00
|
|
|
FunctionStats functionStat;
|
2024-04-19 22:48:02 +01:00
|
|
|
|
|
|
|
// function name is empty for anonymous and pseudo top-level functions
|
|
|
|
// properly name pseudo top-level function because it will be compiled natively if it has loops
|
|
|
|
functionStat.name = p->debugname ? getstr(p->debugname) : p->bytecodeid == root->bytecodeid ? "[top level]" : "[anonymous]";
|
2024-01-12 22:25:27 +00:00
|
|
|
functionStat.line = p->linedefined;
|
|
|
|
functionStat.bcodeCount = getInstructionCount(p->code, p->sizecode);
|
|
|
|
functionStat.irCount = unsigned(ir.function.instructions.size());
|
|
|
|
functionStat.asmSize = asmSize;
|
|
|
|
functionStat.asmCount = asmCount;
|
|
|
|
if (stats->functionStatsFlags & FunctionStats_BytecodeSummary)
|
|
|
|
{
|
|
|
|
FunctionBytecodeSummary summary(FunctionBytecodeSummary::fromProto(p, 0));
|
|
|
|
functionStat.bytecodeSummary.push_back(summary.getCounts(0));
|
|
|
|
}
|
|
|
|
stats->functions.push_back(std::move(functionStat));
|
2023-12-02 07:46:57 +00:00
|
|
|
}
|
2023-06-24 07:19:39 +01:00
|
|
|
|
2023-10-06 20:02:32 +01:00
|
|
|
if (build.logText)
|
|
|
|
build.logAppend("\n");
|
|
|
|
}
|
|
|
|
|
2023-06-24 07:19:39 +01:00
|
|
|
if (!build.finalize())
|
|
|
|
return std::string();
|
|
|
|
|
|
|
|
if (options.outputBinary)
|
|
|
|
return std::string(reinterpret_cast<const char*>(build.code.data()), reinterpret_cast<const char*>(build.code.data() + build.code.size())) +
|
|
|
|
std::string(build.data.begin(), build.data.end());
|
|
|
|
else
|
|
|
|
return build.text;
|
|
|
|
}
|
|
|
|
|
2024-05-17 00:02:03 +01:00
|
|
|
#if defined(CODEGEN_TARGET_A64)
|
2023-06-24 07:19:39 +01:00
|
|
|
unsigned int getCpuFeaturesA64();
|
|
|
|
#endif
|
|
|
|
|
2023-09-15 18:26:59 +01:00
|
|
|
std::string getAssembly(lua_State* L, int idx, AssemblyOptions options, LoweringStats* stats)
|
2023-06-24 07:19:39 +01:00
|
|
|
{
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(lua_isLfunction(L, idx));
|
2023-06-24 07:19:39 +01:00
|
|
|
const TValue* func = luaA_toobject(L, idx);
|
|
|
|
|
|
|
|
switch (options.target)
|
|
|
|
{
|
|
|
|
case AssemblyOptions::Host:
|
|
|
|
{
|
2024-05-17 00:02:03 +01:00
|
|
|
#if defined(CODEGEN_TARGET_A64)
|
2023-07-14 19:08:53 +01:00
|
|
|
static unsigned int cpuFeatures = getCpuFeaturesA64();
|
|
|
|
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, cpuFeatures);
|
2023-06-24 07:19:39 +01:00
|
|
|
#else
|
|
|
|
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly);
|
|
|
|
#endif
|
|
|
|
|
2023-09-15 18:26:59 +01:00
|
|
|
return getAssemblyImpl(build, func, options, stats);
|
2023-06-24 07:19:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case AssemblyOptions::A64:
|
|
|
|
{
|
|
|
|
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, /* features= */ A64::Feature_JSCVT);
|
|
|
|
|
2023-09-15 18:26:59 +01:00
|
|
|
return getAssemblyImpl(build, func, options, stats);
|
2023-06-24 07:19:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case AssemblyOptions::A64_NoFeatures:
|
|
|
|
{
|
|
|
|
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, /* features= */ 0);
|
|
|
|
|
2023-09-15 18:26:59 +01:00
|
|
|
return getAssemblyImpl(build, func, options, stats);
|
2023-06-24 07:19:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case AssemblyOptions::X64_Windows:
|
|
|
|
{
|
|
|
|
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly, X64::ABIX64::Windows);
|
|
|
|
|
2023-09-15 18:26:59 +01:00
|
|
|
return getAssemblyImpl(build, func, options, stats);
|
2023-06-24 07:19:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case AssemblyOptions::X64_SystemV:
|
|
|
|
{
|
|
|
|
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly, X64::ABIX64::SystemV);
|
|
|
|
|
2023-09-15 18:26:59 +01:00
|
|
|
return getAssemblyImpl(build, func, options, stats);
|
2023-06-24 07:19:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2024-02-16 02:04:39 +00:00
|
|
|
CODEGEN_ASSERT(!"Unknown target");
|
2023-06-24 07:19:39 +01:00
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace CodeGen
|
|
|
|
} // namespace Luau
|