Skip to content

Commit effc89f

Browse files
committed
Add Lua::set_jit_options with support of Luau JIT inliner
Requires Luau 0.727+
1 parent 82111f0 commit effc89f

8 files changed

Lines changed: 117 additions & 7 deletions

File tree

mlua-sys/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ cfg-if = "1.0"
4343
pkg-config = "0.3.17"
4444
lua-src = { version = ">= 550.1.0, < 550.2.0", optional = true }
4545
luajit-src = { version = ">= 210.7.0, < 210.8.0", optional = true }
46-
luau0-src = { version = "0.20.0", optional = true }
46+
luau0-src = { version = "0.20.6", optional = true }
4747

4848
[lints.rust]
4949
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(raw_dylib)'] }

mlua-sys/src/luau/compat.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ use super::luacode::*;
1212

1313
pub const LUA_RESUMEERROR: c_int = -1;
1414

15+
// Keep in sync with Bytecode.h
16+
const LBC_VERSION_MAX: u8 = 11;
17+
const LBC_TYPE_VERSION_MIN: u8 = 1;
18+
const LBC_TYPE_VERSION_MAX: u8 = 3;
19+
1520
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
1621
while a < b {
1722
lua_pushvalue(L, a);
@@ -368,6 +373,24 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
368373
}
369374
}
370375

