Skip to content
Closed
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
6 changes: 6 additions & 0 deletions changelog.d/8720-native-value-profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Added

- Stabilized the native value profile with checked exact-width scalars,
source-linked and nested POD layouts, and value-copy semantics across local
assignments and ordinary function boundaries. Invalid or imprecise native
crossings now fail explicitly instead of truncating or losing precision.
56 changes: 54 additions & 2 deletions crates/perry-api-manifest/src/native_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,20 +226,32 @@ pub enum NativeAbiType {
Json,
/// JavaScript truthiness lowered to a C `i32` boolean slot.
Bool,
/// Signed 8-bit integer slot.
I8,
/// Signed 16-bit integer slot.
I16,
/// Signed 32-bit integer slot.
I32,
/// Signed 64-bit integer slot.
I64,
/// Legacy string return where the native function returns the string
/// pointer as an `i64` instead of a C pointer.
I64String,
/// Unsigned 8-bit integer slot. The manifest spelling `byte` is accepted
/// as an alias and canonicalizes to `u8`.
U8,
/// Unsigned 16-bit integer slot.
U16,
/// Unsigned 32-bit integer slot.
U32,
/// Unsigned 64-bit integer slot.
U64,
/// Pointer-sized unsigned integer slot. Perry's native runtime targets are
/// currently 64-bit, so this lowers as an LLVM `i64`.
USize,
/// Pointer-sized signed integer slot. Perry's native runtime targets are
/// currently 64-bit, so this lowers as an LLVM `i64`.
ISize,
/// 32-bit float slot.
F32,
/// 64-bit float slot. The legacy manifest spelling `"number"` is accepted
Expand Down Expand Up @@ -280,12 +292,17 @@ impl NativeAbiType {
"string" => Ok(Self::String),
"json" => Ok(Self::Json),
"bool" | "boolean" => Ok(Self::Bool),
"i8" => Ok(Self::I8),
"i16" => Ok(Self::I16),
"i32" => Ok(Self::I32),
"i64" => Ok(Self::I64),
"i64_str" => Ok(Self::I64String),
"u8" | "byte" => Ok(Self::U8),
"u16" => Ok(Self::U16),
"u32" => Ok(Self::U32),
"u64" => Ok(Self::U64),
"usize" => Ok(Self::USize),
"isize" => Ok(Self::ISize),
"f32" => Ok(Self::F32),
"f64" | "number" => Ok(Self::F64),
"ptr" => Ok(Self::Ptr),
Expand Down Expand Up @@ -338,12 +355,17 @@ impl NativeAbiType {
Self::String => "string",
Self::Json => "json",
Self::Bool => "bool",
Self::I8 => "i8",
Self::I16 => "i16",
Self::I32 => "i32",
Self::I64 => "i64",
Self::I64String => "i64_str",
Self::U8 => "u8",
Self::U16 => "u16",
Self::U32 => "u32",
Self::U64 => "u64",
Self::USize => "usize",
Self::ISize => "isize",
Self::F32 => "f32",
Self::F64 => "f64",
Self::Ptr => "ptr",
Expand Down Expand Up @@ -419,11 +441,16 @@ impl NativeAbiType {
pub fn is_valid_pod_field(&self) -> bool {
matches!(
self,
Self::I32
Self::I8
| Self::I16
| Self::I32
| Self::I64
| Self::U8
| Self::U16
| Self::U32
| Self::U64
| Self::USize
| Self::ISize
| Self::F32
| Self::F64
| Self::BufferLen
Expand Down Expand Up @@ -464,11 +491,16 @@ impl NativeAbiType {
Self::Pod(_) => "object",
Self::PodAndCount(_) => "PerryPodView<any>",
Self::BufferAndLen => "Buffer",
Self::I32
Self::I8
| Self::I16
| Self::I32
| Self::I64
| Self::U8
| Self::U16
| Self::U32
| Self::U64
| Self::USize
| Self::ISize
| Self::F32
| Self::F64
| Self::BufferLen
Expand Down Expand Up @@ -576,4 +608,24 @@ mod tests {
// Not a scalar POD field.
assert!(!json.is_valid_pod_field());
}

#[test]
fn exact_width_scalar_spellings_are_canonical_and_pod_safe() {
for (spelling, expected, canonical) in [
("i8", NativeAbiType::I8, "i8"),
("i16", NativeAbiType::I16, "i16"),
("u8", NativeAbiType::U8, "u8"),
("byte", NativeAbiType::U8, "u8"),
("u16", NativeAbiType::U16, "u16"),
("isize", NativeAbiType::ISize, "isize"),
] {
let parsed = NativeAbiType::parse_str(spelling).expect("exact-width descriptor");
assert_eq!(parsed, expected);
assert_eq!(parsed.canonical_kind(), canonical);
assert!(parsed.is_valid_param());
assert!(parsed.is_valid_return());
assert!(parsed.is_valid_pod_field());
assert_eq!(parsed.js_type_name(), "number");
}
}
}
5 changes: 2 additions & 3 deletions crates/perry-codegen/src/expr/literals_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ use crate::lower_string_concat::{
lower_string_self_append_chain,
};
use crate::nanbox::double_literal;
use crate::native_value::MaterializationReason;
use crate::type_analysis::{is_map_expr, is_set_expr, receiver_class_name};
use crate::types::{DOUBLE, I32, I64};

use super::{
can_lower_expr_as_i32_in_current_region, emit_root_nanbox_store_on_block,
emit_shadow_slot_clear, emit_shadow_slot_update_for_expr, emit_write_barrier,
is_global_this_builtin_function_name, lower_expr, lower_expr_as_i32,
lower_pod_local_reassignment, materialize_pod_local, nanbox_string_inline, FnCtx,
lower_pod_local_reassignment, materialize_pod_value_copy, nanbox_string_inline, FnCtx,
TrustedBoxCapturePtr,
};

Expand Down Expand Up @@ -439,7 +438,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
// module-scope `let`s (the ones in `hir.init` at top level).
Expr::LocalGet(id) => {
if ctx.pod_records.contains_key(id) {
return materialize_pod_local(ctx, *id, MaterializationReason::PodMaterialization);
return materialize_pod_value_copy(ctx, *id);
}
// Captured by closure (from outer scope):
if let Some(&capture_idx) = ctx.closure_captures.get(id) {
Expand Down
19 changes: 13 additions & 6 deletions crates/perry-codegen/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ pub(crate) use nanbox_inline::{
pub(crate) use native_record::{array_kind_fact, effect_fact, raw_f64_layout_fact};
pub(crate) use object_literal::lower_object_literal;
pub(crate) use pod_record::{
lower_and_store_initial_pod_field, lower_pod_local_reassignment, materialize_pod_local,
try_lower_pod_field_get, try_lower_pod_field_set,
copy_pod_local, lower_and_store_initial_pod_field, lower_pod_local_reassignment,
materialize_pod_local, materialize_pod_value_copy, try_lower_pod_field_get,
try_lower_pod_field_set,
};
pub(crate) use proven_view_access::{
index_is_exact_i32_shape, local_is_proven_int_store_view,
Expand Down Expand Up @@ -2873,10 +2874,16 @@ fn native_number_to_f64(ctx: &mut FnCtx<'_>, lowered: &LoweredValue) -> Option<S
NativeRep::U32 | NativeRep::BufferLen => {
Some(ctx.block().uitofp(I32, &lowered.value, DOUBLE))
}
NativeRep::I64 | NativeRep::ISize => Some(ctx.block().sitofp(I64, &lowered.value, DOUBLE)),
NativeRep::U64 | NativeRep::USize | NativeRep::HandleId => {
Some(ctx.block().uitofp(I64, &lowered.value, DOUBLE))
}
NativeRep::I64 | NativeRep::ISize => Some(ctx.block().call(
DOUBLE,
"js_native_abi_materialize_i64",
&[(I64, &lowered.value)],
)),
NativeRep::U64 | NativeRep::USize | NativeRep::HandleId => Some(ctx.block().call(
DOUBLE,
"js_native_abi_materialize_u64",
&[(I64, &lowered.value)],
)),
_ => None,
}
}
Expand Down
Loading
Loading