Skip to content
Draft
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
4 changes: 4 additions & 0 deletions src/codegen/runtime/data/fixed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ pub(crate) fn emit_runtime_data_fixed(heap_size: usize, target: Target) -> Strin
out.push_str(".globl _diag_define_already_defined_msg\n_diag_define_already_defined_msg:\n .ascii \"Warning: define(): Constant already defined\\n\"\n");
out.push_str(".globl _diag_undefined_array_key_prefix\n_diag_undefined_array_key_prefix:\n .ascii \"Warning: Undefined array key \"\n");
out.push_str(".globl _diag_undefined_array_key_suffix\n_diag_undefined_array_key_suffix:\n .ascii \"\\n\"\n");
out.push_str(".globl _diag_string_offset_prefix\n_diag_string_offset_prefix:\n .ascii \"Warning: Uninitialized string offset \"\n");
out.push_str(".globl _diag_string_offset_nl\n_diag_string_offset_nl:\n .ascii \"\\n\"\n");
out.push_str(".globl _diag_float_key_prefix\n_diag_float_key_prefix:\n .ascii \"Deprecated: Implicit conversion from float \"\n");
out.push_str(".globl _diag_float_key_suffix\n_diag_float_key_suffix:\n .ascii \" to int loses precision\\n\"\n");
out.push_str(".globl _fiber_msg_already_started\n_fiber_msg_already_started:\n .ascii \"Cannot start a fiber that has already been started\"\n");
out.push_str(".globl _fiber_msg_not_suspended\n_fiber_msg_not_suspended:\n .ascii \"Cannot resume a fiber that is not suspended\"\n");
out.push_str(".globl _fiber_msg_throw_not_suspended\n_fiber_msg_throw_not_suspended:\n .ascii \"Cannot resume a fiber that is not suspended\"\n");
Expand Down
108 changes: 108 additions & 0 deletions src/codegen/runtime/diagnostics/float_to_int_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Purpose:
//! Emits the `__rt_warn_float_to_int_key` runtime helper for float-to-int array key conversion.
//! Formats the PHP "Implicit conversion from float … to int loses precision" deprecation.
//!
//! Called from:
//! - `crate::codegen::runtime::diagnostics::emit_float_to_int_key_deprecation()`.
//!
//! Key details:
//! - The helper is deprecation-only: callers still truncate the float to perform the lookup.
//! - `__rt_ftoa` uses `_concat_buf`, so `_concat_off` is restored before returning.

use crate::codegen::abi;
use crate::codegen::emit::Emitter;
use crate::codegen::platform::Arch;

const FLOAT_KEY_PREFIX_LEN: usize = "Deprecated: Implicit conversion from float ".len();
const FLOAT_KEY_SUFFIX_LEN: usize = " to int loses precision\n".len();

