This commit is contained in:
Steven 2026-01-09 08:23:23 -05:00 committed by GitHub
commit 7d60d5ca71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 263 additions and 0 deletions

View File

@ -440,3 +440,13 @@ jobs:
cmake ..
cmake --build . --config Release --parallel 4
./import-func-callback
- name: Test x18 register reservation (macOS ARM64 only)
if: matrix.os == 'macos-15'
run: |
cd product-mini/platforms/darwin
mkdir -p build && cd build
cmake .. -DWAMR_BUILD_AOT=1
cmake --build . --config Release --parallel 4
cd ../../../../tests/standalone/test-aot-x18-reserve
./run.sh --aot

View File

@ -3005,6 +3005,10 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
else
vendor_sys = "-pc-windows-";
}
else if (!strcmp(abi, "darwin") || !strcmp(abi, "macho")) {
/* macOS/Darwin: x18 is reserved by Apple */
vendor_sys = "-apple-";
}
else {
if (is_baremetal_target(arch, cpu, abi))
vendor_sys = "-unknown-none-";
@ -3050,6 +3054,14 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
if (!abi)
abi = "gnu";
}
else if (strstr(default_triple, "darwin")
|| strstr(default_triple, "apple")) {
/* macOS/Darwin: x18 is reserved by Apple, must use correct
* triple to prevent LLVM from using it */
vendor_sys = "-apple-darwin";
if (!abi)
abi = "";
}
else {
vendor_sys = "-pc-linux-";
if (!abi)
@ -3139,6 +3151,30 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
if (!features)
features = "";
#if (defined(__APPLE__) || defined(__MACH__)) && defined(BUILD_TARGET_AARCH64)
/* On macOS ARM64, x18 is reserved by Apple for TLS. Even though we're
* generating ELF (Linux-style) AOT files, we must tell LLVM to not
* use x18, otherwise the AOT code will crash when running on macOS. */
{
bool is_aarch64 = false;
if (arch && !strncmp(arch, "aarch64", 7))
is_aarch64 = true;
else if (triple_norm && strstr(triple_norm, "aarch64"))
is_aarch64 = true;
if (is_aarch64) {
if (features[0] != '\0') {
snprintf(features_buf, sizeof(features_buf),
"%s,+reserve-x18", features);
features = features_buf;
}
else {
features = "+reserve-x18";
}
}
}
#endif
/* Get target with triple, note that LLVMGetTargetFromTriple()
return 0 when success, but not true. */
if (LLVMGetTargetFromTriple(triple_norm, &target, &err) != 0) {

View File

@ -0,0 +1,120 @@
#!/bin/bash
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# Test for x18 register reservation on macOS ARM64 (aarch64).
#
# On macOS ARM64, x18 is reserved by Apple for TLS (Thread Local Storage).
# Without the +reserve-x18 LLVM flag, the AOT compiler may generate code
# that uses x18, causing random SIGSEGV crashes when run on macOS.
#
# This test compiles a WASM module that stresses register allocation
# (forcing x18 usage without the fix) and runs it 1000 times to verify
# no crashes occur.
#
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
WAMR_DIR="${SCRIPT_DIR}/../../.."
# Detect platform
UNAME_S=$(uname -s)
UNAME_M=$(uname -m)
# Only run this test on macOS ARM64
if [[ "${UNAME_S}" != "Darwin" ]] || [[ "${UNAME_M}" != "arm64" ]]; then
echo "Skipping x18 reserve test: only applicable on macOS ARM64"
echo "Current platform: ${UNAME_S} ${UNAME_M}"
exit 0
fi
# Determine iwasm path based on platform
if [[ "${UNAME_S}" == "Darwin" ]]; then
IWASM_CMD="${WAMR_DIR}/product-mini/platforms/darwin/build/iwasm"
else
IWASM_CMD="${WAMR_DIR}/product-mini/platforms/linux/build/iwasm"
fi
WAMRC_CMD="${WAMR_DIR}/wamr-compiler/build/wamrc"
# Check if required binaries exist
if [[ ! -x "${IWASM_CMD}" ]]; then
echo "Error: iwasm not found at ${IWASM_CMD}"
echo "Please build iwasm first"
exit 1
fi
if [[ ! -x "${WAMRC_CMD}" ]]; then
echo "Error: wamrc not found at ${WAMRC_CMD}"
echo "Please build wamrc first"
exit 1
fi
cd "${SCRIPT_DIR}"
# Find wat2wasm (check CI path first, then system PATH)
if [[ -x "/opt/wabt/bin/wat2wasm" ]]; then
WAT2WASM="/opt/wabt/bin/wat2wasm"
elif command -v wat2wasm &> /dev/null; then
WAT2WASM="wat2wasm"
else
echo "Error: wat2wasm not found"
echo "Please install wabt tools"
exit 1
fi
# Compile WAT to WASM if needed
if [[ ! -f stress_registers.wasm ]] || [[ stress_registers.wat -nt stress_registers.wasm ]]; then
echo "Compiling stress_registers.wat to WASM..."
if ! ${WAT2WASM} stress_registers.wat -o stress_registers.wasm; then
echo "Error: Failed to compile WAT to WASM"
exit 1
fi
fi
if [[ $1 != "--aot" ]]; then
echo "============> run stress_registers.wasm (interpreter mode)"
echo "Running 1000 iterations in interpreter mode..."
for i in $(seq 1 1000); do
if ! ${IWASM_CMD} stress_registers.wasm 2>&1; then
echo "FAILED: Crash at iteration $i"
exit 1
fi
done
echo "PASSED: 1000 iterations completed without crash"
else
echo "============> compile stress_registers.wasm to AOT"
# Compile to AOT - the fix should add +reserve-x18 automatically on macOS ARM64
if ! ${WAMRC_CMD} --opt-level=3 -o stress_registers.aot stress_registers.wasm; then
echo "Error: Failed to compile WASM to AOT"
exit 1
fi
echo "============> run stress_registers.aot"
echo "Running 1000 iterations to verify x18 is properly reserved..."
echo "(Without the fix, this would crash ~80% of the time)"
failed=0
for i in $(seq 1 1000); do
if ! ${IWASM_CMD} stress_registers.aot 2>&1 > /dev/null; then
echo "FAILED: Crash at iteration $i"
failed=1
break
fi
# Progress indicator every 100 iterations
if [[ $((i % 100)) -eq 0 ]]; then
echo " Progress: $i/1000 iterations completed"
fi
done
if [[ ${failed} -eq 0 ]]; then
echo "PASSED: 1000 iterations completed without crash"
echo "The +reserve-x18 fix is working correctly"
exit 0
else
echo "FAILED: x18 register corruption detected"
echo "The +reserve-x18 fix may not be applied correctly"
exit 1
fi
fi

View File

@ -0,0 +1,97 @@
;; Copyright (C) 2019 Intel Corporation. All rights reserved.
;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
;; Test module that uses many local variables to stress register allocation.
;; On ARM64, this will force LLVM to use x18 register if +reserve-x18 is not set.
;; x18 is reserved by Apple on macOS for TLS, so using it causes crashes.
(module
(memory (export "memory") 1)
(func $stress_registers (export "stress_registers") (param $input i64) (result i64)
(local $a i64) (local $b i64) (local $c i64) (local $d i64)
(local $e i64) (local $f i64) (local $g i64) (local $h i64)
(local $i i64) (local $j i64) (local $k i64) (local $l i64)
(local $m i64) (local $n i64) (local $o i64) (local $p i64)
(local $q i64) (local $r i64) (local $s i64) (local $t i64)
(local $u i64) (local $v i64) (local $w i64) (local $x i64)
;; Initialize all locals with different values based on input
(local.set $a (i64.add (local.get $input) (i64.const 1)))
(local.set $b (i64.mul (local.get $a) (i64.const 2)))
(local.set $c (i64.add (local.get $b) (i64.const 3)))
(local.set $d (i64.mul (local.get $c) (i64.const 4)))
(local.set $e (i64.add (local.get $d) (i64.const 5)))
(local.set $f (i64.mul (local.get $e) (i64.const 6)))
(local.set $g (i64.add (local.get $f) (i64.const 7)))
(local.set $h (i64.mul (local.get $g) (i64.const 8)))
(local.set $i (i64.add (local.get $h) (i64.const 9)))
(local.set $j (i64.mul (local.get $i) (i64.const 10)))
(local.set $k (i64.add (local.get $j) (i64.const 11)))
(local.set $l (i64.mul (local.get $k) (i64.const 12)))
(local.set $m (i64.add (local.get $l) (i64.const 13)))
(local.set $n (i64.mul (local.get $m) (i64.const 14)))
(local.set $o (i64.add (local.get $n) (i64.const 15)))
(local.set $p (i64.mul (local.get $o) (i64.const 16)))
(local.set $q (i64.add (local.get $p) (i64.const 17)))
(local.set $r (i64.mul (local.get $q) (i64.const 18)))
(local.set $s (i64.add (local.get $r) (i64.const 19)))
(local.set $t (i64.mul (local.get $s) (i64.const 20)))
(local.set $u (i64.add (local.get $t) (i64.const 21)))
(local.set $v (i64.mul (local.get $u) (i64.const 22)))
(local.set $w (i64.add (local.get $v) (i64.const 23)))
(local.set $x (i64.mul (local.get $w) (i64.const 24)))
;; Now use all of them together to prevent optimization
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(local.get $a)
(local.get $b))
(local.get $c))
(local.get $d))
(local.get $e))
(local.get $f))
(local.get $g))
(local.get $h))
(local.get $i))
(local.get $j))
(local.get $k))
(local.get $l))
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(i64.add
(local.get $m)
(local.get $n))
(local.get $o))
(local.get $p))
(local.get $q))
(local.get $r))
(local.get $s))
(local.get $t))
(local.get $u))
(local.get $v))
(local.get $w))
(local.get $x))))
(func $_start (export "_start")
(drop (call $stress_registers (i64.const 42)))
)
)