rust: zerocopy: update to v0.8.62

Update our vendored copy of `zerocopy` (and `zerocopy-derive`) to v0.8.62.

This update advances 8 releases, with 3 of them carrying most of the
changes. It contains new features as well as fixes [1]. However, almost
nothing has changed that requires updates on our side: we only need to
move our non-ASCII change to the recently added `project.rs` file, and
to add an SPDX license identifier to the also new `invariant.rs` file
(I reported it to upstream [2]).

The following script may be used to check for the remaining differences:

    for path in $(cd rust/zerocopy-derive/ && find . -type f ! -name README.md); do
        curl --silent --show-error --location \
            https://github.com/google/zerocopy/raw/v0.8.62/zerocopy/zerocopy-derive/src/$path |
            git diff --no-index - rust/zerocopy-derive/$path &&
            echo $path: OK
    done

    for path in $(cd rust/zerocopy/ && find . -type f ! -name README.md); do
        curl --silent --show-error --location \
            https://github.com/google/zerocopy/raw/v0.8.62/zerocopy/$path |
            git diff --no-index - rust/zerocopy/$path &&
            echo $path: OK
    done

Comparing the output above with the one for the latest update (v0.8.54)
shows no new differences on our side apart from the two mentioned above.

Cc: Joshua Liebow-Feeser <joshlf@google.com>
Cc: Jack Wrenn <jswrenn@google.com>
Link: https://github.com/google/zerocopy/releases [1]
Link: https://github.com/google/zerocopy/issues/3858 [2]
Assisted-by: LLM
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/20261013184809.35143-2-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
Miguel Ojeda committed 2026-10-15 09:14:38 +02:00
1 parent d61be5c960
commit 7c306f0083
184 files changed
+12265 -3051

No files matched your search

