Browse Source

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.
Alex Szpakowski 3 years ago
parent
commit
888051d01e

+ 2 - 0
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 = "<group>"; };
 		FA620A391AA305F6005DB4C2 /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = "<group>"; };
 		FA665DC321C34C900074BBD6 /* wrap_GraphicsShader.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_GraphicsShader.lua; sourceTree = "<group>"; };
+		FA69B918273828DD00CDC2E7 /* jitsetup.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = jitsetup.lua; sourceTree = "<group>"; };
 		FA6A2B641F5F7B6B0074C308 /* wrap_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Data.h; sourceTree = "<group>"; };
 		FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Data.cpp; sourceTree = "<group>"; };
 		FA6A2B681F5F7F560074C308 /* DataView.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DataView.cpp; sourceTree = "<group>"; };
@@ -2966,6 +2967,7 @@
 				FA1E95B4271F932B0044CF08 /* arg.lua */,
 				FA577A8D16C71D3600860150 /* boot.lua */,
 				FA1E95B5271F932B0044CF08 /* callbacks.lua */,
+				FA69B918273828DD00CDC2E7 /* jitsetup.lua */,
 				FA0B7BFE1A95902C000E1D17 /* love.cpp */,
 				FA0B7BFF1A95902C000E1D17 /* love.h */,
 			);

+ 16 - 8
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");
 

+ 71 - 0
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"--"

+ 23 - 4
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)

+ 3 - 0
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