Skip to content

Commit 5acbc9d

Browse files
committed
v0.18.3+luau709
1 parent 8aae594 commit 5acbc9d

22 files changed

Lines changed: 822 additions & 276 deletions

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "luau0-src"
3-
version = "0.18.2+luau708"
3+
version = "0.18.3+luau709"
44
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
55
edition = "2024"
66
repository = "https://github.com/mlua-rs/luau-src-rs"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2+
#pragma once
3+
4+
#include <stddef.h>
5+
#include <stdint.h>
6+
7+
namespace Luau
8+
{
9+
namespace CodeGen
10+
{
11+
12+
struct CodeAllocationData
13+
{
14+
uint8_t* start = nullptr;
15+
size_t size = 0;
16+
uint8_t* codeStart = nullptr;
17+
18+
// Allocation is page-aligned and can contain extra data
19+
uint8_t* allocationStart = nullptr;
20+
size_t allocationSize = 0;
21+
};
22+
23+
} // namespace CodeGen
24+
} // namespace Luau

luau/CodeGen/include/Luau/CodeAllocator.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
22
#pragma once
33

4+
#include "Luau/CodeAllocationData.h"
45
#include "Luau/CodeGenOptions.h"
56

67
#include <vector>
@@ -24,7 +25,7 @@ struct CodeAllocator
2425
// Places data and code into the executable page area
2526
// To allow allocation while previously allocated code is already running, allocation has page granularity
2627
// It's important to group functions together so that page alignment won't result in a lot of wasted space
27-
bool allocate(
28+
bool allocate_DEPRECATED(
2829
const uint8_t* data,
2930
size_t dataSize,
3031
const uint8_t* code,
@@ -34,6 +35,15 @@ struct CodeAllocator
3435
uint8_t*& resultCodeStart
3536
);
3637

38+
// Places data and code into the executable page area
39+
// To allow allocation while previously allocated code is already running, allocation has page granularity
40+
// It's important to group functions together so that page alignment won't result in a lot of wasted space
41+
CodeAllocationData allocate(const uint8_t* data, size_t dataSize, const uint8_t* code, size_t codeSize);
42+
43+
// Marks executable page area as no longer executable
44+
// Freed allocation area can be reused for future allocations
45+
void deallocate(CodeAllocationData codeAllocationData);
46+
3747
// Provided to unwind info callbacks
3848
void* context = nullptr;
3949

@@ -64,6 +74,7 @@ struct CodeAllocator
6474

6575
size_t blockSize = 0;
6676
size_t maxTotalSize = 0;
77+
size_t liveAllocations = 0;
6778

6879
AllocationCallback* allocationCallback = nullptr;
6980
void* allocationCallbackContext = nullptr;

luau/CodeGen/include/Luau/IrData.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,14 @@ inline bool hasOp(IrInst& inst, uint32_t idx)
10801080
#define HAS_OP_F(inst) (5 < (inst).ops.size() && (inst).ops[5].kind != IrOpKind::None)
10811081
#define HAS_OP_G(inst) (6 < (inst).ops.size() && (inst).ops[6].kind != IrOpKind::None)
10821082

1083+
#define OPT_OP_A(inst) (0 < (inst).ops.size() && (inst).ops[0].kind != IrOpKind::None ? (inst).ops[0] : IrOp{})
1084+
#define OPT_OP_B(inst) (1 < (inst).ops.size() && (inst).ops[1].kind != IrOpKind::None ? (inst).ops[1] : IrOp{})
1085+
#define OPT_OP_C(inst) (2 < (inst).ops.size() && (inst).ops[2].kind != IrOpKind::None ? (inst).ops[2] : IrOp{})
1086+
#define OPT_OP_D(inst) (3 < (inst).ops.size() && (inst).ops[3].kind != IrOpKind::None ? (inst).ops[3] : IrOp{})
1087+
#define OPT_OP_E(inst) (4 < (inst).ops.size() && (inst).ops[4].kind != IrOpKind::None ? (inst).ops[4] : IrOp{})
1088+
#define OPT_OP_F(inst) (5 < (inst).ops.size() && (inst).ops[5].kind != IrOpKind::None ? (inst).ops[5] : IrOp{})
1089+
#define OPT_OP_G(inst) (6 < (inst).ops.size() && (inst).ops[6].kind != IrOpKind::None ? (inst).ops[6] : IrOp{})
1090+
10831091
// When IrInst operands are used, current instruction index is often required to track lifetime
10841092
inline constexpr uint32_t kInvalidInstIdx = ~0u;
10851093

luau/CodeGen/include/Luau/SharedCodeAllocator.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
22
#pragma once
33

4+
#include "Luau/CodeAllocationData.h"
45
#include "Luau/CodeGen.h"
56
#include "Luau/Common.h"
67
#include "Luau/NativeProtoExecData.h"
@@ -48,6 +49,12 @@ class NativeModule
4849
const uint8_t* moduleBaseAddress,
4950
std::vector<NativeProtoExecDataPtr> nativeProtos
5051
) noexcept;
52+
NativeModule(
53+
SharedCodeAllocator* allocator,
54+
const std::optional<ModuleId>& moduleId,
55+
CodeAllocationData codeAllocationData,
56+
std::vector<NativeProtoExecDataPtr> nativeProtos
57+
) noexcept;
5158

