luau/CodeGen/src/CodeGenAssembly.cpp
Alexander McCord 259e509038
Sync to upstream/release/623 (#1236)
# What's changed?

### New Type Solver

- Unification of two fresh types no longer binds them together.
- Replaced uses of raw `emplace` with `emplaceType` to catch cyclic
bound types when they are created.
- `SetIndexerConstraint` is blocked until the indexer result type is not
blocked.
- Fix a case where a blocked type got past the constraint solver.
- Searching for free types should no longer traverse into `ClassType`s.
- Fix a corner case that could result in the non-testable type `~{}`.
- Fix incorrect flagging when `any` was a parameter of some checked
function in nonstrict type checker.
- `IterableConstraint` now consider tables without `__iter` to be
iterables.

### Native Code Generation

- Improve register type info lookup by program counter.
- Generate type information for locals and upvalues

---

### 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: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@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>
2024-04-25 15:26:09 -07:00

250 lines
7.5 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/CodeGen.h"
#include "Luau/BytecodeAnalysis.h"
#include "Luau/BytecodeUtils.h"
#include "Luau/BytecodeSummary.h"
#include "Luau/IrDump.h"
#include "CodeGenLower.h"
#include "CodeGenA64.h"
#include "CodeGenX64.h"
#include "lapi.h"
LUAU_FASTFLAG(LuauCodegenTypeInfo)
namespace Luau
{
namespace CodeGen
{
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++)
{
LocVar* var = proto->locvars ? &proto->locvars[proto->sizelocvars - proto->numparams + i] : nullptr;
if (var && var->varname)
build.logAppend("%s%s", i == 0 ? "" : ", ", getstr(var->varname));
else
build.logAppend("%s$arg%d", i == 0 ? "" : ", ", i);
}
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");
}
template<typename AssemblyBuilder>
static void logFunctionTypes(AssemblyBuilder& build, const IrFunction& function)
{
CODEGEN_ASSERT(FFlag::LuauCodegenTypeInfo);
const BytecodeTypeInfo& typeInfo = function.bcTypeInfo;
for (size_t i = 0; i < typeInfo.argumentTypes.size(); i++)
{
uint8_t ty = typeInfo.argumentTypes[i];
if (ty != LBC_TYPE_ANY)
build.logAppend("; R%d: %s [argument]\n", int(i), getBytecodeTypeName(ty));
}
for (size_t i = 0; i < typeInfo.upvalueTypes.size(); i++)
{
uint8_t ty = typeInfo.upvalueTypes[i];
if (ty != LBC_TYPE_ANY)
build.logAppend("; U%d: %s\n", int(i), getBytecodeTypeName(ty));
}
for (const BytecodeRegTypeInfo& el : typeInfo.regTypes)
{
build.logAppend("; R%d: %s from %d to %d\n", el.reg, getBytecodeTypeName(el.type), el.startpc, el.endpc);
}
}
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;
}
template<typename AssemblyBuilder>
static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, AssemblyOptions options, LoweringStats* stats)
{
Proto* root = clvalue(func)->l.p;
if ((options.flags & CodeGen_OnlyNativeModules) != 0 && (root->flags & LPF_NATIVE_MODULE) == 0)
return std::string();
std::vector<Proto*> protos;
gatherFunctions(protos, root, options.flags);
protos.erase(std::remove_if(protos.begin(), protos.end(),
[](Proto* p) {
return p == nullptr;
}),
protos.end());
if (stats)
stats->totalFunctions += unsigned(protos.size());
if (protos.empty())
{
build.finalize(); // to avoid assertion in AssemblyBuilder dtor
return std::string();
}
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)
{
IrBuilder ir;
ir.buildFunctionIr(p);
unsigned asmSize = build.getCodeSize();
unsigned asmCount = build.getInstructionCount();
if (options.includeAssembly || options.includeIr)
logFunctionHeader(build, p);
if (FFlag::LuauCodegenTypeInfo && options.includeIrTypes)
logFunctionTypes(build, ir.function);
CodeGenCompilationResult result = CodeGenCompilationResult::Success;
if (!lowerFunction(ir, build, helpers, p, options, stats, result))
{
if (build.logText)
build.logAppend("; skipping (can't lower)\n");
asmSize = 0;
asmCount = 0;
if (stats)
stats->skippedFunctions += 1;
}
else
{
asmSize = build.getCodeSize() - asmSize;
asmCount = build.getInstructionCount() - asmCount;
}
if (stats && (stats->functionStatsFlags & FunctionStats_Enable))
{
FunctionStats functionStat;
// 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]";
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));
}
if (build.logText)
build.logAppend("\n");
}
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;
}
#if defined(__aarch64__)
unsigned int getCpuFeaturesA64();
#endif
std::string getAssembly(lua_State* L, int idx, AssemblyOptions options, LoweringStats* stats)
{
CODEGEN_ASSERT(lua_isLfunction(L, idx));
const TValue* func = luaA_toobject(L, idx);
switch (options.target)
{
case AssemblyOptions::Host:
{
#if defined(__aarch64__)
static unsigned int cpuFeatures = getCpuFeaturesA64();
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, cpuFeatures);
#else
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly);
#endif
return getAssemblyImpl(build, func, options, stats);
}
case AssemblyOptions::A64:
{
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, /* features= */ A64::Feature_JSCVT);
return getAssemblyImpl(build, func, options, stats);
}
case AssemblyOptions::A64_NoFeatures:
{
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, /* features= */ 0);
return getAssemblyImpl(build, func, options, stats);
}
case AssemblyOptions::X64_Windows:
{
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly, X64::ABIX64::Windows);
return getAssemblyImpl(build, func, options, stats);
}
case AssemblyOptions::X64_SystemV:
{
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly, X64::ABIX64::SystemV);
return getAssemblyImpl(build, func, options, stats);
}
default:
CODEGEN_ASSERT(!"Unknown target");
return std::string();
}
}
} // namespace CodeGen
} // namespace Luau