+1 -1
View File
@@ -94,7 +94,7 @@ zerocopy-flags := \
$(call cfgs-to-flags,$(zerocopy-cfgs))
zerocopy-envs := \
CARGO_PKG_VERSION=0.8.54
CARGO_PKG_VERSION=0.8.62
proc_macro2-cfgs := \
feature="proc-macro" \
+6 -6
View File
@@ -1,14 +1,14 @@
# `zerocopy-derive`
These source files come from the Rust `zerocopy-derive` crate, version v0.8.54
(released 2026-07-08), hosted in the <https://github.com/google/zerocopy>
These source files come from the Rust `zerocopy-derive` crate, version v0.8.62
(released 2026-10-08), hosted in the <https://github.com/google/zerocopy>
repository, licensed under "BSD-2-Clause OR Apache-2.0 OR MIT" and only
modified to tweak the SPDX license identifiers and to remove the generation of
non-ASCII identifiers.
For copyright details, please see:
https://github.com/google/zerocopy/blob/v0.8.54/zerocopy/README.md?plain=1
https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-BSD
https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-APACHE
https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-MIT
https://github.com/google/zerocopy/blob/v0.8.62/zerocopy/README.md?plain=1
https://github.com/google/zerocopy/blob/v0.8.62/LICENSE-BSD
https://github.com/google/zerocopy/blob/v0.8.62/LICENSE-APACHE
https://github.com/google/zerocopy/blob/v0.8.62/LICENSE-MIT
+12
View File
@@ -89,6 +89,12 @@ pub(crate) fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
Err(has_unknown_discriminants)
}
pub(crate) fn derive_from_zeros(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
if let Some(span) = ctx.invariant_span {
return ctx.error_or_skip(Error::new(
span,
"cannot derive `FromZeros` for a type with invariants",
));
}
let try_from_bytes = derive_try_from_bytes(ctx, top_level)?;
let from_zeros = match &ctx.ast.data {
Data::Struct(strct) => derive_from_zeros_struct(ctx, strct),
@@ -98,6 +104,12 @@ pub(crate) fn derive_from_zeros(ctx: &Ctx, top_level: Trait) -> Result<TokenStre
Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
}
pub(crate) fn derive_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
if let Some(span) = ctx.invariant_span {
return ctx.error_or_skip(Error::new(
span,
"cannot derive `FromBytes` for a type with invariants",
));
}
let from_zeros = derive_from_zeros(ctx, top_level)?;
let from_bytes = match &ctx.ast.data {
Data::Struct(strct) => derive_from_bytes_struct(ctx, strct),
+139 -8
View File
@@ -2,7 +2,7 @@
//
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Data, DataEnum, DataStruct, DataUnion, Error, Type};
use syn::{parse_quote, Data, DataEnum, DataStruct, DataUnion, Error, Ident, Type, WherePredicate};
use crate::{
repr::{EnumRepr, StructUnionRepr},
@@ -18,6 +18,74 @@ pub(crate) fn derive_into_bytes(ctx: &Ctx, _top_level: Trait) -> Result<TokenStr
Data::Union(unn) => derive_into_bytes_union(ctx, unn),
}
}
/// If every field is exactly `T`, `[T; _]`, or a final `[T]` for the same type
/// parameter `T`, returns the bounds required to prove this to rustc.
fn homogeneous_field_bounds(ctx: &Ctx, strct: &DataStruct) -> Option<Vec<WherePredicate>> {
fn strip_parens_and_groups(mut ty: &Type) -> &Type {
loop {
ty = match ty {
Type::Group(group) => &group.elem,
Type::Paren(paren) => &paren.elem,
ty => return ty,
};
}
}
fn type_is_parameter(ty: &Type, parameter: &Ident) -> bool {
match strip_parens_and_groups(ty) {
Type::Path(path) => path.qself.is_none() && path.path.is_ident(parameter),
_ => false,
}
}
let fields = strct.fields();
ctx.ast.generics.type_params().find_map(|parameter| {
let parameter = &parameter.ident;
let elements = fields
.iter()
.enumerate()
.map(|(index, (_, _, ty))| match strip_parens_and_groups(ty) {
Type::Array(array) => Some(&*array.elem),
Type::Slice(slice) if index + 1 == fields.len() => Some(&*slice.elem),
Type::Slice(_) => None,
ty => Some(ty),
})
.collect::<Option<Vec<_>>>()?;
if !elements.iter().all(|element| type_is_parameter(element, parameter)) {
return None;
}
let zerocopy_crate = &ctx.zerocopy_crate;
let mut bounds = fields
.iter()
.map(|(_, _, ty)| parse_quote!(#ty: #zerocopy_crate::IntoBytes))
.collect::<Vec<WherePredicate>>();
// `Ident` equality is only a syntactic prefilter above. In
// macro-generated input, two identifiers can both print as `T` and
// compare equal here while having different hygiene contexts. For
// example, a call-site `T` could resolve to a `u8` type alias while a
// def-site `T` resolves to this struct's type parameter. Instantiating
// that parameter with `u16` would make the fields actually be `u8` and
// `u16`; treating them as homogeneous could overlook padding and cause
// us to emit an unsound `IntoBytes` impl.
//
// Quote every element type from its original field AST so that its
// hygiene context is retained, then use `Identity` to have rustc verify
// that it resolves to `parameter`. Do not deduplicate these predicates:
// identically-printed types may have different hygiene contexts and
// must each be checked by rustc.
bounds.extend(elements.into_iter().map(|element| {
parse_quote! {
#element: #zerocopy_crate::util::macro_util::Identity<Type = #parameter>
}
}));
Some(bounds)
})
}
fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream, Error> {
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
@@ -25,8 +93,12 @@ fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream
let is_c = repr.is_c();
let is_packed_1 = repr.is_packed_1();
let num_fields = strct.fields().len();
let mut homogeneous_bounds =
if is_c && !repr.is_align_gt_1() { homogeneous_field_bounds(ctx, strct) } else { None };
let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
let (padding_check, require_unaligned_fields, explicit_field_bounds) = if is_transparent
|| is_packed_1
{
// No padding check needed.
// - repr(transparent): The layout and ABI of the whole struct is the
// same as its only non-ZST field (meaning there's no padding outside
@@ -43,12 +115,12 @@ fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream
// An important consequence of these rules is that a type with
// `#[repr(packed(1))]`` (or `#[repr(packed)]``) will have no
// inter-field padding.
(None, false)
(None, false, None)
} else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
// No padding check needed. A repr(C) struct with zero or one field has
// no padding unless #[repr(align)] explicitly adds padding, which we
// check for in this branch's condition.
(None, false)
(None, false, None)
} else if ctx.ast.generics.params.is_empty() {
// Is the last field a syntactic slice, i.e., `[SomeType]`.
let is_syntactic_dst =
@@ -66,10 +138,31 @@ fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream
// 2. The fields do not overlap.
// ...
if is_c && is_syntactic_dst {
(Some(PaddingCheck::ReprCStruct), false)
(Some(PaddingCheck::ReprCStruct), false, None)
} else {
(Some(PaddingCheck::Struct), false)
(Some(PaddingCheck::Struct), false, None)
}
} else if let Some(bounds) = homogeneous_bounds.take() {
// Let `a` be the alignment of `T` and `s` be its size. Rust guarantees
// that `s` is a multiple of `a`. `T`, `[T; N]`, and `[T]` all have
// alignment `a`, and their sizes are `s`, `N * s`, and `len * s`,
// respectively.
//
// Without a `packed` modifier, each field and the struct have
// alignment `a`. `align(1)` does not change that because every Rust
// type already has alignment at least 1. With `packed(P)`, each field
// and the struct instead have alignment `b = min(a, P)`. Rust
// alignments and valid values of `P` are powers of two, so `b` divides
// `a`; every field size is therefore also a multiple of `b`.
//
// Thus, after placing any field, the running offset is already aligned
// for the next field, and the final offset is already aligned for the
// struct. A repr(C) without an `align` modifier greater than 1
// therefore introduces neither inter-field nor trailing padding,
// including for a slice DST. The bounds returned by
// `homogeneous_field_bounds` require rustc to verify that the element
// type of every field is the same type `T`.
(None, false, Some(bounds))
} else if is_c && !repr.is_align_gt_1() {
// We can't use a padding check since there are generic type arguments.
// Instead, we require all field types to implement `Unaligned`. This
@@ -79,7 +172,7 @@ fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream
//
// FIXME(#10): Support type parameters for non-transparent, non-packed
// structs without requiring `Unaligned`.
(None, true)
(None, true, None)
} else {
return ctx.error_or_skip(Error::new(
Span::call_site(),
@@ -87,7 +180,9 @@ fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream
));
};
let field_bounds = if require_unaligned_fields {
let field_bounds = if let Some(bounds) = explicit_field_bounds {
FieldBounds::Explicit(bounds)
} else if require_unaligned_fields {
FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
} else {
FieldBounds::ALL_SELF
@@ -162,3 +257,39 @@ please let us know you use this feature: https://github.com/google/zerocopy/disc
.build();
Ok(quote!(#cfg_compile_error #impl_block))
}
#[cfg(test)]
mod tests {
use super::*;
fn derive(input: syn::DeriveInput) -> TokenStream {
let ctx = Ctx::try_from_derive_input(input).unwrap();
derive_into_bytes(&ctx, Trait::IntoBytes).unwrap()
}
#[test]
fn raw_repr_matches_ordinary_repr() {
let ordinary = derive(parse_quote! {
#[repr(C)]
#[repr(align(8))]
struct Response {
status: u8,
}
});
let raw = derive(parse_quote! {
#[repr(C)]
#[r#repr(r#align(8))]
struct Response {
status: u8,
}
});
let ordinary = ordinary.to_string();
let raw = raw.to_string();
// In particular, both expansions contain the padding check which
// prevents the resulting type from implementing `IntoBytes`.
assert!(ordinary.contains("PaddingFree"));
assert_eq!(ordinary, raw);
}
}
+3 -1
View File
@@ -81,7 +81,7 @@ fn derive_known_layout_for_repr_c_struct<'a>(
#[inline(always)]
fn pointer_to_metadata(ptr: *mut Self) -> <Self as #zerocopy_crate::KnownLayout>::PointerMetadata {
<#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
<#trailing_field_ty as #zerocopy_crate::KnownLayout>::pointer_to_metadata(ptr as *mut _)
}
}
};
@@ -144,6 +144,7 @@ fn derive_known_layout_for_repr_c_struct<'a>(
// Define the collection of type-level field handles.
let field_defs = field_indices.iter().zip(fields).map(|(idx, (vis, _, _))| {
quote! {
#[allow(missing_debug_implementations)]
#vis struct #idx;
}
});
@@ -200,6 +201,7 @@ fn derive_known_layout_for_repr_c_struct<'a>(
// `#ty`, not `__ZerocopyKnownLayoutMaybeUninit` (see #2116).
#repr
#[doc(hidden)]
#[allow(missing_debug_implementations)]
#vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
#(#core::mem::MaybeUninit<
<#ident #ty_generics as
+21 -12
View File
@@ -3,6 +3,7 @@
pub mod from_bytes;
pub mod into_bytes;
pub mod known_layout;
pub mod project;
pub mod try_from_bytes;
pub mod unaligned;
@@ -102,13 +103,6 @@ pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStrea
}
};
if repr.get_packed().is_some() {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must not have #[repr(packed)] attribute",
));
}
if !(repr.is_c() || repr.is_transparent()) {
return ctx.error_or_skip(Error::new(
Span::call_site(),
@@ -124,13 +118,28 @@ pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStrea
};
let zerocopy_crate = &ctx.zerocopy_crate;
// SAFETY: `#ty`, per the above checks, is `repr(C)` or `repr(transparent)`
// and is not packed; its trailing field is guaranteed to be well-aligned
// for its type. By invariant on `FieldBounds::TRAILING_SELF`, the trailing
// slice of the trailing field is also well-aligned for its type.
let elem = quote!(<#trailing_field as #zerocopy_crate::SplitAt>::Elem);
let elem =
if repr.get_packed().is_some() { quote!(#zerocopy_crate::Unalign<#elem>) } else { elem };
// SAFETY: The checks above require `repr(C)` or `repr(transparent)`, and
// `FieldBounds::TRAILING_SELF` requires the trailing field to be `SplitAt`.
// Its `Elem` therefore has the size, bit validity, and `UnsafeCell`
// coverage of the actual trailing element. For an unpacked struct, the
// trailing field is aligned, so its `SplitAt` alignment guarantee applies.
// For a packed struct, wrapping its `Elem` in `Unalign` preserves those
// representation properties and gives alignment 1. Thus the trailing
// slice can be accessed as `[#elem]` even if packing misaligns its field.
// Packing does not change the field's internal layout [1], so its element
// size and byte offsets are preserved in either case.
//
// [1] Per https://doc.rust-lang.org/1.93.0/reference/type-layout.html#layout.repr.alignment.intro:
//
// `packed` may also alter the padding between fields (although it will
// not alter the padding inside of any field).
Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF)
.inner_extras(quote! {
type Elem = <#trailing_field as #zerocopy_crate::SplitAt>::Elem;
type Elem = #elem;
})
.build())
}
+569
View File
@@ -0,0 +1,569 @@
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
//
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
parse_quote, spanned::Spanned as _, Data, DataEnum, DeriveInput, Error, Expr, Fields, Ident,
Index, Type,
};
use crate::{
repr::{EnumRepr, StructUnionRepr},
util::{
const_block, generate_tag_enum, Client, Ctx, DataExt, FieldBounds, FieldProjection,
ImplBlockBuilder, Trait,
},
};
pub(crate) fn tag_ident(variant: &Ident) -> Ident {
ident!(("___ZEROCOPY_TAG_{}", variant), variant.span())
}
pub(crate) fn variant_struct_ident(variant: &Ident) -> Ident {
ident!(("___ZerocopyVariantStruct_{}", variant), variant.span())
}
pub(crate) fn variants_union_field_ident(variant: &Ident) -> Ident {
ident!(("__field_{}", variant), variant.span())
}
/// Generates a constant for the tag associated with each variant of the enum.
/// When we match on the enum's tag, each arm matches one of these constants. We
/// have to use constants here because:
///
/// - The type that we're matching on is not the type of the tag, it's an
/// integer of the same size as the tag type and with the same bit patterns.
/// - We can't read the enum tag as an enum because the bytes may not represent
/// a valid variant.
/// - Patterns do not currently support const expressions, so we have to assign
/// these constants to names rather than use them inline in the `match`
/// statement.
pub(crate) fn generate_tag_consts(data: &DataEnum) -> TokenStream {
let tags = data.variants.iter().map(|variant| {
let variant_ident = &variant.ident;
let tag = tag_ident(variant_ident);
quote! {
// This casts the enum variant to its discriminant, and then
// converts the discriminant to the target integral type via a
// numeric cast [1].
//
// Because these are the same size, this is defined to be a no-op
// and therefore is a lossless conversion [2].
//
// [1] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#enum-cast:
//
// Casts an enum to its discriminant.
//
// [2] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#numeric-cast:
//
// Casting between two integers of the same size (e.g. i32 -> u32)
// is a no-op.
const #tag: ___ZerocopyTagPrimitive =
___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive;
}
});
quote! {
#(#tags)*
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum Validity {
Uninit,
Initialized,
Safe,
}
impl Validity {
fn as_type(self, ctx: &Ctx) -> Type {
let zerocopy_crate = &ctx.zerocopy_crate;
match self {
Validity::Uninit => parse_quote!(#zerocopy_crate::invariant::Uninit),
Validity::Initialized => {
parse_quote!(#zerocopy_crate::invariant::Initialized)
}
Validity::Safe => parse_quote!(#zerocopy_crate::invariant::Safe),
}
}
/// Computes the output validity of projecting a struct or union field, or
/// returns `None` if that projection must not be generated.
///
/// Struct fields preserve all three validity invariants. Union fields
/// preserve `Uninit` and `Initialized`. No projection is generated from a
/// `Safe` union.
fn output_validity_for_struct_or_union(self, data: &Data) -> Option<Self> {
match (data, self) {
(Data::Struct(_), validity) => Some(validity),
(Data::Union(_), validity @ (Validity::Uninit | Validity::Initialized)) => {
Some(validity)
}
(Data::Union(_), Validity::Safe) => None,
(Data::Enum(_), _) => unreachable!(),
}
}
}
/// Returns the projection variant ID for the struct or union described by
/// `ctx`.
///
/// This intentionally does not handle enums: an enum field's variant ID
/// identifies its source variant rather than the representation of its
/// containing type.
pub(crate) fn struct_union_variant_id(ctx: &Ctx) -> Box<Expr> {
let zerocopy_crate = &ctx.zerocopy_crate;
match &ctx.ast.data {
Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }),
Data::Union(_) => {
if StructUnionRepr::from_attrs(&ctx.ast.attrs).map(|repr| repr.is_c()).unwrap() {
parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID })
} else {
parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID })
}
}
Data::Enum(_) => unreachable!("struct_union_variant_id called for enum"),
}
}
/// Generates field projection implementations for a struct or union.
///
/// `repr(C)` unions use `REPR_C_UNION_VARIANT_ID`, so their projections
/// additionally implement `pointer::cast::Cast`. Other unions use
/// `UNION_VARIANT_ID`.
pub(crate) fn derive_projection_struct_union(
ctx: &Ctx,
data: &dyn DataExt,
client: Client,
) -> TokenStream {
let fields = ctx.ast.data.fields();
if fields.is_empty() {
return quote! {};
}
let field_tokens = fields.iter().map(|(vis, ident, _)| {
let ident = ident!(("__z{}", ident), ident.span());
quote! {
#vis enum #ident {}
}
});
let zerocopy_crate = &ctx.zerocopy_crate;
let variant_id = struct_union_variant_id(ctx);
let core = ctx.core_path();
let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag { client }, FieldBounds::None)
.inner_extras(quote! {
type Tag = ();
type ProjectToTag = #zerocopy_crate::pointer::cast::CastToUnit;
})
.build();
let has_fields = fields.iter().map(move |(_, ident, ty)| {
let field_token = ident!(("__z{}", ident), ident.span());
let projection = FieldProjection {
variant_id: variant_id.clone(),
field: parse_quote!(#field_token),
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
};
let has_field_trait = Trait::HasField { client, projection: projection.clone() };
let has_field_path = has_field_trait.crate_path(ctx);
let has_field = ImplBlockBuilder::new(ctx, data, has_field_trait, FieldBounds::None)
.inner_extras(quote! {
type Type = #ty;
#[inline(always)]
fn project(
slf: #zerocopy_crate::pointer::PtrInner<'_, Self>,
) -> *mut <Self as #has_field_path>::Type {
let slf = slf.as_ptr();
// SAFETY: By invariant on `PtrInner`, `slf` is a non-null
// pointer whose referent is zero-sized or lives in a valid
// allocation. Since `#ident` is a struct or union field of
// `Self`, this projection preserves or shrinks the referent
// size, and so the resulting referent also fits in the same
// allocation.
unsafe { #core::ptr::addr_of_mut!((*slf).#ident) }
}
})
.build();
let derive_project_field = |input_validity: Validity| {
let output_validity =
match input_validity.output_validity_for_struct_or_union(&ctx.ast.data) {
Some(output_validity) => output_validity.as_type(ctx),
None => return TokenStream::new(),
};
let input_validity = input_validity.as_type(ctx);
let projection_preserves_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs)
.map(|repr| repr.get_packed().is_none())
.unwrap();
let output_alignment = if projection_preserves_alignment {
quote! { ___ZcAlignment }
} else {
quote! { #zerocopy_crate::invariant::Unaligned }
};
ImplBlockBuilder::new(
ctx,
data,
Trait::ProjectField {
client,
projection: projection.clone(),
invariants: parse_quote!((___ZcAliasing, ___ZcAlignment, #input_validity)),
},
FieldBounds::None,
)
.param_extras(vec![
parse_quote!(___ZcAliasing: #zerocopy_crate::invariant::Aliasing),
parse_quote!(___ZcAlignment: #zerocopy_crate::invariant::Alignment),
])
.inner_extras(quote! {
// SAFETY: Struct and union projections do not depend on the value of
// the referent, and are therefore infallible.
type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible;
// SAFETY: Projection preserves aliasing. It also preserves
// alignment unless the containing type is packed. `Validity`
// determines whether a projection is emitted and, if so,
// computes its output validity: struct fields preserve validity;
// union fields preserve `Uninit` and `Initialized`, and no
// projection is emitted for `Safe` union input.
type Invariants = (___ZcAliasing, #output_alignment, #output_validity);
})
.build()
};
let project_uninit = derive_project_field(Validity::Uninit);
let project_initialized = derive_project_field(Validity::Initialized);
let project_safe = derive_project_field(Validity::Safe);
quote! {
#has_field
#project_uninit
#project_initialized
#project_safe
}
});
const_block(field_tokens.into_iter().chain(Some(has_tag)).chain(has_fields).map(Some))
}
pub(crate) fn derive_enum(
ctx: &Ctx,
data: &DataEnum,
client: Client,
) -> Result<TokenStream, Error> {
// With no fields, there are no `HasField` or `ProjectField` impls to emit,
// and therefore no representation requirement to enforce.
if data.fields().is_empty() {
return Ok(TokenStream::new());
}
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
let (outer_tag_type, inner_tag_type) = if repr.is_c() {
(quote! { ___ZerocopyTag }, quote! { () })
} else if repr.is_primitive() {
(quote! { () }, quote! { ___ZerocopyTag })
} else {
return Err(Error::new(
ctx.ast.span(),
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
));
};
let zerocopy_crate = &ctx.zerocopy_crate;
let core = &ctx.core_path();
let client_path = &client.crate_path(ctx);
let has_tag_trait = Trait::HasTag { client };
let has_tag_path = &has_tag_trait.crate_path(ctx);
let infallible =
quote! { #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible };
let tag_enum = generate_tag_enum(ctx, &repr, data);
let tag_consts = generate_tag_consts(data);
let generics = &ctx.ast.generics;
let (impl_generics, ref ty_generics, where_clause) = generics.split_for_impl();
// Synthesize a `repr(C)` struct for each fieldful variant. We must do this
// because Rust does not provide an `addr_of!` that works on enums.
let variant_structs = data.variants.iter().filter_map(move |variant| {
if matches!(variant.fields, Fields::Unit) {
return None;
}
let ident = variant_struct_ident(&variant.ident);
let field_types = variant.fields.iter().map(|field| &field.ty);
let enum_name = &ctx.ast.ident;
let variant_struct: DeriveInput = parse_quote! {
#[repr(C)]
struct #ident #impl_generics (
#core::mem::MaybeUninit<___ZerocopyInnerTag>,
#(#field_types,)*
#core::marker::PhantomData<#enum_name #ty_generics>,
) #where_clause;
};
let projections = derive_projection_struct_union(
&ctx.with_input(&variant_struct),
&variant_struct.data,
client,
);
Some(quote! {
#variant_struct
#projections
})
});
// Synthesize a `repr(C)` union encompassing all synthesized variants.
let variants_union = {
let fields = data.variants.iter().filter_map(|variant| {
if matches!(variant.fields, Fields::Unit) {
return None;
}
let field_name = variants_union_field_ident(&variant.ident);
let variant_struct = variant_struct_ident(&variant.ident);
Some(quote! {
#field_name: #core::mem::ManuallyDrop<#variant_struct #ty_generics>,
})
});
let variants_union: DeriveInput = parse_quote! {
#[repr(C)]
union ___ZerocopyVariants #generics {
#(#fields)*
// A fieldless enum produces no variant structs, but a union must
// have at least one field. This unit does not affect `repr(C)`
// layout.
__nonempty: (),
}
};
let projections = derive_projection_struct_union(
&ctx.with_input(&variants_union),
&variants_union.data,
client,
);
quote! {
#variants_union
#projections
}
};
// Synthesize a `repr(C)` struct that pairs the the tag with the synthesized
// variants union. In practice, the actual tag storage may be here, or
// inside the synthesized variant structs, depending on the `repr` of the
// enum.
let raw_enum: DeriveInput = parse_quote! {
#[repr(C)]
struct ___ZerocopyRawEnum #generics {
tag: ___ZerocopyOuterTag,
variants: ___ZerocopyVariants #ty_generics,
}
};
let raw_projections =
derive_projection_struct_union(&ctx.with_input(&raw_enum), &raw_enum.data, client);
let has_tag = ImplBlockBuilder::new(ctx, data, has_tag_trait, FieldBounds::None)
.inner_extras(quote! {
type Tag = ___ZerocopyTag;
type ProjectToTag = #zerocopy_crate::pointer::cast::CastSized;
})
.build();
let aliasing_param: &syn::GenericParam =
&parse_quote!(___ZcAliasing: #zerocopy_crate::invariant::Aliasing);
let reference_param: &syn::GenericParam =
&parse_quote!(___ZcAliasing: #zerocopy_crate::invariant::Reference);
let alignment_param: &syn::GenericParam =
&parse_quote!(___ZcAlignment: #zerocopy_crate::invariant::Alignment);
let has_fields = data.variants().into_iter().flat_map(|(variant, fields)| {
let variant_ident = &variant.unwrap().ident;
let variants_union_field = variants_union_field_ident(variant_ident);
let infallible = &infallible;
fields.into_iter().enumerate().map(move |(idx, (vis, ident, ty))| {
// Rust does not presently support explicit visibility modifiers on
// enum fields. Keep this assertion so that a future language
// change cannot silently invalidate the visibility invariant.
assert!(matches!(vis, syn::Visibility::Inherited));
let projection = FieldProjection {
variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }),
field: parse_quote!(()),
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
};
// The generated variant struct's first field is its inner tag.
let variant_struct_field_index = Index::from(idx + 1);
let has_field_trait = Trait::HasField {
client,
projection: projection.clone(),
};
let has_field_path = has_field_trait.crate_path(ctx);
let has_field = ImplBlockBuilder::new(ctx, data, has_field_trait, FieldBounds::None)
.inner_extras(quote! {
type Type = #ty;
#[inline(always)]
fn project(
slf: #zerocopy_crate::pointer::PtrInner<'_, Self>,
) -> *mut <Self as #has_field_path>::Type {
use #zerocopy_crate::pointer::cast::{CastSized, Projection};
slf.project::<___ZerocopyRawEnum #ty_generics, CastSized>()
.project::<_, Projection<#client_path, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>>()
.project::<_, Projection<#client_path, _, { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field) }>>()
.project::<_, Projection<#client_path, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(value) }>>()
.project::<_, Projection<#client_path, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>>()
.as_ptr()
}
})
.build();
// SAFETY: `projection` is constructed from the current field and
// `variant_ident`, and that same `variant_ident` selects the
// variants union field through which `has_field` projects. Thus the
// `HasField` impl, `ProjectField` impls, and valid projection's tag
// check all identify the same enum field. `idx` is the current
// field's zero-based position, and the generated variant struct
// prepends its inner tag before reproducing the enum fields in
// source order, so `idx + 1` selects that same field. The assertion
// above enforces that `repr` is `repr(C)` or a primitive
// representation; `___ZerocopyRawEnum` and its nested types model
// exactly the representations specified by [1] and [2]. Those
// representations use `repr(C)` structs for variant fields, so field
// projection preserves alignment. Each `ProjectField` impl preserves
// the input validity. The `Uninit` and `Initialized` projections are
// infallible because those validity invariants do not depend on the
// enum's tag. The `Safe` projection first checks that the tag
// selects `variant_ident`; its `Reference` aliasing bound prevents
// mutation of the tag between that check and field projection. The
// corresponding `HasTag` impl projects to the raw representation's
// tag using `CastSized`.
//
// [1] Per https://doc.rust-lang.org/1.56.0/reference/type-layout.html#reprc-enums-with-fields:
//
// The representation of a `repr(C)` enum with fields is a
// `repr(C)` struct with two fields [...]: a `repr(C)` version of
// the enum with all fields removed ("the tag") [and] a `repr(C)`
// union of `repr(C)` structs for the fields of each variant [...].
//
// [2] Per https://doc.rust-lang.org/1.56.0/reference/type-layout.html#primitive-representation-of-enums-with-fields:
//
// The representation of a primitive representation enum is a
// `repr(C)` union of `repr(C)` structs for each variant with a
// field. The first field of each struct in the union is [...] the
// tag and the remaining fields are the fields of that variant.
let derive_enum_project_field = |validity| {
let (error, is_projectable, aliasing_bound) = if validity == Validity::Safe {
assert!(!matches!(validity, Validity::Uninit | Validity::Initialized));
(
quote! { () },
quote! {
#[inline(always)]
fn is_projectable(
tag: #zerocopy_crate::pointer::Ptr<
'_,
<Self as #has_tag_path>::Tag,
(
#zerocopy_crate::invariant::Shared,
___ZcAlignment,
#zerocopy_crate::invariant::Safe,
),
>,
) -> #core::result::Result<(), ()> {
let tag = tag.read::<#zerocopy_crate::BecauseImmutable>();
if tag == ___ZerocopyTag::#variant_ident {
#core::result::Result::Ok(())
} else {
#core::result::Result::Err(())
}
}
},
reference_param.clone(),
)
} else {
(infallible.clone(), quote! {}, aliasing_param.clone())
};
let validity = validity.as_type(ctx);
let invariants: Box<Type> =
parse_quote!((___ZcAliasing, ___ZcAlignment, #validity));
let project_field_trait = Trait::ProjectField {
client,
projection: projection.clone(),
invariants: invariants.clone(),
};
ImplBlockBuilder::new(ctx, data, project_field_trait, FieldBounds::None)
.param_extras(vec![aliasing_bound.clone(), alignment_param.clone()])
.inner_extras(quote! {
type Error = #error;
type Invariants = #invariants;
#is_projectable
})
.build()
};
let project_uninit = derive_enum_project_field(Validity::Uninit);
let project_initialized = derive_enum_project_field(Validity::Initialized);
let project_valid = derive_enum_project_field(Validity::Safe);
quote! {
#has_field
#project_uninit
#project_initialized
#project_valid
}
})
});
Ok(quote! {
#tag_enum
type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag<
{ #core::mem::size_of::<___ZerocopyTag>() },
>;
#tag_consts
type ___ZerocopyOuterTag = #outer_tag_type;
type ___ZerocopyInnerTag = #inner_tag_type;
#(#variant_structs)*
#variants_union
#raw_enum
#raw_projections
#has_tag
#(#has_fields)*
})
}
pub(crate) fn derive(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
match &ctx.ast.data {
Data::Struct(strct) => {
Ok(derive_projection_struct_union(ctx, strct, Client::ProjectDerive))
}
Data::Union(unn) => Ok(derive_projection_struct_union(ctx, unn, Client::ProjectDerive)),
Data::Enum(enm) => {
let projections = derive_enum(ctx, enm, Client::ProjectDerive)?;
if projections.is_empty() {
Ok(projections)
} else {
Ok(const_block([Some(projections)]))
}
}
}
}
+285 -596
View File
@@ -1,383 +1,144 @@
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
//
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error,
Expr, Fields, Ident, Index, Type,
};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens as _};
use syn::{spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, Error, Field};
use crate::{
repr::{EnumRepr, StructUnionRepr},
derive::project::{
derive_enum, derive_projection_struct_union, generate_tag_consts, struct_union_variant_id,
tag_ident,
},
repr::EnumRepr,
util::{
const_block, enum_size_from_repr, generate_tag_enum, Ctx, DataExt, FieldBounds,
enum_size_from_repr, generate_tag_enum, Client, Ctx, DataExt, FieldBounds,
ImplBlockBuilder, Trait, TraitBound,
},
};
fn tag_ident(variant_ident: &Ident) -> Ident {
ident!(("___ZEROCOPY_TAG_{}", variant_ident), variant_ident.span())
}
/// Generates a constant for the tag associated with each variant of the enum.
/// When we match on the enum's tag, each arm matches one of these constants. We
/// have to use constants here because:
///
/// - The type that we're matching on is not the type of the tag, it's an
/// integer of the same size as the tag type and with the same bit patterns.
/// - We can't read the enum tag as an enum because the bytes may not represent
/// a valid variant.
/// - Patterns do not currently support const expressions, so we have to assign
/// these constants to names rather than use them inline in the `match`
/// statement.
fn generate_tag_consts(data: &DataEnum) -> TokenStream {
let tags = data.variants.iter().map(|v| {
let variant_ident = &v.ident;
let tag_ident = tag_ident(variant_ident);
quote! {
// This casts the enum variant to its discriminant, and then
// converts the discriminant to the target integral type via a
// numeric cast [1].
//
// Because these are the same size, this is defined to be a no-op
// and therefore is a lossless conversion [2].
//
// [1] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#enum-cast:
//
// Casts an enum to its discriminant.
//
// [2] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#numeric-cast:
//
// Casting between two integers of the same size (e.g. i32 -> u32)
// is a no-op.
const #tag_ident: ___ZerocopyTagPrimitive =
___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive;
}
});
quote! {
#(#tags)*
fn candidate_ident(ctx: &Ctx) -> Ident {
// Choose a source-pointer name distinct from the field names. Mixed-site
// hygiene keeps it out of the user's invariant expressions.
let fields = ctx.ast.data.fields();
let mut name = "candidate".to_owned();
while fields.iter().any(|(_, field, _)| field.to_string().trim_start_matches("r#") == name) {
name.push('_');
}
Ident::new(&name, Span::mixed_site())
}
fn variant_struct_ident(variant_ident: &Ident) -> Ident {
ident!(("___ZerocopyVariantStruct_{}", variant_ident), variant_ident.span())
}
/// Generates validation of every field of a struct or enum variant, leaving
/// the validated pointers in scope for subsequent field invariants.
/// A union is checked by invoking this once per field.
fn derive_variant_is_safe<'a>(
ctx: &Ctx,
variant_id: &TokenStream,
fields: impl IntoIterator<Item = &'a Field>,
) -> Result<TokenStream, Error> {
let fields = fields.into_iter().collect::<Vec<_>>();
if fields.is_empty() {
return Ok(quote!(true));
}
/// Generates variant structs for the given enum variant.
///
/// These are structs associated with each variant of an enum. They are
/// `repr(C)` tuple structs with the same fields as the variant after a
/// `MaybeUninit<___ZerocopyInnerTag>`.
///
/// In order to unify the generated types for `repr(C)` and `repr(int)` enums,
/// we use a "fused" representation with fields for both an inner tag and an
/// outer tag. Depending on the repr, we will set one of these tags to the tag
/// type and the other to `()`. This lets us generate the same code but put the
/// tags in different locations.
fn generate_variant_structs(ctx: &Ctx, data: &DataEnum) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let enum_name = &ctx.ast.ident;
// All variant structs have a `PhantomData<MyEnum<...>>` field because we
// don't know which generic parameters each variant will use, and unused
// generic parameters are a compile error.
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
let phantom_ty = quote! {
#core::marker::PhantomData<#enum_name #ty_generics>
};
let variant_structs = data.variants.iter().filter_map(|variant| {
// We don't generate variant structs for unit variants because we only
// need to check the tag. This helps cut down our generated code a bit.
if matches!(variant.fields, Fields::Unit) {
return None;
}
let variant_struct_ident = variant_struct_ident(&variant.ident);
let field_types = variant.fields.iter().map(|f| &f.ty);
let variant_struct = parse_quote! {
#[repr(C)]
struct #variant_struct_ident #impl_generics (
#core::mem::MaybeUninit<___ZerocopyInnerTag>,
#(#field_types,)*
#phantom_ty,
) #where_clause;
};
// We do this rather than emitting `#[derive(::zerocopy::TryFromBytes)]`
// because that is not hygienic, and this is also more performant.
let try_from_bytes_impl =
derive_try_from_bytes(&ctx.with_input(&variant_struct), Trait::TryFromBytes)
.expect("derive_try_from_bytes should not fail on synthesized type");
Some(quote! {
#variant_struct
#try_from_bytes_impl
})
let candidate = candidate_ident(ctx);
let field_names = fields.iter().enumerate().map(|(idx, field)| {
field
.ident
.as_ref()
.map(|name| name.to_token_stream())
.unwrap_or_else(|| syn::Index::from(idx).to_token_stream())
});
quote! {
#(#variant_structs)*
}
}
fn variants_union_field_ident(ident: &Ident) -> Ident {
// Field names are prefixed with `__field_` to prevent name collision
// with the `__nonempty` field.
ident!(("__field_{}", ident), ident.span())
}
fn generate_variants_union(ctx: &Ctx, data: &DataEnum) -> TokenStream {
let generics = &ctx.ast.generics;
let (_, ty_generics, _) = generics.split_for_impl();
let fields = data.variants.iter().filter_map(|variant| {
// We don't generate variant structs for unit variants because we only
// need to check the tag. This helps cut down our generated code a bit.
if matches!(variant.fields, Fields::Unit) {
return None;
}
let field_name = variants_union_field_ident(&variant.ident);
let variant_struct_ident = variant_struct_ident(&variant.ident);
let core = ctx.core_path();
Some(quote! {
#field_name: #core::mem::ManuallyDrop<#variant_struct_ident #ty_generics>,
let field_checks = field_names
.map(|name| {
quote! {
#zerocopy_crate::into_inner!(
#candidate.project::<
#zerocopy_crate::project_clients::TryFromBytesDerive,
_,
{ #variant_id },
{ #zerocopy_crate::ident_id!(#name) },
>()
).try_into_safe::<_, #zerocopy_crate::BecauseImmutable>()
}
})
});
let variants_union = parse_quote! {
#[repr(C)]
union ___ZerocopyVariants #generics {
#(#fields)*
// Enums can have variants with no fields, but unions must
// have at least one field. So we just add a trailing unit
// to ensure that this union always has at least one field.
// Because this union is `repr(C)`, this unit type does not
// affect the layout.
__nonempty: (),
}
};
let has_field =
derive_has_field_struct_union(&ctx.with_input(&variants_union), &variants_union.data);
quote! {
#variants_union
#has_field
.collect::<Vec<_>>();
// Without invariants, no expression needs the validated pointers. Avoid
// introducing bindings that could collide with caller-defined items.
if ctx.invariant_span.is_none() {
return Ok(quote! { true #(&& #field_checks.is_ok())* });
}
let field_bindings = fields
.iter()
.enumerate()
.map(|(idx, _)| ctx.fresh_ident(&format!("field_{}", idx)))
.collect::<Vec<_>>();
let bound_fields =
fields.iter().copied().zip(field_bindings.iter().cloned()).collect::<Vec<_>>();
let field_validations = fields
.iter()
.enumerate()
.map(|(idx, field)| {
let invariants = crate::invariant::parse(&field.attrs)?;
let invariants = invariants
.iter()
.map(|expression| crate::invariant::bind_fields(&bound_fields[..=idx], expression));
Ok(quote! {
#(
// Keep `return` in an invariant from bypassing later fields.
if !{ #[inline(always)] || -> #core::primitive::bool { #invariants } }() {
return false;
}
)*
})
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(quote! {
{
#(
// Retain each shared pointer for subsequent field invariants.
#[allow(unused_variables)]
let #field_bindings = match #field_checks {
#core::result::Result::Ok(#field_bindings) => #field_bindings,
#core::result::Result::Err(_) => return false,
};
#field_validations
)*
true
}
})
}
/// Generates an implementation of `is_bit_valid` for an arbitrary enum.
/// Generates an implementation of `is_safe` for an arbitrary enum.
///
/// The general process is:
///
/// 1. Generate a tag enum. This is an enum with the same repr, variants, and
/// corresponding discriminants as the original enum, but without any fields
/// on the variants. This gives us access to an enum where the variants have
/// the same discriminants as the one we're writing `is_bit_valid` for.
/// 2. Make constants from the variants of the tag enum. We need these because
/// we can't put const exprs in match arms.
/// 3. Generate variant structs. These are structs which have the same fields as
/// each variant of the enum, and are `#[repr(C)]` with an optional "inner
/// tag".
/// 4. Generate a variants union, with one field for each variant struct type.
/// 5. And finally, our raw enum is a `#[repr(C)]` struct of an "outer tag" and
/// the variants union.
///
/// See these reference links for fully-worked example decompositions.
///
/// - `repr(C)`: <https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields>
/// - `repr(int)`: <https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields>
/// - `repr(C, int)`: <https://doc.rust-lang.org/reference/type-layout.html#combining-primitive-representations-of-enums-with-fields-and-reprc>
pub(crate) fn derive_is_bit_valid(
/// For an enum with fields, [`derive_enum`] generates the representation model
/// and projection impls. This function reads the tag, matches it against the
/// enum's discriminants, and validates each field of the selected variant
/// through those projections. A fieldless enum needs only the generated tag
/// enum and discriminant constants.
pub(crate) fn derive_is_safe(
ctx: &Ctx,
data: &DataEnum,
repr: &EnumRepr,
) -> Result<TokenStream, Error> {
let trait_path = Trait::TryFromBytes.crate_path(ctx);
let tag_enum = generate_tag_enum(ctx, repr, data);
let tag_consts = generate_tag_consts(data);
let (outer_tag_type, inner_tag_type) = if repr.is_c() {
(quote! { ___ZerocopyTag }, quote! { () })
} else if repr.is_primitive() {
(quote! { () }, quote! { ___ZerocopyTag })
} else {
if !(repr.is_c() || repr.is_primitive()) {
return Err(Error::new(
ctx.ast.span(),
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
));
};
let variant_structs = generate_variant_structs(ctx, data);
let variants_union = generate_variants_union(ctx, data);
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
}
let zerocopy_crate = &ctx.zerocopy_crate;
let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None)
.inner_extras(quote! {
type Tag = ___ZerocopyTag;
type ProjectToTag = #zerocopy_crate::pointer::cast::CastSized;
})
.build();
let has_fields = data.variants().into_iter().flat_map(|(variant, fields)| {
let variant_ident = &variant.unwrap().ident;
let variants_union_field_ident = variants_union_field_ident(variant_ident);
let field: Box<syn::Type> = parse_quote!(());
fields.into_iter().enumerate().map(move |(idx, (vis, ident, ty))| {
// Rust does not presently support explicit visibility modifiers on
// enum fields, but we guard against the possibility to ensure this
// derive remains sound.
assert!(matches!(vis, syn::Visibility::Inherited));
let variant_struct_field_index = Index::from(idx + 1);
let (_, ty_generics, _) = ctx.ast.generics.split_for_impl();
let has_field_trait = Trait::HasField {
variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }),
// Since Rust does not presently support explicit visibility
// modifiers on enum fields, any public type is suitable here;
// we use `()`.
field: field.clone(),
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
};
let has_field_path = has_field_trait.crate_path(ctx);
let has_field = ImplBlockBuilder::new(
ctx,
data,
has_field_trait,
FieldBounds::None,
)
.inner_extras(quote! {
type Type = #ty;
#[inline(always)]
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
use #zerocopy_crate::pointer::cast::{CastSized, Projection};
slf.project::<___ZerocopyRawEnum #ty_generics, CastSized>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>>()
.project::<_, Projection<_, { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field_ident) }>>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(value) }>>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>>()
.as_ptr()
}
})
.build();
let project = ImplBlockBuilder::new(
ctx,
data,
Trait::ProjectField {
variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }),
// Since Rust does not presently support explicit visibility
// modifiers on enum fields, any public type is suitable
// here; we use `()`.
field: field.clone(),
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)),
},
FieldBounds::None,
)
.param_extras(vec![
parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing),
parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment),
])
.inner_extras(quote! {
type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible;
type Invariants = (Aliasing, Alignment, #zerocopy_crate::invariant::Initialized);
})
.build();
quote! {
#has_field
#project
}
})
});
let core = ctx.core_path();
let match_arms = data.variants.iter().map(|variant| {
let tag_ident = tag_ident(&variant.ident);
let variant_struct_ident = variant_struct_ident(&variant.ident);
let variants_union_field_ident = variants_union_field_ident(&variant.ident);
if matches!(variant.fields, Fields::Unit) {
// Unit variants don't need any further validation beyond checking
// the tag.
quote! {
#tag_ident => true
}
} else {
quote! {
#tag_ident => {
// SAFETY: Since we know that the tag is `#tag_ident`, we
// know that no other `&`s exist which refer to this enum
// as any other variant.
let variant_md = variants.cast::<
_,
#zerocopy_crate::pointer::cast::Projection<
// #zerocopy_crate::ReadOnly<_>,
_,
{ #zerocopy_crate::REPR_C_UNION_VARIANT_ID },
{ #zerocopy_crate::ident_id!(#variants_union_field_ident) }
>,
_
>();
let variant = variant_md.cast::<
#zerocopy_crate::ReadOnly<#variant_struct_ident #ty_generics>,
#zerocopy_crate::pointer::cast::CastSized,
(#zerocopy_crate::pointer::BecauseRead, _)
>();
<
#variant_struct_ident #ty_generics as #trait_path
>::is_bit_valid(variant)
}
}
}
});
let generics = &ctx.ast.generics;
let raw_enum: DeriveInput = parse_quote! {
#[repr(C)]
struct ___ZerocopyRawEnum #generics {
tag: ___ZerocopyOuterTag,
variants: ___ZerocopyVariants #ty_generics,
}
};
let self_ident = &ctx.ast.ident;
let invariants_eq_impl = quote! {
// SAFETY: `___ZerocopyRawEnum` is designed to have the same layout,
// validity, and invariants as `Self`.
unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<___ZerocopyRawEnum #ty_generics> for #self_ident #ty_generics #where_clause {}
};
let raw_enum_projections =
derive_has_field_struct_union(&ctx.with_input(&raw_enum), &raw_enum.data);
let raw_enum = quote! {
#raw_enum
#invariants_eq_impl
#raw_enum_projections
};
Ok(quote! {
// SAFETY: We use `is_bit_valid` to validate that the bit pattern of the
// enum's tag corresponds to one of the enum's discriminants. Then, we
// check the bit validity of each field of the corresponding variant.
// Thus, this is a sound implementation of `is_bit_valid`.
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
{
let candidate = candidate_ident(ctx);
let alignment = ctx.fresh_ident("___ZcAlignment");
let tag = Ident::new("tag", Span::mixed_site());
let projections = if data.fields().is_empty() {
let tag_enum = generate_tag_enum(ctx, repr, data);
let tag_consts = generate_tag_consts(data);
quote! {
#tag_enum
type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag<
@@ -385,58 +146,79 @@ pub(crate) fn derive_is_bit_valid(
>;
#tag_consts
}
} else {
derive_enum(ctx, data, Client::TryFromBytesDerive)?
};
type ___ZerocopyOuterTag = #outer_tag_type;
type ___ZerocopyInnerTag = #inner_tag_type;
#variant_structs
#variants_union
#raw_enum
#has_tag
#(#has_fields)*
let tag = {
// SAFETY:
// - The provided cast addresses a subset of the bytes addressed
// by `candidate` because it addresses the starting tag of the
// enum.
// - Because the pointer is cast from `candidate`, it has the
// same provenance as it.
// - There are no `UnsafeCell`s in the tag because it is a
// primitive integer.
// - `tag_ptr` is casted from `candidate`, whose referent is
// `Initialized`. Since we have not written uninitialized
// bytes into the referent, `tag_ptr` is also `Initialized`.
//
// FIXME(#2874): Revise this to a `cast` once `candidate`
// references a `ReadOnly<Self>`.
let tag_ptr = unsafe {
candidate.reborrow().project_transmute_unchecked::<
_,
#zerocopy_crate::invariant::Initialized,
#zerocopy_crate::pointer::cast::CastSized
>()
};
tag_ptr.recall_validity::<_, (_, (_, _))>().read::<#zerocopy_crate::BecauseImmutable>()
let match_arms = data
.variants
.iter()
.enumerate()
.map(|(idx, variant)| {
let name = &variant.ident;
let variant_id = quote! { #zerocopy_crate::ident_id!(#name) };
let fields_is_safe = derive_variant_is_safe(ctx, &variant_id, &variant.fields)?;
let pattern = if ctx.invariant_span.is_some() {
quote! { #core::option::Option::Some(#idx) }
} else {
tag_ident(name).to_token_stream()
};
Ok(quote! {
#pattern => { #fields_is_safe }
})
})
.collect::<Result<Vec<_>, Error>>()?;
let mut raw_enum = candidate.cast::<
#zerocopy_crate::ReadOnly<___ZerocopyRawEnum #ty_generics>,
let read_tag = quote! {
let #tag = #candidate
.reborrow()
.cast::<
___ZerocopyTagPrimitive,
#zerocopy_crate::pointer::cast::CastSized,
(#zerocopy_crate::pointer::BecauseRead, _)
>();
(#zerocopy_crate::pointer::BecauseRead, _),
>()
.recall_validity::<_, (_, (_, _))>()
.read::<#zerocopy_crate::BecauseImmutable>();
};
let tag_init = if ctx.invariant_span.is_some() {
let allow = crate::util::allow_generated_code();
let tag_arms = data.variants.iter().enumerate().map(|(idx, variant)| {
let tag = tag_ident(&variant.ident);
quote! { #tag => #core::option::Option::Some(#idx) }
});
quote! {
// Keep implementation-only items and their lint allowances out
// of the scopes containing caller-authored invariant expressions.
#allow
let #tag = {
#projections
#read_tag
match #tag {
#(#tag_arms,)*
_ => #core::option::Option::None,
}
};
}
} else {
quote! { #projections #read_tag }
};
let variants = #zerocopy_crate::into_inner!(raw_enum.project::<
_,
{ #zerocopy_crate::STRUCT_VARIANT_ID },
{ #zerocopy_crate::ident_id!(variants) }
>());
Ok(quote! {
// SAFETY: We use `is_safe` to validate that the bit pattern of the
// enum's tag corresponds to one of the enum's discriminants. Then, we
// check the bit validity of each field of the corresponding variant.
// Thus, this is a sound implementation of `is_safe`.
#[inline]
fn is_safe<#alignment>(
mut #candidate: #zerocopy_crate::Maybe<'_, Self, #alignment>,
) -> #core::primitive::bool
where
#alignment: #zerocopy_crate::invariant::Alignment,
{
#tag_init
match tag {
match #tag {
#(#match_arms,)*
_ => false,
}
@@ -447,219 +229,88 @@ pub(crate) fn derive_try_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<Token
match &ctx.ast.data {
Data::Struct(strct) => derive_try_from_bytes_struct(ctx, strct, top_level),
Data::Enum(enm) => derive_try_from_bytes_enum(ctx, enm, top_level),
Data::Union(unn) => Ok(derive_try_from_bytes_union(ctx, unn, top_level)),
Data::Union(unn) => derive_try_from_bytes_union(ctx, unn, top_level),
}
}
fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream {
let fields = ctx.ast.data.fields();
if fields.is_empty() {
return quote! {};
}
let field_tokens = fields.iter().map(|(vis, ident, _)| {
let ident = ident!(("__z{}", ident), ident.span());
quote!(
#vis enum #ident {}
)
});
let zerocopy_crate = &ctx.zerocopy_crate;
let variant_id: Box<Expr> = match &ctx.ast.data {
Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }),
Data::Union(_) => {
let is_repr_c = StructUnionRepr::from_attrs(&ctx.ast.attrs)
.map(|repr| repr.is_c())
.unwrap_or(false);
if is_repr_c {
parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID })
} else {
parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID })
}
}
_ => unreachable!(),
};
let core = ctx.core_path();
let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None)
.inner_extras(quote! {
type Tag = ();
type ProjectToTag = #zerocopy_crate::pointer::cast::CastToUnit;
})
.build();
let has_fields = fields.iter().map(move |(_, ident, ty)| {
let field_token = ident!(("__z{}", ident), ident.span());
let field: Box<Type> = parse_quote!(#field_token);
let field_id: Box<Expr> = parse_quote!({ #zerocopy_crate::ident_id!(#ident) });
let has_field_trait = Trait::HasField {
variant_id: variant_id.clone(),
field: field.clone(),
field_id: field_id.clone(),
};
let has_field_path = has_field_trait.crate_path(ctx);
ImplBlockBuilder::new(
ctx,
data,
has_field_trait,
FieldBounds::None,
)
.inner_extras(quote! {
type Type = #ty;
#[inline(always)]
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
let slf = slf.as_ptr();
// SAFETY: By invariant on `PtrInner`, `slf` is a non-null
// pointer whose referent is zero-sized or lives in a valid
// allocation. Since `#ident` is a struct or union field of
// `Self`, this projection preserves or shrinks the referent
// size, and so the resulting referent also fits in the same
// allocation.
unsafe { #core::ptr::addr_of_mut!((*slf).#ident) }
}
}).outer_extras(if matches!(&ctx.ast.data, Data::Struct(..)) {
let fields_preserve_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs)
.map(|repr| repr.get_packed().is_none())
.unwrap();
let alignment = if fields_preserve_alignment {
quote! { Alignment }
} else {
quote! { #zerocopy_crate::invariant::Unaligned }
};
// SAFETY: See comments on items.
ImplBlockBuilder::new(
ctx,
data,
Trait::ProjectField {
variant_id: variant_id.clone(),
field,
field_id,
invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)),
},
FieldBounds::None,
)
.param_extras(vec![
parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing),
parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment),
])
.inner_extras(quote! {
// SAFETY: Projection into structs is always infallible.
type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible;
// SAFETY: The alignment of the projected `Ptr` is `Unaligned`
// if the structure is packed; otherwise inherited from the
// outer `Ptr`. If the validity of the outer pointer is
// `Initialized`, so too is the validity of its fields.
type Invariants = (Aliasing, #alignment, #zerocopy_crate::invariant::Initialized);
})
.build()
} else {
quote! {}
})
.build()
});
const_block(field_tokens.into_iter().chain(Some(has_tag)).chain(has_fields).map(Some))
}
fn derive_try_from_bytes_struct(
ctx: &Ctx,
strct: &DataStruct,
top_level: Trait,
) -> Result<TokenStream, Error> {
let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| {
let extras = if let Some(extras) = try_gen_trivial_is_safe(ctx, top_level) {
extras
} else {
let zerocopy_crate = &ctx.zerocopy_crate;
let fields = strct.fields();
let field_names = fields.iter().map(|(_vis, name, _ty)| name);
let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
let variant_id = quote! { #zerocopy_crate::STRUCT_VARIANT_ID };
let fields_is_safe = derive_variant_is_safe(ctx, &variant_id, &strct.fields)?;
let core = ctx.core_path();
let candidate = candidate_ident(ctx);
let alignment = ctx.fresh_ident("___ZcAlignment");
quote!(
// SAFETY: We use `is_bit_valid` to validate that each field is
// bit-valid, and only return `true` if all of them are. The bit
// validity of a struct is just the composition of the bit
// validities of its fields, so this is a sound implementation
// of `is_bit_valid`.
// SAFETY: We use `is_safe` to validate that each field is bit-valid,
// and only return `true` if all of them are. The bit validity of a
// struct is just the composition of the bit validities of its
// fields, so this is a sound implementation of `is_safe`.
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
fn is_safe<#alignment>(
mut #candidate: #zerocopy_crate::Maybe<'_, Self, #alignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
#alignment: #zerocopy_crate::invariant::Alignment,
{
true #(&& {
let field_candidate = #zerocopy_crate::into_inner!(candidate.reborrow().project::<
_,
{ #zerocopy_crate::STRUCT_VARIANT_ID },
{ #zerocopy_crate::ident_id!(#field_names) }
>());
<#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
})*
#fields_is_safe
}
)
});
};
Ok(ImplBlockBuilder::new(ctx, strct, Trait::TryFromBytes, FieldBounds::ALL_SELF)
.inner_extras(extras)
.outer_extras(derive_has_field_struct_union(ctx, strct))
.outer_extras(derive_projection_struct_union(ctx, strct, Client::TryFromBytesDerive))
.build())
}
fn derive_try_from_bytes_union(ctx: &Ctx, unn: &DataUnion, top_level: Trait) -> TokenStream {
fn derive_try_from_bytes_union(
ctx: &Ctx,
unn: &DataUnion,
top_level: Trait,
) -> Result<TokenStream, Error> {
let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
let zerocopy_crate = &ctx.zerocopy_crate;
let variant_id: Box<Expr> = {
let is_repr_c =
StructUnionRepr::from_attrs(&ctx.ast.attrs).map(|repr| repr.is_c()).unwrap_or(false);
if is_repr_c {
parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID })
} else {
parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID })
}
};
let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| {
let fields = unn.fields();
let field_names = fields.iter().map(|(_vis, name, _ty)| name);
let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
let union_variant_id = struct_union_variant_id(ctx).to_token_stream();
let extras = if let Some(extras) = try_gen_trivial_is_safe(ctx, top_level) {
extras
} else {
let fields_is_safe = unn
.fields
.named
.iter()
.map(|field| derive_variant_is_safe(ctx, &union_variant_id, [field]))
.collect::<Result<Vec<_>, _>>()?;
let core = ctx.core_path();
let candidate = candidate_ident(ctx);
let alignment = ctx.fresh_ident("___ZcAlignment");
quote!(
// SAFETY: We use `is_bit_valid` to validate that any field is
// bit-valid; we only return `true` if at least one of them is.
// The bit validity of a union is not yet well defined in Rust,
// but it is guaranteed to be no more strict than this
// definition. See #696 for a more in-depth discussion.
// SAFETY: We use `is_safe` to validate that any field is bit-valid;
// we only return `true` if at least one of them is and its
// invariants also hold. The bit validity of a union is not yet
// well defined in Rust, but it is guaranteed to be no more strict
// than this definition. See #696 for a more in-depth discussion.
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
fn is_safe<#alignment>(
mut #candidate: #zerocopy_crate::Maybe<'_, Self, #alignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
#alignment: #zerocopy_crate::invariant::Alignment,
{
false #(|| {
// SAFETY:
// - Since `ReadOnly<Self>: Immutable` unconditionally,
// neither `*slf` nor the returned pointer's referent
// permit interior mutation.
// - Both source and destination validity are
// `Initialized`, which is always a sound
// transmutation.
let field_candidate = unsafe {
candidate.reborrow().project_transmute_unchecked::<
_,
_,
#zerocopy_crate::pointer::cast::Projection<
_,
#variant_id,
{ #zerocopy_crate::ident_id!(#field_names) }
>
>()
};
<#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
})*
// Keep each field's bindings and early returns local to that
// field, so a failure lets validation try the next field.
false #(|| { #[inline(always)] || { #fields_is_safe } }())*
}
)
});
ImplBlockBuilder::new(ctx, unn, Trait::TryFromBytes, field_type_trait_bounds)
};
Ok(ImplBlockBuilder::new(ctx, unn, Trait::TryFromBytes, field_type_trait_bounds)
.inner_extras(extras)
.outer_extras(derive_has_field_struct_union(ctx, unn))
.build()
.outer_extras(derive_projection_struct_union(ctx, unn, Client::TryFromBytesDerive))
.build())
}
fn derive_try_from_bytes_enum(
ctx: &Ctx,
@@ -677,16 +328,15 @@ fn derive_try_from_bytes_enum(
.map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
.unwrap_or(false);
let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ctx, top_level);
let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
(Some(is_bit_valid), _) => is_bit_valid,
let trivial_is_safe = try_gen_trivial_is_safe(ctx, top_level);
let extra = match (trivial_is_safe, could_be_from_bytes) {
(Some(is_safe), _) => is_safe,
// SAFETY: It would be sound for the enum to implement `FromBytes`, as
// required by `gen_trivial_is_bit_valid_unchecked`.
(None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(ctx) },
(None, false) => match derive_is_bit_valid(ctx, enm, &repr) {
// required by `gen_trivial_is_safe_unchecked`.
(None, true) => unsafe { gen_trivial_is_safe_unchecked(ctx) },
(None, false) => match derive_is_safe(ctx, enm, &repr) {
Ok(extra) => extra,
Err(_) if ctx.skip_on_error => return Ok(TokenStream::new()),
Err(e) => return Err(e),
Err(e) => return ctx.error_or_skip(e),
},
};
@@ -694,15 +344,14 @@ fn derive_try_from_bytes_enum(
.inner_extras(extra)
.build())
}
fn try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream> {
fn try_gen_trivial_is_safe(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream> {
// If the top-level trait is `FromBytes` and `Self` has no type parameters,
// then the `FromBytes` derive will fail compilation if `Self` is not
// actually soundly `FromBytes`, and so we can rely on that for our
// `is_bit_valid` impl. It's plausible that we could make changes - or Rust
// could make changes (such as the "trivial bounds" language feature) - that
// make this no longer true. To hedge against these, we include an explicit
// `Self: FromBytes` check in the generated `is_bit_valid`, which is
// bulletproof.
// actually soundly `FromBytes`, and so we can rely on that for our `is_safe`
// impl. It's plausible that we could make changes - or Rust could make
// changes (such as the "trivial bounds" language feature) - that make this
// no longer true. To hedge against these, we include an explicit `Self:
// FromBytes` check in the generated `is_safe`, which is bulletproof.
//
// If `ctx.skip_on_error` is true, we can't rely on the `FromBytes` derive
// to fail compilation if `Self` is not actually soundly `FromBytes`.
@@ -715,7 +364,7 @@ fn try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macr
Some(quote!(
// SAFETY: See inline.
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
fn is_safe<___ZcAlignment>(
_candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
@@ -746,14 +395,14 @@ fn try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macr
/// # Safety
///
/// All initialized bit patterns must be valid for `Self`.
unsafe fn gen_trivial_is_bit_valid_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream {
unsafe fn gen_trivial_is_safe_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream {
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
quote!(
// SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has
// promised that all initialized bit patterns are valid for `Self`.
// SAFETY: The caller of `gen_trivial_is_safe_unchecked` has promised
// that all initialized bit patterns are valid for `Self`.
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
fn is_safe<___ZcAlignment>(
_candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
@@ -763,3 +412,43 @@ unsafe fn gen_trivial_is_bit_valid_unchecked(ctx: &Ctx) -> proc_macro2::TokenStr
}
)
}
#[cfg(test)]
mod tests {
use syn::{parse_quote, DeriveInput};
use super::*;
fn derive_error(input: DeriveInput) -> String {
let ctx = Ctx::try_from_derive_input(input).unwrap();
match derive_try_from_bytes(&ctx, Trait::TryFromBytes) {
Ok(_) => panic!("expected derive to reject conflicting representations"),
Err(error) => error.to_string(),
}
}
#[test]
fn raw_repr_conflict_matches_ordinary_repr() {
let ordinary = derive_error(parse_quote! {
#[repr(C, align(8))]
#[repr(u8)]
enum Packet {
Value(core::num::NonZeroU8),
End,
}
});
let raw = derive_error(parse_quote! {
#[repr(C, align(8))]
#[r#repr(r#u8)]
enum Packet {
Value(core::num::NonZeroU8),
End,
}
});
// Rejecting both spellings prevents generating a validator whose
// synthetic enum layout disagrees with the compiler's actual layout.
assert_eq!(ordinary, "this conflicts with another representation hint");
assert_eq!(ordinary, raw);
}
}
+116
View File
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
//
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use quote::{quote, ToTokens as _};
use syn::{
punctuated::Punctuated, spanned::Spanned as _, Attribute, Data, DeriveInput, Error, Expr,
Field, MacroDelimiter, Meta, Token,
};
use crate::util::path_is_ident;
/// Parses field invariants in attribute order.
pub(crate) fn parse(attrs: &[Attribute]) -> Result<Vec<Expr>, Error> {
let mut invariants = Vec::new();
for attr in attrs.iter().filter(|attr| path_is_ident(attr.path(), "zerocopy")) {
let options = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for option in options {
if !path_is_ident(option.path(), "invariant") {
return Err(Error::new_spanned(option, "expected `invariant(...)`"));
}
match option {
Meta::List(list) if matches!(list.delimiter, MacroDelimiter::Paren(_)) => {
invariants.push(list.parse_args::<Expr>()?)
}
other => return Err(Error::new_spanned(other, "expected `invariant(...)`")),
}
}
}
Ok(invariants)
}
/// Validates invariant syntax and placement, returning the first invariant's
/// span so derives which cannot honor invariants can reject them.
pub(crate) fn validate(ast: &DeriveInput) -> Result<Option<Span>, Error> {
let fields: Vec<&Field> = match &ast.data {
Data::Struct(data) => data.fields.iter().collect(),
Data::Enum(data) => {
for variant in &data.variants {
if let Some(invariant) = parse(&variant.attrs)?.first() {
return Err(Error::new(
invariant.span(),
"invariants are only supported on named fields",
));
}
}
data.variants.iter().flat_map(|variant| &variant.fields).collect()
}
Data::Union(data) => data.fields.named.iter().collect(),
};
let mut first = None;
for field in fields {
if let Some(invariant) = parse(&field.attrs)?.first() {
if field.ident.is_none() {
return Err(Error::new(
invariant.span(),
"invariants are only supported on named fields",
));
}
first.get_or_insert(invariant.span());
}
}
Ok(first)
}
/// Visits identifiers, including those inside macro arguments.
pub(crate) fn idents(tokens: TokenStream) -> Vec<Ident> {
tokens
.into_iter()
.flat_map(|token| match token {
TokenTree::Ident(ident) => vec![ident],
TokenTree::Group(group) => idents(group.stream()),
_ => Vec::new(),
})
.collect()
}
/// Binds semantic field names in every syntax context used by the expression.
/// Preserve the expression's tokens so its other names retain their hygiene.
pub(crate) fn bind_fields(fields: &[(&Field, Ident)], expression: &Expr) -> TokenStream {
let mut body = expression.to_token_stream();
let references = idents(body.clone());
for (field, binding) in fields {
let name = match &field.ident {
Some(name) => name,
None => continue,
};
let spelling = name.to_string();
let spelling = spelling.trim_start_matches("r#");
// Also support references produced by a macro invoked by the expression.
let mut call_site = name.clone();
call_site.set_span(Span::call_site());
for alias in [name.clone(), call_site].into_iter().chain(
references
.iter()
.filter(|ident| ident.to_string().trim_start_matches("r#") == spelling)
.cloned(),
) {
body = quote! {{
// Require a binding; reject names Rust would resolve as constant
// patterns. Item aliases could capture helper calls in macros.
#[allow(unused_variables, non_snake_case, clippy::redundant_pattern)]
let #alias @ _ = #binding;
#body
}};
}
}
body
}
+109 -9
View File
@@ -28,6 +28,10 @@
#![allow(clippy::type_complexity)]
// Inlining format args isn't supported on our MSRV.
#![allow(clippy::uninlined_format_args)]
// `cargo-zerocopy` supplies this cfg for pinned-nightly tests. During UI tests,
// `testutil::UiTestRunner` explicitly supplies it to the host-built proc macro;
// ordinary `RUSTFLAGS` are not sufficient when Cargo receives `--target`.
#![cfg_attr(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, feature(proc_macro_def_site))]
#![deny(
rustdoc::bare_urls,
rustdoc::broken_intra_doc_links,
@@ -46,6 +50,7 @@ macro_rules! ident {
}
mod derive;
mod invariant;
#[cfg(test)]
mod output_tests;
mod repr;
@@ -79,7 +84,8 @@ use crate::util::*;
/// are currently required to live at the crate root, and so the caller must
/// specify the name in order to avoid name collisions.
macro_rules! derive {
($trait:ident => $outer:ident => $inner:path) => {
($(#[$attr:meta])* $trait:ident => $outer:ident => $inner:path) => {
$(#[$attr])*
#[proc_macro_derive($trait, attributes(zerocopy))]
pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(ts as DeriveInput);
@@ -88,10 +94,13 @@ macro_rules! derive {
Err(e) => return e.into_compile_error().into(),
};
let ts = $inner(&ctx, Trait::$trait).into_ts();
// We wrap in `const_block` as a backstop in case any derive fails
// to wrap its output in `const_block` (and thus fails to annotate)
// with the full set of `#[allow(...)]` attributes).
let ts = const_block([Some(ts)]);
// Apply generated-code lint allowances as a backstop, except
// around caller-authored invariant expressions.
let ts = if matches!(Trait::$trait, Trait::TryFromBytes) {
ctx.const_block([Some(ts)])
} else {
const_block([Some(ts)])
};
#[cfg(test)]
crate::util::testutil::check_hygiene(ts.clone());
ts.into()
@@ -120,6 +129,7 @@ impl IntoTokenStream for Result<proc_macro2::TokenStream, Error> {
derive!(KnownLayout => derive_known_layout => crate::derive::known_layout::derive);
derive!(Immutable => derive_immutable => crate::derive::derive_immutable);
derive!(#[doc(hidden)] Project => derive_project => crate::derive::project::derive);
derive!(TryFromBytes => derive_try_from_bytes => crate::derive::try_from_bytes::derive_try_from_bytes);
derive!(FromZeros => derive_from_zeros => crate::derive::from_bytes::derive_from_zeros);
derive!(FromBytes => derive_from_bytes => crate::derive::from_bytes::derive_from_bytes);
@@ -129,6 +139,89 @@ derive!(ByteHash => derive_hash => crate::derive::derive_hash);
derive!(ByteEq => derive_eq => crate::derive::derive_eq);
derive!(SplitAt => derive_split_at => crate::derive::derive_split_at);
/// Generates a struct whose two identically-printed field types resolve to
/// different types. This is used to test that derives preserve type identity
/// across macro hygiene contexts.
#[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
#[doc(hidden)]
#[proc_macro]
pub fn __test_hygienically_mixed_into_bytes(
_input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
use proc_macro::{Group, Ident, Span, TokenStream, TokenTree};
fn rewrite(input: TokenStream) -> TokenStream {
input
.into_iter()
.map(|token| match token {
TokenTree::Ident(ident) if ident.to_string() == "CallT" => {
TokenTree::Ident(Ident::new("T", Span::call_site()))
}
TokenTree::Ident(ident) if ident.to_string() == "DefT" => {
TokenTree::Ident(Ident::new("T", Span::def_site()))
}
TokenTree::Group(group) => {
let mut rewritten = Group::new(group.delimiter(), rewrite(group.stream()));
rewritten.set_span(group.span());
TokenTree::Group(rewritten)
}
token => token,
})
.collect()
}
rewrite(
"#[derive(zerocopy_renamed::IntoBytes)] \
#[zerocopy(crate = \"zerocopy_renamed\")] \
#[repr(C)] \
struct IntoBytes15<DefT>(CallT, DefT);"
.parse()
.expect("test input must parse"),
)
}
/// Constructs an invariant whose field declaration and reference have different
/// syntax contexts, while preserving all other caller tokens.
#[doc(hidden)]
#[proc_macro]
pub fn __test_hygienically_mixed_invariant(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
// Cross-compilation does not necessarily pass the nightly test cfg to
// host proc macros. Omit the fixture and its test together in that case.
#[cfg(not(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS))]
{
let _ = input;
proc_macro::TokenStream::new()
}
#[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
{
use proc_macro::{Group, Ident, Span, TokenStream, TokenTree};
fn rewrite(input: TokenStream) -> TokenStream {
input
.into_iter()
.map(|token| match token {
TokenTree::Ident(ident) if ident.to_string() == "DefField" => {
TokenTree::Ident(Ident::new("field", Span::def_site()))
}
TokenTree::Ident(ident) if ident.to_string() == "CallField" => {
TokenTree::Ident(Ident::new("field", Span::call_site()))
}
TokenTree::Group(group) => {
let mut rewritten = Group::new(group.delimiter(), rewrite(group.stream()));
rewritten.set_span(group.span());
TokenTree::Group(rewritten)
}
token => token,
})
.collect()
}
rewrite(input)
}
}
#[cfg_attr(not(zerocopy_unstable_linux), doc(hidden))]
#[proc_macro_derive(most_traits, attributes(zerocopy))]
pub fn most_traits(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -153,11 +246,18 @@ pub fn most_traits(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
for (derive, t) in derives {
tokens.extend(derive(&ctx, t))
}
// Invariants prevent `FromBytes` from generating its usual supertrait
// impls, but still permit checked conversions through `TryFromBytes`.
if ctx.invariant_span.is_some() {
tokens.extend(crate::derive::try_from_bytes::derive_try_from_bytes(
&ctx,
Trait::TryFromBytes,
));
}
// We wrap in `const_block` as a backstop in case any derive fails
// to wrap its output in `const_block` (and thus fails to annotate)
// with the full set of `#[allow(...)]` attributes).
let ts = const_block([Some(tokens)]);
// Apply generated-code lint allowances as a backstop, except around
// caller-authored invariant expressions.
let ts = ctx.const_block([Some(tokens)]);
#[cfg(test)]
crate::util::testutil::check_hygiene(ts.clone());
ts.into()
+43 -3
View File
@@ -20,6 +20,8 @@ use syn::{
MetaList,
};
use crate::util::{path_is_ident, to_ident_str};
/// The computed representation of a type.
///
/// This is the result of processing all `#[repr(...)]` attributes on a type, if
@@ -524,11 +526,11 @@ impl RawRepr {
let mut reprs = Vec::new();
for attr in attrs {
// Ignore documentation attributes.
if attr.path().is_ident("doc") {
if path_is_ident(attr.path(), "doc") {
continue;
}
if let Meta::List(ref meta_list) = attr.meta {
if meta_list.path.is_ident("repr") {
if path_is_ident(&meta_list.path, "repr") {
let parsed: Punctuated<Meta, Comma> =
match meta_list.parse_args_with(Punctuated::parse_terminated) {
Ok(parsed) => parsed,
@@ -564,6 +566,8 @@ impl RawRepr {
let ident = path.get_ident().ok_or(UnrecognizedReprError)?;
// Only returns `Ok` for non-zero power-of-two values.
// `NonZeroU32::is_power_of_two` is unsupported on our MSRV.
#[allow(clippy::needless_nonzero_get)]
let parse_nzu64 = |list: &MetaList| {
list.parse_args::<LitInt>()
.and_then(|int| int.base10_parse::<NonZeroU32>())
@@ -578,7 +582,8 @@ impl RawRepr {
};
use RawRepr::*;
Ok(match (ident.to_string().as_str(), list) {
let ident = to_ident_str(ident);
Ok(match (ident.as_str(), list) {
("u8", None) => U8,
("u16", None) => U16,
("u32", None) => U32,
@@ -743,7 +748,40 @@ mod tests {
use PrimitiveRepr::*;
let nz = |n: u32| NonZeroU32::new(n).unwrap();
// Rust treats raw identifiers and their ordinary spellings as the
// same representation hints. Exercise every spelling recognized by
// `RawRepr::from_meta` so that adding a hint cannot accidentally
// reintroduce raw-identifier-sensitive parsing.
macro_rules! test_raw_repr {
($ordinary:meta, $raw:meta => $expected:expr) => {{
let ordinary: Meta = parse_quote!($ordinary);
let raw: Meta = parse_quote!($raw);
assert_eq!(RawRepr::from_meta(&ordinary).ok().unwrap(), $expected);
assert_eq!(RawRepr::from_meta(&raw).ok().unwrap(), $expected);
}};
}
test_raw_repr!(transparent, r#transparent => RawRepr::Transparent);
test_raw_repr!(C, r#C => RawRepr::C);
test_raw_repr!(Rust, r#Rust => RawRepr::Rust);
test_raw_repr!(u8, r#u8 => RawRepr::U8);
test_raw_repr!(u16, r#u16 => RawRepr::U16);
test_raw_repr!(u32, r#u32 => RawRepr::U32);
test_raw_repr!(u64, r#u64 => RawRepr::U64);
test_raw_repr!(u128, r#u128 => RawRepr::U128);
test_raw_repr!(usize, r#usize => RawRepr::Usize);
test_raw_repr!(i8, r#i8 => RawRepr::I8);
test_raw_repr!(i16, r#i16 => RawRepr::I16);
test_raw_repr!(i32, r#i32 => RawRepr::I32);
test_raw_repr!(i64, r#i64 => RawRepr::I64);
test_raw_repr!(i128, r#i128 => RawRepr::I128);
test_raw_repr!(isize, r#isize => RawRepr::Isize);
test_raw_repr!(align(2), r#align(2) => RawRepr::Align(nz(2)));
test_raw_repr!(packed, r#packed => RawRepr::Packed);
test_raw_repr!(packed(2), r#packed(2) => RawRepr::PackedN(nz(2)));
test!(#[repr(transparent)] => StructUnionRepr::Transparent(s()));
test!(#[r#repr(r#transparent)] => StructUnionRepr::Transparent(s()));
test!(#[repr()] => StructUnionRepr::Compound(Rust.into(), None));
test!(#[repr(packed)] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(1)).into())));
test!(#[repr(packed(2))] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(2)).into())));
@@ -754,6 +792,8 @@ mod tests {
test!(#[repr(C, packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into())));
test!(#[repr(C, align(1))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(1)).into())));
test!(#[repr(C, align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into())));
test!(#[r#repr(r#C, r#align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into())));
test!(#[r#repr(r#C, r#packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into())));
test!(#[repr(transparent)] => EnumRepr::Transparent(s()));
test!(#[repr()] => EnumRepr::Compound(Rust.into(), None));
+369 -45
View File
@@ -13,9 +13,9 @@ use std::num::NonZeroU32;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{
parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error,
Expr, ExprLit, Field, GenericParam, Ident, Index, Lit, LitStr, Meta, Path, Type, Variant,
Visibility, WherePredicate,
parse::ParseBuffer, parse_quote, spanned::Spanned as _, token::PathSep, Data, DataEnum,
DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit, Field, GenericParam, Ident, Index,
Lit, LitStr, Meta, Path, Type, Variant, Visibility, WherePredicate,
};
use crate::repr::{CompoundRepr, EnumRepr, PrimitiveRepr, Repr, Spanned};
@@ -30,6 +30,59 @@ pub(crate) struct Ctx {
// The span of the last `#[zerocopy(on_error = ...)]` attribute, if any.
pub(crate) on_error_span: Option<proc_macro2::Span>,
/// The first field invariant on the source type, if any.
pub(crate) invariant_span: Option<Span>,
}
#[derive(Eq, PartialEq)]
enum CratePath {
External,
CrateRelative,
ModuleRelative,
}
fn validate_crate_path(path: &Path) -> Result<CratePath, ()> {
if path.segments.is_empty() {
return Err(());
}
enum ModuleRelative {
Yes,
No,
}
let first = path.segments[0].ident.to_string();
let (mut prev_segment, path_type) = match first.as_str() {
"Self" => return Err(()),
"crate" => {
if path.leading_colon.is_some() {
return Err(());
}
(ModuleRelative::No, CratePath::CrateRelative)
}
"self" | "super" => {
if path.leading_colon.is_some() {
return Err(());
}
(ModuleRelative::Yes, CratePath::ModuleRelative)
}
_ => (ModuleRelative::No, CratePath::External),
};
for seg in path.segments.iter().skip(1) {
let ident = seg.ident.to_string();
match ident.as_str() {
"Self" | "crate" | "self" => return Err(()),
"super" => match prev_segment {
ModuleRelative::Yes => {}
ModuleRelative::No => return Err(()),
},
_ => {
prev_segment = ModuleRelative::No;
}
}
}
Ok(path_type)
}
impl Ctx {
@@ -42,24 +95,40 @@ impl Ctx {
for attr in &ast.attrs {
if let Meta::List(ref meta_list) = attr.meta {
if meta_list.path.is_ident("zerocopy") {
if path_is_ident(&meta_list.path, "zerocopy") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("crate") {
let expr = meta.value().and_then(|value| value.parse());
if path_is_ident(&meta.path, "crate") {
let expr = meta.value().and_then(ParseBuffer::parse);
if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
if let Ok(path_lit) = lit.parse::<Ident>() {
path = parse_quote!(::#path_lit);
return Ok(());
if let Ok(mut path_lit) = lit.parse_with(Path::parse_mod_style) {
if let Ok(crate_path) = validate_crate_path(&path_lit) {
// If not expressly relative, absolutize.
if path_lit.leading_colon.is_none() && crate_path == CratePath::External {
path_lit.leading_colon = Some(PathSep::default());
}
path = path_lit;
return Ok(());
}
return Err(Error::new(
lit.span(),
"`crate` attribute requires a valid module path",
));
}
return Err(Error::new(
lit.span(),
"`crate` attribute requires a path as the value",
));
}
return Err(Error::new(
Span::call_site(),
meta.path.span(),
"`crate` attribute requires a path as the value",
));
}
if meta.path.is_ident("on_error") {
if path_is_ident(&meta.path, "on_error") {
on_error_span = Some(meta.path.span());
let value = meta.value()?;
let s: LitStr = value.parse()?;
@@ -86,7 +155,8 @@ impl Ctx {
}
}
Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span })
let invariant_span = crate::invariant::validate(&ast)?;
Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span, invariant_span })
}
pub(crate) fn with_input(&self, input: &DeriveInput) -> Self {
@@ -95,6 +165,7 @@ impl Ctx {
zerocopy_crate: self.zerocopy_crate.clone(),
skip_on_error: self.skip_on_error,
on_error_span: self.on_error_span,
invariant_span: self.invariant_span,
}
}
@@ -108,15 +179,41 @@ impl Ctx {
quote!(#zerocopy_crate::util::macro_util::core_reexport)
}
/// Choose an implementation name absent from the caller's tokens. This is
/// best-effort; names introduced by nested macros are not considered.
/// Unlike local variables, type parameters are not hidden by mixed-site
/// hygiene.
pub(crate) fn fresh_ident(&self, name: &str) -> Ident {
let idents = crate::invariant::idents(self.ast.to_token_stream());
let mut name = name.to_owned();
while idents.iter().any(|ident| ident.to_string().trim_start_matches("r#") == name) {
name.push('_');
}
Ident::new(&name, Span::mixed_site())
}
/// Caller-authored invariant expressions must inherit the caller's lint
/// policy, rather than the blanket allowances for generated glue.
pub(crate) fn const_block(
&self,
items: impl IntoIterator<Item = Option<TokenStream>>,
) -> TokenStream {
if self.invariant_span.is_none() {
const_block(items)
} else {
let items = items.into_iter().flatten();
quote! { const _: () = { #(#items)* }; }
}
}
pub(crate) fn cfg_compile_error(&self) -> TokenStream {
// By checking both during the compilation of the proc macro *and* in
// the generated code, we ensure that `--cfg
// zerocopy_unstable_linux` need only be passed *either* when
// the generated code, we ensure that each cfg need only be passed when
// compiling this crate *or* when compiling the user's crate. The former
// is preferable, but in some situations (such as when cross-compiling
// using `cargo build --target`), it doesn't get propagated to this
// crate's build by default.
if cfg!(zerocopy_unstable_linux) {
let on_error = if cfg!(zerocopy_unstable_linux) {
quote!()
} else if let Some(span) = self.on_error_span {
let core = self.core_path();
@@ -131,7 +228,24 @@ impl Ctx {
}
} else {
quote!()
}
};
let invariant = if cfg!(zerocopy_unstable_ptr) {
quote!()
} else if let Some(span) = self.invariant_span {
let core = self.core_path();
let error_message =
"`invariant` is experimental; pass '--cfg zerocopy_unstable_ptr' to enable";
quote::quote_spanned! {span=>
#[allow(unused_attributes, unexpected_cfgs)]
const _: () = {
#[cfg(not(zerocopy_unstable_ptr))]
#core::compile_error!(#error_message);
};
}
} else {
quote!()
};
quote!(#on_error #invariant)
}
pub(crate) fn error_or_skip<E>(&self, error: E) -> Result<TokenStream, E> {
@@ -151,7 +265,7 @@ pub(crate) trait DataExt {
/// FIXME: Extracting field names for enums doesn't really make sense. Types
/// makes sense because we don't care about where they live - we just care
/// about transitive ownership. But for field names, we'd only use them when
/// generating is_bit_valid, which cares about where they live.
/// generating is_safe, which cares about where they live.
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>;
fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>;
@@ -255,6 +369,18 @@ pub(crate) fn to_ident_str(t: &impl ToString) -> String {
}
}
/// Does `path` consist solely of the identifier `expected`?
///
/// Unlike [`Path::is_ident`], this treats a raw identifier and its ordinary
/// spelling as the same identifier. Rust applies that same normalization when
/// interpreting attribute names and arguments.
pub(crate) fn path_is_ident(path: &Path, expected: &str) -> bool {
match path.get_ident() {
Some(ident) => to_ident_str(ident) == expected,
None => false,
}
}
/// This enum describes what kind of padding check needs to be generated for the
/// associated impl.
pub(crate) enum PaddingCheck {
@@ -298,22 +424,46 @@ impl PaddingCheck {
}
}
#[derive(Copy, Clone)]
pub(crate) enum Client {
ProjectDerive,
TryFromBytesDerive,
}
impl ToTokens for Client {
fn to_tokens(&self, tokens: &mut TokenStream) {
let s = match self {
Client::ProjectDerive => "ProjectDerive",
Client::TryFromBytesDerive => "TryFromBytesDerive",
};
let ident = Ident::new(s, Span::call_site());
tokens.extend(quote!(project_clients::#ident));
}
}
impl Client {
pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path {
let zerocopy_crate = &ctx.zerocopy_crate;
parse_quote!(#zerocopy_crate::#self)
}
}
/// Bundles the trait parameters for `HasField` and `ProjectField`.
#[derive(Clone)]
pub(crate) struct FieldProjection {
pub(crate) variant_id: Box<Expr>,
pub(crate) field: Box<Type>,
pub(crate) field_id: Box<Expr>,
}
#[derive(Clone)]
pub(crate) enum Trait {
KnownLayout,
HasTag,
HasField {
variant_id: Box<Expr>,
field: Box<Type>,
field_id: Box<Expr>,
},
ProjectField {
variant_id: Box<Expr>,
field: Box<Type>,
field_id: Box<Expr>,
invariants: Box<Type>,
},
HasTag { client: Client },
HasField { client: Client, projection: FieldProjection },
ProjectField { client: Client, projection: FieldProjection, invariants: Box<Type> },
Immutable,
Project,
TryFromBytes,
FromZeros,
FromBytes,
@@ -340,8 +490,9 @@ impl ToTokens for Trait {
Trait::HasField { .. } => "HasField",
Trait::ProjectField { .. } => "ProjectField",
Trait::KnownLayout => "KnownLayout",
Trait::HasTag => "HasTag",
Trait::HasTag { .. } => "HasTag",
Trait::Immutable => "Immutable",
Trait::Project => "Project",
Trait::TryFromBytes => "TryFromBytes",
Trait::FromZeros => "FromZeros",
Trait::FromBytes => "FromBytes",
@@ -354,15 +505,18 @@ impl ToTokens for Trait {
};
let ident = Ident::new(s, Span::call_site());
let arguments: Option<syn::AngleBracketedGenericArguments> = match self {
Trait::HasField { variant_id, field, field_id } => {
Some(parse_quote!(<#field, #variant_id, #field_id>))
Trait::HasTag { client } => Some(parse_quote!(<#client>)),
Trait::HasField { client, projection } => {
let FieldProjection { variant_id, field, field_id } = projection;
Some(parse_quote!(<#client, #field, #variant_id, #field_id>))
}
Trait::ProjectField { variant_id, field, field_id, invariants } => {
Some(parse_quote!(<#field, #invariants, #variant_id, #field_id>))
Trait::ProjectField { client, projection, invariants } => {
let FieldProjection { variant_id, field, field_id } = projection;
Some(parse_quote!(<#client, #field, #invariants, #variant_id, #field_id>))
}
Trait::KnownLayout
| Trait::HasTag
| Trait::Immutable
| Trait::Project
| Trait::TryFromBytes
| Trait::FromZeros
| Trait::FromBytes
@@ -383,6 +537,28 @@ impl Trait {
let core = ctx.core_path();
match self {
Self::Sized => parse_quote!(#core::marker::#self),
Self::HasTag { client } => {
let client = client.crate_path(ctx);
parse_quote!(#zerocopy_crate::HasTag<#client>)
}
Self::HasField { client, projection } => {
let FieldProjection { variant_id, field, field_id } = projection;
let client = client.crate_path(ctx);
parse_quote!(#zerocopy_crate::HasField<#client, #field, #variant_id, #field_id>)
}
Self::ProjectField { client, projection, invariants } => {
let FieldProjection { variant_id, field, field_id } = projection;
let client = client.crate_path(ctx);
parse_quote!(
#zerocopy_crate::ProjectField<
#client,
#field,
#invariants,
#variant_id,
#field_id
>
)
}
_ => parse_quote!(#zerocopy_crate::#self),
}
}
@@ -704,7 +880,12 @@ impl<'a> ImplBlockBuilder<'a> {
let outer_extras = self.outer_extras.filter(|e| !e.is_empty());
let cfg_compile_error = self.ctx.cfg_compile_error();
const_block([Some(cfg_compile_error), Some(impl_tokens), outer_extras])
let items = [Some(cfg_compile_error), Some(impl_tokens), outer_extras];
if matches!(self.trt, Trait::TryFromBytes) {
self.ctx.const_block(items)
} else {
const_block(items)
}
}
}
@@ -730,8 +911,7 @@ impl BoolExt for bool {
}
}
pub(crate) fn const_block(items: impl IntoIterator<Item = Option<TokenStream>>) -> TokenStream {
let items = items.into_iter().flatten();
pub(crate) fn allow_generated_code() -> TokenStream {
quote! {
#[allow(
// FIXME(#553): Add a test that generates a warning when
@@ -748,6 +928,14 @@ pub(crate) fn const_block(items: impl IntoIterator<Item = Option<TokenStream>>)
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
}
}
pub(crate) fn const_block(items: impl IntoIterator<Item = Option<TokenStream>>) -> TokenStream {
let items = items.into_iter().flatten();
let allow = allow_generated_code();
quote! {
#allow
#[deny(ambiguous_associated_items)]
// While there are not currently any warnings that this suppresses
// (that we're aware of), it's good future-proofing hygiene.
@@ -778,7 +966,8 @@ pub(crate) fn generate_tag_enum(ctx: &Ctx, repr: &EnumRepr, data: &DataEnum) ->
quote! {
#repr
#[allow(dead_code)]
#[allow(dead_code, clippy::derive_partial_eq_without_eq)]
#[derive(Copy, Clone, PartialEq)]
pub enum ___ZerocopyTag {
#(#variants,)*
}
@@ -824,18 +1013,40 @@ pub(crate) mod testutil {
pub(crate) fn check_hygiene(ts: TokenStream) {
struct AmbiguousItemVisitor;
fn panic_ambiguous_path(path: &impl quote::ToTokens) -> ! {
panic!(
"Found ambiguous path `{}` in generated output. \
All associated item access must be fully qualified (e.g., `<Self as Trait>::Item`) \
to prevent hygiene issues.",
quote::quote!(#path)
);
}
impl<'ast> Visit<'ast> for AmbiguousItemVisitor {
fn visit_expr_path(&mut self, i: &'ast syn::ExprPath) {
if let Some(qself) = &i.qself {
if qself.position == 0 {
panic_ambiguous_path(i);
}
}
visit::visit_expr_path(self, i);
}
fn visit_path(&mut self, i: &'ast syn::Path) {
if i.segments.len() > 1 && i.segments.first().unwrap().ident == "Self" {
panic!(
"Found ambiguous path `{}` in generated output. \
All associated item access must be fully qualified (e.g., `<Self as Trait>::Item`) \
to prevent hygiene issues.",
quote::quote!(#i)
);
panic_ambiguous_path(i);
}
visit::visit_path(self, i);
}
fn visit_type_path(&mut self, i: &'ast syn::TypePath) {
if let Some(qself) = &i.qself {
if qself.position == 0 {
panic_ambiguous_path(i);
}
}
visit::visit_type_path(self, i);
}
}
let file = syn::parse2::<syn::File>(ts).expect("failed to parse generated output as File");
@@ -860,4 +1071,117 @@ pub(crate) mod testutil {
}
});
}
#[test]
#[should_panic(expected = "Found ambiguous path `< T > :: Ambiguous`")]
fn test_check_hygiene_type_relative_expr_failure() {
check_hygiene(quote::quote! {
fn foo<T>() {
let _ = <T>::Ambiguous;
}
});
}
#[test]
#[should_panic(expected = "Found ambiguous path `< T > :: Ambiguous`")]
fn test_check_hygiene_type_relative_type_failure() {
check_hygiene(quote::quote! {
fn foo<T>() {
let _: <T>::Ambiguous;
}
});
}
#[test]
fn test_validate_crate_path() {
use syn::parse_str;
let valid = [
"zerocopy",
"crate",
"crate::foo::bar",
"self",
"self::foo",
"self::super::foo",
"super",
"super::foo",
"super::super::foo",
"super::super::super",
"foo::bar::baz",
"::foo::bar",
];
for path_str in valid {
let path = parse_str::<syn::Path>(path_str).unwrap();
assert!(
super::validate_crate_path(&path).is_ok(),
"expected valid path for `{}`",
path_str
);
}
let invalid = [
"::crate::foo",
"::self::foo",
"::super::foo",
"Self",
"Self::foo",
"foo::Self::bar",
"foo::crate::bar",
"foo::super::bar",
"foo::self",
"super::foo::super",
"super::crate::foo",
"crate::super::foo",
"self::self::foo",
];
for path_str in invalid {
let path = parse_str::<syn::Path>(path_str).unwrap();
assert!(
super::validate_crate_path(&path).is_err(),
"expected invalid path for `{}`",
path_str
);
}
}
#[test]
fn test_path_is_ident() {
use syn::parse_str;
for (expected, ordinary, raw) in [
("doc", "doc", "r#doc"),
("repr", "repr", "r#repr"),
("zerocopy", "zerocopy", "r#zerocopy"),
("on_error", "on_error", "r#on_error"),
] {
let ordinary = parse_str::<syn::Path>(ordinary).unwrap();
let raw = parse_str::<syn::Path>(raw).unwrap();
assert!(super::path_is_ident(&ordinary, expected));
assert!(super::path_is_ident(&raw, expected));
}
// `r#crate` is forbidden by Rust's raw identifier grammar.
let crate_path = parse_str::<syn::Path>("crate").unwrap();
assert!(super::path_is_ident(&crate_path, "crate"));
for path in ["::zerocopy", "module::zerocopy"] {
let path = parse_str::<syn::Path>(path).unwrap();
assert!(!super::path_is_ident(&path, "zerocopy"));
}
}
#[test]
fn test_raw_zerocopy_attributes() {
let ast: syn::DeriveInput = syn::parse_quote! {
#[r#zerocopy(crate = "renamed", r#on_error = "skip")]
struct Foo;
};
let ctx = super::Ctx::try_from_derive_input(ast).unwrap();
let path = &ctx.zerocopy_crate;
assert_eq!(quote::quote!(#path).to_string(), ":: renamed");
assert!(ctx.skip_on_error);
}
}
+6 -6
View File
@@ -1,13 +1,13 @@
# `zerocopy`
These source files come from the Rust `zerocopy` crate, version v0.8.54
(released 2026-07-08), hosted in the <https://github.com/google/zerocopy>
These source files come from the Rust `zerocopy` crate, version v0.8.62
(released 2026-10-08), hosted in the <https://github.com/google/zerocopy>
repository, licensed under "BSD-2-Clause OR Apache-2.0 OR MIT" and only
modified to tweak the SPDX license identifiers.
For copyright details, please see:
https://github.com/google/zerocopy/blob/v0.8.54/zerocopy/README.md?plain=1
https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-BSD
https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-APACHE
https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-MIT
https://github.com/google/zerocopy/blob/v0.8.62/zerocopy/README.md?plain=1
https://github.com/google/zerocopy/blob/v0.8.62/LICENSE-BSD
https://github.com/google/zerocopy/blob/v0.8.62/LICENSE-APACHE
https://github.com/google/zerocopy/blob/v0.8.62/LICENSE-MIT
@@ -1,5 +1,4 @@
bench_as_bytes_dynamic_size:
mov rax, rdi
lea rdx, [2*rsi + 5]
and rdx, -2
lea rdx, [2*rsi + 4]
ret
@@ -1,11 +1,11 @@
Iterations: 100
Instructions: 400
Total Cycles: 137
Total uOps: 400
Instructions: 300
Total Cycles: 104
Total uOps: 300
Dispatch Width: 4
uOps Per Cycle: 2.92
IPC: 2.92
uOps Per Cycle: 2.88
IPC: 2.88
Block RThroughput: 1.0
@@ -19,8 +19,7 @@ Instruction Info:
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.50 lea rdx, [2*rsi + 5]
1 1 0.33 and rdx, -2
1 1 0.50 lea rdx, [2*rsi + 4]
1 1 1.00 U ret
@@ -37,11 +36,10 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 1.33 1.33 - 1.34 - -
- - 0.99 1.00 - 1.01 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.66 - 0.34 - - mov rax, rdi
- - 0.33 0.67 - - - - lea rdx, [2*rsi + 5]
- - 1.00 - - - - - and rdx, -2
- - 0.99 - - 0.01 - - mov rax, rdi
- - - 1.00 - - - - lea rdx, [2*rsi + 4]
- - - - - 1.00 - - ret
@@ -41,8 +41,8 @@ bench_extend_vec_zeroed:
.LBB6_6:
mov r13, rsi
lea rcx, [rax + rax]
cmp r15, rcx
cmova rcx, r15
cmp rcx, r15
cmovbe rcx, r15
cmp rcx, 5
mov r14d, 4
cmovae r14, rcx
@@ -55,8 +55,8 @@ Instruction Info:
1 1 1.00 U ret
1 1 0.33 mov r13, rsi
1 1 0.50 lea rcx, [rax + rax]
1 1 0.33 cmp r15, rcx
3 3 1.00 cmova rcx, r15
1 1 0.33 cmp rcx, r15
3 3 1.00 cmovbe rcx, r15
1 1 0.33 cmp rcx, 5
1 1 0.33 mov r14d, 4
2 2 0.67 cmovae r14, rcx
@@ -128,8 +128,8 @@ Resource pressure by instruction:
- - - - - 1.00 - - ret
- - 1.00 - - - - - mov r13, rsi
- - 0.01 0.99 - - - - lea rcx, [rax + rax]
- - 0.99 0.01 - - - - cmp r15, rcx
- - 2.00 0.01 - 0.99 - - cmova rcx, r15
- - 0.99 0.01 - - - - cmp rcx, r15
- - 2.00 0.01 - 0.99 - - cmovbe rcx, r15
- - 0.01 0.99 - - - - cmp rcx, 5
- - 0.01 0.99 - - - - mov r14d, 4
- - 1.00 0.01 - 0.99 - - cmovae r14, rcx
@@ -54,8 +54,8 @@ bench_insert_vec_zeroed:
mov r15, rsi
mov rbp, rdx
lea rcx, [rax + rax]
cmp r12, rcx
cmova rcx, r12
cmp rcx, r12
cmovbe rcx, r12
cmp rcx, 5
mov r14d, 4
cmovae r14, rcx
@@ -68,8 +68,8 @@ Instruction Info:
1 1 0.33 mov r15, rsi
1 1 0.33 mov rbp, rdx
1 1 0.50 lea rcx, [rax + rax]
1 1 0.33 cmp r12, rcx
3 3 1.00 cmova rcx, r12
1 1 0.33 cmp rcx, r12
3 3 1.00 cmovbe rcx, r12
1 1 0.33 cmp rcx, 5
1 1 0.33 mov r14d, 4
2 2 0.67 cmovae r14, rcx
@@ -159,8 +159,8 @@ Resource pressure by instruction:
- - 0.49 0.51 - - - - mov r15, rsi
- - 0.51 0.49 - - - - mov rbp, rdx
- - 0.49 0.51 - - - - lea rcx, [rax + rax]
- - 0.49 0.50 - 0.01 - - cmp r12, rcx
- - 1.04 0.50 - 1.46 - - cmova rcx, r12
- - 0.49 0.50 - 0.01 - - cmp rcx, r12
- - 1.04 0.50 - 1.46 - - cmovbe rcx, r12
- - 0.49 0.49 - 0.02 - - cmp rcx, 5
- - 0.50 - - 0.50 - - mov r14d, 4
- - 0.50 0.51 - 0.99 - - cmovae r14, rcx
+1 -2
View File
@@ -29,8 +29,7 @@ bench_new_vec_zeroed:
mov qword ptr [rax + 16], rsi
ret
.LBB5_5:
movabs rcx, -9223372036854775808
mov qword ptr [rax], rcx
mov qword ptr [rax], -1
ret
.LBB5_2:
mov ecx, 2
+22 -24
View File
@@ -1,11 +1,11 @@
Iterations: 100
Instructions: 3700
Instructions: 3600
Total Cycles: 3486
Total uOps: 4600
Total uOps: 4500
Dispatch Width: 4
uOps Per Cycle: 1.32
IPC: 1.06
uOps Per Cycle: 1.29
IPC: 1.03
Block RThroughput: 12.0
@@ -47,8 +47,7 @@ Instruction Info:
1 1 1.00 * mov qword ptr [rax + 8], rcx
1 1 1.00 * mov qword ptr [rax + 16], rsi
1 1 1.00 U ret
1 1 0.33 movabs rcx, -9223372036854775808
1 1 1.00 * mov qword ptr [rax], rcx
1 1 1.00 * mov qword ptr [rax], -1
1 1 1.00 U ret
1 1 0.33 mov ecx, 2
1 1 1.00 * mov qword ptr [rax], rsi
@@ -70,31 +69,31 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.99 6.99 12.00 10.02 8.00 9.00
- - 5.99 6.01 12.00 11.00 8.00 9.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.01 0.98 - 0.01 - - mov rax, rdi
- - 0.98 0.02 - - - - movabs rcx, 1537228672809129301
- - 0.02 0.98 - - - - cmp rsi, rcx
- - 0.97 0.02 - 0.01 - - mov rax, rdi
- - 0.02 0.98 - - - - movabs rcx, 1537228672809129301
- - 0.97 0.03 - - - - cmp rsi, rcx
- - - - - 1.00 - - ja .LBB5_5
- - 0.98 - - 0.02 - - test rsi, rsi
- - 0.03 0.96 - 0.01 - - test rsi, rsi
- - - - - 1.00 - - je .LBB5_2
- - - - 1.00 - - 1.00 push r15
- - - - 1.00 - 1.00 - push r14
- - - - 1.00 - - 1.00 push rbx
- - - 1.00 - - - - lea rcx, [rsi + rsi]
- - - 1.00 - - - - lea rbx, [rcx + 2*rcx]
- - 1.00 - - - - - mov r14, rax
- - 1.00 - - - - - mov r15, rsi
- - 0.99 0.01 - - - - lea rcx, [rsi + rsi]
- - 0.99 0.01 - - - - lea rbx, [rcx + 2*rcx]
- - 0.01 0.99 - - - - mov r14, rax
- - 0.01 0.01 - 0.98 - - mov r15, rsi
- - - - 1.00 1.00 2.00 - call qword ptr [rip + __rustc::__rust_no_alloc_shim_is_unstable_v2@GOTPCREL]
- - - 0.01 - 0.99 - - mov esi, 2
- - 0.01 0.99 - - - - mov rdi, rbx
- - - 0.98 - 0.02 - - mov esi, 2
- - 0.96 0.02 - 0.02 - - mov rdi, rbx
- - - - 1.00 1.00 - 2.00 call qword ptr [rip + __rustc::__rust_alloc_zeroed@GOTPCREL]
- - 0.01 - - 0.99 - - mov rsi, r15
- - 0.99 0.01 - - - - mov rcx, rax
- - - 0.99 - 0.01 - - mov rax, r14
- - 0.99 0.01 - - - - test rcx, rcx
- - 0.02 0.96 - 0.02 - - mov rsi, r15
- - 0.02 0.02 - 0.96 - - mov rcx, rax
- - 0.96 0.02 - 0.02 - - mov rax, r14
- - 0.02 0.02 - 0.96 - - test rcx, rcx
- - - - - - - 1.00 pop rbx
- - - - - - 1.00 - pop r14
- - - - - - - 1.00 pop r15
@@ -103,10 +102,9 @@ Resource pressure by instruction:
- - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rcx
- - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rsi
- - - - - 1.00 - - ret
- - 0.01 0.99 - - - - movabs rcx, -9223372036854775808
- - - - 1.00 - - 1.00 mov qword ptr [rax], rcx
- - - - 1.00 - - 1.00 mov qword ptr [rax], -1
- - - - - 1.00 - - ret
- - 0.99 0.01 - - - - mov ecx, 2
- - 0.02 0.98 - - - - mov ecx, 2
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rcx
- - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rsi
@@ -1,22 +1,21 @@
bench_ref_from_bytes_dynamic_padding:
test dil, 3
jne .LBB5_3
movabs rax, 9223372036854775804
and rax, rsi
cmp rax, 9
jb .LBB5_3
add rax, -9
movabs rcx, -6148914691236517205
mul rcx
shr rdx
lea rax, [rdx + 2*rdx]
or rax, 3
add rax, 9
cmp rsi, rax
je .LBB5_4
.LBB5_3:
cmp rsi, 12
setae al
mov ecx, esi
or ecx, edi
test cl, 3
sete cl
and cl, al
cmp cl, 1
je .LBB5_2
xor edi, edi
mov rdx, rsi
.LBB5_4:
mov rax, rdi
ret
.LBB5_2:
add rsi, -9
movabs rcx, -6148914691236517205
mov rax, rsi
mul rcx
shr rdx
mov rax, rdi
ret
@@ -1,11 +1,11 @@
Iterations: 100
Instructions: 1900
Total Cycles: 645
Total Cycles: 641
Total uOps: 2000
Dispatch Width: 4
uOps Per Cycle: 3.10
IPC: 2.95
uOps Per Cycle: 3.12
IPC: 2.96
Block RThroughput: 5.0
@@ -18,23 +18,23 @@ Instruction Info:
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 test dil, 3
1 1 1.00 jne .LBB5_3
1 1 0.33 movabs rax, 9223372036854775804
1 1 0.33 and rax, rsi
1 1 0.33 cmp rax, 9
1 1 1.00 jb .LBB5_3
1 1 0.33 add rax, -9
1 1 0.33 cmp rsi, 12
1 1 0.50 setae al
1 1 0.33 mov ecx, esi
1 1 0.33 or ecx, edi
1 1 0.33 test cl, 3
1 1 0.50 sete cl
1 1 0.33 and cl, al
1 1 0.33 cmp cl, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor edi, edi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
1 1 0.33 add rsi, -9
1 1 0.33 movabs rcx, -6148914691236517205
1 1 0.33 mov rax, rsi
2 4 1.00 mul rcx
1 1 0.50 shr rdx
1 1 0.50 lea rax, [rdx + 2*rdx]
1 1 0.33 or rax, 3
1 1 0.33 add rax, 9
1 1 0.33 cmp rsi, rax
1 1 1.00 je .LBB5_4
1 0 0.25 xor edi, edi
1 1 0.33 mov rdx, rsi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
@@ -52,26 +52,26 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.32 6.33 - 6.35 - -
- - 6.33 6.33 - 6.34 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.64 0.35 - 0.01 - - test dil, 3
- - - - - 1.00 - - jne .LBB5_3
- - 0.34 0.65 - 0.01 - - movabs rax, 9223372036854775804
- - 0.35 0.65 - - - - and rax, rsi
- - 0.33 0.34 - 0.33 - - cmp rax, 9
- - - - - 1.00 - - jb .LBB5_3
- - 0.35 - - 0.65 - - add rax, -9
- - 0.97 0.01 - 0.02 - - movabs rcx, -6148914691236517205
- - 1.00 1.00 - - - - mul rcx
- - 0.99 - - 0.01 - - shr rdx
- - 0.33 0.67 - - - - lea rax, [rdx + 2*rdx]
- - 0.34 0.66 - - - - or rax, 3
- - 0.33 0.66 - 0.01 - - add rax, 9
- - 0.01 0.99 - - - - cmp rsi, rax
- - - - - 1.00 - - je .LBB5_4
- - 0.99 - - 0.01 - - cmp rsi, 12
- - 1.00 - - - - - setae al
- - - 0.34 - 0.66 - - mov ecx, esi
- - - 0.99 - 0.01 - - or ecx, edi
- - - 1.00 - - - - test cl, 3
- - 0.67 - - 0.33 - - sete cl
- - - 1.00 - - - - and cl, al
- - - 1.00 - - - - cmp cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor edi, edi
- - 0.32 0.01 - 0.67 - - mov rdx, rsi
- - 0.02 0.34 - 0.64 - - mov rax, rdi
- - 0.66 - - 0.34 - - mov rax, rdi
- - - - - 1.00 - - ret
- - 0.35 0.01 - 0.64 - - add rsi, -9
- - 0.65 0.33 - 0.02 - - movabs rcx, -6148914691236517205
- - 0.01 0.33 - 0.66 - - mov rax, rsi
- - 1.00 1.00 - - - - mul rcx
- - 1.00 - - - - - shr rdx
- - - 0.33 - 0.67 - - mov rax, rdi
- - - - - 1.00 - - ret
@@ -1,20 +1,11 @@
bench_ref_from_bytes_dynamic_size:
mov rdx, rsi
cmp rsi, 4
setb al
or al, dil
test al, 1
je .LBB5_2
mov ecx, edx
or ecx, edi
xor eax, eax
ret
.LBB5_2:
lea rcx, [rdx - 4]
mov rsi, rcx
and rsi, -2
add rsi, 4
shr rcx
xor eax, eax
cmp rdx, rsi
cmove rdx, rcx
sub rdx, 4
cmovb rdi, rax
shr rdx
test cl, 1
cmove rax, rdi
ret
@@ -1,12 +1,12 @@
Iterations: 100
Instructions: 1800
Total Cycles: 704
Total uOps: 2000
Instructions: 1000
Total Cycles: 373
Total uOps: 1200
Dispatch Width: 4
uOps Per Cycle: 2.84
IPC: 2.56
Block RThroughput: 5.0
uOps Per Cycle: 3.22
IPC: 2.68
Block RThroughput: 3.0
Instruction Info:
@@ -19,21 +19,13 @@ Instruction Info:
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rdx, rsi
1 1 0.33 cmp rsi, 4
1 1 0.50 setb al
1 1 0.33 or al, dil
1 1 0.33 test al, 1
1 1 1.00 je .LBB5_2
1 1 0.33 mov ecx, edx
1 1 0.33 or ecx, edi
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.50 lea rcx, [rdx - 4]
1 1 0.33 mov rsi, rcx
1 1 0.33 and rsi, -2
1 1 0.33 add rsi, 4
1 1 0.50 shr rcx
1 0 0.25 xor eax, eax
1 1 0.33 cmp rdx, rsi
2 2 0.67 cmove rdx, rcx
1 1 0.33 sub rdx, 4
2 2 0.67 cmovb rdi, rax
1 1 0.50 shr rdx
1 1 0.33 test cl, 1
2 2 0.67 cmove rax, rdi
1 1 1.00 U ret
@@ -51,25 +43,17 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 5.97 5.98 - 6.05 - -
- - 3.66 3.67 - 3.67 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.97 0.01 - 0.02 - - mov rdx, rsi
- - 0.01 0.02 - 0.97 - - cmp rsi, 4
- - 0.03 - - 0.97 - - setb al
- - 0.01 0.02 - 0.97 - - or al, dil
- - - 0.98 - 0.02 - - test al, 1
- - - - - 1.00 - - je .LBB5_2
- - 0.32 0.01 - 0.67 - - mov rdx, rsi
- - 0.33 0.35 - 0.32 - - mov ecx, edx
- - 0.01 0.34 - 0.65 - - or ecx, edi
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.98 0.02 - - - - lea rcx, [rdx - 4]
- - 0.01 0.99 - - - - mov rsi, rcx
- - - 0.98 - 0.02 - - and rsi, -2
- - 0.98 0.01 - 0.01 - - add rsi, 4
- - 0.99 - - 0.01 - - shr rcx
- - - - - - - - xor eax, eax
- - 0.02 0.97 - 0.01 - - cmp rdx, rsi
- - 0.99 0.99 - 0.02 - - cmove rdx, rcx
- - 0.98 0.99 - 0.03 - - cmove rax, rdi
- - 0.67 0.33 - - - - sub rdx, 4
- - 0.99 0.98 - 0.03 - - cmovb rdi, rax
- - 0.03 - - 0.97 - - shr rdx
- - 0.32 0.67 - 0.01 - - test cl, 1
- - 0.99 0.99 - 0.02 - - cmove rax, rdi
- - - - - 1.00 - - ret
@@ -1,8 +1,7 @@
bench_ref_from_bytes_static_size:
mov ecx, edi
and ecx, 1
xor rsi, 6
xor eax, eax
or rsi, rcx
test dil, 1
cmovne rdi, rax
cmp rsi, 6
cmove rax, rdi
ret
@@ -1,11 +1,11 @@
Iterations: 100
Instructions: 700
Total Cycles: 240
Instructions: 600
Total Cycles: 305
Total uOps: 800
Dispatch Width: 4
uOps Per Cycle: 3.33
IPC: 2.92
uOps Per Cycle: 2.62
IPC: 1.97
Block RThroughput: 2.0
@@ -18,11 +18,10 @@ Instruction Info:
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov ecx, edi
1 1 0.33 and ecx, 1
1 1 0.33 xor rsi, 6
1 0 0.25 xor eax, eax
1 1 0.33 or rsi, rcx
1 1 0.33 test dil, 1
2 2 0.67 cmovne rdi, rax
1 1 0.33 cmp rsi, 6
2 2 0.67 cmove rax, rdi
1 1 1.00 U ret
@@ -40,14 +39,13 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 2.33 2.33 - 2.34 - -
- - 1.95 2.28 - 2.77 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.01 0.98 - 0.01 - - mov ecx, edi
- - 0.02 0.66 - 0.32 - - and ecx, 1
- - 0.33 0.66 - 0.01 - - xor rsi, 6
- - - - - - - - xor eax, eax
- - 0.98 0.02 - - - - or rsi, rcx
- - 0.99 0.01 - 1.00 - - cmove rax, rdi
- - 0.05 0.06 - 0.89 - - test dil, 1
- - 0.95 0.94 - 0.11 - - cmovne rdi, rax
- - - 0.34 - 0.66 - - cmp rsi, 6
- - 0.95 0.94 - 0.11 - - cmove rax, rdi
- - - - - 1.00 - - ret
@@ -1,19 +1,19 @@
bench_ref_from_prefix_dynamic_padding:
xor edx, edx
mov eax, 0
test dil, 3
je .LBB5_1
sete al
cmp rsi, 12
setae cl
test al, cl
jne .LBB5_2
xor edx, edx
test dil, 3
sete dl
xor edi, edi
mov rax, rdi
ret
.LBB5_1:
movabs rax, 9223372036854775804
and rsi, rax
cmp rsi, 9
jae .LBB5_3
mov edx, 1
xor eax, eax
ret
.LBB5_3:
add rsi, -9
.LBB5_2:
or rsi, 3
add rsi, -12
movabs rcx, -6148914691236517205
mov rax, rsi
mul rcx
@@ -1,12 +1,12 @@
Iterations: 100
Instructions: 1900
Total Cycles: 608
Total uOps: 2000
Instructions: 2000
Total Cycles: 640
Total uOps: 2100
Dispatch Width: 4
uOps Per Cycle: 3.29
uOps Per Cycle: 3.28
IPC: 3.13
Block RThroughput: 5.0
Block RThroughput: 5.3
Instruction Info:
@@ -18,19 +18,20 @@ Instruction Info:
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 0 0.25 xor edx, edx
1 1 0.33 mov eax, 0
1 1 0.33 test dil, 3
1 1 1.00 je .LBB5_1
1 1 0.50 sete al
1 1 0.33 cmp rsi, 12
1 1 0.50 setae cl
1 1 0.33 test al, cl
1 1 1.00 jne .LBB5_2
1 0 0.25 xor edx, edx
1 1 0.33 test dil, 3
1 1 0.50 sete dl
1 0 0.25 xor edi, edi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
1 1 0.33 movabs rax, 9223372036854775804
1 1 0.33 and rsi, rax
1 1 0.33 cmp rsi, 9
1 1 1.00 jae .LBB5_3
1 1 0.33 mov edx, 1
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.33 add rsi, -9
1 1 0.33 or rsi, 3
1 1 0.33 add rsi, -12
1 1 0.33 movabs rcx, -6148914691236517205
1 1 0.33 mov rax, rsi
2 4 1.00 mul rcx
@@ -52,26 +53,27 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.00 6.00 - 6.00 - -
- - 6.33 6.33 - 6.34 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.65 0.34 - 0.01 - - test dil, 3
- - 0.99 - - 0.01 - - sete al
- - 0.01 0.33 - 0.66 - - cmp rsi, 12
- - 0.34 - - 0.66 - - setae cl
- - 1.00 - - - - - test al, cl
- - - - - 1.00 - - jne .LBB5_2
- - - - - - - - xor edx, edx
- - 0.01 0.98 - 0.01 - - mov eax, 0
- - 0.98 0.01 - 0.01 - - test dil, 3
- - - - - 1.00 - - je .LBB5_1
- - - 0.99 - 0.01 - - test dil, 3
- - - - - 1.00 - - sete dl
- - - - - - - - xor edi, edi
- - - 1.00 - - - - mov rax, rdi
- - - - - 1.00 - - ret
- - 0.01 0.99 - - - - movabs rax, 9223372036854775804
- - - 1.00 - - - - and rsi, rax
- - - 1.00 - - - - cmp rsi, 9
- - - - - 1.00 - - jae .LBB5_3
- - 1.00 - - - - - mov edx, 1
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.02 0.02 - 0.96 - - add rsi, -9
- - 0.99 0.01 - - - - movabs rcx, -6148914691236517205
- - 0.01 0.99 - - - - mov rax, rsi
- - 0.01 0.99 - - - - or rsi, 3
- - 0.33 0.67 - - - - add rsi, -12
- - 0.66 0.34 - - - - movabs rcx, -6148914691236517205
- - 0.34 0.33 - 0.33 - - mov rax, rsi
- - 1.00 1.00 - - - - mul rcx
- - 1.00 - - - - - shr rdx
- - 0.98 - - 0.02 - - mov rax, rdi
- - - 0.34 - 0.66 - - mov rax, rdi
- - - - - 1.00 - - ret
@@ -1,17 +1,10 @@
bench_ref_from_prefix_dynamic_size:
xor edx, edx
mov eax, 0
test dil, 1
jne .LBB5_4
cmp rsi, 4
jae .LBB5_3
mov edx, 1
xor eax, eax
ret
.LBB5_3:
add rsi, -4
shr rsi
mov rdx, rsi
mov rax, rdi
.LBB5_4:
xor eax, eax
sub rdx, 4
mov rcx, rdi
cmovb rcx, rax
shr rdx
test dil, 1
cmove rax, rcx
ret
@@ -1,12 +1,12 @@
Iterations: 100
Instructions: 1400
Total Cycles: 405
Total uOps: 1400
Instructions: 900
Total Cycles: 339
Total uOps: 1100
Dispatch Width: 4
uOps Per Cycle: 3.46
IPC: 3.46
Block RThroughput: 4.0
uOps Per Cycle: 3.24
IPC: 2.65
Block RThroughput: 2.8
Instruction Info:
@@ -18,19 +18,14 @@ Instruction Info:
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 0 0.25 xor edx, edx
1 1 0.33 mov eax, 0
1 1 0.33 test dil, 1
1 1 1.00 jne .LBB5_4
1 1 0.33 cmp rsi, 4
1 1 1.00 jae .LBB5_3
1 1 0.33 mov edx, 1
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.33 add rsi, -4
1 1 0.50 shr rsi
1 1 0.33 mov rdx, rsi
1 1 0.33 mov rax, rdi
1 0 0.25 xor eax, eax
1 1 0.33 sub rdx, 4
1 1 0.33 mov rcx, rdi
2 2 0.67 cmovb rcx, rax
1 1 0.50 shr rdx
1 1 0.33 test dil, 1
2 2 0.67 cmove rax, rcx
1 1 1.00 U ret
@@ -47,21 +42,16 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 3.99 3.99 - 4.02 - -
- - 3.33 3.33 - 3.34 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - - xor edx, edx
- - 0.01 0.98 - 0.01 - - mov eax, 0
- - 0.98 0.02 - - - - test dil, 1
- - - - - 1.00 - - jne .LBB5_4
- - 0.02 0.98 - - - - cmp rsi, 4
- - - - - 1.00 - - jae .LBB5_3
- - 0.98 0.01 - 0.01 - - mov edx, 1
- - 0.33 0.66 - 0.01 - - mov rdx, rsi
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.01 0.99 - - - - add rsi, -4
- - 1.00 - - - - - shr rsi
- - - 1.00 - - - - mov rdx, rsi
- - 0.99 0.01 - - - - mov rax, rdi
- - 0.01 - - 0.99 - - sub rdx, 4
- - 0.66 0.34 - - - - mov rcx, rdi
- - 1.00 1.00 - - - - cmovb rcx, rax
- - 0.33 - - 0.67 - - shr rdx
- - - 0.33 - 0.67 - - test dil, 1
- - 1.00 1.00 - - - - cmove rax, rcx
- - - - - 1.00 - - ret
@@ -1,23 +1,22 @@
bench_ref_from_suffix_dynamic_padding:
lea eax, [rsi + rdi]
test al, 3
jne .LBB5_1
movabs rax, 9223372036854775804
and rax, rsi
cmp rax, 9
jae .LBB5_3
.LBB5_1:
xor eax, eax
setne al
cmp rsi, 12
setb cl
or cl, al
je .LBB5_2
xor edi, edi
mov rax, rdi
ret
.LBB5_3:
add rax, -9
.LBB5_2:
mov rax, rsi
or rax, 3
add rax, -12
movabs rcx, -6148914691236517205
mul rcx
shr rdx
lea rax, [rdx + 2*rdx]
sub rsi, rax
or rax, -4
add rsi, rdi
add rax, rsi
add rax, -8
and esi, 3
add rdi, rsi
mov rax, rdi
ret
@@ -1,11 +1,11 @@
Iterations: 100
Instructions: 2000
Total Cycles: 682
Total Cycles: 673
Total uOps: 2100
Dispatch Width: 4
uOps Per Cycle: 3.08
IPC: 2.93
uOps Per Cycle: 3.12
IPC: 2.97
Block RThroughput: 5.3
@@ -20,23 +20,23 @@ Instruction Info:
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.50 lea eax, [rsi + rdi]
1 1 0.33 test al, 3
1 1 1.00 jne .LBB5_1
1 1 0.33 movabs rax, 9223372036854775804
1 1 0.33 and rax, rsi
1 1 0.33 cmp rax, 9
1 1 1.00 jae .LBB5_3
1 0 0.25 xor eax, eax
1 1 0.50 setne al
1 1 0.33 cmp rsi, 12
1 1 0.50 setb cl
1 1 0.33 or cl, al
1 1 1.00 je .LBB5_2
1 0 0.25 xor edi, edi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
1 1 0.33 add rax, -9
1 1 0.33 mov rax, rsi
1 1 0.33 or rax, 3
1 1 0.33 add rax, -12
1 1 0.33 movabs rcx, -6148914691236517205
2 4 1.00 mul rcx
1 1 0.50 shr rdx
1 1 0.50 lea rax, [rdx + 2*rdx]
1 1 0.33 sub rsi, rax
1 1 0.33 or rax, -4
1 1 0.33 add rsi, rdi
1 1 0.33 add rax, rsi
1 1 0.33 add rax, -8
1 1 0.33 and esi, 3
1 1 0.33 add rdi, rsi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
@@ -53,27 +53,27 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.65 6.67 - 6.68 - -
- - 6.67 6.66 - 6.67 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.90 0.10 - - - - lea eax, [rsi + rdi]
- - 0.93 - - 0.07 - - test al, 3
- - - - - 1.00 - - jne .LBB5_1
- - 0.51 0.47 - 0.02 - - movabs rax, 9223372036854775804
- - - - - 1.00 - - and rax, rsi
- - - 0.09 - 0.91 - - cmp rax, 9
- - - - - 1.00 - - jae .LBB5_3
- - - - - - - - xor eax, eax
- - 0.35 0.65 - - - - lea eax, [rsi + rdi]
- - 0.01 0.99 - - - - test al, 3
- - 1.00 - - - - - setne al
- - 0.97 - - 0.03 - - cmp rsi, 12
- - 0.66 - - 0.34 - - setb cl
- - - 1.00 - - - - or cl, al
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor edi, edi
- - 0.01 0.01 - 0.98 - - mov rax, rdi
- - - - - 1.00 - - ret
- - 0.43 0.47 - 0.10 - - add rax, -9
- - 0.42 0.39 - 0.19 - - movabs rcx, -6148914691236517205
- - 0.01 0.99 - - - - mov rax, rsi
- - - 0.34 - 0.66 - - or rax, 3
- - - 1.00 - - - - add rax, -12
- - 0.66 0.31 - 0.03 - - movabs rcx, -6148914691236517205
- - 1.00 1.00 - - - - mul rcx
- - 0.69 - - 0.31 - - shr rdx
- - 0.54 0.46 - - - - lea rax, [rdx + 2*rdx]
- - 0.07 0.91 - 0.02 - - sub rsi, rax
- - 0.91 0.05 - 0.04 - - or rax, -4
- - 0.08 0.90 - 0.02 - - add rsi, rdi
- - 0.09 0.91 - - - - add rax, rsi
- - 0.08 0.92 - - - - add rax, -8
- - 1.00 - - - - - shr rdx
- - 0.67 0.01 - 0.32 - - and esi, 3
- - 0.32 0.01 - 0.67 - - add rdi, rsi
- - 0.01 0.35 - 0.64 - - mov rax, rdi
- - - - - 1.00 - - ret
@@ -1,27 +1,21 @@
bench_ref_from_suffix_with_elems_dynamic_padding:
movabs rax, 3074457345618258598
cmp rdx, rax
ja .LBB5_1
lea r8d, [rsi + rdi]
xor ecx, ecx
mov eax, 0
test r8b, 3
je .LBB5_3
mov rdx, rcx
ret
movabs rcx, 3074457345618258598
cmp rdx, rcx
ja .LBB5_3
mov rax, rdi
lea rcx, [rdx + 2*rdx]
or rcx, 3
add rcx, 9
add edi, esi
test dil, 3
setne dil
sub rsi, rcx
setb cl
or cl, dil
je .LBB5_4
.LBB5_3:
lea rax, [rdx + 2*rdx]
or rax, 3
add rax, 9
sub rsi, rax
jae .LBB5_4
.LBB5_1:
xor eax, eax
mov edx, 1
ret
.LBB5_4:
add rdi, rsi
mov rcx, rdx
mov rax, rdi
mov rdx, rcx
add rax, rsi
ret
@@ -1,12 +1,12 @@
Iterations: 100
Instructions: 2300
Total Cycles: 706
Total uOps: 2300
Instructions: 1800
Total Cycles: 572
Total uOps: 1800
Dispatch Width: 4
uOps Per Cycle: 3.26
IPC: 3.26
Block RThroughput: 6.0
uOps Per Cycle: 3.15
IPC: 3.15
Block RThroughput: 4.5
Instruction Info:
@@ -18,28 +18,23 @@ Instruction Info:
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 movabs rax, 3074457345618258598
1 1 0.33 cmp rdx, rax
1 1 1.00 ja .LBB5_1
1 1 0.50 lea r8d, [rsi + rdi]
1 0 0.25 xor ecx, ecx
1 1 0.33 mov eax, 0
1 1 0.33 test r8b, 3
1 1 1.00 je .LBB5_3
1 1 0.33 mov rdx, rcx
1 1 1.00 U ret
1 1 0.50 lea rax, [rdx + 2*rdx]
1 1 0.33 or rax, 3
1 1 0.33 add rax, 9
1 1 0.33 sub rsi, rax
1 1 1.00 jae .LBB5_4
1 0 0.25 xor eax, eax
1 1 0.33 mov edx, 1
1 1 1.00 U ret
1 1 0.33 add rdi, rsi
1 1 0.33 mov rcx, rdx
1 1 0.33 movabs rcx, 3074457345618258598
1 1 0.33 cmp rdx, rcx
1 1 1.00 ja .LBB5_3
1 1 0.33 mov rax, rdi
1 1 0.33 mov rdx, rcx
1 1 0.50 lea rcx, [rdx + 2*rdx]
1 1 0.33 or rcx, 3
1 1 0.33 add rcx, 9
1 1 0.33 add edi, esi
1 1 0.33 test dil, 3
1 1 0.50 setne dil
1 1 0.33 sub rsi, rcx
1 1 0.50 setb cl
1 1 0.33 or cl, dil
1 1 1.00 je .LBB5_4
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.33 add rax, rsi
1 1 1.00 U ret
@@ -56,30 +51,25 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.99 7.00 - 7.01 - -
- - 5.66 5.66 - 5.68 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - movabs rax, 3074457345618258598
- - 0.01 0.50 - 0.49 - - cmp rdx, rax
- - - - - 1.00 - - ja .LBB5_1
- - - 1.00 - - - - lea r8d, [rsi + rdi]
- - - - - - - - xor ecx, ecx
- - 0.50 0.49 - 0.01 - - mov eax, 0
- - 0.49 0.51 - - - - test r8b, 3
- - - - - 1.00 - - je .LBB5_3
- - 0.51 0.49 - - - - mov rdx, rcx
- - - - - 1.00 - - ret
- - 0.50 0.50 - - - - lea rax, [rdx + 2*rdx]
- - 1.00 - - - - - or rax, 3
- - 1.00 - - - - - add rax, 9
- - 0.99 0.01 - - - - sub rsi, rax
- - - - - 1.00 - - jae .LBB5_4
- - 0.67 - - 0.33 - - movabs rcx, 3074457345618258598
- - 0.34 - - 0.66 - - cmp rdx, rcx
- - - - - 1.00 - - ja .LBB5_3
- - 0.33 0.01 - 0.66 - - mov rax, rdi
- - 0.33 0.67 - - - - lea rcx, [rdx + 2*rdx]
- - 0.01 0.99 - - - - or rcx, 3
- - 0.01 0.99 - - - - add rcx, 9
- - 0.99 - - 0.01 - - add edi, esi
- - 0.99 0.01 - - - - test dil, 3
- - 0.99 - - 0.01 - - setne dil
- - - 1.00 - - - - sub rsi, rcx
- - 1.00 - - - - - setb cl
- - - 0.99 - 0.01 - - or cl, dil
- - - - - 1.00 - - je .LBB5_4
- - - - - - - - xor eax, eax
- - - 1.00 - - - - mov edx, 1
- - - - - 1.00 - - ret
- - 1.00 - - - - - add rdi, rsi
- - - 1.00 - - - - mov rcx, rdx
- - 0.99 0.01 - - - - mov rax, rdi
- - - 0.50 - 0.50 - - mov rdx, rcx
- - - 1.00 - - - - add rax, rsi
- - - - - 1.00 - - ret
@@ -1,23 +1,18 @@
bench_ref_from_suffix_with_elems_dynamic_size:
movabs rax, 4611686018427387901
cmp rdx, rax
ja .LBB5_1
lea r8d, [rsi + rdi]
xor ecx, ecx
mov eax, 0
test r8b, 1
jne .LBB5_5
lea rax, [2*rdx + 4]
sub rsi, rax
jae .LBB5_4
.LBB5_1:
movabs rcx, 4611686018427387901
cmp rdx, rcx
ja .LBB5_3
mov rax, rdi
lea rcx, [2*rdx + 4]
add edi, esi
sub rsi, rcx
setb cl
or cl, dil
test cl, 1
je .LBB5_4
.LBB5_3:
xor eax, eax
mov edx, 1
ret
.LBB5_4:
add rdi, rsi
mov rcx, rdx
mov rax, rdi
.LBB5_5:
mov rdx, rcx
add rax, rsi
ret
@@ -1,12 +1,12 @@
Iterations: 100
Instructions: 1900
Total Cycles: 571
Total uOps: 1900
Instructions: 1500
Total Cycles: 472
Total uOps: 1500
Dispatch Width: 4
uOps Per Cycle: 3.33
IPC: 3.33
Block RThroughput: 5.0
uOps Per Cycle: 3.18
IPC: 3.18
Block RThroughput: 4.0
Instruction Info:
@@ -18,24 +18,20 @@ Instruction Info:
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 movabs rax, 4611686018427387901
1 1 0.33 cmp rdx, rax
1 1 1.00 ja .LBB5_1
1 1 0.50 lea r8d, [rsi + rdi]
1 0 0.25 xor ecx, ecx
1 1 0.33 mov eax, 0
1 1 0.33 test r8b, 1
1 1 1.00 jne .LBB5_5
1 1 0.50 lea rax, [2*rdx + 4]
1 1 0.33 sub rsi, rax
1 1 1.00 jae .LBB5_4
1 0 0.25 xor eax, eax
1 1 0.33 mov edx, 1
1 1 1.00 U ret
1 1 0.33 add rdi, rsi
1 1 0.33 mov rcx, rdx
1 1 0.33 movabs rcx, 4611686018427387901
1 1 0.33 cmp rdx, rcx
1 1 1.00 ja .LBB5_3
1 1 0.33 mov rax, rdi
1 1 0.33 mov rdx, rcx
1 1 0.50 lea rcx, [2*rdx + 4]
1 1 0.33 add edi, esi
1 1 0.33 sub rsi, rcx
1 1 0.50 setb cl
1 1 0.33 or cl, dil
1 1 0.33 test cl, 1
1 1 1.00 je .LBB5_4
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.33 add rax, rsi
1 1 1.00 U ret
@@ -52,26 +48,22 @@ Resources:
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 5.66 5.66 - 5.68 - -
- - 4.66 4.66 - 4.68 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.66 0.33 - 0.01 - - movabs rax, 4611686018427387901
- - 0.01 0.99 - - - - cmp rdx, rax
- - - - - 1.00 - - ja .LBB5_1
- - 0.99 0.01 - - - - lea r8d, [rsi + rdi]
- - - - - - - - xor ecx, ecx
- - 0.33 0.33 - 0.34 - - mov eax, 0
- - 0.33 0.34 - 0.33 - - test r8b, 1
- - - - - 1.00 - - jne .LBB5_5
- - 0.34 0.66 - - - - lea rax, [2*rdx + 4]
- - - 1.00 - - - - sub rsi, rax
- - - - - 1.00 - - jae .LBB5_4
- - 0.99 - - 0.01 - - movabs rcx, 4611686018427387901
- - 1.00 - - - - - cmp rdx, rcx
- - - - - 1.00 - - ja .LBB5_3
- - 0.63 0.02 - 0.35 - - mov rax, rdi
- - 0.35 0.65 - - - - lea rcx, [2*rdx + 4]
- - 0.65 0.03 - 0.32 - - add edi, esi
- - 0.03 0.97 - - - - sub rsi, rcx
- - 1.00 - - - - - setb cl
- - - 1.00 - - - - or cl, dil
- - 0.01 0.99 - - - - test cl, 1
- - - - - 1.00 - - je .LBB5_4
- - - - - - - - xor eax, eax
- - 1.00 - - - - - mov edx, 1
- - - - - 1.00 - - ret
- - - 1.00 - - - - add rdi, rsi
- - 1.00 - - - - - mov rcx, rdx
- - 0.32 0.68 - - - - mov rax, rdi
- - 0.68 0.32 - - - - mov rdx, rcx
- - - 1.00 - - - - add rax, rsi
- - - - - 1.00 - - ret
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_bytes_dynamic_padding<'a>(
source: &'a Ref<&[u8], format::LocoPacket>,
) -> &'a [u8] {
Ref::bytes(source)
}
@@ -0,0 +1,4 @@
bench_ref_type_bytes_dynamic_padding:
mov rax, qword ptr [rdi]
mov rdx, qword ptr [rdi + 8]
ret
@@ -0,0 +1,45 @@
Iterations: 100
Instructions: 300
Total Cycles: 107
Total uOps: 300
Dispatch Width: 4
uOps Per Cycle: 2.80
IPC: 2.80
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rdx, qword ptr [rdi + 8]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - 1.00 mov rax, qword ptr [rdi]
- - - - - - 1.00 - mov rdx, qword ptr [rdi + 8]
- - - - - 1.00 - - ret
@@ -0,0 +1,17 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_bytes_dynamic_size<'a>(source: &'a Ref<&[u8], format::LocoPacket>) -> &'a [u8] {
Ref::bytes(source)
}
@@ -0,0 +1,4 @@
bench_ref_type_bytes_dynamic_size:
mov rax, qword ptr [rdi]
mov rdx, qword ptr [rdi + 8]
ret
@@ -0,0 +1,45 @@
Iterations: 100
Instructions: 300
Total Cycles: 107
Total uOps: 300
Dispatch Width: 4
uOps Per Cycle: 2.80
IPC: 2.80
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rdx, qword ptr [rdi + 8]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - 1.00 mov rax, qword ptr [rdi]
- - - - - - 1.00 - mov rdx, qword ptr [rdi + 8]
- - - - - 1.00 - - ret
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_bytes_mut_dynamic_padding<'a>(
source: &'a mut Ref<&mut [u8], format::LocoPacket>,
) -> &'a mut [u8] {
Ref::bytes_mut(source)
}
@@ -0,0 +1,4 @@
bench_ref_type_bytes_mut_dynamic_padding:
mov rax, qword ptr [rdi]
mov rdx, qword ptr [rdi + 8]
ret
@@ -0,0 +1,45 @@
Iterations: 100
Instructions: 300
Total Cycles: 107
Total uOps: 300
Dispatch Width: 4
uOps Per Cycle: 2.80
IPC: 2.80
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rdx, qword ptr [rdi + 8]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - 1.00 mov rax, qword ptr [rdi]
- - - - - - 1.00 - mov rdx, qword ptr [rdi + 8]
- - - - - 1.00 - - ret
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_bytes_mut_dynamic_size<'a>(
source: &'a mut Ref<&mut [u8], format::LocoPacket>,
) -> &'a mut [u8] {
Ref::bytes_mut(source)
}
@@ -0,0 +1,4 @@
bench_ref_type_bytes_mut_dynamic_size:
mov rax, qword ptr [rdi]
mov rdx, qword ptr [rdi + 8]
ret
@@ -0,0 +1,45 @@
Iterations: 100
Instructions: 300
Total Cycles: 107
Total uOps: 300
Dispatch Width: 4
uOps Per Cycle: 2.80
IPC: 2.80
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rdx, qword ptr [rdi + 8]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - 1.00 mov rax, qword ptr [rdi]
- - - - - - 1.00 - mov rdx, qword ptr [rdi + 8]
- - - - - 1.00 - - ret
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_bytes_mut_static_size<'a>(
source: &'a mut Ref<&mut [u8], format::LocoPacket>,
) -> &'a mut [u8] {
Ref::bytes_mut(source)
}
@@ -0,0 +1,4 @@
bench_ref_type_bytes_mut_static_size:
mov rax, qword ptr [rdi]
mov rdx, qword ptr [rdi + 8]
ret
@@ -0,0 +1,45 @@
Iterations: 100
Instructions: 300
Total Cycles: 107
Total uOps: 300
Dispatch Width: 4
uOps Per Cycle: 2.80
IPC: 2.80
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rdx, qword ptr [rdi + 8]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - 1.00 mov rax, qword ptr [rdi]
- - - - - - 1.00 - mov rdx, qword ptr [rdi + 8]
- - - - - 1.00 - - ret
@@ -0,0 +1,17 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_bytes_static_size<'a>(source: &'a Ref<&[u8], format::LocoPacket>) -> &'a [u8] {
Ref::bytes(source)
}
@@ -0,0 +1,4 @@
bench_ref_type_bytes_static_size:
mov rax, qword ptr [rdi]
mov rdx, qword ptr [rdi + 8]
ret
@@ -0,0 +1,45 @@
Iterations: 100
Instructions: 300
Total Cycles: 107
Total uOps: 300
Dispatch Width: 4
uOps Per Cycle: 2.80
IPC: 2.80
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rdx, qword ptr [rdi + 8]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - 1.00 mov rax, qword ptr [rdi]
- - - - - - 1.00 - mov rdx, qword ptr [rdi + 8]
- - - - - 1.00 - - ret
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_deref_dynamic_padding<'a>(
source: &'a Ref<&[u8], format::LocoPacket>,
) -> &'a format::LocoPacket {
core::ops::Deref::deref(source)
}
@@ -0,0 +1,29 @@
bench_ref_type_deref_dynamic_padding:
mov rdx, qword ptr [rdi + 16]
mov rax, qword ptr [rdi]
mov rcx, qword ptr [rdi + 8]
mov esi, 1
movabs rdi, 3074457345618258599
cmp rdx, rdi
jae .LBB8_3
test al, 3
jne .LBB8_2
lea rdi, [rdx + 2*rdx]
or rdi, 3
add rdi, 9
cmp rcx, rdi
jne .LBB8_3
ret
.LBB8_2:
xor esi, esi
.LBB8_3:
sub rsp, 24
mov qword ptr [rsp], rsi
mov qword ptr [rsp + 8], rax
mov qword ptr [rsp + 16], rcx
lea rdi, [rip + .Lanon.HASH.7]
lea rcx, [rip + .Lanon.HASH.1]
lea r8, [rip + .Lanon.HASH.9]
mov rdx, rsp
mov esi, 58
call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
@@ -0,0 +1,91 @@
Iterations: 100
Instructions: 2600
Total Cycles: 1812
Total uOps: 2900
Dispatch Width: 4
uOps Per Cycle: 1.60
IPC: 1.43
Block RThroughput: 7.3
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rdx, qword ptr [rdi + 16]
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rcx, qword ptr [rdi + 8]
1 1 0.33 mov esi, 1
1 1 0.33 movabs rdi, 3074457345618258599
1 1 0.33 cmp rdx, rdi
1 1 1.00 jae .LBB8_3
1 1 0.33 test al, 3
1 1 1.00 jne .LBB8_2
1 1 0.50 lea rdi, [rdx + 2*rdx]
1 1 0.33 or rdi, 3
1 1 0.33 add rdi, 9
1 1 0.33 cmp rcx, rdi
1 1 1.00 jne .LBB8_3
1 1 1.00 U ret
1 0 0.25 xor esi, esi
1 1 0.33 sub rsp, 24
1 1 1.00 * mov qword ptr [rsp], rsi
1 1 1.00 * mov qword ptr [rsp + 8], rax
1 1 1.00 * mov qword ptr [rsp + 16], rcx
1 1 0.50 lea rdi, [rip + .Lanon.HASH.7]
1 1 0.50 lea rcx, [rip + .Lanon.HASH.1]
1 1 0.50 lea r8, [rip + .Lanon.HASH.9]
1 1 0.33 mov rdx, rsp
1 1 0.33 mov esi, 58
4 7 1.00 * call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.41 6.42 4.00 6.17 3.99 4.01
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - 0.54 0.46 mov rdx, qword ptr [rdi + 16]
- - - - - - 0.50 0.50 mov rax, qword ptr [rdi]
- - - - - - 0.49 0.51 mov rcx, qword ptr [rdi + 8]
- - 0.52 0.47 - 0.01 - - mov esi, 1
- - 0.47 0.53 - - - - movabs rdi, 3074457345618258599
- - 0.47 0.53 - - - - cmp rdx, rdi
- - - - - 1.00 - - jae .LBB8_3
- - 0.54 0.44 - 0.02 - - test al, 3
- - - - - 1.00 - - jne .LBB8_2
- - 0.44 0.56 - - - - lea rdi, [rdx + 2*rdx]
- - 0.48 0.52 - - - - or rdi, 3
- - 0.48 0.48 - 0.04 - - add rdi, 9
- - 0.51 0.39 - 0.10 - - cmp rcx, rdi
- - - - - 1.00 - - jne .LBB8_3
- - - - - 1.00 - - ret
- - - - - - - - xor esi, esi
- - 0.53 0.47 - - - - sub rsp, 24
- - - - 1.00 - 0.52 0.48 mov qword ptr [rsp], rsi
- - - - 1.00 - 0.48 0.52 mov qword ptr [rsp + 8], rax
- - - - 1.00 - 0.52 0.48 mov qword ptr [rsp + 16], rcx
- - 0.49 0.51 - - - - lea rdi, [rip + .Lanon.HASH.7]
- - 0.46 0.54 - - - - lea rcx, [rip + .Lanon.HASH.1]
- - 0.54 0.46 - - - - lea r8, [rip + .Lanon.HASH.9]
- - 0.04 0.06 - 0.90 - - mov rdx, rsp
- - 0.44 0.46 - 0.10 - - mov esi, 58
- - - - 1.00 1.00 0.94 1.06 call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_deref_dynamic_size<'a>(
source: &'a Ref<&[u8], format::LocoPacket>,
) -> &'a format::LocoPacket {
core::ops::Deref::deref(source)
}
@@ -0,0 +1,27 @@
bench_ref_type_deref_dynamic_size:
mov rdx, qword ptr [rdi + 16]
mov rax, qword ptr [rdi]
mov rcx, qword ptr [rdi + 8]
mov esi, 1
movabs rdi, 4611686018427387902
cmp rdx, rdi
jae .LBB8_3
test al, 1
jne .LBB8_2
lea rdi, [2*rdx + 4]
cmp rcx, rdi
jne .LBB8_3
ret
.LBB8_2:
xor esi, esi
.LBB8_3:
sub rsp, 24
mov qword ptr [rsp], rsi
mov qword ptr [rsp + 8], rax
mov qword ptr [rsp + 16], rcx
lea rdi, [rip + .Lanon.HASH.7]
lea rcx, [rip + .Lanon.HASH.1]
lea r8, [rip + .Lanon.HASH.9]
mov rdx, rsp
mov esi, 58
call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
@@ -0,0 +1,87 @@
Iterations: 100
Instructions: 2400
Total Cycles: 1647
Total uOps: 2700
Dispatch Width: 4
uOps Per Cycle: 1.64
IPC: 1.46
Block RThroughput: 6.8
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rdx, qword ptr [rdi + 16]
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rcx, qword ptr [rdi + 8]
1 1 0.33 mov esi, 1
1 1 0.33 movabs rdi, 4611686018427387902
1 1 0.33 cmp rdx, rdi
1 1 1.00 jae .LBB8_3
1 1 0.33 test al, 1
1 1 1.00 jne .LBB8_2
1 1 0.50 lea rdi, [2*rdx + 4]
1 1 0.33 cmp rcx, rdi
1 1 1.00 jne .LBB8_3
1 1 1.00 U ret
1 0 0.25 xor esi, esi
1 1 0.33 sub rsp, 24
1 1 1.00 * mov qword ptr [rsp], rsi
1 1 1.00 * mov qword ptr [rsp + 8], rax
1 1 1.00 * mov qword ptr [rsp + 16], rcx
1 1 0.50 lea rdi, [rip + .Lanon.HASH.7]
1 1 0.50 lea rcx, [rip + .Lanon.HASH.1]
1 1 0.50 lea r8, [rip + .Lanon.HASH.9]
1 1 0.33 mov rdx, rsp
1 1 0.33 mov esi, 58
4 7 1.00 * call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 5.15 5.74 4.00 6.11 3.98 4.02
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - 0.71 0.29 mov rdx, qword ptr [rdi + 16]
- - - - - - 0.72 0.28 mov rax, qword ptr [rdi]
- - - - - - 0.29 0.71 mov rcx, qword ptr [rdi + 8]
- - 0.83 0.16 - 0.01 - - mov esi, 1
- - 0.16 0.84 - - - - movabs rdi, 4611686018427387902
- - 0.80 0.04 - 0.16 - - cmp rdx, rdi
- - - - - 1.00 - - jae .LBB8_3
- - 0.13 0.74 - 0.13 - - test al, 1
- - - - - 1.00 - - jne .LBB8_2
- - 0.10 0.90 - - - - lea rdi, [2*rdx + 4]
- - 0.09 0.91 - - - - cmp rcx, rdi
- - - - - 1.00 - - jne .LBB8_3
- - - - - 1.00 - - ret
- - - - - - - - xor esi, esi
- - 0.29 0.13 - 0.58 - - sub rsp, 24
- - - - 1.00 - 0.72 0.28 mov qword ptr [rsp], rsi
- - - - 1.00 - 0.28 0.72 mov qword ptr [rsp + 8], rax
- - - - 1.00 - 0.72 0.28 mov qword ptr [rsp + 16], rcx
- - 0.76 0.24 - - - - lea rdi, [rip + .Lanon.HASH.7]
- - 0.82 0.18 - - - - lea rcx, [rip + .Lanon.HASH.1]
- - 0.92 0.08 - - - - lea r8, [rip + .Lanon.HASH.9]
- - 0.09 0.68 - 0.23 - - mov rdx, rsp
- - 0.16 0.84 - - - - mov esi, 58
- - - - 1.00 1.00 0.54 1.46 call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_deref_mut_dynamic_size<'a>(
source: &'a mut Ref<&mut [u8], format::LocoPacket>,
) -> &'a mut format::LocoPacket {
core::ops::DerefMut::deref_mut(source)
}
@@ -0,0 +1,27 @@
bench_ref_type_deref_mut_dynamic_size:
mov rdx, qword ptr [rdi + 16]
mov rax, qword ptr [rdi]
mov rcx, qword ptr [rdi + 8]
mov esi, 1
movabs rdi, 4611686018427387902
cmp rdx, rdi
jae .LBB8_3
test al, 1
jne .LBB8_2
lea rdi, [2*rdx + 4]
cmp rcx, rdi
jne .LBB8_3
ret
.LBB8_2:
xor esi, esi
.LBB8_3:
sub rsp, 24
mov qword ptr [rsp], rsi
mov qword ptr [rsp + 8], rax
mov qword ptr [rsp + 16], rcx
lea rdi, [rip + .Lanon.HASH.7]
lea rcx, [rip + .Lanon.HASH.1]
lea r8, [rip + .Lanon.HASH.9]
mov rdx, rsp
mov esi, 65
call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
@@ -0,0 +1,87 @@
Iterations: 100
Instructions: 2400
Total Cycles: 1647
Total uOps: 2700
Dispatch Width: 4
uOps Per Cycle: 1.64
IPC: 1.46
Block RThroughput: 6.8
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rdx, qword ptr [rdi + 16]
1 5 0.50 * mov rax, qword ptr [rdi]
1 5 0.50 * mov rcx, qword ptr [rdi + 8]
1 1 0.33 mov esi, 1
1 1 0.33 movabs rdi, 4611686018427387902
1 1 0.33 cmp rdx, rdi
1 1 1.00 jae .LBB8_3
1 1 0.33 test al, 1
1 1 1.00 jne .LBB8_2
1 1 0.50 lea rdi, [2*rdx + 4]
1 1 0.33 cmp rcx, rdi
1 1 1.00 jne .LBB8_3
1 1 1.00 U ret
1 0 0.25 xor esi, esi
1 1 0.33 sub rsp, 24
1 1 1.00 * mov qword ptr [rsp], rsi
1 1 1.00 * mov qword ptr [rsp + 8], rax
1 1 1.00 * mov qword ptr [rsp + 16], rcx
1 1 0.50 lea rdi, [rip + .Lanon.HASH.7]
1 1 0.50 lea rcx, [rip + .Lanon.HASH.1]
1 1 0.50 lea r8, [rip + .Lanon.HASH.9]
1 1 0.33 mov rdx, rsp
1 1 0.33 mov esi, 65
4 7 1.00 * call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 5.15 5.74 4.00 6.11 3.98 4.02
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - 0.71 0.29 mov rdx, qword ptr [rdi + 16]
- - - - - - 0.72 0.28 mov rax, qword ptr [rdi]
- - - - - - 0.29 0.71 mov rcx, qword ptr [rdi + 8]
- - 0.83 0.16 - 0.01 - - mov esi, 1
- - 0.16 0.84 - - - - movabs rdi, 4611686018427387902
- - 0.80 0.04 - 0.16 - - cmp rdx, rdi
- - - - - 1.00 - - jae .LBB8_3
- - 0.13 0.74 - 0.13 - - test al, 1
- - - - - 1.00 - - jne .LBB8_2
- - 0.10 0.90 - - - - lea rdi, [2*rdx + 4]
- - 0.09 0.91 - - - - cmp rcx, rdi
- - - - - 1.00 - - jne .LBB8_3
- - - - - 1.00 - - ret
- - - - - - - - xor esi, esi
- - 0.29 0.13 - 0.58 - - sub rsp, 24
- - - - 1.00 - 0.72 0.28 mov qword ptr [rsp], rsi
- - - - 1.00 - 0.28 0.72 mov qword ptr [rsp + 8], rax
- - - - 1.00 - 0.72 0.28 mov qword ptr [rsp + 16], rcx
- - 0.76 0.24 - - - - lea rdi, [rip + .Lanon.HASH.7]
- - 0.82 0.18 - - - - lea rcx, [rip + .Lanon.HASH.1]
- - 0.92 0.08 - - - - lea r8, [rip + .Lanon.HASH.9]
- - 0.09 0.68 - 0.23 - - mov rdx, rsp
- - 0.16 0.84 - - - - mov esi, 65
- - - - 1.00 1.00 0.54 1.46 call qword ptr [rip + core::result::unwrap_failed@GOTPCREL]
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_deref_mut_static_size<'a>(
source: &'a mut Ref<&mut [u8], format::LocoPacket>,
) -> &'a mut format::LocoPacket {
core::ops::DerefMut::deref_mut(source)
}
@@ -0,0 +1,3 @@
bench_ref_type_deref_mut_static_size:
mov rax, qword ptr [rdi]
ret
@@ -0,0 +1,43 @@
Iterations: 100
Instructions: 200
Total Cycles: 103
Total uOps: 200
Dispatch Width: 4
uOps Per Cycle: 1.94
IPC: 1.94
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - 0.50 0.50 mov rax, qword ptr [rdi]
- - - - - 1.00 - - ret
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_deref_static_size<'a>(
source: &'a Ref<&[u8], format::LocoPacket>,
) -> &'a format::LocoPacket {
core::ops::Deref::deref(source)
}
@@ -0,0 +1,3 @@
bench_ref_type_deref_static_size:
mov rax, qword ptr [rdi]
ret
@@ -0,0 +1,43 @@
Iterations: 100
Instructions: 200
Total Cycles: 103
Total uOps: 200
Dispatch Width: 4
uOps Per Cycle: 1.94
IPC: 1.94
Block RThroughput: 1.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 5 0.50 * mov rax, qword ptr [rdi]
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - - - - 1.00 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - 0.50 0.50 mov rax, qword ptr [rdi]
- - - - - 1.00 - - ret
@@ -0,0 +1,19 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_bytes_dynamic_padding(
source: &[u8],
) -> Option<Ref<&[u8], format::LocoPacket>> {
Ref::from_bytes(source).ok()
}
@@ -0,0 +1,25 @@
bench_ref_type_from_bytes_dynamic_padding:
cmp rdx, 12
setae al
mov ecx, edx
or ecx, esi
test cl, 3
sete cl
and cl, al
cmp cl, 1
je .LBB5_2
xor esi, esi
mov qword ptr [rdi], rsi
mov rax, rdi
ret
.LBB5_2:
lea rax, [rdx - 9]
movabs rcx, -6148914691236517205
mov r8, rdx
mul rcx
shr rdx
mov qword ptr [rdi + 8], r8
mov qword ptr [rdi + 16], rdx
mov qword ptr [rdi], rsi
mov rax, rdi
ret
@@ -0,0 +1,85 @@
Iterations: 100
Instructions: 2300
Total Cycles: 909
Total uOps: 2400
Dispatch Width: 4
uOps Per Cycle: 2.64
IPC: 2.53
Block RThroughput: 6.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 cmp rdx, 12
1 1 0.50 setae al
1 1 0.33 mov ecx, edx
1 1 0.33 or ecx, esi
1 1 0.33 test cl, 3
1 1 0.50 sete cl
1 1 0.33 and cl, al
1 1 0.33 cmp cl, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor esi, esi
1 1 1.00 * mov qword ptr [rdi], rsi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
1 1 0.50 lea rax, [rdx - 9]
1 1 0.33 movabs rcx, -6148914691236517205
1 1 0.33 mov r8, rdx
2 4 1.00 mul rcx
1 1 0.50 shr rdx
1 1 1.00 * mov qword ptr [rdi + 8], r8
1 1 1.00 * mov qword ptr [rdi + 16], rdx
1 1 1.00 * mov qword ptr [rdi], rsi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.49 5.99 4.00 6.52 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - cmp rdx, 12
- - 0.53 - - 0.47 - - setae al
- - 0.52 0.01 - 0.47 - - mov ecx, edx
- - - 0.99 - 0.01 - - or ecx, esi
- - - 0.51 - 0.49 - - test cl, 3
- - 0.52 - - 0.48 - - sete cl
- - 0.47 0.48 - 0.05 - - and cl, al
- - 0.48 0.48 - 0.04 - - cmp cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor esi, esi
- - - - 1.00 - - 1.00 mov qword ptr [rdi], rsi
- - 0.03 0.48 - 0.49 - - mov rax, rdi
- - - - - 1.00 - - ret
- - 0.97 0.03 - - - - lea rax, [rdx - 9]
- - 0.49 0.51 - - - - movabs rcx, -6148914691236517205
- - 0.47 0.01 - 0.52 - - mov r8, rdx
- - 1.00 1.00 - - - - mul rcx
- - 1.00 - - - - - shr rdx
- - - - 1.00 - 1.00 - mov qword ptr [rdi + 8], r8
- - - - 1.00 - - 1.00 mov qword ptr [rdi + 16], rdx
- - - - 1.00 - 1.00 - mov qword ptr [rdi], rsi
- - 0.01 0.50 - 0.49 - - mov rax, rdi
- - - - - 1.00 - - ret
@@ -0,0 +1,17 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_bytes_dynamic_size(source: &[u8]) -> Option<Ref<&[u8], format::LocoPacket>> {
Ref::from_bytes(source).ok()
}
@@ -0,0 +1,21 @@
bench_ref_type_from_bytes_dynamic_size:
mov rax, rdi
cmp rdx, 4
setae cl
mov edi, edx
or edi, esi
test dil, 1
sete dil
and dil, cl
cmp dil, 1
je .LBB5_2
xor esi, esi
mov qword ptr [rax], rsi
ret
.LBB5_2:
lea rcx, [rdx - 4]
shr rcx
mov qword ptr [rax + 8], rdx
mov qword ptr [rax + 16], rcx
mov qword ptr [rax], rsi
ret
@@ -0,0 +1,77 @@
Iterations: 100
Instructions: 1900
Total Cycles: 482
Total uOps: 1900
Dispatch Width: 4
uOps Per Cycle: 3.94
IPC: 3.94
Block RThroughput: 4.8
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.33 cmp rdx, 4
1 1 0.50 setae cl
1 1 0.33 mov edi, edx
1 1 0.33 or edi, esi
1 1 0.33 test dil, 1
1 1 0.50 sete dil
1 1 0.33 and dil, cl
1 1 0.33 cmp dil, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor esi, esi
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
1 1 0.50 lea rcx, [rdx - 4]
1 1 0.50 shr rcx
1 1 1.00 * mov qword ptr [rax + 8], rdx
1 1 1.00 * mov qword ptr [rax + 16], rcx
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 4.66 4.66 4.00 4.68 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.40 0.43 - 0.17 - - mov rax, rdi
- - 0.13 0.83 - 0.04 - - cmp rdx, 4
- - 0.67 - - 0.33 - - setae cl
- - 0.05 0.79 - 0.16 - - mov edi, edx
- - 0.36 0.52 - 0.12 - - or edi, esi
- - 0.39 0.52 - 0.09 - - test dil, 1
- - 0.98 - - 0.02 - - sete dil
- - 0.42 0.49 - 0.09 - - and dil, cl
- - 0.43 0.41 - 0.16 - - cmp dil, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor esi, esi
- - - - 1.00 - - 1.00 mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
- - 0.33 0.67 - - - - lea rcx, [rdx - 4]
- - 0.50 - - 0.50 - - shr rcx
- - - - 1.00 - 1.00 - mov qword ptr [rax + 8], rdx
- - - - 1.00 - - 1.00 mov qword ptr [rax + 16], rcx
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
@@ -0,0 +1,17 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_bytes_static_size(source: &[u8]) -> Option<Ref<&[u8], format::LocoPacket>> {
Ref::from_bytes(source).ok()
}
@@ -0,0 +1,15 @@
bench_ref_type_from_bytes_static_size:
mov rax, rdi
cmp rdx, 6
setne cl
or cl, sil
test cl, 1
je .LBB5_2
xor esi, esi
mov qword ptr [rax], rsi
ret
.LBB5_2:
mov qword ptr [rax + 8], 6
mov qword ptr [rax + 16], 0
mov qword ptr [rax], rsi
ret
@@ -0,0 +1,65 @@
Iterations: 100
Instructions: 1300
Total Cycles: 404
Total uOps: 1300
Dispatch Width: 4
uOps Per Cycle: 3.22
IPC: 3.22
Block RThroughput: 4.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.33 cmp rdx, 6
1 1 0.50 setne cl
1 1 0.33 or cl, sil
1 1 0.33 test cl, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor esi, esi
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
1 1 1.00 * mov qword ptr [rax + 8], 6
1 1 1.00 * mov qword ptr [rax + 16], 0
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 2.55 2.34 4.00 3.11 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.81 0.18 - 0.01 - - mov rax, rdi
- - 0.56 0.43 - 0.01 - - cmp rdx, 6
- - 1.00 - - - - - setne cl
- - - 0.99 - 0.01 - - or cl, sil
- - 0.18 0.74 - 0.08 - - test cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor esi, esi
- - - - 1.00 - - 1.00 mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
- - - - 1.00 - 1.00 - mov qword ptr [rax + 8], 6
- - - - 1.00 - - 1.00 mov qword ptr [rax + 16], 0
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
@@ -0,0 +1,20 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_bytes_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<Ref<&[u8], format::LocoPacket>> {
Ref::from_bytes_with_elems(source, count).ok()
}
@@ -0,0 +1,26 @@
bench_ref_type_from_bytes_with_elems_dynamic_padding:
mov rax, rdi
movabs rdi, 6148914691236517201
cmp rcx, rdi
ja .LBB5_3
lea rdi, [rcx + 2*rcx]
or rdi, 3
add rdi, 9
cmp rdx, rdi
sete dil
movabs r8, 3074457345618258599
cmp rcx, r8
setb r8b
and r8b, dil
cmp r8b, 1
jne .LBB5_3
test sil, 3
je .LBB5_4
.LBB5_3:
mov qword ptr [rax], 0
ret
.LBB5_4:
mov qword ptr [rax], rsi
mov qword ptr [rax + 8], rdx
mov qword ptr [rax + 16], rcx
ret
@@ -0,0 +1,85 @@
Iterations: 100
Instructions: 2300
Total Cycles: 640
Total uOps: 2300
Dispatch Width: 4
uOps Per Cycle: 3.59
IPC: 3.59
Block RThroughput: 5.8
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.33 movabs rdi, 6148914691236517201
1 1 0.33 cmp rcx, rdi
1 1 1.00 ja .LBB5_3
1 1 0.50 lea rdi, [rcx + 2*rcx]
1 1 0.33 or rdi, 3
1 1 0.33 add rdi, 9
1 1 0.33 cmp rdx, rdi
1 1 0.50 sete dil
1 1 0.33 movabs r8, 3074457345618258599
1 1 0.33 cmp rcx, r8
1 1 0.50 setb r8b
1 1 0.33 and r8b, dil
1 1 0.33 cmp r8b, 1
1 1 1.00 jne .LBB5_3
1 1 0.33 test sil, 3
1 1 1.00 je .LBB5_4
1 1 1.00 * mov qword ptr [rax], 0
1 1 1.00 U ret
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 * mov qword ptr [rax + 8], rdx
1 1 1.00 * mov qword ptr [rax + 16], rcx
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.32 6.33 4.00 6.35 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.99 - - 0.01 - - mov rax, rdi
- - 0.66 0.34 - - - - movabs rdi, 6148914691236517201
- - 0.34 0.66 - - - - cmp rcx, rdi
- - - - - 1.00 - - ja .LBB5_3
- - 0.66 0.34 - - - - lea rdi, [rcx + 2*rcx]
- - 0.01 0.99 - - - - or rdi, 3
- - 0.01 0.99 - - - - add rdi, 9
- - 0.01 0.99 - - - - cmp rdx, rdi
- - 0.99 - - 0.01 - - sete dil
- - 0.35 0.01 - 0.64 - - movabs r8, 3074457345618258599
- - 0.99 - - 0.01 - - cmp rcx, r8
- - 0.99 - - 0.01 - - setb r8b
- - - 1.00 - - - - and r8b, dil
- - - 1.00 - - - - cmp r8b, 1
- - - - - 1.00 - - jne .LBB5_3
- - 0.32 0.01 - 0.67 - - test sil, 3
- - - - - 1.00 - - je .LBB5_4
- - - - 1.00 - - 1.00 mov qword ptr [rax], 0
- - - - - 1.00 - - ret
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rdx
- - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rcx
- - - - - 1.00 - - ret
@@ -0,0 +1,20 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_bytes_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<Ref<&[u8], format::LocoPacket>> {
Ref::from_bytes_with_elems(source, count).ok()
}
@@ -0,0 +1,28 @@
bench_ref_type_from_bytes_with_elems_dynamic_size:
mov rax, rdi
lea rdi, [rcx + rcx]
test rcx, rcx
setns r8b
cmp rdi, -5
setb dil
and dil, r8b
lea r8, [2*rcx + 4]
cmp rdx, r8
sete r8b
movabs r9, 4611686018427387902
cmp rcx, r9
setb r9b
and r9b, r8b
and r9b, dil
cmp r9b, 1
je .LBB5_1
.LBB5_2:
mov qword ptr [rax], 0
ret
.LBB5_1:
test sil, 1
jne .LBB5_2
mov qword ptr [rax], rsi
mov qword ptr [rax + 8], rdx
mov qword ptr [rax + 16], rcx
ret
@@ -0,0 +1,89 @@
Iterations: 100
Instructions: 2500
Total Cycles: 706
Total uOps: 2500
Dispatch Width: 4
uOps Per Cycle: 3.54
IPC: 3.54
Block RThroughput: 6.3
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.50 lea rdi, [rcx + rcx]
1 1 0.33 test rcx, rcx
1 1 0.50 setns r8b
1 1 0.33 cmp rdi, -5
1 1 0.50 setb dil
1 1 0.33 and dil, r8b
1 1 0.50 lea r8, [2*rcx + 4]
1 1 0.33 cmp rdx, r8
1 1 0.50 sete r8b
1 1 0.33 movabs r9, 4611686018427387902
1 1 0.33 cmp rcx, r9
1 1 0.50 setb r9b
1 1 0.33 and r9b, r8b
1 1 0.33 and r9b, dil
1 1 0.33 cmp r9b, 1
1 1 1.00 je .LBB5_1
1 1 1.00 * mov qword ptr [rax], 0
1 1 1.00 U ret
1 1 0.33 test sil, 1
1 1 1.00 jne .LBB5_2
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 * mov qword ptr [rax + 8], rdx
1 1 1.00 * mov qword ptr [rax + 16], rcx
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.99 7.00 4.00 7.01 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.08 - - 0.92 - - mov rax, rdi
- - 0.99 0.01 - - - - lea rdi, [rcx + rcx]
- - 0.92 0.01 - 0.07 - - test rcx, rcx
- - 0.99 - - 0.01 - - setns r8b
- - 0.01 0.01 - 0.98 - - cmp rdi, -5
- - 0.98 - - 0.02 - - setb dil
- - - 0.99 - 0.01 - - and dil, r8b
- - 0.01 0.99 - - - - lea r8, [2*rcx + 4]
- - 0.99 0.01 - - - - cmp rdx, r8
- - 1.00 - - - - - sete r8b
- - 0.01 - - 0.99 - - movabs r9, 4611686018427387902
- - - 1.00 - - - - cmp rcx, r9
- - 0.99 - - 0.01 - - setb r9b
- - 0.01 0.99 - - - - and r9b, r8b
- - 0.01 0.99 - - - - and r9b, dil
- - - 1.00 - - - - cmp r9b, 1
- - - - - 1.00 - - je .LBB5_1
- - - - 1.00 - - 1.00 mov qword ptr [rax], 0
- - - - - 1.00 - - ret
- - - 1.00 - - - - test sil, 1
- - - - - 1.00 - - jne .LBB5_2
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rdx
- - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rcx
- - - - - 1.00 - - ret
@@ -0,0 +1,22 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_prefix_dynamic_padding(
source: &[u8],
) -> Option<Ref<&[u8], format::LocoPacket>> {
match Ref::from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
@@ -0,0 +1,26 @@
bench_ref_type_from_prefix_dynamic_padding:
test sil, 3
sete al
cmp rdx, 12
setae cl
and cl, al
cmp cl, 1
je .LBB5_2
xor esi, esi
mov qword ptr [rdi], rsi
mov rax, rdi
ret
.LBB5_2:
movabs rcx, 9223372036854775804
and rcx, rdx
or rdx, 3
add rdx, -12
movabs r8, -6148914691236517205
mov rax, rdx
mul r8
shr rdx
mov qword ptr [rdi + 8], rcx
mov qword ptr [rdi + 16], rdx
mov qword ptr [rdi], rsi
mov rax, rdi
ret
@@ -0,0 +1,87 @@
Iterations: 100
Instructions: 2400
Total Cycles: 907
Total uOps: 2500
Dispatch Width: 4
uOps Per Cycle: 2.76
IPC: 2.65
Block RThroughput: 6.3
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 test sil, 3
1 1 0.50 sete al
1 1 0.33 cmp rdx, 12
1 1 0.50 setae cl
1 1 0.33 and cl, al
1 1 0.33 cmp cl, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor esi, esi
1 1 1.00 * mov qword ptr [rdi], rsi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
1 1 0.33 movabs rcx, 9223372036854775804
1 1 0.33 and rcx, rdx
1 1 0.33 or rdx, 3
1 1 0.33 add rdx, -12
1 1 0.33 movabs r8, -6148914691236517205
1 1 0.33 mov rax, rdx
2 4 1.00 mul r8
1 1 0.50 shr rdx
1 1 1.00 * mov qword ptr [rdi + 8], rcx
1 1 1.00 * mov qword ptr [rdi + 16], rdx
1 1 1.00 * mov qword ptr [rdi], rsi
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.96 6.07 4.00 6.97 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.92 0.07 - 0.01 - - test sil, 3
- - 0.98 - - 0.02 - - sete al
- - 0.03 0.97 - - - - cmp rdx, 12
- - 0.99 - - 0.01 - - setae cl
- - 0.03 0.94 - 0.03 - - and cl, al
- - 0.06 0.94 - - - - cmp cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor esi, esi
- - - - 1.00 - - 1.00 mov qword ptr [rdi], rsi
- - 0.01 0.05 - 0.94 - - mov rax, rdi
- - - - - 1.00 - - ret
- - 0.92 0.08 - - - - movabs rcx, 9223372036854775804
- - 0.96 0.01 - 0.03 - - and rcx, rdx
- - - 0.03 - 0.97 - - or rdx, 3
- - 0.01 0.96 - 0.03 - - add rdx, -12
- - 0.03 0.06 - 0.91 - - movabs r8, -6148914691236517205
- - 0.94 0.03 - 0.03 - - mov rax, rdx
- - 1.00 1.00 - - - - mul r8
- - 0.05 - - 0.95 - - shr rdx
- - - - 1.00 - 1.00 - mov qword ptr [rdi + 8], rcx
- - - - 1.00 - - 1.00 mov qword ptr [rdi + 16], rdx
- - - - 1.00 - 1.00 - mov qword ptr [rdi], rsi
- - 0.03 0.93 - 0.04 - - mov rax, rdi
- - - - - 1.00 - - ret
@@ -0,0 +1,22 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_prefix_dynamic_size(
source: &[u8],
) -> Option<Ref<&[u8], format::LocoPacket>> {
match Ref::from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
@@ -0,0 +1,19 @@
bench_ref_type_from_prefix_dynamic_size:
mov rax, rdi
cmp rdx, 4
setb cl
or cl, sil
test cl, 1
je .LBB5_2
xor esi, esi
mov qword ptr [rax], rsi
ret
.LBB5_2:
movabs rcx, 9223372036854775806
and rcx, rdx
add rdx, -4
shr rdx
mov qword ptr [rax + 8], rcx
mov qword ptr [rax + 16], rdx
mov qword ptr [rax], rsi
ret
@@ -0,0 +1,73 @@
Iterations: 100
Instructions: 1700
Total Cycles: 431
Total uOps: 1700
Dispatch Width: 4
uOps Per Cycle: 3.94
IPC: 3.94
Block RThroughput: 4.3
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.33 cmp rdx, 4
1 1 0.50 setb cl
1 1 0.33 or cl, sil
1 1 0.33 test cl, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor esi, esi
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
1 1 0.33 movabs rcx, 9223372036854775806
1 1 0.33 and rcx, rdx
1 1 0.33 add rdx, -4
1 1 0.50 shr rdx
1 1 1.00 * mov qword ptr [rax + 8], rcx
1 1 1.00 * mov qword ptr [rax + 16], rdx
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 3.99 3.75 4.00 4.26 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - mov rax, rdi
- - 0.74 0.26 - - - - cmp rdx, 4
- - 1.00 - - - - - setb cl
- - 0.25 0.74 - 0.01 - - or cl, sil
- - 0.01 0.97 - 0.02 - - test cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor esi, esi
- - - - 1.00 - - 1.00 mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
- - 0.48 0.52 - - - - movabs rcx, 9223372036854775806
- - 0.73 0.27 - - - - and rcx, rdx
- - 0.02 - - 0.98 - - add rdx, -4
- - 0.76 - - 0.24 - - shr rdx
- - - - 1.00 - 1.00 - mov qword ptr [rax + 8], rcx
- - - - 1.00 - - 1.00 mov qword ptr [rax + 16], rdx
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
@@ -0,0 +1,20 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_prefix_static_size(source: &[u8]) -> Option<Ref<&[u8], format::LocoPacket>> {
match Ref::from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
@@ -0,0 +1,15 @@
bench_ref_type_from_prefix_static_size:
mov rax, rdi
cmp rdx, 6
setb cl
or cl, sil
test cl, 1
je .LBB5_2
xor esi, esi
mov qword ptr [rax], rsi
ret
.LBB5_2:
mov qword ptr [rax + 8], 6
mov qword ptr [rax + 16], 0
mov qword ptr [rax], rsi
ret
@@ -0,0 +1,65 @@
Iterations: 100
Instructions: 1300
Total Cycles: 404
Total uOps: 1300
Dispatch Width: 4
uOps Per Cycle: 3.22
IPC: 3.22
Block RThroughput: 4.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.33 cmp rdx, 6
1 1 0.50 setb cl
1 1 0.33 or cl, sil
1 1 0.33 test cl, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor esi, esi
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
1 1 1.00 * mov qword ptr [rax + 8], 6
1 1 1.00 * mov qword ptr [rax + 16], 0
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 2.55 2.34 4.00 3.11 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.81 0.18 - 0.01 - - mov rax, rdi
- - 0.56 0.43 - 0.01 - - cmp rdx, 6
- - 1.00 - - - - - setb cl
- - - 0.99 - 0.01 - - or cl, sil
- - 0.18 0.74 - 0.08 - - test cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor esi, esi
- - - - 1.00 - - 1.00 mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
- - - - 1.00 - 1.00 - mov qword ptr [rax + 8], 6
- - - - 1.00 - - 1.00 mov qword ptr [rax + 16], 0
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - - 1.00 - - ret
@@ -0,0 +1,23 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_prefix_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<Ref<&[u8], format::LocoPacket>> {
match Ref::from_prefix_with_elems(source, count) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
@@ -0,0 +1,26 @@
bench_ref_type_from_prefix_with_elems_dynamic_padding:
mov rax, rdi
movabs rdi, 6148914691236517201
cmp rcx, rdi
ja .LBB5_4
lea rdi, [rcx + 2*rcx]
or rdi, 3
add rdi, 9
cmp rdi, rdx
setbe dl
movabs r8, 3074457345618258599
cmp rcx, r8
setb r8b
and r8b, dl
cmp r8b, 1
jne .LBB5_4
test sil, 3
je .LBB5_3
.LBB5_4:
mov qword ptr [rax], 0
ret
.LBB5_3:
mov qword ptr [rax], rsi
mov qword ptr [rax + 8], rdi
mov qword ptr [rax + 16], rcx
ret
@@ -0,0 +1,85 @@
Iterations: 100
Instructions: 2300
Total Cycles: 674
Total uOps: 2400
Dispatch Width: 4
uOps Per Cycle: 3.56
IPC: 3.41
Block RThroughput: 6.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.33 movabs rdi, 6148914691236517201
1 1 0.33 cmp rcx, rdi
1 1 1.00 ja .LBB5_4
1 1 0.50 lea rdi, [rcx + 2*rcx]
1 1 0.33 or rdi, 3
1 1 0.33 add rdi, 9
1 1 0.33 cmp rdi, rdx
2 2 1.00 setbe dl
1 1 0.33 movabs r8, 3074457345618258599
1 1 0.33 cmp rcx, r8
1 1 0.50 setb r8b
1 1 0.33 and r8b, dl
1 1 0.33 cmp r8b, 1
1 1 1.00 jne .LBB5_4
1 1 0.33 test sil, 3
1 1 1.00 je .LBB5_3
1 1 1.00 * mov qword ptr [rax], 0
1 1 1.00 U ret
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 * mov qword ptr [rax + 8], rdi
1 1 1.00 * mov qword ptr [rax + 16], rcx
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.66 6.65 4.00 6.69 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.01 0.98 - 0.01 - - mov rax, rdi
- - 0.01 0.99 - - - - movabs rdi, 6148914691236517201
- - 0.02 0.98 - - - - cmp rcx, rdi
- - - - - 1.00 - - ja .LBB5_4
- - 0.02 0.98 - - - - lea rdi, [rcx + 2*rcx]
- - 0.68 0.32 - - - - or rdi, 3
- - 0.98 0.02 - - - - add rdi, 9
- - 0.97 0.03 - - - - cmp rdi, rdx
- - 1.96 - - 0.04 - - setbe dl
- - 0.02 0.98 - - - - movabs r8, 3074457345618258599
- - 0.02 0.34 - 0.64 - - cmp rcx, r8
- - 0.02 - - 0.98 - - setb r8b
- - 0.97 0.02 - 0.01 - - and r8b, dl
- - 0.97 0.02 - 0.01 - - cmp r8b, 1
- - - - - 1.00 - - jne .LBB5_4
- - 0.01 0.99 - - - - test sil, 3
- - - - - 1.00 - - je .LBB5_3
- - - - 1.00 - - 1.00 mov qword ptr [rax], 0
- - - - - 1.00 - - ret
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rdi
- - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rcx
- - - - - 1.00 - - ret
@@ -0,0 +1,23 @@
// Copyright 2026 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use zerocopy::Ref;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_type_from_prefix_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<Ref<&[u8], format::LocoPacket>> {
match Ref::from_prefix_with_elems(source, count) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
@@ -0,0 +1,27 @@
bench_ref_type_from_prefix_with_elems_dynamic_size:
mov rax, rdi
lea rdi, [rcx + rcx]
test rcx, rcx
sets r8b
cmp rdi, -5
setae r9b
or r9b, r8b
jne .LBB5_3
add rdi, 4
cmp rdi, rdx
ja .LBB5_3
movabs rdx, 4611686018427387902
cmp rcx, rdx
setb dl
test sil, 1
sete r8b
test dl, r8b
jne .LBB5_4
.LBB5_3:
mov qword ptr [rax], 0
ret
.LBB5_4:
mov qword ptr [rax], rsi
mov qword ptr [rax + 8], rdi
mov qword ptr [rax + 16], rcx
ret
@@ -0,0 +1,87 @@
Iterations: 100
Instructions: 2400
Total Cycles: 672
Total uOps: 2400
Dispatch Width: 4
uOps Per Cycle: 3.57
IPC: 3.57
Block RThroughput: 6.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.33 mov rax, rdi
1 1 0.50 lea rdi, [rcx + rcx]
1 1 0.33 test rcx, rcx
1 1 0.50 sets r8b
1 1 0.33 cmp rdi, -5
1 1 0.50 setae r9b
1 1 0.33 or r9b, r8b
1 1 1.00 jne .LBB5_3
1 1 0.33 add rdi, 4
1 1 0.33 cmp rdi, rdx
1 1 1.00 ja .LBB5_3
1 1 0.33 movabs rdx, 4611686018427387902
1 1 0.33 cmp rcx, rdx
1 1 0.50 setb dl
1 1 0.33 test sil, 1
1 1 0.50 sete r8b
1 1 0.33 test dl, r8b
1 1 1.00 jne .LBB5_4
1 1 1.00 * mov qword ptr [rax], 0
1 1 1.00 U ret
1 1 1.00 * mov qword ptr [rax], rsi
1 1 1.00 * mov qword ptr [rax + 8], rdi
1 1 1.00 * mov qword ptr [rax + 16], rcx
1 1 1.00 U ret
Resources:
[0] - SBDivider
[1] - SBFPDivider
[2] - SBPort0
[3] - SBPort1
[4] - SBPort4
[5] - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23
Resource pressure per iteration:
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
- - 6.66 6.65 4.00 6.69 2.00 2.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.03 0.96 - 0.01 - - mov rax, rdi
- - 0.64 0.36 - - - - lea rdi, [rcx + rcx]
- - 0.34 0.66 - - - - test rcx, rcx
- - 0.99 - - 0.01 - - sets r8b
- - 0.66 0.34 - - - - cmp rdi, -5
- - 0.67 - - 0.33 - - setae r9b
- - 0.01 0.99 - - - - or r9b, r8b
- - - - - 1.00 - - jne .LBB5_3
- - - 0.99 - 0.01 - - add rdi, 4
- - 0.66 0.33 - 0.01 - - cmp rdi, rdx
- - - - - 1.00 - - ja .LBB5_3
- - - 0.01 - 0.99 - - movabs rdx, 4611686018427387902
- - 0.33 0.34 - 0.33 - - cmp rcx, rdx
- - 1.00 - - - - - setb dl
- - 0.33 0.67 - - - - test sil, 1
- - 1.00 - - - - - sete r8b
- - - 1.00 - - - - test dl, r8b
- - - - - 1.00 - - jne .LBB5_4
- - - - 1.00 - - 1.00 mov qword ptr [rax], 0
- - - - - 1.00 - - ret
- - - - 1.00 - 1.00 - mov qword ptr [rax], rsi
- - - - 1.00 - - 1.00 mov qword ptr [rax + 8], rdi
- - - - 1.00 - 1.00 - mov qword ptr [rax + 16], rcx
- - - - - 1.00 - - ret
Loaded 100 of 184 files, more files were not shown because too many files have changed in this diff. Show more