#include "CodeGenX64.h"
#include "Luau/AssemblyBuilderX64.h"
#include "Luau/UnwindBuilder.h"
#include "CodeGenContext.h"
#include "NativeState.h"
#include "EmitCommonX64.h"
#include "lstate.h"
LUAU_FASTFLAG(LuauCodegenFreeBlocks)
namespace Luau
{
namespace CodeGen
{
namespace X64
{
struct EntryLocations
{
Label start;
Label prologueEnd;
Label epilogueStart;
};
static EntryLocations buildEntryFunction(AssemblyBuilderX64& build, UnwindBuilder& unwind)
{
EntryLocations locations;
build.align(kFunctionAlignment, X64::AlignmentDataX64::Ud2);
locations.start = build.setLabel();
unwind.startFunction();
RegisterX64 rArg1 = (build.abi == ABIX64::Windows) ? rcx : rdi;
RegisterX64 rArg2 = (build.abi == ABIX64::Windows) ? rdx : rsi;
RegisterX64 rArg3 = (build.abi == ABIX64::Windows) ? r8 : rdx;
RegisterX64 rArg4 = (build.abi == ABIX64::Windows) ? r9 : rcx;
if (build.abi == ABIX64::SystemV)
{
build.push(rbp);
build.mov(rbp, rsp);
}
build.push(rbx);
build.push(r12);
build.push(r13);
build.push(r14);
build.push(r15);
if (build.abi == ABIX64::Windows)
{
build.push(rdi);
build.push(rsi);
build.push(rbp);
}
uint8_t usableXmmRegCount = getXmmRegisterCount(build.abi);
unsigned xmmStorageSize = getNonVolXmmStorageSize(build.abi, usableXmmRegCount);
unsigned fullStackSize = getFullStackSize(build.abi, usableXmmRegCount);
build.sub(rsp, fullStackSize);
OperandX64 xmmStorageOffset = rsp + (fullStackSize - (kStackAlign + xmmStorageSize));
std::vector<RegisterX64> savedXmmRegs;
if (build.abi == ABIX64::Windows)
{
if (usableXmmRegCount > kWindowsFirstNonVolXmmReg)
savedXmmRegs.reserve(usableXmmRegCount - kWindowsFirstNonVolXmmReg);
for (uint8_t i = kWindowsFirstNonVolXmmReg, offset = 0; i < usableXmmRegCount; i++, offset += 16)
{
RegisterX64 xmmReg = RegisterX64{SizeX64::xmmword, i};
build.vmovaps(xmmword[xmmStorageOffset + offset], xmmReg);
savedXmmRegs.push_back(xmmReg);
}
}
locations.prologueEnd = build.setLabel();
uint32_t prologueSize = build.getLabelOffset(locations.prologueEnd) - build.getLabelOffset(locations.start);
if (build.abi == ABIX64::SystemV)
unwind.prologueX64(prologueSize, fullStackSize, true, {rbx, r12, r13, r14, r15}, {});
else if (build.abi == ABIX64::Windows)
unwind.prologueX64(prologueSize, fullStackSize, false, {rbx, r12, r13, r14, r15, rdi, rsi, rbp}, savedXmmRegs);
build.mov(rState, rArg1);
build.mov(rNativeContext, rArg4);
build.mov(rBase, qword[rState + offsetof(lua_State, base)]);
build.mov(rax, qword[rState + offsetof(lua_State, ci)]);
build.mov(rax, qword[rax + offsetof(CallInfo, func)]);
build.mov(rax, qword[rax + offsetof(TValue, value.gc)]);
build.mov(sClosure, rax);
build.mov(rConstants, qword[rArg2 + offsetof(Proto, k)]);
build.mov(rax, qword[rArg2 + offsetof(Proto, code)]);
build.mov(sCode, rax);
build.jmp(rArg3);
locations.epilogueStart = build.setLabel();
if (build.abi == ABIX64::Windows)
{
for (uint8_t i = kWindowsFirstNonVolXmmReg, offset = 0; i < usableXmmRegCount; i++, offset += 16)
build.vmovaps(RegisterX64{SizeX64::xmmword, i}, xmmword[xmmStorageOffset + offset]);
}
build.add(rsp, fullStackSize);
if (build.abi == ABIX64::Windows)
{
build.pop(rbp);
build.pop(rsi);
build.pop(rdi);
}
build.pop(r15);
build.pop(r14);
build.pop(r13);
build.pop(r12);
build.pop(rbx);
if (build.abi == ABIX64::SystemV)
build.pop(rbp);
build.ret();
unwind.finishFunction(build.getLabelOffset(locations.start), kFullBlockFunction);
return locations;
}
bool initHeaderFunctions(BaseCodeGenContext& codeGenContext)
{
AssemblyBuilderX64 build( false);
UnwindBuilder& unwind = *codeGenContext.unwindBuilder.get();
unwind.startInfo(UnwindBuilder::X64);
EntryLocations entryLocations = buildEntryFunction(build, unwind);
build.finalize();
unwind.finishInfo();
CODEGEN_ASSERT(build.data.empty());
uint8_t* codeStart = nullptr;
if (FFlag::LuauCodegenFreeBlocks)
{
codeGenContext.gateAllocationData =
codeGenContext.codeAllocator.allocate(build.data.data(), int(build.data.size()), build.code.data(), int(build.code.size()));
if (!codeGenContext.gateAllocationData.start)
return false;
codeStart = codeGenContext.gateAllocationData.codeStart;
}
else
{
if (!codeGenContext.codeAllocator.allocate_DEPRECATED(
build.data.data(),
int(build.data.size()),
build.code.data(),
int(build.code.size()),
codeGenContext.gateData_DEPRECATED,
codeGenContext.gateDataSize_DEPRECATED,
codeStart
))
{
return false;
}
}
unwind.setBeginOffset(build.getLabelOffset(entryLocations.prologueEnd));
codeGenContext.context.gateEntry = codeStart + build.getLabelOffset(entryLocations.start);
codeGenContext.context.gateExit = codeStart + build.getLabelOffset(entryLocations.epilogueStart);
return true;
}
void assembleHelpers(X64::AssemblyBuilderX64& build, ModuleHelpers& helpers)
{
if (build.logText)
build.logAppend("; updatePcAndContinueInVm\n");
build.setLabel(helpers.updatePcAndContinueInVm);
emitUpdatePcForExit(build);
if (build.logText)
build.logAppend("; exitContinueVmClearNativeFlag\n");
build.setLabel(helpers.exitContinueVmClearNativeFlag);
emitClearNativeFlag(build);
if (build.logText)
build.logAppend("; exitContinueVm\n");
build.setLabel(helpers.exitContinueVm);
emitExit(build, true);
if (build.logText)
build.logAppend("; exitNoContinueVm\n");
build.setLabel(helpers.exitNoContinueVm);
emitExit(build, false);
if (build.logText)
build.logAppend("; interrupt\n");
build.setLabel(helpers.interrupt);
emitInterrupt(build);
if (build.logText)
build.logAppend("; return\n");
build.setLabel(helpers.return_);
emitReturn(build, helpers);
}
}
}
}