/// Emits `__rt_warn_float_to_int_key` for the active target.
///
/// # ABI
/// - ARM64: input float in `d0`.
/// - x86_64 Linux: input float in `xmm0`.
///
/// # Behavior
/// Writes `Deprecated: Implicit conversion from float <V> to int loses precision\n`
/// to stderr (via `__rt_diag_warning`) when `@` suppression is inactive, then returns.
pub fn emit_float_to_int_key_deprecation(emitter: &mut Emitter) {
if emitter.target.arch == Arch::X86_64 {
emit_float_to_int_key_deprecation_x86_64(emitter);
return;
}

emitter.blank();
emitter.comment("--- runtime: float_to_int_key_deprecation ---");
emitter.label_global("__rt_warn_float_to_int_key");

// -- set up stack frame --
emitter.instruction("sub sp, sp, #64"); // reserve saved float, concat cursor, and frame linkage
emitter.instruction("stp x29, x30, [sp, #48]"); // save frame pointer and return address
emitter.instruction("add x29, sp, #48"); // establish a stable runtime deprecation frame
emitter.instruction("str d0, [sp, #0]"); // save the float key across deprecation fragments
abi::emit_symbol_address(emitter, "x9", "_concat_off");
emitter.instruction("ldr x10, [x9]"); // snapshot concat scratch state before formatting the float
emitter.instruction("str x10, [sp, #8]"); // preserve the concat cursor across ftoa

// -- emit prefix --
abi::emit_symbol_address(emitter, "x1", "_diag_float_key_prefix");
emitter.instruction(&format!("mov x2, #{}", FLOAT_KEY_PREFIX_LEN)); // pass the float-key deprecation prefix length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the float-key deprecation prefix

// -- emit formatted float --
emitter.instruction("ldr d0, [sp, #0]"); // reload the float key for decimal formatting
abi::emit_call_label(emitter, "__rt_ftoa"); // format the float key into concat scratch
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the formatted float-key value
emitter.instruction("ldr x10, [sp, #8]"); // reload the pre-warning concat cursor
abi::emit_symbol_address(emitter, "x9", "_concat_off");
emitter.instruction("str x10, [x9]"); // restore concat scratch state for surrounding expressions

// -- emit suffix --
abi::emit_symbol_address(emitter, "x1", "_diag_float_key_suffix");
emitter.instruction(&format!("mov x2, #{}", FLOAT_KEY_SUFFIX_LEN)); // pass the float-key deprecation suffix length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the float-key deprecation suffix

// -- restore stack frame --
emitter.instruction("ldp x29, x30, [sp, #48]"); // restore frame pointer and return address
emitter.instruction("add sp, sp, #64"); // release the runtime deprecation frame
emitter.instruction("ret"); // return to the hash-key caller
}

