1use super::Lowerer;
2use crate::mir::{FunctionBuilder, ValueId};
3use alloy_primitives::U256;
4use solar_interface::Span;
5use solar_sema::{
6 hir,
7 hir::ElementaryType,
8 ty::{Ty, TyKind},
9};
10
11#[derive(Clone, Copy, Debug)]
13pub(super) struct StorageLocation {
14 pub(super) slot: u64,
15 pub(super) offset: u8,
16 pub(super) size: u8,
17}
18
19impl StorageLocation {
20 const WORD_SIZE: u8 = 32;
21
22 const fn full_word(slot: u64) -> Self {
23 Self { slot, offset: 0, size: Self::WORD_SIZE }
24 }
25
26 const fn is_packed(self) -> bool {
27 self.offset != 0 || self.size != Self::WORD_SIZE
28 }
29}
30
31impl<'gcx> Lowerer<'gcx> {
32 pub(super) fn allocate_storage_location(
34 &mut self,
35 ty: Ty<'gcx>,
36 span: Span,
37 ) -> StorageLocation {
38 if let Some(size) = self.packed_storage_size(ty)
39 && size < StorageLocation::WORD_SIZE
40 {
41 if self.next_storage_offset + size > StorageLocation::WORD_SIZE {
42 self.next_storage_slot += 1;
43 self.next_storage_offset = 0;
44 }
45 let location = StorageLocation {
46 slot: self.next_storage_slot,
47 offset: self.next_storage_offset,
48 size,
49 };
50 self.next_storage_offset += size;
51 if self.next_storage_offset == StorageLocation::WORD_SIZE {
52 self.next_storage_slot += 1;
53 self.next_storage_offset = 0;
54 }
55 return location;
56 }
57
58 if self.next_storage_offset != 0 {
59 self.next_storage_slot += 1;
60 self.next_storage_offset = 0;
61 }
62
63 let slot = self.next_storage_slot;
64 let num_slots = self.calculate_storage_slots_for_ty(ty, span);
65 self.next_storage_slot += num_slots;
66 StorageLocation::full_word(slot)
67 }
68
69 fn packed_storage_size(&self, ty: Ty<'gcx>) -> Option<u8> {
71 match ty.peel_refs().kind {
72 TyKind::Elementary(ElementaryType::Bool) => Some(1),
73 TyKind::Udvt(inner, _) => self.packed_storage_size(inner),
74 _ => None,
75 }
76 }
77
78 pub(super) fn calculate_storage_slots_for_ty(&self, ty: Ty<'gcx>, span: Span) -> u64 {
80 match ty.peel_refs().kind {
81 TyKind::Struct(struct_id) => {
82 let mut total = 0u64;
83 for &field_ty in self.gcx.struct_field_types(struct_id) {
84 total += self.calculate_storage_slots_for_ty(field_ty, span);
85 }
86 total.max(1)
87 }
88 TyKind::Array(elem, len) => {
91 let elem_slots = self.calculate_storage_slots_for_ty(elem, span);
92 match u64::try_from(len).ok().and_then(|len| len.checked_mul(elem_slots)) {
93 Some(slots) => slots.max(1),
94 None => {
95 self.gcx
96 .dcx()
97 .err("fixed-size storage arrays this large are not supported")
98 .span(span)
99 .emit();
100 1
101 }
102 }
103 }
104 _ => 1,
105 }
106 }
107
108 pub(super) fn load_storage_location_at_slot(
109 &self,
110 builder: &mut FunctionBuilder<'_>,
111 location: StorageLocation,
112 slot: ValueId,
113 ) -> ValueId {
114 let word = builder.sload(slot);
115 if !location.is_packed() {
116 return word;
117 }
118
119 let shifted = if location.offset == 0 {
120 word
121 } else {
122 let shift = builder.imm_u64(u64::from(location.offset) * 8);
123 builder.shr(shift, word)
124 };
125 let mask = Self::packed_storage_mask(location.size);
126 let mask = builder.imm_u256(mask);
127 builder.and(shifted, mask)
128 }
129
130 pub(super) fn store_storage_location(
131 &self,
132 builder: &mut FunctionBuilder<'_>,
133 location: StorageLocation,
134 value: ValueId,
135 ) {
136 let slot = builder.imm_u64(location.slot);
137 if !location.is_packed() {
138 builder.sstore(slot, value);
139 return;
140 }
141
142 let shift_bits = usize::from(location.offset) * 8;
143 let field_mask = Self::packed_storage_mask(location.size);
144 let shifted_mask = field_mask << shift_bits;
145 let keep_mask = builder.imm_u256(!shifted_mask);
146 let value_mask = builder.imm_u256(field_mask);
147
148 let word = builder.sload(slot);
149 let cleared = builder.and(word, keep_mask);
150 let masked = builder.and(value, value_mask);
151 let shifted = if location.offset == 0 {
152 masked
153 } else {
154 let shift = builder.imm_u64(shift_bits as u64);
155 builder.shl(shift, masked)
156 };
157 let updated = builder.or(cleared, shifted);
158 builder.sstore(slot, updated);
159 }
160
161 fn packed_storage_mask(size: u8) -> U256 {
162 if size >= StorageLocation::WORD_SIZE {
163 U256::MAX
164 } else {
165 (U256::from(1) << (usize::from(size) * 8)) - U256::from(1)
166 }
167 }
168
169 pub fn get_struct_field_slot_offset(
171 &mut self,
172 struct_id: hir::StructId,
173 field_index: usize,
174 ) -> u64 {
175 if let Some(&offset) = self.struct_field_offsets.get(&(struct_id, field_index)) {
176 return offset;
177 }
178
179 let mut offset = 0u64;
180 for (i, &field_ty) in self.gcx.struct_field_types(struct_id).iter().enumerate() {
181 if i == field_index {
182 break;
183 }
184 offset += self.calculate_storage_slots_for_ty(field_ty, Span::DUMMY);
185 }
186
187 self.struct_field_offsets.insert((struct_id, field_index), offset);
188 offset
189 }
190
191 pub fn calculate_memory_words_for_ty(&self, ty: Ty<'gcx>) -> u64 {
193 match ty.peel_refs().kind {
194 TyKind::Struct(struct_id) => {
195 let mut total = 0u64;
196 for &field_ty in self.gcx.struct_field_types(struct_id) {
197 total += self.calculate_memory_words_for_ty(field_ty);
198 }
199 total.max(1)
200 }
201 _ => 1,
202 }
203 }
204
205 pub fn get_struct_field_memory_offset(
207 &mut self,
208 struct_id: hir::StructId,
209 field_index: usize,
210 ) -> u64 {
211 if let Some(&offset) = self.struct_field_memory_offsets.get(&(struct_id, field_index)) {
212 return offset;
213 }
214
215 let offset = (field_index as u64) * 32;
216
217 self.struct_field_memory_offsets.insert((struct_id, field_index), offset);
218 offset
219 }
220
221 pub fn copy_storage_to_memory(
225 &mut self,
226 builder: &mut FunctionBuilder<'_>,
227 struct_id: hir::StructId,
228 base_slot: u64,
229 mem_ptr: ValueId,
230 mem_offset: u64,
231 ) -> u64 {
232 let mut current_slot_offset = 0u64;
233 let mut current_mem_offset = mem_offset;
234
235 let field_tys = self.gcx.struct_field_types(struct_id).to_vec();
236 for field_ty in field_tys {
237 if let TyKind::Struct(inner_struct_id) = field_ty.peel_refs().kind {
238 current_mem_offset = self.copy_storage_to_memory(
239 builder,
240 inner_struct_id,
241 base_slot + current_slot_offset,
242 mem_ptr,
243 current_mem_offset,
244 );
245 current_slot_offset += self.calculate_storage_slots_for_ty(field_ty, Span::DUMMY);
246 } else {
247 let slot = base_slot + current_slot_offset;
248 let slot_val = builder.imm_u64(slot);
249 let field_val = builder.sload(slot_val);
250
251 if current_mem_offset == 0 {
252 builder.mstore(mem_ptr, field_val);
253 } else {
254 let offset_val = builder.imm_u64(current_mem_offset);
255 let field_addr = builder.add(mem_ptr, offset_val);
256 builder.mstore(field_addr, field_val);
257 }
258
259 current_slot_offset += 1;
260 current_mem_offset += 32;
261 }
262 }
263
264 current_mem_offset
265 }
266
267 pub fn copy_memory_to_storage(
271 &mut self,
272 builder: &mut FunctionBuilder<'_>,
273 struct_id: hir::StructId,
274 base_slot: u64,
275 mem_ptr: ValueId,
276 mem_offset: u64,
277 ) -> u64 {
278 let mut current_slot_offset = 0u64;
279 let mut current_mem_offset = mem_offset;
280
281 let field_tys = self.gcx.struct_field_types(struct_id).to_vec();
282 for field_ty in field_tys {
283 if let TyKind::Struct(inner_struct_id) = field_ty.peel_refs().kind {
284 current_mem_offset = self.copy_memory_to_storage(
285 builder,
286 inner_struct_id,
287 base_slot + current_slot_offset,
288 mem_ptr,
289 current_mem_offset,
290 );
291 current_slot_offset += self.calculate_storage_slots_for_ty(field_ty, Span::DUMMY);
292 } else {
293 let slot = base_slot + current_slot_offset;
294 let slot_val = builder.imm_u64(slot);
295
296 let field_val = if current_mem_offset == 0 {
297 builder.mload(mem_ptr)
298 } else {
299 let offset_val = builder.imm_u64(current_mem_offset);
300 let field_addr = builder.add(mem_ptr, offset_val);
301 builder.mload(field_addr)
302 };
303
304 builder.sstore(slot_val, field_val);
305
306 current_slot_offset += 1;
307 current_mem_offset += 32;
308 }
309 }
310
311 current_mem_offset
312 }
313}