Skip to main content

solar_codegen/mir/
module.rs

1//! MIR module (top-level container).
2
3use super::{Function, FunctionId, MirType};
4use solar_data_structures::{
5    fmt::{self, FmtIteratorExt},
6    index::IndexVec,
7};
8use solar_interface::Ident;
9
10/// Current immutable staging and placeholder width.
11///
12/// TODO: Support immutable references with byte widths `<= 32` instead of
13/// forcing every immutable through a full `PUSH32`/word patch. Solidity's
14/// standard JSON format permits shorter immutable reference lengths, and solc
15/// can emit `PUSH<N>` for small immutable types. Doing that here requires
16/// carrying the byte width through MIR, assembler immutable refs, and the
17/// constructor patch loop instead of blindly patching with `MSTORE`.
18pub const IMMUTABLE_WORD_SIZE: usize = 32;
19
20/// A MIR module representing a compiled contract.
21#[derive(Clone, Debug)]
22pub struct Module {
23    /// Module/contract name.
24    pub name: Ident,
25    /// All functions in this module.
26    pub functions: IndexVec<FunctionId, Function>,
27    /// Data segments (for string literals, etc.).
28    pub data_segments: Vec<DataSegment>,
29    /// Storage layout.
30    pub storage_layout: Vec<StorageSlot>,
31    /// Immutable scratch-area layout (currently one staged word per immutable).
32    pub immutables: Vec<ImmutableSlot>,
33    /// Whether this is an interface (no bytecode generation).
34    pub is_interface: bool,
35}
36
37impl Module {
38    /// Creates a new module.
39    #[must_use]
40    pub fn new(name: Ident) -> Self {
41        Self {
42            name,
43            functions: IndexVec::new(),
44            data_segments: Vec::new(),
45            storage_layout: Vec::new(),
46            immutables: Vec::new(),
47            is_interface: false,
48        }
49    }
50
51    /// Adds a function to the module.
52    pub fn add_function(&mut self, function: Function) -> FunctionId {
53        self.functions.push(function)
54    }
55
56    /// Returns the function for the given ID.
57    #[must_use]
58    pub fn function(&self, id: FunctionId) -> &Function {
59        &self.functions[id]
60    }
61
62    /// Returns a mutable reference to the function.
63    pub fn function_mut(&mut self, id: FunctionId) -> &mut Function {
64        &mut self.functions[id]
65    }
66
67    /// Adds a data segment.
68    pub fn add_data_segment(&mut self, data: Vec<u8>) -> usize {
69        let index = self.data_segments.len();
70        self.data_segments.push(DataSegment { data });
71        index
72    }
73
74    /// Adds a storage slot.
75    pub fn add_storage_slot(&mut self, slot: StorageSlot) -> usize {
76        let index = self.storage_layout.len();
77        self.storage_layout.push(slot);
78        index
79    }
80
81    /// Adds an immutable data slot.
82    pub fn add_immutable_slot(&mut self, slot: ImmutableSlot) -> usize {
83        let index = self.immutables.len();
84        self.immutables.push(slot);
85        index
86    }
87
88    /// Returns the size in bytes of the constructor scratch area that stages
89    /// immutable words before they are patched into the runtime code.
90    #[must_use]
91    pub fn immutable_data_len(&self) -> usize {
92        self.immutables.len() * IMMUTABLE_WORD_SIZE
93    }
94
95    /// Returns an iterator over all functions.
96    pub fn iter_functions(&self) -> impl Iterator<Item = (FunctionId, &Function)> {
97        self.functions.iter_enumerated()
98    }
99
100    /// Returns the human-readable textual MIR representation of this module.
101    pub fn to_text(&self) -> impl fmt::Display + '_ {
102        fmt::from_fn(move |f| {
103            writeln!(f, "; module @{}", self.name)?;
104            write!(
105                f,
106                "{}",
107                self.functions.iter().map(super::display::display_function_text).format("\n")
108            )
109        })
110    }
111
112    /// Returns this module's DOT-format CFGs.
113    pub fn to_dot(&self) -> impl fmt::Display + '_ {
114        fmt::from_fn(move |f| {
115            write!(
116                f,
117                "{}",
118                self.functions.iter().map(super::display::display_function_dot).format("\n\n")
119            )
120        })
121    }
122}
123
124/// A data segment in the module.
125#[derive(Clone, Debug)]
126pub struct DataSegment {
127    /// The raw bytes of this segment.
128    pub data: Vec<u8>,
129}
130
131/// A storage slot in the contract.
132#[derive(Clone, Debug)]
133pub struct StorageSlot {
134    /// The slot number.
135    pub slot: u64,
136    /// The offset within the slot (for packed storage).
137    pub offset: u8,
138    /// The type of the value stored.
139    pub ty: MirType,
140    /// The variable name (for debugging).
141    pub name: Option<Ident>,
142}
143
144/// An immutable value staged in constructor scratch memory and patched into the
145/// runtime code's immutable placeholders at deploy time.
146#[derive(Clone, Debug)]
147pub struct ImmutableSlot {
148    /// Byte offset from the start of the immutable scratch area.
149    pub offset: u32,
150    /// The type of the value stored.
151    pub ty: MirType,
152    /// The variable name (for debugging).
153    pub name: Option<Ident>,
154}
155
156impl StorageSlot {
157    /// Creates a new storage slot.
158    #[must_use]
159    pub fn new(slot: u64, ty: MirType) -> Self {
160        Self { slot, offset: 0, ty, name: None }
161    }
162
163    /// Creates a new storage slot with an offset.
164    #[must_use]
165    pub fn with_offset(slot: u64, offset: u8, ty: MirType) -> Self {
166        Self { slot, offset, ty, name: None }
167    }
168}
169
170impl fmt::Display for Module {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        writeln!(f, "module {} {{", self.name)?;
173
174        if !self.storage_layout.is_empty() {
175            writeln!(f, "  storage:")?;
176            for slot in &self.storage_layout {
177                writeln!(f, "    slot {} @ {}: {}", slot.slot, slot.offset, slot.ty)?;
178            }
179            writeln!(f)?;
180        }
181
182        for (id, func) in self.functions.iter_enumerated() {
183            writeln!(f, "  ; function {}", id.index())?;
184            writeln!(f, "  {func}")?;
185        }
186
187        writeln!(f, "}}")
188    }
189}