Path: blob/main/cranelift/codegen/src/data_value.rs
1693 views
//! This module gives users to instantiate values that Cranelift understands. These values are used,1//! for example, during interpretation and for wrapping immediates.2use crate::ir::immediates::{Ieee16, Ieee32, Ieee64, Ieee128, Offset32};3use crate::ir::{ConstantData, Type, types};4use core::cmp::Ordering;5use core::fmt::{self, Display, Formatter};67/// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value8/// that would be referred to by a [Value].9///10/// [Value]: crate::ir::Value11#[expect(missing_docs, reason = "self-describing variants")]12#[derive(Clone, Debug, PartialOrd)]13pub enum DataValue {14I8(i8),15I16(i16),16I32(i32),17I64(i64),18I128(i128),19F16(Ieee16),20F32(Ieee32),21F64(Ieee64),22F128(Ieee128),23V128([u8; 16]),24V64([u8; 8]),25V32([u8; 4]),26V16([u8; 2]),27}2829impl PartialEq for DataValue {30fn eq(&self, other: &Self) -> bool {31use DataValue::*;32match (self, other) {33(I8(l), I8(r)) => l == r,34(I8(_), _) => false,35(I16(l), I16(r)) => l == r,36(I16(_), _) => false,37(I32(l), I32(r)) => l == r,38(I32(_), _) => false,39(I64(l), I64(r)) => l == r,40(I64(_), _) => false,41(I128(l), I128(r)) => l == r,42(I128(_), _) => false,43(F16(l), F16(r)) => l.partial_cmp(&r) == Some(Ordering::Equal),44(F16(_), _) => false,45(F32(l), F32(r)) => l.as_f32() == r.as_f32(),46(F32(_), _) => false,47(F64(l), F64(r)) => l.as_f64() == r.as_f64(),48(F64(_), _) => false,49(F128(l), F128(r)) => l.partial_cmp(&r) == Some(Ordering::Equal),50(F128(_), _) => false,51(V128(l), V128(r)) => l == r,52(V128(_), _) => false,53(V64(l), V64(r)) => l == r,54(V64(_), _) => false,55(V32(l), V32(r)) => l == r,56(V32(_), _) => false,57(V16(l), V16(r)) => l == r,58(V16(_), _) => false,59}60}61}6263impl DataValue {64/// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the65/// given Cranelift [Type].66pub fn from_integer(imm: i128, ty: Type) -> Result<DataValue, DataValueCastFailure> {67match ty {68types::I8 => Ok(DataValue::I8(imm as i8)),69types::I16 => Ok(DataValue::I16(imm as i16)),70types::I32 => Ok(DataValue::I32(imm as i32)),71types::I64 => Ok(DataValue::I64(imm as i64)),72types::I128 => Ok(DataValue::I128(imm)),73_ => Err(DataValueCastFailure::FromInteger(imm, ty)),74}75}7677/// Return the Cranelift IR [Type] for this [DataValue].78pub fn ty(&self) -> Type {79match self {80DataValue::I8(_) => types::I8,81DataValue::I16(_) => types::I16,82DataValue::I32(_) => types::I32,83DataValue::I64(_) => types::I64,84DataValue::I128(_) => types::I128,85DataValue::F16(_) => types::F16,86DataValue::F32(_) => types::F32,87DataValue::F64(_) => types::F64,88DataValue::F128(_) => types::F128,89DataValue::V128(_) => types::I8X16, // A default type.90DataValue::V64(_) => types::I8X8, // A default type.91DataValue::V32(_) => types::I8X4, // A default type.92DataValue::V16(_) => types::I8X2, // A default type.93}94}9596/// Return true if the value is a vector (i.e. `DataValue::V128`).97pub fn is_vector(&self) -> bool {98match self {99DataValue::V128(_) | DataValue::V64(_) | DataValue::V32(_) | DataValue::V16(_) => true,100_ => false,101}102}103104fn swap_bytes(self) -> Self {105match self {106DataValue::I8(i) => DataValue::I8(i.swap_bytes()),107DataValue::I16(i) => DataValue::I16(i.swap_bytes()),108DataValue::I32(i) => DataValue::I32(i.swap_bytes()),109DataValue::I64(i) => DataValue::I64(i.swap_bytes()),110DataValue::I128(i) => DataValue::I128(i.swap_bytes()),111DataValue::F16(f) => DataValue::F16(Ieee16::with_bits(f.bits().swap_bytes())),112DataValue::F32(f) => DataValue::F32(Ieee32::with_bits(f.bits().swap_bytes())),113DataValue::F64(f) => DataValue::F64(Ieee64::with_bits(f.bits().swap_bytes())),114DataValue::F128(f) => DataValue::F128(Ieee128::with_bits(f.bits().swap_bytes())),115DataValue::V128(mut v) => {116v.reverse();117DataValue::V128(v)118}119DataValue::V64(mut v) => {120v.reverse();121DataValue::V64(v)122}123DataValue::V32(mut v) => {124v.reverse();125DataValue::V32(v)126}127DataValue::V16(mut v) => {128v.reverse();129DataValue::V16(v)130}131}132}133134/// Converts `self` to big endian from target's endianness.135pub fn to_be(self) -> Self {136if cfg!(target_endian = "big") {137self138} else {139self.swap_bytes()140}141}142143/// Converts `self` to little endian from target's endianness.144pub fn to_le(self) -> Self {145if cfg!(target_endian = "little") {146self147} else {148self.swap_bytes()149}150}151152/// Write a [DataValue] to a slice in native-endian byte order.153///154/// # Panics:155///156/// Panics if the slice does not have enough space to accommodate the [DataValue]157pub fn write_to_slice_ne(&self, dst: &mut [u8]) {158match self {159DataValue::I8(i) => dst[..1].copy_from_slice(&i.to_ne_bytes()[..]),160DataValue::I16(i) => dst[..2].copy_from_slice(&i.to_ne_bytes()[..]),161DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_ne_bytes()[..]),162DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_ne_bytes()[..]),163DataValue::I128(i) => dst[..16].copy_from_slice(&i.to_ne_bytes()[..]),164DataValue::F16(f) => dst[..2].copy_from_slice(&f.bits().to_ne_bytes()[..]),165DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_ne_bytes()[..]),166DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_ne_bytes()[..]),167DataValue::F128(f) => dst[..16].copy_from_slice(&f.bits().to_ne_bytes()[..]),168DataValue::V128(v) => dst[..16].copy_from_slice(&v[..]),169DataValue::V64(v) => dst[..8].copy_from_slice(&v[..]),170DataValue::V32(v) => dst[..4].copy_from_slice(&v[..]),171DataValue::V16(v) => dst[..2].copy_from_slice(&v[..]),172};173}174175/// Write a [DataValue] to a slice in big-endian byte order.176///177/// # Panics:178///179/// Panics if the slice does not have enough space to accommodate the [DataValue]180pub fn write_to_slice_be(&self, dst: &mut [u8]) {181self.clone().to_be().write_to_slice_ne(dst);182}183184/// Write a [DataValue] to a slice in little-endian byte order.185///186/// # Panics:187///188/// Panics if the slice does not have enough space to accommodate the [DataValue]189pub fn write_to_slice_le(&self, dst: &mut [u8]) {190self.clone().to_le().write_to_slice_ne(dst);191}192193/// Read a [DataValue] from a slice using a given [Type] with native-endian byte order.194///195/// # Panics:196///197/// Panics if the slice does not have enough space to accommodate the [DataValue]198pub fn read_from_slice_ne(src: &[u8], ty: Type) -> Self {199match ty {200types::I8 => DataValue::I8(i8::from_ne_bytes(src[..1].try_into().unwrap())),201types::I16 => DataValue::I16(i16::from_ne_bytes(src[..2].try_into().unwrap())),202types::I32 => DataValue::I32(i32::from_ne_bytes(src[..4].try_into().unwrap())),203types::I64 => DataValue::I64(i64::from_ne_bytes(src[..8].try_into().unwrap())),204types::I128 => DataValue::I128(i128::from_ne_bytes(src[..16].try_into().unwrap())),205types::F16 => DataValue::F16(Ieee16::with_bits(u16::from_ne_bytes(206src[..2].try_into().unwrap(),207))),208types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_ne_bytes(209src[..4].try_into().unwrap(),210))),211types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_ne_bytes(212src[..8].try_into().unwrap(),213))),214types::F128 => DataValue::F128(Ieee128::with_bits(u128::from_ne_bytes(215src[..16].try_into().unwrap(),216))),217_ if ty.is_vector() => match ty.bytes() {21816 => DataValue::V128(src[..16].try_into().unwrap()),2198 => DataValue::V64(src[..8].try_into().unwrap()),2204 => DataValue::V32(src[..4].try_into().unwrap()),2212 => DataValue::V16(src[..2].try_into().unwrap()),222_ => unimplemented!(),223},224_ => unimplemented!(),225}226}227228/// Read a [DataValue] from a slice using a given [Type] in big-endian byte order.229///230/// # Panics:231///232/// Panics if the slice does not have enough space to accommodate the [DataValue]233pub fn read_from_slice_be(src: &[u8], ty: Type) -> Self {234DataValue::read_from_slice_ne(src, ty).to_be()235}236237/// Read a [DataValue] from a slice using a given [Type] in little-endian byte order.238///239/// # Panics:240///241/// Panics if the slice does not have enough space to accommodate the [DataValue]242pub fn read_from_slice_le(src: &[u8], ty: Type) -> Self {243DataValue::read_from_slice_ne(src, ty).to_le()244}245246/// Write a [DataValue] to a memory location in native-endian byte order.247pub unsafe fn write_value_to(&self, p: *mut u128) {248let size = self.ty().bytes() as usize;249self.write_to_slice_ne(unsafe { std::slice::from_raw_parts_mut(p as *mut u8, size) });250}251252/// Read a [DataValue] from a memory location using a given [Type] in native-endian byte order.253pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self {254DataValue::read_from_slice_ne(255unsafe { std::slice::from_raw_parts(p as *const u8, ty.bytes() as usize) },256ty,257)258}259260/// Performs a bitwise comparison over the contents of [DataValue].261///262/// Returns true if all bits are equal.263///264/// This behaviour is different from PartialEq for NaN floats.265pub fn bitwise_eq(&self, other: &DataValue) -> bool {266match (self, other) {267// We need to bit compare the floats to ensure that we produce the correct values268// on NaN's. The test suite expects to assert the precise bit pattern on NaN's or269// works around it in the tests themselves.270(DataValue::F16(a), DataValue::F16(b)) => a.bits() == b.bits(),271(DataValue::F32(a), DataValue::F32(b)) => a.bits() == b.bits(),272(DataValue::F64(a), DataValue::F64(b)) => a.bits() == b.bits(),273(DataValue::F128(a), DataValue::F128(b)) => a.bits() == b.bits(),274275// We don't need to worry about F32x4 / F64x2 Since we compare V128 which is already the276// raw bytes anyway277(a, b) => a == b,278}279}280}281282/// Record failures to cast [DataValue].283#[derive(Debug, PartialEq)]284#[expect(missing_docs, reason = "self-describing variants")]285pub enum DataValueCastFailure {286TryInto(Type, Type),287FromInteger(i128, Type),288}289290// This is manually implementing Error and Display instead of using thiserror to reduce the amount291// of dependencies used by Cranelift.292impl std::error::Error for DataValueCastFailure {}293294impl Display for DataValueCastFailure {295fn fmt(&self, f: &mut Formatter) -> fmt::Result {296match self {297DataValueCastFailure::TryInto(from, to) => {298write!(f, "unable to cast data value of type {from} to type {to}")299}300DataValueCastFailure::FromInteger(val, to) => {301write!(f, "unable to cast i64({val}) to a data value of type {to}")302}303}304}305}306307/// Helper for creating conversion implementations for [DataValue].308macro_rules! build_conversion_impl {309( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => {310impl From<$rust_ty> for DataValue {311fn from(data: $rust_ty) -> Self {312DataValue::$data_value_ty(data)313}314}315316impl TryInto<$rust_ty> for DataValue {317type Error = DataValueCastFailure;318fn try_into(self) -> Result<$rust_ty, Self::Error> {319if let DataValue::$data_value_ty(v) = self {320Ok(v)321} else {322Err(DataValueCastFailure::TryInto(323self.ty(),324types::$cranelift_ty,325))326}327}328}329};330}331build_conversion_impl!(i8, I8, I8);332build_conversion_impl!(i16, I16, I16);333build_conversion_impl!(i32, I32, I32);334build_conversion_impl!(i64, I64, I64);335build_conversion_impl!(i128, I128, I128);336build_conversion_impl!(Ieee16, F16, F16);337build_conversion_impl!(Ieee32, F32, F32);338build_conversion_impl!(Ieee64, F64, F64);339build_conversion_impl!(Ieee128, F128, F128);340build_conversion_impl!([u8; 16], V128, I8X16);341build_conversion_impl!([u8; 8], V64, I8X8);342build_conversion_impl!([u8; 4], V32, I8X4);343build_conversion_impl!([u8; 2], V16, I8X2);344impl From<Offset32> for DataValue {345fn from(o: Offset32) -> Self {346DataValue::from(Into::<i32>::into(o))347}348}349350impl Display for DataValue {351fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {352match self {353DataValue::I8(dv) => write!(f, "{dv}"),354DataValue::I16(dv) => write!(f, "{dv}"),355DataValue::I32(dv) => write!(f, "{dv}"),356DataValue::I64(dv) => write!(f, "{dv}"),357DataValue::I128(dv) => write!(f, "{dv}"),358// The Ieee* wrappers here print the expected syntax.359DataValue::F16(dv) => write!(f, "{dv}"),360DataValue::F32(dv) => write!(f, "{dv}"),361DataValue::F64(dv) => write!(f, "{dv}"),362DataValue::F128(dv) => write!(f, "{dv}"),363// Again, for syntax consistency, use ConstantData, which in this case displays as hex.364DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),365DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])),366DataValue::V32(dv) => write!(f, "{}", ConstantData::from(&dv[..])),367DataValue::V16(dv) => write!(f, "{}", ConstantData::from(&dv[..])),368}369}370}371372/// Helper structure for printing bracket-enclosed vectors of [DataValue]s.373/// - for empty vectors, display `[]`374/// - for single item vectors, display `42`, e.g.375/// - for multiple item vectors, display `[42, 43, 44]`, e.g.376pub struct DisplayDataValues<'a>(pub &'a [DataValue]);377378impl<'a> Display for DisplayDataValues<'a> {379fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {380if self.0.len() == 1 {381write!(f, "{}", self.0[0])382} else {383write!(f, "[")?;384write_data_value_list(f, &self.0)?;385write!(f, "]")386}387}388}389390/// Helper function for displaying `Vec<DataValue>`.391pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result {392match list.len() {3930 => Ok(()),3941 => write!(f, "{}", list[0]),395_ => {396write!(f, "{}", list[0])?;397for dv in list.iter().skip(1) {398write!(f, ", {dv}")?;399}400Ok(())401}402}403}404405#[cfg(test)]406mod test {407use super::*;408409#[test]410fn type_conversions() {411assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16);412assert_eq!(413TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(),414[0; 16]415);416assert_eq!(417TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(),418DataValueCastFailure::TryInto(types::I8X16, types::I32)419);420}421}422423424