5259
NativeModule(const NativeModule&) = delete;
5360
NativeModule(NativeModule&&) = delete;
@@ -69,6 +76,9 @@ class NativeModule
6976
// Gets the base address of the executable native code for the module.
7077
[[nodiscard]] const uint8_t* getModuleBaseAddress() const noexcept;
7178

79+
// Gets the information about code allocation for this module.
80+
[[nodiscard]] CodeAllocationData getCodeAllocationData() const noexcept;
81+
7282
// Attempts to find the NativeProto with the given bytecode id. If no
7383
// NativeProto for that bytecode id exists, a null pointer is returned.
7484
[[nodiscard]] const uint32_t* tryGetNativeProto(uint32_t bytecodeId) const noexcept;
@@ -80,7 +90,8 @@ class NativeModule
8090

8191
SharedCodeAllocator* allocator = nullptr;
8292
std::optional<ModuleId> moduleId = {};
83-
const uint8_t* moduleBaseAddress = nullptr;
93+
const uint8_t* moduleBaseAddress_DEPRECATED = nullptr;
94+
CodeAllocationData codeAllocationData;
8495

8596
std::vector<NativeProtoExecDataPtr> nativeProtos = {};
8697
};

luau/CodeGen/src/AssemblyBuilderX64.cpp

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
#include <stdarg.h>
77
#include <stdio.h>
88

9-
LUAU_FASTFLAG(LuauCodegenBufferLoadProp2)
10-
119
namespace Luau
1210
{
1311
namespace CodeGen
@@ -266,55 +264,29 @@ void AssemblyBuilderX64::movsx(RegisterX64 lhs, OperandX64 rhs)
266264
if (logText)
267265
log("movsx", lhs, rhs);
268266

269-
if (FFlag::LuauCodegenBufferLoadProp2)
270-
{
271-
SizeX64 size = rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize;
272-
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::word);
273-
274-
placeRex(lhs, rhs);
275-
place(0x0f);
276-
place(size == SizeX64::byte ? 0xbe : 0xbf);
277-
placeRegAndModRegMem(lhs, rhs);
278-
commit();
279-
}
280-
else
281-
{
282-
CODEGEN_ASSERT(rhs.memSize == SizeX64::byte || rhs.memSize == SizeX64::word);
267+
SizeX64 size = rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize;
268+
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::word);
283269

284-
placeRex(lhs, rhs);
285-
place(0x0f);
286-
place(rhs.memSize == SizeX64::byte ? 0xbe : 0xbf);
287-
placeRegAndModRegMem(lhs, rhs);
288-
commit();
289-
}
270+
placeRex(lhs, rhs);
271+
place(0x0f);
272+
place(size == SizeX64::byte ? 0xbe : 0xbf);
273+
placeRegAndModRegMem(lhs, rhs);
274+
commit();
290275
}
291276

