blob: d4def626848425ab0623e28748d04fc76b991cac [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 default
-- sw; wasm_hw_bounds below. No libc-wasi: AssemblyScript modules import
-- none, and the runtime serves the Luau host module's two wasi calls itself.
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 (the tier transplant) 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=0',
'WASM_ENABLE_MODULE_INST_CONTEXT=1',
'WASM_ENABLE_BULK_MEMORY=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',
'WASM_DISABLE_STACK_HW_BOUND_CHECK=1',
'BH_MALLOC=wasm_runtime_malloc',
'BH_FREE=wasm_runtime_free',
}
-- Opt-in hw bounds serve AOT artifacts compiled --bounds-checks=0 only:
-- patch 0009 keeps the interpreter sw-checked and patch 0010 keeps sw
-- artifacts unwinding by checked returns, so the Luau lane never takes
-- a hardware trap. Native stack checks stay sw everywhere (artifacts
-- must carry --stack-bounds-checks=1).
if not _OPTIONS['wasm_hw_bounds'] 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
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=')
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' })
-- 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, so cross 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 not isNx then
buildoptions({
'-mllvm -tail-dup-pred-size=5000',
'-mllvm -tail-dup-succ-size=5000',
})
end
defines(wamrConfigDefines)
filter({ 'system:macosx' })
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
-- '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 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 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 invokeNative = 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 = isArm32 and 'aot_reloc_thumb.c'
or isArm64 and 'aot_reloc_aarch64.c'
or 'aot_reloc_x86_64.c'
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',
wamr .. '/core/shared/utils',
wamr .. '/core/shared/utils/uncommon',
simde,
})
files({
wamr .. '/core/iwasm/common/*.c',
wamr .. '/core/iwasm/common/arch/' .. 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',
wamr .. '/core/iwasm/aot/arch/' .. aotReloc,
wamr .. '/core/iwasm/libraries/libc-builtin/*.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',
})
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' })
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',
wamr .. '/core/shared/utils',
},
}