pub(crate) fn jit_debug_enabled() -> bool {
use std::sync::OnceLock;
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| std::env::var("FACET_JIT_DEBUG").is_ok())
}
macro_rules! jit_debug {
($($arg:tt)*) => {
if $crate::jit::jit_debug_enabled() {
eprintln!("[JIT] {}", format!($($arg)*));
}
}
}
pub(crate) use jit_debug;
use std::sync::atomic::{AtomicU64, Ordering};
static TIER2_ATTEMPTS: AtomicU64 = AtomicU64::new(0);
static TIER2_SUCCESSES: AtomicU64 = AtomicU64::new(0);
static TIER2_COMPILE_UNSUPPORTED: AtomicU64 = AtomicU64::new(0);
static TIER2_RUNTIME_UNSUPPORTED: AtomicU64 = AtomicU64::new(0);
static TIER2_RUNTIME_ERROR: AtomicU64 = AtomicU64::new(0);
static TIER1_USES: AtomicU64 = AtomicU64::new(0);
pub fn get_tier_stats() -> (u64, u64, u64, u64, u64, u64) {
(
TIER2_ATTEMPTS.load(Ordering::Relaxed),
TIER2_SUCCESSES.load(Ordering::Relaxed),
TIER2_COMPILE_UNSUPPORTED.load(Ordering::Relaxed),
TIER2_RUNTIME_UNSUPPORTED.load(Ordering::Relaxed),
TIER2_RUNTIME_ERROR.load(Ordering::Relaxed),
TIER1_USES.load(Ordering::Relaxed),
)
}
pub fn get_and_reset_tier_stats() -> (u64, u64, u64, u64, u64, u64) {
(
TIER2_ATTEMPTS.swap(0, Ordering::Relaxed),
TIER2_SUCCESSES.swap(0, Ordering::Relaxed),
TIER2_COMPILE_UNSUPPORTED.swap(0, Ordering::Relaxed),
TIER2_RUNTIME_UNSUPPORTED.swap(0, Ordering::Relaxed),
TIER2_RUNTIME_ERROR.swap(0, Ordering::Relaxed),
TIER1_USES.swap(0, Ordering::Relaxed),
)
}
pub fn reset_tier_stats() {
TIER2_ATTEMPTS.store(0, Ordering::Relaxed);
TIER2_SUCCESSES.store(0, Ordering::Relaxed);
TIER2_COMPILE_UNSUPPORTED.store(0, Ordering::Relaxed);
TIER2_RUNTIME_UNSUPPORTED.store(0, Ordering::Relaxed);
TIER2_RUNTIME_ERROR.store(0, Ordering::Relaxed);
TIER1_USES.store(0, Ordering::Relaxed);
}
pub fn print_tier_stats() {
let (t2_attempts, t2_successes, t2_compile_unsup, t2_runtime_unsup, t2_runtime_err, t1_uses) =
get_and_reset_tier_stats();
if t2_attempts > 0 || t1_uses > 0 {
eprintln!("━━━ JIT Tier Usage ━━━");
eprintln!(" Tier-2 attempts: {}", t2_attempts);
eprintln!(
" Tier-2 successes: {} ({:.1}%)",
t2_successes,
if t2_attempts > 0 {
(t2_successes as f64 / t2_attempts as f64) * 100.0
} else {
0.0
}
);
if t2_compile_unsup > 0 {
eprintln!(" Tier-2 compile unsupported: {}", t2_compile_unsup);
}
if t2_runtime_unsup > 0 {
eprintln!(" Tier-2 runtime unsupported: {}", t2_runtime_unsup);
}
if t2_runtime_err > 0 {
eprintln!(" Tier-2 runtime errors: {}", t2_runtime_err);
}
eprintln!(" Tier-1 fallbacks: {}", t1_uses);
eprintln!("━━━━━━━━━━━━━━━━━━━━━");
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Tier2Incompatibility {
Not64BitPlatform,
UnrecognizedShapeType {
type_name: &'static str,
},
TupleStructWithMapFormat {
type_name: &'static str,
},
FieldHasCustomDefault {
type_name: &'static str,
field_name: &'static str,
},
UnsupportedFieldType {
type_name: &'static str,
field_name: &'static str,
field_type: &'static str,
},
UnsupportedFlattenType {
type_name: &'static str,
field_name: &'static str,
},
FlattenedMapNonStringKey {
type_name: &'static str,
field_name: &'static str,
},
UnsupportedEnumRepr {
type_name: &'static str,
repr: &'static str,
},
EnumVariantNoDiscriminant {
type_name: &'static str,
variant_name: &'static str,
},
UnsupportedEnumVariantField {
type_name: &'static str,
variant_name: &'static str,
field_name: &'static str,
},
FlattenedEnumUnitVariant {
type_name: &'static str,
variant_name: &'static str,
},
EnumRequiresPositionalFormat {
type_name: &'static str,
},
UnsupportedResultType {
type_name: &'static str,
which: &'static str,
},
MapNonStringKey {
type_name: &'static str,
},
BudgetExceeded {
type_name: &'static str,
reason: &'static str,
},
JitBuilderFailed {
error: String,
},
CompilationFailed {
type_name: &'static str,
stage: &'static str,
},
FinalizationFailed {
type_name: &'static str,
error: String,
},
}
impl std::fmt::Display for Tier2Incompatibility {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Not64BitPlatform => {
write!(
f,
"Tier-2 JIT requires 64-bit platform (for ABI bit-packing)"
)
}
Self::UnrecognizedShapeType { type_name } => {
write!(
f,
"type `{}` is not a supported shape (must be struct, enum, Vec, or HashMap)",
type_name
)
}
Self::TupleStructWithMapFormat { type_name } => {
write!(
f,
"type `{}` is a tuple/unit struct, which requires positional format (e.g., postcard), not map-based format (e.g., JSON)",
type_name
)
}
Self::FieldHasCustomDefault {
type_name,
field_name,
} => {
write!(
f,
"field `{}::{}` has a custom default, which is not supported in Tier-2 JIT (use Option<T> instead)",
type_name, field_name
)
}
Self::UnsupportedFieldType {
type_name,
field_name,
field_type,
} => {
write!(
f,
"field `{}::{}` has unsupported type `{}` (supported: scalars, String, Option<T>, Vec<T>, HashMap<String, V>, nested structs)",
type_name, field_name, field_type
)
}
Self::UnsupportedFlattenType {
type_name,
field_name,
} => {
write!(
f,
"flattened field `{}::{}` must be a struct, enum, or HashMap<String, V>",
type_name, field_name
)
}
Self::FlattenedMapNonStringKey {
type_name,
field_name,
} => {
write!(
f,
"flattened map `{}::{}` must have String keys",
type_name, field_name
)
}
Self::UnsupportedEnumRepr { type_name, repr } => {
write!(
f,
"enum `{}` has unsupported repr `{}` (use #[repr(C)] or explicit integer repr like #[repr(u8)])",
type_name, repr
)
}
Self::EnumVariantNoDiscriminant {
type_name,
variant_name,
} => {
write!(
f,
"enum `{}` variant `{}` has no discriminant value (required for JIT)",
type_name, variant_name
)
}
Self::UnsupportedEnumVariantField {
type_name,
variant_name,
field_name,
} => {
write!(
f,
"enum `{}::{}` field `{}` has unsupported type (supported: scalars, String, structs)",
type_name, variant_name, field_name
)
}
Self::FlattenedEnumUnitVariant {
type_name,
variant_name,
} => {
write!(
f,
"flattened enum `{}` variant `{}` is a unit variant, which is not supported (flattened variants must have payload)",
type_name, variant_name
)
}
Self::EnumRequiresPositionalFormat { type_name } => {
write!(
f,
"enum `{}` requires positional format (e.g., postcard); map-based formats (e.g., JSON) only support enums as struct fields",
type_name
)
}
Self::UnsupportedResultType { type_name, which } => {
write!(
f,
"Result type `{}` has unsupported {} type",
type_name, which
)
}
Self::MapNonStringKey { type_name } => {
write!(
f,
"map `{}` must have String keys (other key types not supported)",
type_name
)
}
Self::BudgetExceeded { type_name, reason } => {
write!(
f,
"type `{}` exceeds Tier-2 budget: {} (configure via FACET_TIER2_MAX_FIELDS or FACET_TIER2_MAX_NESTING)",
type_name, reason
)
}
Self::JitBuilderFailed { error } => {
write!(f, "JIT builder initialization failed: {}", error)
}
Self::CompilationFailed { type_name, stage } => {
write!(
f,
"compilation failed for `{}` at stage: {}",
type_name, stage
)
}
Self::FinalizationFailed { type_name, error } => {
write!(f, "JIT finalization failed for `{}`: {}", type_name, error)
}
}
}
}
impl std::error::Error for Tier2Incompatibility {}
pub mod cache; mod compiler;
#[cfg(all(debug_assertions, unix))]
pub mod crash_handler;
mod format;
mod format_compiler;
pub mod helpers;
use facet_core::{ConstTypeId, Facet};
use crate::{DeserializeError, DeserializeErrorKind, FormatDeserializer, FormatParser};
pub use compiler::CompiledDeserializer;
pub use format::{
JIT_SCRATCH_MAX_COLLECTION_ELEMENTS_OFFSET, JitCursor, JitFormat, JitScratch, JitStringValue,
NoFormatJit, StructEncoding,
};
pub use format_compiler::CompiledFormatDeserializer;
pub use cache::get_format_deserializer;
pub use cache::get_format_deserializer_with_reason;
pub use crate::FormatJitParser;
pub use format::{c_call_conv, make_c_sig};
pub use cranelift::codegen::ir::BlockArg;
pub use cranelift::codegen::ir::{ExtFuncData, ExternalName, SigRef, Type, UserExternalName};
pub use cranelift::codegen::isa::CallConv;
pub use cranelift::prelude::{
AbiParam, Block, FunctionBuilder, InstBuilder, IntCC, MemFlags, Signature, StackSlotData,
StackSlotKind, Value, Variable, types,
};
pub use cranelift_jit::{JITBuilder, JITModule};
pub use cranelift_module::{Linkage, Module};
pub fn try_deserialize<'de, T, P>(parser: &mut P) -> Option<Result<T, DeserializeError>>
where
T: Facet<'de>,
P: FormatParser<'de>,
{
if !compiler::is_jit_compatible(T::SHAPE) {
return None;
}
let key = (T::SHAPE.id, ConstTypeId::of::<P>());
let compiled = cache::get_or_compile::<T, P>(key)?;
Some(compiled.deserialize(parser))
}
pub fn is_jit_compatible<'a, T: Facet<'a>>() -> bool {
compiler::is_jit_compatible(T::SHAPE)
}
pub fn deserialize_with_fallback<'de, T, P>(mut parser: P) -> Result<T, DeserializeError>
where
T: Facet<'de>,
P: FormatParser<'de> + 'static,
{
if let Some(result) = try_deserialize::<T, P>(&mut parser) {
return result;
}
FormatDeserializer::new(&mut parser).deserialize()
}
pub fn try_deserialize_format<'de, T, P>(parser: &mut P) -> Option<Result<T, DeserializeError>>
where
T: Facet<'de>,
P: FormatJitParser<'de>,
{
parser.jit_pos()?;
let key = (T::SHAPE.id, ConstTypeId::of::<P>());
let compiled = match cache::get_or_compile_format::<T, P>(key) {
Some(c) => c,
None => {
TIER2_COMPILE_UNSUPPORTED.fetch_add(1, Ordering::Relaxed);
jit_debug!(
"✗ Tier-2 COMPILE UNSUPPORTED for {}",
std::any::type_name::<T>()
);
return None;
}
};
match compiled.deserialize(parser) {
Ok(value) => Some(Ok(value)),
Err(DeserializeError {
kind: DeserializeErrorKind::Unsupported { .. },
..
}) => {
TIER2_RUNTIME_UNSUPPORTED.fetch_add(1, Ordering::Relaxed);
jit_debug!(
"✗ Tier-2 RUNTIME UNSUPPORTED for {}",
std::any::type_name::<T>()
);
None
}
Err(e) => {
TIER2_RUNTIME_ERROR.fetch_add(1, Ordering::Relaxed);
jit_debug!("✗ Tier-2 RUNTIME ERROR for {}", std::any::type_name::<T>());
Some(Err(e))
}
}
}
#[derive(Debug)]
pub enum Tier2DeserializeError {
ParserHasBufferedState,
Incompatible(Tier2Incompatibility),
Deserialize(DeserializeError),
}
impl std::fmt::Display for Tier2DeserializeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ParserHasBufferedState => {
write!(f, "Tier-2 JIT unavailable: parser has buffered state")
}
Self::Incompatible(reason) => {
write!(f, "Tier-2 JIT unavailable: {}", reason)
}
Self::Deserialize(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for Tier2DeserializeError {}
pub fn try_deserialize_format_with_reason<'de, T, P>(
parser: &mut P,
) -> Result<T, Tier2DeserializeError>
where
T: Facet<'de>,
P: FormatJitParser<'de>,
{
if parser.jit_pos().is_none() {
return Err(Tier2DeserializeError::ParserHasBufferedState);
}
let key = (T::SHAPE.id, ConstTypeId::of::<P>());
let compiled = cache::get_or_compile_format_with_reason::<T, P>(key)
.map_err(Tier2DeserializeError::Incompatible)?;
compiled
.deserialize(parser)
.map_err(Tier2DeserializeError::Deserialize)
}
pub fn is_format_jit_compatible<'a, T: Facet<'a>>() -> bool {
format_compiler::ensure_format_jit_compatible(T::SHAPE, std::any::type_name::<T>()).is_ok()
}
pub fn ensure_format_jit_compatible<'a, T: Facet<'a>>() -> Result<(), Tier2Incompatibility> {
format_compiler::ensure_format_jit_compatible(T::SHAPE, std::any::type_name::<T>())
}
pub fn is_format_jit_compatible_for<'a, T: Facet<'a>, F: JitFormat>() -> bool {
format_compiler::ensure_format_jit_compatible_with_encoding(
T::SHAPE,
F::STRUCT_ENCODING,
std::any::type_name::<T>(),
)
.is_ok()
}
pub fn ensure_format_jit_compatible_for<'a, T: Facet<'a>, F: JitFormat>()
-> Result<(), Tier2Incompatibility> {
format_compiler::ensure_format_jit_compatible_with_encoding(
T::SHAPE,
F::STRUCT_ENCODING,
std::any::type_name::<T>(),
)
}
pub fn try_deserialize_with_format_jit<'de, T, P>(
parser: &mut P,
) -> Option<Result<T, DeserializeError>>
where
T: Facet<'de>,
P: FormatJitParser<'de>,
{
TIER2_ATTEMPTS.fetch_add(1, Ordering::Relaxed);
jit_debug!("Attempting Tier-2 for {}", std::any::type_name::<T>());
if let Some(result) = try_deserialize_format::<T, P>(parser) {
TIER2_SUCCESSES.fetch_add(1, Ordering::Relaxed);
jit_debug!("✓ Tier-2 USED for {}", std::any::type_name::<T>());
return Some(result);
}
jit_debug!(
"Tier-2 unavailable, falling back to Tier-1 for {}",
std::any::type_name::<T>()
);
let result = try_deserialize::<T, P>(parser);
if result.is_some() {
TIER1_USES.fetch_add(1, Ordering::Relaxed);
jit_debug!("✓ Tier-1 USED for {}", std::any::type_name::<T>());
} else {
jit_debug!(
"✗ NO JIT (both tiers unavailable) for {}",
std::any::type_name::<T>()
);
}
result
}
pub fn deserialize_with_format_jit_fallback<'de, T, P>(mut parser: P) -> Result<T, DeserializeError>
where
T: Facet<'de>,
P: FormatJitParser<'de> + 'static,
{
if let Some(result) = try_deserialize_with_format_jit::<T, P>(&mut parser) {
return result;
}
FormatDeserializer::new(&mut parser).deserialize()
}