blob: 2a7c2460febe702d3af8ae16d1dabd92ac792448 [file]
-- WAMR for the wasm scripting backend. Flags proven by the luau_wasm EH
-- spike: sw-bounds artifacts must never unwind through hardware traps
-- (the longjmp skips the sjlj glue's native frames). Bounds checks are hw
-- where premake5_v2.lua's wasmHwBounds says so, else sw. libc-wasi stays on:
-- it makes WAMR leave the Luau blob's __wasm_call_ctors to the host, off it
-- also runs them at instantiate.
local dependency = require('dependency')
-- The patch set carries the rive fast-interp fixes (linear_mem_size
-- refreshes around growth, gated OOB diagnostics); a clone without them is
-- subtly wrong under memory growth.
local wamr = dependency.github('bytecodealliance/wasm-micro-runtime', 'WAMR-2.4.5', {
patches = path.join(path.getdirectory(_SCRIPT), 'wamr_patches'),
})
-- Header-only simd library the fast interpreter's v128 handlers compile
-- against; upstream fetches the same tag through CMake FetchContent.
local simde = dependency.github('simd-everywhere/simde', 'v0.8.2')
-- Struct layouts in wamr's internal headers follow these; any rive TU that
-- includes them must compile with the identical set or instance layouts
-- silently diverge.
local wamrConfigDefines = {
'WASM_ENABLE_INTERP=1',
'WASM_ENABLE_FAST_INTERP=1',
'WASM_ENABLE_AOT=1',
'WASM_ENABLE_JIT=0',
'WASM_ENABLE_LIBC_BUILTIN=1',
'WASM_ENABLE_LIBC_WASI=1',
'WASM_ENABLE_MODULE_INST_CONTEXT=1',
'WASM_ENABLE_BULK_MEMORY=1',
-- A host watchdog, not module fuel, bounds how long a call may run.
'WASM_ENABLE_RIVE_INTERRUPT=1',
-- Simde-backed v128 in the fast interpreter; scalar modules
-- translate the same as before, simd modules stop being refused.
'WASM_ENABLE_SIMD=1',
'WASM_ENABLE_SIMDE=1',
-- Parallel calls run extra instances of a module over its shared
-- memory; no thread manager, the host runs the threads.
'WASM_ENABLE_SHARED_MEMORY=1',
'WASM_DISABLE_STACK_HW_BOUND_CHECK=1',
'BH_MALLOC=wasm_runtime_malloc',
'BH_FREE=wasm_runtime_free',
}
-- Hw bounds serve AOT artifacts compiled --bounds-checks=0 only: patch 0009
-- keeps the interpreter sw-checked, patch 0010 keeps sw artifacts unwinding
-- by checked returns, so the Luau lane never takes a hardware trap, and
-- patch 0047 maps only hw artifacts' memories with guard pages. Native stack
-- checks stay sw everywhere (artifacts must carry --stack-bounds-checks=1).
if not wasmHwBounds then
table.insert(wamrConfigDefines, 'WASM_DISABLE_HW_BOUND_CHECK=1')
end
-- Nintendo cross builds set this from packages/runtime_switch and run on a
-- Windows host, so every os.target() gate below must yield to it. nnSdk
-- ships no mmap family and no signal API, so the platform layer is ours.
local isNx = _OPTIONS['_nx_platform'] ~= nil
if isNx then
-- Console trap forensics: no debugger reaches the dev kit, so name the
-- wasm frames when a call dies. Config defines so every TU agrees on
-- layouts.
table.insert(wamrConfigDefines, 'WASM_ENABLE_DUMP_CALL_STACK=1')
-- The dump's AOT side compiles against frame bookkeeping gated on this.
table.insert(wamrConfigDefines, 'WASM_ENABLE_AOT_STACK_FRAME=1')
-- Patch 0017: XIP modules loaded from sections execute text linked into
-- the host image, the only AOT shape possible without runtime
-- executable memory.
table.insert(wamrConfigDefines, 'WASM_ENABLE_PRELINKED_AOT=1')
end
-- iOS devices map no executable memory either, so rive --ios --aot links
-- modules into the player the same way.
if
_OPTIONS['os'] == 'ios'
and _OPTIONS['variant'] ~= 'emulator'
and _OPTIONS['variant'] ~= 'xrsimulator'
and _OPTIONS['variant'] ~= 'appletvsimulator'
then
table.insert(wamrConfigDefines, 'WASM_ENABLE_PRELINKED_AOT=1')
end
if os.target() == 'windows' and not isNx then
-- We link wamr statically; under clang's msvc mode wasm_export.h and
-- wasm_c_api.h otherwise declare every API dllimport.
table.insert(wamrConfigDefines, 'WASM_RUNTIME_API_EXTERN=')
table.insert(wamrConfigDefines, 'WASM_API_EXTERN=')
-- WAMR keys threaded dispatch on __GNUC__, which clang leaves undefined
-- for msvc targets even though it supports computed goto.
table.insert(wamrConfigDefines, 'WASM_ENABLE_LABELS_AS_VALUES=1')
end
-- os.target() stays the host under --for_android; the option is the
-- truth for the platform dir.
local forAndroid = _OPTIONS['for_android'] ~= nil
local platformDir = forAndroid and 'android'
or os.target() == 'linux' and 'linux'
or os.target() == 'windows' and 'windows'
or 'darwin'
-- Where platform_internal.h and the os_* implementations come from.
local platformPath = isNx
and path.join(path.getdirectory(_SCRIPT), 'wamr_nx')
or (wamr .. '/core/shared/platform/' .. platformDir)
project('wamr')
do
kind('StaticLib')
language('C')
warnings('Off')
-- Vendored dep nobody debugs into: an unoptimized interpreter is 6x
-- slower, so every config builds it optimized. NDEBUG matches the CMake
-- Release baseline; without it the interpreter's computed-goto dispatch
-- tail-merges into a single prediction-killing branch.
optimize('Speed')
defines({ 'NDEBUG' })
-- Compiled code may call the host with only this guard of stack left,
-- and WAMR's 1 to 3 KB default cannot hold a printf.
defines({ 'WASM_STACK_GUARD_SIZE=65536' })
-- Replicated per-handler dispatch is what makes the fast interpreter
-- fast; default heuristics tail-merge the computed gotos into a single
-- indirect branch (+27% on box2d). Guarded by bench/check_dispatch_sites.
-- LTO would run codegen at link where these -mllvm knobs cannot reach,
-- so this library opts out and keeps its dispatch replication.
buildoptions({ '-fno-lto' })
-- The tail-dup knobs need llvm 19; older clangs build correct but
-- slower dispatch, still caught by the dispatch-site guard where it runs.
-- The probe runs host clang; cross toolchains (the NDK's) may reject
-- the knobs the host accepts, and iOS embeds bitcode, which refuses
-- -mllvm, so those builds keep default dispatch.
local devNull = os.ishost('windows') and 'NUL' or '/dev/null'
local _, tailDupProbe = os.outputof(
'clang -fsyntax-only -x c ' .. devNull ..
' -mllvm -tail-dup-pred-size=5000' ..
' -mllvm -tail-dup-succ-size=5000 2>&1'
)
if
tailDupProbe == 0
and _OPTIONS['for_android'] == nil
and _OPTIONS['os'] ~= 'ios'
and not isNx
then
buildoptions({
'-mllvm -tail-dup-pred-size=5000',
'-mllvm -tail-dup-succ-size=5000',
})
end
defines(wamrConfigDefines)
filter({ 'system:macosx or system:ios' })
defines({ 'BH_PLATFORM_DARWIN' })
filter({ 'system:linux' })
defines({ 'BH_PLATFORM_LINUX' })
filter({ 'system:android' })
defines({ 'BH_PLATFORM_ANDROID' })
filter({ 'system:windows' })
defines({
'BH_PLATFORM_WINDOWS',
'HAVE_STRUCT_TIMESPEC',
'_WINSOCK_DEPRECATED_NO_WARNINGS',
})
filter({})
-- Per-lane arch beats host arch: universal builds compile both lanes
-- on one machine and each must get its own invoke shim and relocs.
local archOption = _OPTIONS['arch']
local machine = os.outputof('uname -m')
local isArm64
local isUniversal = false
-- 'host' is the option's default, not an explicit lane.
if isNx then
-- Windows hosted cross build, so neither the host arch nor --arch
-- describes the target.
isArm64 = true
elseif
_OPTIONS['os'] == 'ios'
and _OPTIONS['variant'] ~= 'emulator'
and _OPTIONS['variant'] ~= 'xrsimulator'
and _OPTIONS['variant'] ~= 'appletvsimulator'
then
-- Every Apple device is arm64, so its universal build has one slice,
-- and WAMR's platform header turns on hardware bounds only for a
-- target named before config.h names one.
isArm64 = true
elseif archOption == 'universal' then
-- One compile for every slice, as the iOS simulator build does.
isUniversal = true
elseif archOption ~= nil and archOption ~= '' and archOption ~= 'host' then
isArm64 = archOption == 'arm64' or archOption == 'aarch64'
else
isArm64 = machine == 'arm64' or machine == 'aarch64'
end
local isArm32 = forAndroid and archOption == 'arm'
-- The quoted string define goes through buildoptions pre-escaped so both
-- the gmake and ninja generators deliver the quotes to the compiler.
if isArm32 then
-- NDK armv7 compiles thumb2 with NEON/VFP by default. Upstream's
-- thumb reloc does pointer arithmetic clang 18 makes a hard error.
-- The THUMBV7 string matters: the aot loader matches it against
-- wamrc's thumbv7 artifacts (bare THUMB defaults to thumbv4t).
defines({ 'BUILD_TARGET_THUMB_VFP' })
buildoptions({
'-DBUILD_TARGET=\\"THUMBV7\\"',
'-Wno-int-conversion',
})
elseif isUniversal then
-- WAMR's config.h names the target per slice when nothing here does.
elseif isArm64 then
defines({ 'BUILD_TARGET_AARCH64' })
buildoptions({ '-DBUILD_TARGET=\\"AARCH64\\"' })
else
defines({ 'BUILD_TARGET_X86_64' })
buildoptions({ '-DBUILD_TARGET=\\"X86_64\\"' })
end
-- em64 is WAMR's x86-64 SysV invoke shim. With WASM_ENABLE_SIMD the
-- invoke marshaling widens float slots to v128, so the shim must be the
-- _simd variant or int registers load from the wrong offsets. arm32 has
-- no simd invoke variant; its marshaling never widens. Windows takes
-- the mingw shim: same Win64 ABI as MSVC targets, and GAS syntax that
-- clang's integrated assembler handles without ml64.
local archDir = wamr .. '/core/iwasm/common/arch/'
local relocDir = wamr .. '/core/iwasm/aot/arch/'
local invokeNative = isUniversal and 'wamr_universal_invoke.S'
or archDir
.. (
isArm32 and 'invokeNative_thumb_vfp.s'
or isArm64 and 'invokeNative_aarch64_simd.s'
or os.target() == 'windows' and 'invokeNative_mingw_x64_simd.s'
or 'invokeNative_em64_simd.s'
)
local aotReloc = isUniversal and 'wamr_universal_reloc.c'
or relocDir
.. (
isArm32 and 'aot_reloc_thumb.c'
or isArm64 and 'aot_reloc_aarch64.c'
or 'aot_reloc_x86_64.c'
)
if isUniversal then
includedirs({ archDir, relocDir })
end
includedirs({
wamr .. '/core/iwasm/include',
wamr .. '/core/iwasm/common',
wamr .. '/core/iwasm/aot',
wamr .. '/core/iwasm/interpreter',
wamr .. '/core/shared/include',
wamr .. '/core/shared/platform/include',
-- platform_internal.h comes from the per-platform dir.
platformPath,
wamr .. '/core/shared/platform/common/libc-util',
wamr .. '/core/shared/mem-alloc',
-- wasm_runtime_common.h pulls the wasi primitives when libc-wasi is
-- on, which our config always is.
wamr .. '/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src',
wamr .. '/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include',
wamr .. '/core/shared/utils',
wamr .. '/core/shared/utils/uncommon',
simde,
})
files({
wamr .. '/core/iwasm/common/*.c',
invokeNative,
wamr .. '/core/iwasm/interpreter/wasm_loader.c',
wamr .. '/core/iwasm/interpreter/wasm_runtime.c',
wamr .. '/core/iwasm/interpreter/wasm_interp_fast.c',
wamr .. '/core/iwasm/aot/*.c',
aotReloc,
wamr .. '/core/iwasm/libraries/libc-builtin/*.c',
wamr .. '/core/iwasm/libraries/libc-wasi/**.c',
platformPath .. '/*.c',
-- Windows replaces the posix layer wholesale; win_atomic is C++.
(os.target() == 'windows' and not isNx)
and (wamr .. '/core/shared/platform/windows/*.cpp')
or (wamr .. '/core/shared/platform/common/posix/*.c'),
wamr .. '/core/shared/platform/common/memory/*.c',
wamr .. '/core/shared/platform/common/libc-util/*.c',
wamr .. '/core/shared/mem-alloc/*.c',
wamr .. '/core/shared/mem-alloc/ems/*.c',
wamr .. '/core/shared/utils/*.c',
wamr .. '/core/shared/utils/uncommon/*.c',
-- Built with WAMR's own flags, so it checks the atomics its sources get.
'wamr_atomics_guard.c',
})
if os.target() == 'windows' and not isNx then
-- Linux perf-map support; leans on pid_t/getpid.
removefiles({ wamr .. '/core/iwasm/aot/aot_perf_map.c' })
-- msbuild hands the GAS shim to clang-cl as C, so assemble it with
-- clang directly.
filter({ 'action:vs*', 'files:**.s' })
do
buildmessage('assembling %{file.name}')
buildcommands({
'clang --target=x86_64-pc-windows-msvc -c "%{file.abspath}" -o "%{cfg.objdir}/%{file.basename}.obj"',
})
buildoutputs({ '%{cfg.objdir}/%{file.basename}.obj' })
end
filter({})
end
if isNx then
files({ platformPath .. '/*.cpp' })
-- The posix mapping calls resolve against symbols nnSdk never
-- exports, so nx_platform.cpp replaces the whole file.
removefiles({ wamr .. '/core/shared/platform/common/posix/posix_memmap.c' })
end
end
return {
wamr = wamr,
configDefines = wamrConfigDefines,
internalIncludes = {
wamr .. '/core/iwasm/interpreter',
wamr .. '/core/iwasm/aot',
wamr .. '/core/iwasm/common',
wamr .. '/core/shared/include',
wamr .. '/core/shared/platform/include',
-- platform_internal.h comes from the per-platform dir.
platformPath,
wamr .. '/core/shared/mem-alloc',
-- wasm_runtime_common.h pulls the wasi primitives when libc-wasi is
-- on, which our config always is.
wamr .. '/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src',
wamr .. '/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include',
wamr .. '/core/shared/utils',
},
}