/// Emits the x86_64 implementation of `__rt_warn_float_to_int_key`.
fn emit_float_to_int_key_deprecation_x86_64(emitter: &mut Emitter) {
emitter.blank();
emitter.comment("--- runtime: float_to_int_key_deprecation ---");
emitter.label_global("__rt_warn_float_to_int_key");

// -- set up stack frame --
emitter.instruction("push rbp"); // save the caller frame pointer
emitter.instruction("mov rbp, rsp"); // establish a stable runtime deprecation frame
emitter.instruction("sub rsp, 32"); // reserve saved float and concat cursor while keeping calls aligned
emitter.instruction("movsd QWORD PTR [rbp - 8], xmm0"); // save the float key across deprecation fragments
abi::emit_load_symbol_to_reg(emitter, "r10", "_concat_off", 0); // snapshot concat scratch state before formatting the float
emitter.instruction("mov QWORD PTR [rbp - 16], r10"); // preserve the concat cursor across ftoa

// -- emit prefix --
abi::emit_symbol_address(emitter, "rdi", "_diag_float_key_prefix");
emitter.instruction(&format!("mov esi, {}", FLOAT_KEY_PREFIX_LEN)); // pass the float-key deprecation prefix length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the float-key deprecation prefix

// -- emit formatted float --
emitter.instruction("movsd xmm0, QWORD PTR [rbp - 8]"); // reload the float key for decimal formatting
abi::emit_call_label(emitter, "__rt_ftoa"); // format the float key into concat scratch
emitter.instruction("mov rdi, rax"); // pass the formatted float-key pointer to the deprecation helper
emitter.instruction("mov rsi, rdx"); // pass the formatted float-key length to the deprecation helper
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the formatted float-key value
emitter.instruction("mov r10, QWORD PTR [rbp - 16]"); // reload the pre-warning concat cursor
abi::emit_store_reg_to_symbol(emitter, "r10", "_concat_off", 0); // restore concat scratch state for surrounding expressions

// -- emit suffix --
abi::emit_symbol_address(emitter, "rdi", "_diag_float_key_suffix");
emitter.instruction(&format!("mov esi, {}", FLOAT_KEY_SUFFIX_LEN)); // pass the float-key deprecation suffix length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the float-key deprecation suffix

// -- restore stack frame --
emitter.instruction("mov rsp, rbp"); // release the runtime deprecation frame
emitter.instruction("pop rbp"); // restore the caller frame pointer
emitter.instruction("ret"); // return to the hash-key caller
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
//! Key details:
//! - Suppression depth lives in _rt_diag_suppression and warning output must follow each target syscall ABI.

mod float_to_int_key;
mod string_offset;

use crate::codegen::emit::Emitter;
use crate::codegen::platform::Arch;
use crate::codegen::abi;
Expand Down Expand Up @@ -106,3 +109,22 @@ fn emit_diagnostics_linux_x86_64(emitter: &mut Emitter) {
emitter.label("__rt_diag_warning_done_linux_x86_64");
emitter.instruction("ret"); // return after either writing or suppressing the warning
}

/// Emits the `__rt_warn_string_offset` runtime helper for the active target.
///
/// Dispatches to the x86_64 variant when targeting Linux x86_64; otherwise
/// emits the ARM64 helper. The helper formats a PHP "Uninitialized string
/// offset" warning carrying the runtime offset value, honoring `@` suppression.
pub(crate) fn emit_string_offset_warning(emitter: &mut Emitter) {
string_offset::emit_string_offset_warning(emitter);
}

/// Emits the `__rt_warn_float_to_int_key` runtime helper for the active target.
///
/// Dispatches to the x86_64 variant when targeting Linux x86_64; otherwise
/// emits the ARM64 helper. The helper formats a PHP "Implicit conversion from
/// float … to int loses precision" deprecation carrying the runtime float
/// value, honoring `@` suppression.
pub(crate) fn emit_float_to_int_key_deprecation(emitter: &mut Emitter) {
float_to_int_key::emit_float_to_int_key_deprecation(emitter);
}
108 changes: 108 additions & 0 deletions src/codegen/runtime/diagnostics/string_offset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Purpose:
//! Emits the `__rt_warn_string_offset` runtime helper for out-of-bounds string offsets.
//! Formats the PHP "Uninitialized string offset N" warning carrying the runtime offset value.
//!
//! Called from:
//! - `crate::codegen::runtime::diagnostics::emit_string_offset_warning()`.
//!
//! Key details:
//! - The helper is warning-only: callers still materialize their own empty-string fallback.
//! - `__rt_itoa` uses `_concat_buf`, so `_concat_off` is restored before returning.

use crate::codegen::abi;
use crate::codegen::emit::Emitter;
use crate::codegen::platform::Arch;

const STRING_OFFSET_PREFIX_LEN: usize = "Warning: Uninitialized string offset ".len();
const STRING_OFFSET_NL_LEN: usize = "\n".len();

/// Emits `__rt_warn_string_offset` for the active target.
///
/// # ABI
/// - ARM64: input offset in `x0`.
/// - x86_64 Linux: input offset in `rax`.
///
/// # Behavior
/// Writes `Warning: Uninitialized string offset <N>\n` to stderr (via
/// `__rt_diag_warning`) when `@` suppression is inactive, then returns.
pub fn emit_string_offset_warning(emitter: &mut Emitter) {
if emitter.target.arch == Arch::X86_64 {
emit_string_offset_warning_x86_64(emitter);
return;
}

emitter.blank();
emitter.comment("--- runtime: string_offset_warning ---");
emitter.label_global("__rt_warn_string_offset");

// -- set up stack frame --
emitter.instruction("sub sp, sp, #48"); // reserve saved offset, concat cursor, and frame linkage
emitter.instruction("stp x29, x30, [sp, #32]"); // save frame pointer and return address
emitter.instruction("add x29, sp, #32"); // establish a stable runtime warning frame
emitter.instruction("str x0, [sp, #0]"); // save the out-of-bounds offset across warning fragments
abi::emit_symbol_address(emitter, "x9", "_concat_off");
emitter.instruction("ldr x10, [x9]"); // snapshot concat scratch state before formatting the offset
emitter.instruction("str x10, [sp, #8]"); // preserve the concat cursor across itoa

// -- emit prefix --
abi::emit_symbol_address(emitter, "x1", "_diag_string_offset_prefix");
emitter.instruction(&format!("mov x2, #{}", STRING_OFFSET_PREFIX_LEN)); // pass the string-offset warning prefix length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the string-offset warning prefix

// -- emit formatted offset --
emitter.instruction("ldr x0, [sp, #0]"); // reload the out-of-bounds offset for decimal formatting
abi::emit_call_label(emitter, "__rt_itoa"); // format the offset into concat scratch
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the formatted offset value
emitter.instruction("ldr x10, [sp, #8]"); // reload the pre-warning concat cursor
abi::emit_symbol_address(emitter, "x9", "_concat_off");
emitter.instruction("str x10, [x9]"); // restore concat scratch state for surrounding expressions

// -- emit newline suffix --
abi::emit_symbol_address(emitter, "x1", "_diag_string_offset_nl");
emitter.instruction(&format!("mov x2, #{}", STRING_OFFSET_NL_LEN)); // pass the string-offset warning newline length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the string-offset warning newline

// -- restore stack frame --
emitter.instruction("ldp x29, x30, [sp, #32]"); // restore frame pointer and return address
emitter.instruction("add sp, sp, #48"); // release the runtime warning frame
emitter.instruction("ret"); // return to the string-index caller
}

/// Emits the x86_64 implementation of `__rt_warn_string_offset`.
fn emit_string_offset_warning_x86_64(emitter: &mut Emitter) {
emitter.blank();
emitter.comment("--- runtime: string_offset_warning ---");
emitter.label_global("__rt_warn_string_offset");

// -- set up stack frame --
emitter.instruction("push rbp"); // save the caller frame pointer
emitter.instruction("mov rbp, rsp"); // establish a stable runtime warning frame
emitter.instruction("sub rsp, 32"); // reserve saved offset and concat cursor while keeping calls aligned
emitter.instruction("mov QWORD PTR [rbp - 8], rax"); // save the out-of-bounds offset across warning fragments
abi::emit_load_symbol_to_reg(emitter, "r10", "_concat_off", 0); // snapshot concat scratch state before formatting the offset
emitter.instruction("mov QWORD PTR [rbp - 16], r10"); // preserve the concat cursor across itoa

// -- emit prefix --
abi::emit_symbol_address(emitter, "rdi", "_diag_string_offset_prefix");
emitter.instruction(&format!("mov esi, {}", STRING_OFFSET_PREFIX_LEN)); // pass the string-offset warning prefix length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the string-offset warning prefix

// -- emit formatted offset --
emitter.instruction("mov rax, QWORD PTR [rbp - 8]"); // reload the out-of-bounds offset for decimal formatting
abi::emit_call_label(emitter, "__rt_itoa"); // format the offset into concat scratch
emitter.instruction("mov rdi, rax"); // pass the formatted offset pointer to the warning helper
emitter.instruction("mov rsi, rdx"); // pass the formatted offset length to the warning helper
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the formatted offset value
emitter.instruction("mov r10, QWORD PTR [rbp - 16]"); // reload the pre-warning concat cursor
abi::emit_store_reg_to_symbol(emitter, "r10", "_concat_off", 0); // restore concat scratch state for surrounding expressions

// -- emit newline suffix --
abi::emit_symbol_address(emitter, "rdi", "_diag_string_offset_nl");
emitter.instruction(&format!("mov esi, {}", STRING_OFFSET_NL_LEN)); // pass the string-offset warning newline length
abi::emit_call_label(emitter, "__rt_diag_warning"); // emit or suppress the string-offset warning newline

// -- restore stack frame --
emitter.instruction("mov rsp, rbp"); // release the runtime warning frame
emitter.instruction("pop rbp"); // restore the caller frame pointer
emitter.instruction("ret"); // return to the string-index caller
}
2 changes: 2 additions & 0 deletions src/codegen/runtime/emitters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ use crate::codegen::RuntimeFeatures;
/// are available when branches are assembled.
pub(crate) fn emit_runtime(emitter: &mut Emitter, features: RuntimeFeatures) {
diagnostics::emit_diagnostics(emitter);
diagnostics::emit_string_offset_warning(emitter);
diagnostics::emit_float_to_int_key_deprecation(emitter);

// String runtime functions
strings::emit_itoa(emitter);
Expand Down
Loading
Loading