use super::TrapCode;
use crate::HashMap;
use crate::entity::{self, PrimaryMap};
pub use crate::machinst::MachMemFlags;
use alloc::borrow::Cow;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::ops::Index;
use core::str::FromStr;
use cranelift_entity::{entity_impl, packed_option::PackedOption};
#[cfg(feature = "enable-serde")]
use serde_derive::{Deserialize, Serialize};
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Endianness {
Little,
Big,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct AliasRegion(u32);
entity_impl!(AliasRegion, "region");
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct AliasRegionData {
pub user_id: u32,
pub description: Cow<'static, str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct MemFlags(u16);
impl MemFlags {
pub fn with_number(n: u32) -> Option<Self> {
let val = u16::try_from(n).ok()?;
if val == u16::MAX {
None
} else {
Some(Self(val))
}
}
}
impl entity::EntityRef for MemFlags {
#[inline]
fn new(index: usize) -> Self {
let val = u16::try_from(index).expect("MemFlags index overflow");
Self(val)
}
#[inline]
fn index(self) -> usize {
usize::from(self.0)
}
}
impl entity::packed_option::ReservedValue for MemFlags {
#[inline]
fn reserved_value() -> Self {
Self(u16::MAX)
}
#[inline]
fn is_reserved_value(&self) -> bool {
self.0 == u16::MAX
}
}
impl MemFlags {
#[inline]
pub fn from_u32(x: u32) -> Self {
Self(u16::try_from(x).unwrap())
}
#[inline]
pub fn as_u32(self) -> u32 {
u32::from(self.0)
}
#[inline]
pub fn as_bits(self) -> u32 {
u32::from(self.0)
}
#[inline]
pub fn from_bits(x: u32) -> Self {
Self(u16::try_from(x).unwrap())
}
}
impl fmt::Display for MemFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "memflags{}", self.0)
}
}
impl fmt::Debug for MemFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(self as &dyn fmt::Display).fmt(f)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct MemFlagsData {
flags: MachMemFlags,
region: PackedOption<AliasRegion>,
}
const fn no_alias_region() -> PackedOption<AliasRegion> {
PackedOption::new(AliasRegion(u32::MAX))
}
impl MemFlagsData {
pub const fn new() -> Self {
Self {
flags: MachMemFlags::new(),
region: no_alias_region(),
}
}
pub const fn trusted() -> Self {
Self::new().with_notrap().with_aligned()
}
pub fn alias_region(self) -> Option<AliasRegion> {
self.region.expand()
}
pub fn with_alias_region(mut self, region: Option<AliasRegion>) -> Self {
self.region = region.into();
self
}
pub fn set_alias_region(&mut self, region: Option<AliasRegion>) {
self.region = region.into();
}
pub fn set_by_name(&mut self, name: &str) -> Result<bool, &'static str> {
*self = match name {
"notrap" => self.with_trap_code(None),
"aligned" => self.with_aligned(),
"readonly" => self.with_readonly(),
"little" => {
if self.flags.explicit_endianness() == Some(Endianness::Big) {
return Err("cannot set both big and little endian bits");
}
self.with_endianness(Endianness::Little)
}
"big" => {
if self.flags.explicit_endianness() == Some(Endianness::Little) {
return Err("cannot set both big and little endian bits");
}
self.with_endianness(Endianness::Big)
}
"can_move" => self.with_can_move(),
other => match TrapCode::from_str(other) {
Ok(code) => self.with_trap_code(Some(code)),
Err(()) => return Ok(false),
},
};
Ok(true)
}
pub const fn endianness(self, native_endianness: Endianness) -> Endianness {
self.flags.endianness(native_endianness)
}
pub const fn explicit_endianness(self) -> Option<Endianness> {
self.flags.explicit_endianness()
}
pub fn set_endianness(&mut self, endianness: Endianness) {
*self = self.with_endianness(endianness);
}
pub const fn with_endianness(mut self, endianness: Endianness) -> Self {
self.flags = self.flags.with_endianness(endianness);
self
}
pub const fn notrap(self) -> bool {
self.trap_code().is_none()
}
pub fn set_notrap(&mut self) {
*self = self.with_notrap();
}
pub const fn with_notrap(self) -> Self {
self.with_trap_code(None)
}
pub const fn can_move(self) -> bool {
self.flags.can_move()
}
pub const fn set_can_move(&mut self) {
*self = self.with_can_move();
}
pub const fn with_can_move(mut self) -> Self {
self.flags = self.flags.with_can_move();
self
}
pub const fn aligned(self) -> bool {
self.flags.aligned()
}
pub fn set_aligned(&mut self) {
*self = self.with_aligned();
}
pub const fn with_aligned(mut self) -> Self {
self.flags = self.flags.with_aligned();
self
}
pub const fn readonly(self) -> bool {
self.flags.readonly()
}
pub fn set_readonly(&mut self) {
*self = self.with_readonly();
}
pub const fn with_readonly(mut self) -> Self {
self.flags = self.flags.with_readonly();
self
}
pub const fn trap_code(self) -> Option<TrapCode> {
self.flags.trap_code()
}
pub const fn with_trap_code(mut self, code: Option<TrapCode>) -> Self {
self.flags = self.flags.with_trap_code(code);
self
}
}
impl From<MemFlagsData> for MachMemFlags {
fn from(flags: MemFlagsData) -> Self {
flags.flags
}
}
impl From<MachMemFlags> for MemFlagsData {
fn from(flags: MachMemFlags) -> Self {
Self {
flags,
region: no_alias_region(),
}
}
}
impl fmt::Display for MemFlagsData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.flags)?;
match self.alias_region() {
None => {}
Some(region) => write!(f, " {region}")?,
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MemFlagsSetOverflow;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct MemFlagsSet {
mem_flags: PrimaryMap<MemFlags, MemFlagsData>,
dedupe_map: HashMap<MemFlagsData, MemFlags>,
}
impl PartialEq for MemFlagsSet {
fn eq(&self, other: &Self) -> bool {
self.mem_flags == other.mem_flags
}
}
impl Eq for MemFlagsSet {}
impl Hash for MemFlagsSet {
fn hash<H: Hasher>(&self, state: &mut H) {
self.mem_flags.hash(state);
}
}
impl MemFlagsSet {
pub fn new() -> Self {
Self {
mem_flags: PrimaryMap::new(),
dedupe_map: HashMap::new(),
}
}
pub fn insert(&mut self, data: MemFlagsData) -> Result<MemFlags, MemFlagsSetOverflow> {
if let Some(&existing) = self.dedupe_map.get(&data) {
return Ok(existing);
}
let next = u32::try_from(self.mem_flags.len())
.ok()
.and_then(MemFlags::with_number)
.ok_or(MemFlagsSetOverflow)?;
let key = self.mem_flags.push(data);
debug_assert_eq!(key, next);
self.dedupe_map.insert(data, key);
Ok(key)
}
pub fn insert_unchecked(&mut self, data: MemFlagsData) -> MemFlags {
match self.insert(data) {
Ok(flags) => flags,
Err(_) => panic!("MemFlags index overflow"),
}
}
pub fn is_valid(&self, mf: MemFlags) -> bool {
self.mem_flags.is_valid(mf)
}
pub fn clear(&mut self) {
*self = Self::new();
}
pub fn get(&self, data: MemFlagsData) -> Option<MemFlags> {
self.dedupe_map.get(&data).copied()
}
}
impl Index<MemFlags> for MemFlagsSet {
type Output = MemFlagsData;
fn index(&self, mf: MemFlags) -> &MemFlagsData {
&self.mem_flags[mf]
}
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct AliasRegionSet {
alias_regions: PrimaryMap<AliasRegion, AliasRegionData>,
dedupe_map: HashMap<u32, AliasRegion>,
}
impl Hash for AliasRegionSet {
fn hash<H: Hasher>(&self, state: &mut H) {
self.alias_regions.hash(state);
}
}
impl AliasRegionSet {
pub fn new() -> Self {
Self {
alias_regions: PrimaryMap::new(),
dedupe_map: HashMap::new(),
}
}
pub fn insert(&mut self, data: AliasRegionData) -> AliasRegion {
if let Some(&existing) = self.dedupe_map.get(&data.user_id) {
return existing;
}
let user_id = data.user_id;
let key = self.alias_regions.push(data);
self.dedupe_map.insert(user_id, key);
key
}
pub fn push(&mut self, data: AliasRegionData) -> AliasRegion {
let user_id = data.user_id;
let key = self.alias_regions.push(data);
self.dedupe_map.insert(user_id, key);
key
}
pub fn contains(&self, user_id: u32) -> bool {
self.dedupe_map.contains_key(&user_id)
}
pub fn is_valid(&self, ar: AliasRegion) -> bool {
self.alias_regions.is_valid(ar)
}
pub fn len(&self) -> usize {
self.alias_regions.len()
}
pub fn iter(&self) -> impl Iterator<Item = (AliasRegion, &AliasRegionData)> {
self.alias_regions.iter()
}
pub fn clear(&mut self) {
self.alias_regions.clear();
self.dedupe_map.clear();
}
}
impl Index<AliasRegion> for AliasRegionSet {
type Output = AliasRegionData;
fn index(&self, ar: AliasRegion) -> &AliasRegionData {
&self.alias_regions[ar]
}
}
#[cfg(test)]
mod tests {
use super::*;
use cranelift_entity::EntityRef;
#[test]
fn roundtrip_traps() {
for trap in TrapCode::non_user_traps().iter().copied() {
let _flags = MemFlagsData::new().with_trap_code(Some(trap));
}
let _flags = MemFlagsData::new().with_trap_code(None);
}
#[test]
fn cannot_set_big_and_little() {
let _big = MemFlagsData::new().with_endianness(Endianness::Big);
let _little = MemFlagsData::new().with_endianness(Endianness::Little);
}
#[test]
fn only_one_region() {
let region0 = AliasRegion::new(0);
let region1 = AliasRegion::new(1);
let flags = MemFlagsData::new().with_alias_region(Some(region0));
assert_eq!(flags.alias_region(), Some(region0));
let flags = flags.with_alias_region(Some(region1));
assert_eq!(flags.alias_region(), Some(region1));
let flags = flags.with_alias_region(None);
assert_eq!(flags.alias_region(), None);
}
#[test]
fn clear_removes_entries() {
let mut set = MemFlagsSet::new();
let trusted = set.insert(MemFlagsData::trusted()).unwrap();
let custom = MemFlagsData::new()
.with_endianness(Endianness::Big)
.with_alias_region(Some(AliasRegion::new(0)));
let custom_key = set.insert(custom).unwrap();
assert!(set.is_valid(trusted));
assert!(set.is_valid(custom_key));
set.clear();
assert!(!set.is_valid(trusted));
assert!(!set.is_valid(custom_key));
let trusted = set.insert(MemFlagsData::trusted()).unwrap();
assert_eq!(set[trusted], MemFlagsData::trusted());
}
}