mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-19 17:28:06 +00:00
d141a5c48d
* Fixed exported types not being suggested in autocomplete * `T...` is now convertible to `...any` (Fixes https://github.com/Roblox/luau/issues/767) * Fixed issue with `T?` not being convertible to `T | T` or `T?` (sometimes when internal pointer identity is different) * Fixed potential crash in missing table key error suggestion to use a similar existing key * `lua_topointer` now returns a pointer for strings C++ API Changes: * `prepareModuleScope` callback has moved from TypeChecker to Frontend * For LSPs, AstQuery functions (and `isWithinComment`) can be used without full Frontend data A lot of changes in our two experimental components as well. In our work on the new type-solver, the following issues were fixed: * Fixed table union and intersection indexing * Correct custom type environments are now used * Fixed issue with values of `free & number` type not accepted in numeric operations And these are the changes in native code generation (JIT): * arm64 lowering is almost complete with support for 99% of IR commands and all fastcalls * Fixed x64 assembly encoding for extended byte registers * More external x64 calls are aware of register allocator * `math.min`/`math.max` with more than 2 arguments are now lowered to IR as well * Fixed correctness issues with `math` library calls with multiple results in variadic context and with x64 register conflicts * x64 register allocator learnt to restore values from VM memory instead of always using stack spills * x64 exception unwind information now supports multiple functions and fixes function start offset in Dwarf2 info
180 lines
5.2 KiB
C++
180 lines
5.2 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#include "Luau/UnwindBuilderWin.h"
|
|
|
|
#include <string.h>
|
|
|
|
// Information about the Windows x64 unwinding data setup can be found at:
|
|
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64 [x64 exception handling]
|
|
|
|
#define UWOP_PUSH_NONVOL 0
|
|
#define UWOP_ALLOC_LARGE 1
|
|
#define UWOP_ALLOC_SMALL 2
|
|
#define UWOP_SET_FPREG 3
|
|
#define UWOP_SAVE_NONVOL 4
|
|
#define UWOP_SAVE_NONVOL_FAR 5
|
|
#define UWOP_SAVE_XMM128 8
|
|
#define UWOP_SAVE_XMM128_FAR 9
|
|
#define UWOP_PUSH_MACHFRAME 10
|
|
|
|
namespace Luau
|
|
{
|
|
namespace CodeGen
|
|
{
|
|
|
|
void UnwindBuilderWin::setBeginOffset(size_t beginOffset)
|
|
{
|
|
this->beginOffset = beginOffset;
|
|
}
|
|
|
|
size_t UnwindBuilderWin::getBeginOffset() const
|
|
{
|
|
return beginOffset;
|
|
}
|
|
|
|
void UnwindBuilderWin::startInfo() {}
|
|
|
|
void UnwindBuilderWin::startFunction()
|
|
{
|
|
// End offset is filled in later and everything gets adjusted at the end
|
|
UnwindFunctionWin func;
|
|
func.beginOffset = 0;
|
|
func.endOffset = 0;
|
|
func.unwindInfoOffset = uint32_t(rawDataPos - rawData);
|
|
unwindFunctions.push_back(func);
|
|
|
|
unwindCodes.clear();
|
|
unwindCodes.reserve(16);
|
|
|
|
prologSize = 0;
|
|
|
|
// rax has register index 0, which in Windows unwind info means that frame register is not used
|
|
frameReg = X64::rax;
|
|
frameRegOffset = 0;
|
|
|
|
// Return address was pushed by calling the function
|
|
stackOffset = 8;
|
|
}
|
|
|
|
void UnwindBuilderWin::spill(int espOffset, X64::RegisterX64 reg)
|
|
{
|
|
prologSize += 5; // REX.W mov [rsp + imm8], reg
|
|
}
|
|
|
|
void UnwindBuilderWin::save(X64::RegisterX64 reg)
|
|
{
|
|
prologSize += 2; // REX.W push reg
|
|
stackOffset += 8;
|
|
unwindCodes.push_back({prologSize, UWOP_PUSH_NONVOL, reg.index});
|
|
}
|
|
|
|
void UnwindBuilderWin::allocStack(int size)
|
|
{
|
|
LUAU_ASSERT(size >= 8 && size <= 128 && size % 8 == 0);
|
|
|
|
prologSize += 4; // REX.W sub rsp, imm8
|
|
stackOffset += size;
|
|
unwindCodes.push_back({prologSize, UWOP_ALLOC_SMALL, uint8_t((size - 8) / 8)});
|
|
}
|
|
|
|
void UnwindBuilderWin::setupFrameReg(X64::RegisterX64 reg, int espOffset)
|
|
{
|
|
LUAU_ASSERT(espOffset < 256 && espOffset % 16 == 0);
|
|
|
|
frameReg = reg;
|
|
frameRegOffset = uint8_t(espOffset / 16);
|
|
|
|
if (espOffset != 0)
|
|
prologSize += 5; // REX.W lea rbp, [rsp + imm8]
|
|
else
|
|
prologSize += 3; // REX.W mov rbp, rsp
|
|
|
|
unwindCodes.push_back({prologSize, UWOP_SET_FPREG, frameRegOffset});
|
|
}
|
|
|
|
void UnwindBuilderWin::finishFunction(uint32_t beginOffset, uint32_t endOffset)
|
|
{
|
|
unwindFunctions.back().beginOffset = beginOffset;
|
|
unwindFunctions.back().endOffset = endOffset;
|
|
|
|
// Windows unwind code count is stored in uint8_t, so we can't have more
|
|
LUAU_ASSERT(unwindCodes.size() < 256);
|
|
|
|
LUAU_ASSERT(stackOffset % 16 == 0 && "stack has to be aligned to 16 bytes after prologue");
|
|
|
|
UnwindInfoWin info;
|
|
info.version = 1;
|
|
info.flags = 0; // No EH
|
|
info.prologsize = prologSize;
|
|
info.unwindcodecount = uint8_t(unwindCodes.size());
|
|
|
|
LUAU_ASSERT(frameReg.index < 16);
|
|
info.framereg = frameReg.index;
|
|
|
|
LUAU_ASSERT(frameRegOffset < 16);
|
|
info.frameregoff = frameRegOffset;
|
|
|
|
LUAU_ASSERT(rawDataPos + sizeof(info) <= rawData + kRawDataLimit);
|
|
memcpy(rawDataPos, &info, sizeof(info));
|
|
rawDataPos += sizeof(info);
|
|
|
|
if (!unwindCodes.empty())
|
|
{
|
|
// Copy unwind codes in reverse order
|
|
// Some unwind codes take up two array slots, but we don't use those atm
|
|
uint8_t* unwindCodePos = rawDataPos + sizeof(UnwindCodeWin) * (unwindCodes.size() - 1);
|
|
LUAU_ASSERT(unwindCodePos <= rawData + kRawDataLimit);
|
|
|
|
for (size_t i = 0; i < unwindCodes.size(); i++)
|
|
{
|
|
memcpy(unwindCodePos, &unwindCodes[i], sizeof(UnwindCodeWin));
|
|
unwindCodePos -= sizeof(UnwindCodeWin);
|
|
}
|
|
}
|
|
|
|
rawDataPos += sizeof(UnwindCodeWin) * unwindCodes.size();
|
|
|
|
// Size has to be even, but unwind code count doesn't have to
|
|
if (unwindCodes.size() % 2 != 0)
|
|
rawDataPos += sizeof(UnwindCodeWin);
|
|
|
|
LUAU_ASSERT(rawDataPos <= rawData + kRawDataLimit);
|
|
}
|
|
|
|
void UnwindBuilderWin::finishInfo() {}
|
|
|
|
size_t UnwindBuilderWin::getSize() const
|
|
{
|
|
return sizeof(UnwindFunctionWin) * unwindFunctions.size() + size_t(rawDataPos - rawData);
|
|
}
|
|
|
|
size_t UnwindBuilderWin::getFunctionCount() const
|
|
{
|
|
return unwindFunctions.size();
|
|
}
|
|
|
|
void UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const
|
|
{
|
|
// Copy adjusted function information
|
|
for (UnwindFunctionWin func : unwindFunctions)
|
|
{
|
|
// Code will start after the unwind info
|
|
func.beginOffset += uint32_t(offset);
|
|
|
|
// Whole block is a part of a 'single function'
|
|
if (func.endOffset == kFullBlockFuncton)
|
|
func.endOffset = uint32_t(funcSize);
|
|
else
|
|
func.endOffset += uint32_t(offset);
|
|
|
|
// Unwind data is placed right after the RUNTIME_FUNCTION data
|
|
func.unwindInfoOffset += uint32_t(sizeof(UnwindFunctionWin) * unwindFunctions.size());
|
|
memcpy(target, &func, sizeof(func));
|
|
target += sizeof(func);
|
|
}
|
|
|
|
// Copy unwind codes
|
|
memcpy(target, rawData, size_t(rawDataPos - rawData));
|
|
}
|
|
|
|
} // namespace CodeGen
|
|
} // namespace Luau
|