From 888051d01eb9108174d9a3baa385e17bdec097f5 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 12 Nov 2021 21:58:49 -0400 Subject: [PATCH] Potential very hacky workaround for arm64 JIT allocation issues. Try to reserve blocks of executable memory before external library code claims the address space. LuaJIT on arm64 currently has a very limited range of memory it can use for JIT code. --- .../xcode/liblove.xcodeproj/project.pbxproj | 2 + src/love.cpp | 24 ++++--- src/modules/love/jitsetup.lua | 71 +++++++++++++++++++ src/modules/love/love.cpp | 27 +++++-- src/modules/love/love.h | 3 + 5 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 src/modules/love/jitsetup.lua diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 3e3226bc0..113fd36ff 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -1849,6 +1849,7 @@ FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Texture.h; sourceTree = ""; }; FA620A391AA305F6005DB4C2 /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = ""; }; FA665DC321C34C900074BBD6 /* wrap_GraphicsShader.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_GraphicsShader.lua; sourceTree = ""; }; + FA69B918273828DD00CDC2E7 /* jitsetup.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = jitsetup.lua; sourceTree = ""; }; FA6A2B641F5F7B6B0074C308 /* wrap_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Data.h; sourceTree = ""; }; FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Data.cpp; sourceTree = ""; }; FA6A2B681F5F7F560074C308 /* DataView.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DataView.cpp; sourceTree = ""; }; @@ -2966,6 +2967,7 @@ FA1E95B4271F932B0044CF08 /* arg.lua */, FA577A8D16C71D3600860150 /* boot.lua */, FA1E95B5271F932B0044CF08 /* callbacks.lua */, + FA69B918273828DD00CDC2E7 /* jitsetup.lua */, FA0B7BFE1A95902C000E1D17 /* love.cpp */, FA0B7BFF1A95902C000E1D17 /* love.h */, ); diff --git a/src/love.cpp b/src/love.cpp index 089440a47..3245d73f6 100644 --- a/src/love.cpp +++ b/src/love.cpp @@ -142,14 +142,6 @@ enum DoneAction static DoneAction runlove(int argc, char **argv, int &retval) { -#ifdef LOVE_LEGENDARY_APP_ARGV_HACK - int hack_argc = 0; - char **hack_argv = 0; - get_app_arguments(argc, argv, hack_argc, hack_argv); - argc = hack_argc; - argv = hack_argv; -#endif // LOVE_LEGENDARY_APP_ARGV_HACK - // Oh, you just want the version? Okay! if (argc > 1 && strcmp(argv[1], "--version") == 0) { @@ -166,6 +158,22 @@ static DoneAction runlove(int argc, char **argv, int &retval) lua_State *L = luaL_newstate(); luaL_openlibs(L); + // LuaJIT-specific setup needs to be done as early as possible - before + // get_app_arguments because that loads external library code. This is also + // loaded inside require("love"). Note that it doesn't use the love table. + love_preload(L, luaopen_love_jitsetup, "love.jitsetup"); + lua_getglobal(L, "require"); + lua_pushstring(L, "love.jitsetup"); + lua_call(L, 1, 0); + +#ifdef LOVE_LEGENDARY_APP_ARGV_HACK + int hack_argc = 0; + char **hack_argv = nullptr; + get_app_arguments(argc, argv, hack_argc, hack_argv); + argc = hack_argc; + argv = hack_argv; +#endif // LOVE_LEGENDARY_APP_ARGV_HACK + // Add love to package.preload for easy requiring. love_preload(L, luaopen_love, "love"); diff --git a/src/modules/love/jitsetup.lua b/src/modules/love/jitsetup.lua new file mode 100644 index 000000000..d87afa3ce --- /dev/null +++ b/src/modules/love/jitsetup.lua @@ -0,0 +1,71 @@ +R"luastring"--( +-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string. +-- There is a matching delimiter at the bottom of the file. + +--[[ +Copyright (c) 2006-2021 LOVE Development Team + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +--]] + +if type(jit) ~= "table" or not jit.status() then + return +end + +-- Double the defaults. +jit.opt.start("maxtrace=2000", "maxrecord=8000") + +-- Somewhat arbitrary value. Needs to be higher than the combined sizes below, +-- and higher than the default (512) because that's already too low. +jit.opt.start("maxmcode=16384") + +if jit.arch == "arm64" then + -- https://github.com/LuaJIT/LuaJIT/issues/285 + -- LuaJIT 2.1 on arm64 currently (as of commit b4b2dce) can only use memory + -- for JIT compilation within a certain short range. Other libraries such as + -- SDL can take all the usable space in that range and cause attempts at JIT + -- compilation to both fail and take a long time. + -- This is a very hacky attempt at a workaround. LuaJIT allocates executable + -- code in pools. We'll try "reserving" pools before any external code is + -- executed, by causing JIT compilation via a small loop. We can't easily + -- tell if JIT compilation succeeded, so we do several successively smaller + -- pool allocations in case previous ones fail. + -- This is a really hacky hack and by no means foolproof - there are a lot of + -- potential situations (especially when threads are used) where previously + -- executed external code will still take up space that LuaJIT needed for itself. + + jit.opt.start("sizemcode=2048") + for i=1, 100 do end + + jit.opt.start("sizemcode=1024") + for i=1, 100 do end + + jit.opt.start("sizemcode=512") + for i=1, 100 do end + + jit.opt.start("sizemcode=256") + for i=1, 100 do end + + jit.opt.start("sizemcode=128") + for i=1, 100 do end +else + -- Somewhat arbitrary value (>= the default). + jit.opt.start("sizemcode=128") +end + +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/src/modules/love/love.cpp b/src/modules/love/love.cpp index 5728514ad..a70125f91 100644 --- a/src/modules/love/love.cpp +++ b/src/modules/love/love.cpp @@ -97,6 +97,10 @@ static const char boot_lua[] = #include "boot.lua" ; +static const char jit_setup_lua[] = +#include "jitsetup.lua" +; + // All modules define a c-accessible luaopen // so let's make use of those, instead // of addressing implementations directly. @@ -160,6 +164,7 @@ extern "C" extern int luaopen_love_window(lua_State*); #endif extern int luaopen_love_nogame(lua_State*); + extern int luaopen_love_jitsetup(lua_State*); extern int luaopen_love_arg(lua_State*); extern int luaopen_love_callbacks(lua_State*); extern int luaopen_love_boot(lua_State*); @@ -224,6 +229,7 @@ static const luaL_Reg modules[] = { { "love.window", luaopen_love_window }, #endif { "love.nogame", luaopen_love_nogame }, + { "love.jitsetup", luaopen_love_jitsetup }, { "love.arg", luaopen_love_arg }, { "love.callbacks", luaopen_love_callbacks }, { "love.boot", luaopen_love_boot }, @@ -404,6 +410,15 @@ static void luax_addcompatibilityalias(lua_State *L, const char *module, const c int luaopen_love(lua_State *L) { + // Preload module loaders. + for (int i = 0; modules[i].name != nullptr; i++) + love::luax_preload(L, modules[i].func, modules[i].name); + + // jitsetup is also loaded in the love executable runlove function. It's + // needed here too for threads. Note that it doesn't use the love table. + love::luax_require(L, "love.jitsetup"); + lua_pop(L, 1); + love::luax_insistpinnedthread(L); love::luax_insistglobal(L, "love"); @@ -501,10 +516,6 @@ int luaopen_love(lua_State *L) lua_setfield(L, -2, "hasDeprecationOutput"); } - // Preload module loaders. - for (int i = 0; modules[i].name != nullptr; i++) - love::luax_preload(L, modules[i].func, modules[i].name); - // Necessary for Data-creating methods to work properly in Data subclasses. love::luax_require(L, "love.data"); lua_pop(L, 1); @@ -657,6 +668,14 @@ int luaopen_love_nogame(lua_State *L) return 1; } +int luaopen_love_jitsetup(lua_State *L) +{ + if (luaL_loadbuffer(L, jit_setup_lua, sizeof(jit_setup_lua), "jitsetup.lua") == 0) + lua_call(L, 0, 1); + + return 1; +} + int luaopen_love_arg(lua_State *L) { if (luaL_loadbuffer(L, arg_lua, sizeof(arg_lua), "arg.lua") == 0) diff --git a/src/modules/love/love.h b/src/modules/love/love.h index ee057f5b4..149bb3eb7 100644 --- a/src/modules/love/love.h +++ b/src/modules/love/love.h @@ -36,6 +36,9 @@ LOVE_EXPORT const char *love_version(); LOVE_EXPORT const char *love_codename(); LOVE_EXPORT int luaopen_love(lua_State *L); LOVE_EXPORT int luaopen_love_nogame(lua_State *L); +LOVE_EXPORT int luaopen_love_jitsetup(lua_State *L); +LOVE_EXPORT int luaopen_love_arg(lua_State *L); +LOVE_EXPORT int luaopen_love_callbacks(lua_State *L); LOVE_EXPORT int luaopen_love_boot(lua_State *L); #ifdef LOVE_LEGENDARY_CONSOLE_IO_HACK