292277
void AssemblyBuilderX64::movzx(RegisterX64 lhs, OperandX64 rhs)
293278
{
294279
if (logText)
295280
log("movzx", lhs, rhs);
296281

297-
if (FFlag::LuauCodegenBufferLoadProp2)
298-
{
299-
SizeX64 size = rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize;
300-
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::word);
301-
302-
placeRex(lhs, rhs);
303-
place(0x0f);
304-
place(size == SizeX64::byte ? 0xb6 : 0xb7);
305-
placeRegAndModRegMem(lhs, rhs);
306-
commit();
307-
}
308-
else
309-
{
310-
CODEGEN_ASSERT(rhs.memSize == SizeX64::byte || rhs.memSize == SizeX64::word);
282+
SizeX64 size = rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize;
283+
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::word);
311284

312-
placeRex(lhs, rhs);
313-
place(0x0f);
314-
place(rhs.memSize == SizeX64::byte ? 0xb6 : 0xb7);
315-
placeRegAndModRegMem(lhs, rhs);
316-
commit();
317-
}
285+
placeRex(lhs, rhs);
286+
place(0x0f);
287+
place(size == SizeX64::byte ? 0xb6 : 0xb7);
288+
placeRegAndModRegMem(lhs, rhs);
289+
commit();
318290
}
319291

320292
void AssemblyBuilderX64::div(OperandX64 op)

luau/CodeGen/src/CodeAllocator.cpp

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include <string.h>
77

8+
LUAU_FASTFLAGVARIABLE(LuauCodegenFreeBlocks)
9+
810
#if defined(_WIN32)
911

1012
#ifndef WIN32_LEAN_AND_MEAN
@@ -61,6 +63,15 @@ static void freePagesImpl(uint8_t* mem, size_t size)
6163
return VirtualProtect(mem, size, PAGE_EXECUTE_READ, &oldProtect) != 0;
6264
}
6365

66+
[[nodiscard]] static bool makePagesNotExecutable(uint8_t* mem, size_t size)
67+
{
68+
CODEGEN_ASSERT((uintptr_t(mem) & (kPageSize - 1)) == 0);
69+
CODEGEN_ASSERT(size == alignToPageSize(size));
70+
71+
DWORD oldProtect;
72+
return VirtualProtect(mem, size, PAGE_READWRITE, &oldProtect) != 0;
73+
}
74+
6475
static void flushInstructionCache(uint8_t* mem, size_t size)
6576
{
6677
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
@@ -98,6 +109,14 @@ static void freePagesImpl(uint8_t* mem, size_t size)
98109
return mprotect(mem, size, PROT_READ | PROT_EXEC) == 0;
99110
}
100111

112+
[[nodiscard]] static bool makePagesNotExecutable(uint8_t* mem, size_t size)
113+
{
114+
CODEGEN_ASSERT((uintptr_t(mem) & (kPageSize - 1)) == 0);
115+
CODEGEN_ASSERT(size == alignToPageSize(size));
116+
117+
return mprotect(mem, size, PROT_READ | PROT_WRITE) == 0;
118+
}
119+
101120
static void flushInstructionCache(uint8_t* mem, size_t size)
102121
{
103122
#ifdef __APPLE__
@@ -136,11 +155,14 @@ CodeAllocator::~CodeAllocator()
136155
destroyBlockUnwindInfo(context, unwindInfo);
137156
}
138157

158+
if (FFlag::LuauCodegenFreeBlocks)
159+
CODEGEN_ASSERT(liveAllocations == 0);
160+
139161
for (uint8_t* block : blocks)
140162
freePages(block, blockSize);
141163
}
142164

