1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
mod function;
pub mod intrinsics;
mod module;
mod program;
mod region;
pub use midenc_hir::{
Local, LocalId, MasmBlock as Block, MasmBlockId as BlockId, MasmImport as Import, MasmOp as Op,
ModuleImportInfo,
};
use serde::{Deserialize, Serialize};
pub use self::{
function::{FrozenFunctionList, Function, FunctionList},
module::{FrozenModuleTree, Module, ModuleTree},
program::{Library, Program},
region::Region,
};
/// This represents a descriptor for a pointer translated from the IR into a form suitable for
/// referencing data in Miden's linear memory.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NativePtr {
/// This is the address of the word containing the first byte of data
pub waddr: u32,
/// This is the element index of the word referenced by `waddr` containing the first byte of
/// data
///
/// Each element is assumed to be a 32-bit value/chunk
pub index: u8,
/// This is the byte offset into the 32-bit chunk referenced by `index`
///
/// This offset is where the data referenced by the pointer actually starts.
pub offset: u8,
/// This is the assumed address space of the pointer value.
///
/// This address space is unknown by default, but can be specified if known statically.
/// The address space determines whether the pointer is valid in certain contexts. For
/// example, attempting to load a pointer with address space 0 is invalid if not operating
/// in the root context.
///
/// Currently this has no effect, but is here as we expand support for multiple memories.
pub addrspace: midenc_hir::AddressSpace,
}
impl NativePtr {
pub fn new(waddr: u32, index: u8, offset: u8) -> Self {
Self {
waddr,
index,
offset,
addrspace: Default::default(),
}
}
/// Translates a raw pointer (assumed to be in a byte-addressable address space) to
/// a native pointer value, in the default [hir::AddressSpace].
pub fn from_ptr(addr: u32) -> Self {
// The native word address for `addr` is derived by splitting
// the byte-addressable space into 32-bit chunks, each chunk
// belonging to a single field element. Thus, each word of the
// native address space represents 128 bits of byte-addressable
// memory.
//
// By dividing `addr` by 16, we get the word index (i.e. address)
// where the data starts.
let waddr = addr / 16;
// If our address is not word-aligned, we need to determine what
// element index contains the 32-bit chunk where the data begins
let woffset = addr % 16;
let index = (woffset / 4) as u8;
// If our address is not element-aligned, we need to determine
// what byte offset contains the first byte of the data
let offset = (woffset % 4) as u8;
Self {
waddr,
index,
offset,
addrspace: Default::default(),
}
}
/// Returns true if this pointer is aligned to a word boundary
pub const fn is_word_aligned(&self) -> bool {
self.index == 0 && self.offset == 0
}
/// Returns true if this pointer is aligned to a field element boundary
pub const fn is_element_aligned(&self) -> bool {
self.offset == 0
}
/// Returns true if this pointer is not word or element aligned
pub const fn is_unaligned(&self) -> bool {
self.offset > 0
}
/// Returns the byte alignment implied by this pointer value.
///
/// For example, a pointer to the first word in linear memory, i.e. address 0,
/// with an element index of 1, and offset of 16, is equivalent to an address
/// in byte-addressable memory of 48, which has an implied alignment of 16 bytes.
pub const fn alignment(&self) -> u32 {
2u32.pow(self.as_ptr().trailing_zeros())
}
/// Converts this native pointer back to a byte-addressable pointer value
pub const fn as_ptr(&self) -> u32 {
(self.waddr * 16) + (self.index as u32 * 4) + self.offset as u32
}
}