376+
// Detects whether a chunk is Luau bytecode or text source.
377+
pub unsafe fn luaL_isbytecode(data: *const c_char, size: usize) -> bool {
378+
if size == 0 {
379+
return false;
380+
}
381+
match *data as u8 {
382+
b if b < b'\t' => true, // bytecode
383+
b if b <= LBC_VERSION_MAX => {
384+
let types_version = (size >= 2).then(|| *data.add(1) as u8);
385+
match types_version {
386+
Some(LBC_TYPE_VERSION_MIN..=LBC_TYPE_VERSION_MAX) => true, // bytecode
387+
_ => false, // text
388+
}
389+
}
390+
_ => false, // text
391+
}
392+
}
393+
371394
pub unsafe fn luaL_loadbufferenv(
372395
L: *mut lua_State,
373396
data: *const c_char,
@@ -384,19 +407,21 @@ pub unsafe fn luaL_loadbufferenv(
384407
free(*(data as *mut *mut c_char) as *mut c_void);
385408
}
386409

387-
let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
410+
let is_bytecode = luaL_isbytecode(data, size);
388411
if !mode.is_null() {
389412
let modeb = CStr::from_ptr(mode).to_bytes();
390-
if !chunk_is_text && !modeb.contains(&b'b') {
413+
let allow_binary = modeb.contains(&b'b');
414+
let allow_text = modeb.contains(&b't');
415+
if is_bytecode && !allow_binary {
391416
lua_pushfstring(L, cstr!("attempt to load a binary chunk (mode is '%s')"), mode);
392417
return LUA_ERRSYNTAX;
393-
} else if chunk_is_text && !modeb.contains(&b't') {
418+
} else if !is_bytecode && !allow_text {
394419
lua_pushfstring(L, cstr!("attempt to load a text chunk (mode is '%s')"), mode);
395420
return LUA_ERRSYNTAX;
396421
}
397422
}
398423

399-
let status = if chunk_is_text {
424+
let status = if !is_bytecode {
400425
if env < 0 {
401426
env -= 1;
402427
}

mlua-sys/src/luau/luacodegen.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ unsafe extern "C-unwind" {
88
pub fn luau_codegen_supported() -> c_int;
99
pub fn luau_codegen_create(state: *mut lua_State);
1010
pub fn luau_codegen_compile(state: *mut lua_State, idx: c_int);
11+
12+
pub fn luau_enable_jit_inliner(state: *mut lua_State);
13+
pub fn luau_disable_jit_inliner(state: *mut lua_State);
1114
}

src/chunk.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ impl Chunk<'_> {
751751
return ChunkMode::Binary;
752752
}
753753
#[cfg(feature = "luau")]
754-
if *source.first().unwrap_or(&u8::MAX) < b'\t' {
754+
if unsafe { ffi::luaL_isbytecode(source.as_ptr().cast(), source.len()) } {
755755
return ChunkMode::Binary;
756756
}
757757
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ pub use crate::userdata::{
153153
#[doc(inline)]
154154
pub use crate::debug::HookTriggers;
155155

156+
#[cfg(any(feature = "luau-jit", doc))]
157+
#[doc(inline)]
158+
pub use crate::state::JitOptions;
156159
#[cfg(any(feature = "luau", doc))]
157160
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
158161
pub use crate::{buffer::Buffer, vector::Vector};

src/prelude.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
2727
#[doc(no_inline)]
2828
pub use crate::state::GcGenParams as LuaGcGenParams;
2929

30-
#[cfg(feature = "luau")]
30+
#[cfg(any(feature = "luau", doc))]
3131
#[doc(no_inline)]
3232
pub use crate::{
3333
Buffer as LuaBuffer, Vector as LuaVector,
@@ -38,6 +38,10 @@ pub use crate::{
3838
},
3939
};
4040

41+
#[cfg(any(feature = "luau-jit", doc))]
42+
#[doc(no_inline)]
43+
pub use crate::state::JitOptions as LuaJitOptions;
44+
4145
#[cfg(feature = "async")]
4246
#[doc(no_inline)]
4347
pub use crate::{function::LuaNativeAsyncFn, thread::AsyncThread as LuaAsyncThread};

src/state.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,38 @@ impl LuaOptions {
255255
}
256256
}
257257

258+
/// Luau JIT options
259+
#[cfg(any(feature = "luau-jit", doc))]
260+
#[cfg_attr(docsrs, doc(cfg(feature = "luau-jit")))]
261+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
262+
pub struct JitOptions {
263+
inliner: bool,
264+
}
265+
266+
#[cfg(any(feature = "luau-jit", doc))]
267+
impl Default for JitOptions {
268+
fn default() -> Self {
269+
const { Self::new() }
270+
}
271+
}
272+
273+
#[cfg(any(feature = "luau-jit", doc))]
274+
impl JitOptions {
275+
/// Creates default JIT options.
276+
pub const fn new() -> Self {
277+
JitOptions { inliner: false }
278+
}
279+
280+
/// Toggles the runtime bytecode inliner.
281+
///
282+
/// Disabled by default. Changing this option does not affect already loaded functions.
283+
#[must_use]
284+
pub const fn set_inliner(mut self, enabled: bool) -> Self {
285+
self.inliner = enabled;
286+
self
287+
}
288+
}
289+
258290
impl Drop for Lua {
259291
fn drop(&mut self) {
260292
if self.collect_garbage {
@@ -1251,6 +1283,23 @@ impl Lua {
12511283
unsafe { (*lua.extra.get()).enable_jit = enable };
12521284
}
12531285

1286+
/// Configures JIT options for this Lua VM.
1287+
#[cfg(any(feature = "luau-jit", doc))]
1288+
#[cfg_attr(docsrs, doc(cfg(feature = "luau-jit")))]
1289+
pub fn set_jit_options(&self, options: JitOptions) {
1290+
let lua = self.lock();
1291+
unsafe {
1292+
let state = lua.main_state();
1293+
if options.inliner {
1294+
let _ = Self::set_fflag("LuauCallFeedback", true);
1295+
let _ = Self::set_fflag("LuauEmitCallFeedback", true);
1296+
ffi::luau_enable_jit_inliner(state);
1297+
} else {
1298+
ffi::luau_disable_jit_inliner(state);
1299+
}
1300+
}
1301+
}
1302+
12541303
/// Sets Luau feature flag (global setting).
12551304
///
12561305
/// See https://github.com/luau-lang/luau/blob/master/CONTRIBUTING.md#feature-flags for details.

tests/luau.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,32 @@ fn test_fflags() {
358358
assert!(Lua::set_fflag("UnknownFlag", true).is_err());
359359
}
360360

361+
#[cfg(feature = "luau-jit")]
362+
#[test]
363+
fn test_jit_inliner() -> Result<()> {
364+
let lua = Lua::new();
365+
lua.set_jit_options(mlua::JitOptions::new().set_inliner(true));
366+
367+
// An inlinable helper called in a hot loop.
368+
let sum = lua
369+
.load(
370+
r#"
371+
local function add(a, b)
372+
return a + b
373+
end
374+
local sum = 0
375+
for i = 1, 1000 do
376+
sum = add(sum, i)
377+
end
378+
return sum
379+
"#,
380+
)
381+
.eval::<i64>()?;
382+
assert_eq!(sum, 500500);
383+
384+
Ok(())
385+
}
386+
361387
#[test]
362388
fn test_loadstring() -> Result<()> {
363389
let lua = Lua::new();

0 commit comments

Comments
 (0)