143-
bool CodeAllocator::allocate(
165+
bool CodeAllocator::allocate_DEPRECATED(
144166
const uint8_t* data,
145167
size_t dataSize,
146168
const uint8_t* code,
@@ -150,6 +172,8 @@ bool CodeAllocator::allocate(
150172
uint8_t*& resultCodeStart
151173
)
152174
{
175+
CODEGEN_ASSERT(!FFlag::LuauCodegenFreeBlocks);
176+
153177
// 'Round up' to preserve code alignment
154178
size_t alignedDataSize = (dataSize + (kCodeAlignment - 1)) & ~(kCodeAlignment - 1);
155179

@@ -209,6 +233,92 @@ bool CodeAllocator::allocate(
209233
return true;
210234
}
211235

236+
CodeAllocationData CodeAllocator::allocate(const uint8_t* data, size_t dataSize, const uint8_t* code, size_t codeSize)
237+
{
238+
CODEGEN_ASSERT(FFlag::LuauCodegenFreeBlocks);
239+
240+
// 'Round up' to preserve code alignment
241+
size_t alignedDataSize = (dataSize + (kCodeAlignment - 1)) & ~(kCodeAlignment - 1);
242+
243+
size_t totalSize = alignedDataSize + codeSize;
244+
245+
// Function has to fit into a single block with unwinding information
246+
if (totalSize > blockSize - kMaxReservedDataSize)
247+
return {};
248+
249+
size_t startOffset = 0;
250+
251+
// We might need a new block
252+
if (totalSize > size_t(blockEnd - blockPos))
253+
{
254+
if (!allocateNewBlock(startOffset))
255+
return {};
256+
257+
CODEGEN_ASSERT(totalSize <= size_t(blockEnd - blockPos));
258+
}
259+
260+
CODEGEN_ASSERT((uintptr_t(blockPos) & (kPageSize - 1)) == 0); // Allocation starts on page boundary
261+
262+
size_t dataOffset = startOffset + alignedDataSize - dataSize;
263+
size_t codeOffset = startOffset + alignedDataSize;
264+
265+
if (dataSize != 0)
266+
memcpy(blockPos + dataOffset, data, dataSize);
267+
if (codeSize != 0)
268+
memcpy(blockPos + codeOffset, code, codeSize);
269+
270+
size_t pageAlignedSize = alignToPageSize(startOffset + totalSize);
271+
272+
if (!makePagesExecutable(blockPos, pageAlignedSize))
273+
return {};
274+
275+
liveAllocations++;
276+
277+
flushInstructionCache(blockPos + codeOffset, codeSize);
278+
279+
CodeAllocationData result;
280+
281+
result.start = blockPos + startOffset;
282+
result.size = totalSize;
283+
result.codeStart = blockPos + codeOffset;
284+
285+
result.allocationStart = blockPos;
286+
result.allocationSize = pageAlignedSize;
287+
288+
// Ensure that future allocations from the block start from a page boundary.
289+
// This is important since we use W^X, and writing to the previous page would require briefly removing
290+
// executable bit from it, which may result in access violations if that code is being executed concurrently.
291+
if (pageAlignedSize <= size_t(blockEnd - blockPos))
292+
{
293+
blockPos += pageAlignedSize;
294+
CODEGEN_ASSERT((uintptr_t(blockPos) & (kPageSize - 1)) == 0);
295+
CODEGEN_ASSERT(blockPos <= blockEnd);
296+
}
297+
else
298+
{
299+
// Future allocations will need to allocate fresh blocks
300+
blockPos = blockEnd;
301+
}
302+
303+
return result;
304+
}
305+
306+
void CodeAllocator::deallocate(CodeAllocationData codeAllocationData)
307+
{
308+
CODEGEN_ASSERT(FFlag::LuauCodegenFreeBlocks);
309+
310+
if (codeAllocationData.allocationStart == nullptr)
311+
return;
312+
313+
[[maybe_unused]] bool result = makePagesNotExecutable(codeAllocationData.allocationStart, codeAllocationData.allocationSize);
314+
CODEGEN_ASSERT(result);
315+
316+
CODEGEN_ASSERT(liveAllocations != 0);
317+
liveAllocations--;
318+
319+
// TODO: new allocations should be able to reuse the freed pages (but note that first block page contains unwind data)
320+
}
321+
212322
bool CodeAllocator::allocateNewBlock(size_t& unwindInfoSize)
213323
{
214324
// Stop allocating once we reach a global limit

0 commit comments

Comments
 (0)