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