Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion crates/perry-codegen/src/codegen/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,15 @@ pub(super) fn compile_module_entry(
let main = if is_dylib {
llmod.define_function("perry_module_init", VOID, vec![])
} else {
llmod.define_function("main", I32, vec![])
// Allow the host build to override the C entry symbol. On arm64_32
// watchOS we can't rename `_main → __perry_user_main` after the
// fact (rust-objcopy's MachOWriter crashes on arm64_32 objects), so
// we emit the final symbol directly. Pass e.g. `_perry_user_main`
// (the leading underscore yields Mach-O `__perry_user_main`, which
// the Swift `@main` shell references via @_silgen_name).
let entry_name =
std::env::var("PERRY_ENTRY_SYMBOL").unwrap_or_else(|_| "main".to_string());
llmod.define_function(&entry_name, I32, vec![])
};
main.add_pre_return_void_call("js_typed_feedback_maybe_dump_trace");
let _ = main.create_block("entry");
Expand Down
6 changes: 6 additions & 0 deletions crates/perry-codegen/src/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,12 @@ pub fn resolve_target_triple(name: &str) -> Option<String> {
"ios-simulator" => Some("arm64-apple-ios17.0-simulator".to_string()),
"visionos" => Some("arm64-apple-xros1.0".to_string()),
"visionos-simulator" => Some("arm64-apple-xros1.0-simulator".to_string()),
// arm64_32 (Series 4-8 / SE) when opted in via PERRY_WATCHOS_ARM64_32;
// otherwise arm64 (S9+). Sets the arch of the emitted TS object files,
// which must match the runtime/native-lib/link triples.
"watchos" if std::env::var("PERRY_WATCHOS_ARM64_32").is_ok() => {
Some("arm64_32-apple-watchos".to_string())
}
"watchos" => Some("aarch64-apple-watchos".to_string()),
"watchos-simulator" => Some("arm64-apple-watchos10.0-simulator".to_string()),
"tvos" => Some("aarch64-apple-tvos".to_string()),
Expand Down
4 changes: 3 additions & 1 deletion crates/perry-runtime/src/array/from_concat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,9 @@ fn array_like_length(items: f64) -> usize {
let n = n.floor();
let max = (1u64 << 53) as f64 - 1.0;
if n > max {
return (1usize << 53) - 1;
// 2^53 - 1 (JS max safe integer). usize can't represent this on 32-bit
// targets (arm64_32 watchOS, wasm32), so saturate to usize::MAX there.
return usize::try_from((1u64 << 53) - 1).unwrap_or(usize::MAX);
}
n as usize
}
Expand Down
4 changes: 2 additions & 2 deletions crates/perry-runtime/src/box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub fn scan_box_roots_mut(visitor: &mut crate::gc::RuntimeRootVisitor<'_>) {
// address (alloc gives 8-aligned pointers in user space)
// matches `is_plausible_box_ptr` to keep this a no-op for
// any pathological entry.
if addr >= 0x1000 && addr < 0x0001_0000_0000_0000 && addr % 8 == 0 {
if addr >= 0x1000 && (addr as u64) < 0x0001_0000_0000_0000 && addr % 8 == 0 {
unsafe {
visitor.visit_nanbox_f64_raw_slot(&raw mut (*ptr).value);
}
Expand Down Expand Up @@ -200,7 +200,7 @@ fn is_plausible_box_ptr(ptr: *mut Box) -> bool {
if addr < 0x1000 {
return false;
}
if addr >= 0x0001_0000_0000_0000 {
if (addr as u64) >= 0x0001_0000_0000_0000 {
return false;
}
if addr % std::mem::align_of::<Box>() != 0 {
Expand Down
5 changes: 4 additions & 1 deletion crates/perry-runtime/src/builtins/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1694,7 +1694,10 @@ fn looks_like_raw_heap_pointer(value: f64) -> bool {
return false;
}
let addr = bits as usize;
(0x1000..0x8000_0000_0000usize).contains(&addr) && addr >= crate::gc::GC_HEADER_SIZE + 0x1000
// Compare in u64 so the 2^47 upper bound stays in range on 32-bit targets
// (arm64_32 watchOS, wasm32), where it's a no-op — no addresses that high.
(0x1000..0x8000_0000_0000u64).contains(&(addr as u64))
&& addr >= crate::gc::GC_HEADER_SIZE + 0x1000
}

fn formatted_deep_equal(left: f64, right: f64, skip_prototype: bool) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion crates/perry-runtime/src/value/dynamic_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub extern "C" fn js_value_length_f64(value: f64) -> f64 {
target_os = "visionos",
)))]
let heap_min: usize = 0x200_0000_0000;
if handle < heap_min || handle >= 0x8000_0000_0000 {
if handle < heap_min || (handle as u64) >= 0x8000_0000_0000 {
return 0.0;
}
if let Some(value) = unsafe {
Expand Down
7 changes: 7 additions & 0 deletions crates/perry/src/commands/compile/app_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ pub(super) fn rust_target_triple(target: Option<&str>) -> Option<&'static str> {
Some("visionos-simulator") => Some("aarch64-apple-visionos-sim"),
Some("visionos") => Some("aarch64-apple-visionos"),
Some("watchos-simulator") => Some("aarch64-apple-watchos-sim"),
// arm64_32 watchOS (Series 4-8 / SE) when opted in; otherwise arm64
// (S9+). Governs the rust target used for the auto-optimize runtime
// rebuild and for building/resolving native libraries, so all three
// must agree with the link triple in platform_cmd.rs / link/mod.rs.
Some("watchos") if std::env::var("PERRY_WATCHOS_ARM64_32").is_ok() => {
Some("arm64_32-apple-watchos")
}
Some("watchos") => Some("aarch64-apple-watchos"),
Some("tvos-simulator") => Some("aarch64-apple-tvos-sim"),
Some("tvos") => Some("aarch64-apple-tvos"),
Expand Down
11 changes: 9 additions & 2 deletions crates/perry/src/commands/compile/bundle_apple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,17 @@ pub(super) fn bundle_for_watchos(
// #4849: read version/build_number from perry.toml (was hardcoded "1.0").
let (app_version, app_build_number) = read_apple_app_version(input);

// Device builds are arm64-only, which requires watchOS 26 (S9+); the
// simulator target keeps the lower floor.
// Device builds default to arm64-only, which requires watchOS 26 (S9+).
// arm64_32 device builds (PERRY_WATCHOS_ARM64_32) reach pre-S9 watches and
// use a low floor (overridable via PERRY_WATCHOS_MIN); the simulator target
// keeps the lower floor too.
let arm64_32 = target == Some("watchos") && std::env::var("PERRY_WATCHOS_ARM64_32").is_ok();
let min_os_owned;
let min_os = if target == Some("watchos-simulator") {
"10.0"
} else if arm64_32 {
min_os_owned = std::env::var("PERRY_WATCHOS_MIN").unwrap_or_else(|_| "11.0".to_string());
min_os_owned.as_str()
} else {
"26.0"
};
Expand Down
13 changes: 11 additions & 2 deletions crates/perry/src/commands/compile/link/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1762,11 +1762,20 @@ pub(super) fn build_and_run_link(
} else {
"watchos"
};
// arm64_32 watchOS (Series 4-8 / SE): opt-in, matches the app
// binary's triple in platform_cmd.rs so the native @main lib
// links against the same arch.
let swift_arm64_32 =
target == Some("watchos") && std::env::var("PERRY_WATCHOS_ARM64_32").is_ok();
let swift_watchos_min =
std::env::var("PERRY_WATCHOS_MIN").unwrap_or_else(|_| "11.0".to_string());
let swift_triple_owned;
let swift_triple = if target == Some("watchos-simulator") {
"arm64-apple-watchos10.0-simulator"
} else if swift_arm64_32 {
swift_triple_owned = format!("arm64_32-apple-watchos{}", swift_watchos_min);
swift_triple_owned.as_str()
} else {
// Device builds are arm64-only (S9+ / watchOS 26): Perry's
// NaN-boxed values need 64-bit pointers, which arm64_32 lacks.
"arm64-apple-watchos26.0"
};
let swift_sysroot = String::from_utf8(
Expand Down
17 changes: 14 additions & 3 deletions crates/perry/src/commands/compile/link/platform_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,19 @@ pub fn select_linker_command(
)?
.trim()
.to_string();
// arm64_32 watchOS (Series 4-8 / SE): opt-in via PERRY_WATCHOS_ARM64_32.
// Lets the device target reach pre-S9 watches; deployment min defaults
// low (these watches run watchOS 9-11) but is overridable.
let arm64_32 = target == Some("watchos") && std::env::var("PERRY_WATCHOS_ARM64_32").is_ok();
let watchos_min = std::env::var("PERRY_WATCHOS_MIN").unwrap_or_else(|_| "11.0".to_string());
let triple_owned;
let triple = if target == Some("watchos-simulator") {
"arm64-apple-watchos10.0-simulator"
} else if arm64_32 {
triple_owned = format!("arm64_32-apple-watchos{}", watchos_min);
triple_owned.as_str()
} else {
// Device builds are arm64-only (S9+ / watchOS 26): Perry's
// NaN-boxed values need 64-bit pointers, which arm64_32 lacks.
// Device builds default to arm64-only (S9+ / watchOS 26).
"arm64-apple-watchos26.0"
};

Expand Down Expand Up @@ -108,7 +116,10 @@ pub fn select_linker_command(
.unwrap_or(false)
})
});
if let Some(entry_obj) = entry_obj {
// arm64_32: rust-objcopy crashes on these Mach-O objects, so the entry
// symbol was emitted directly by codegen (PERRY_ENTRY_SYMBOL) instead of
// renamed here. Skip the objcopy pass entirely.
if let Some(entry_obj) = entry_obj.filter(|_| !arm64_32) {
let objcopy = std::env::var("HOME").ok()
.map(|h| PathBuf::from(h).join(".rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/bin/rust-objcopy"))
.filter(|p| p.exists())
Expand Down