1use std::sync::Arc;
2
3use dada_ir_sym::{
4 ir::classes::SymField,
5 ir::exprs::{SymPlaceExpr, SymPlaceExprKind},
6 ir::types::{SymTy, SymTyKind, SymTyName},
7 ir::variables::SymVariable,
8};
9use wasm_encoder::{Instruction, ValType};
10
11use crate::cx::wasm_repr::WasmRepr;
12
13use super::ExprCodegen;
14
15#[derive(Debug)]
19pub enum WasmPlaceRepr {
20 Local(WasmLocal, ValType),
22 Heap(WasmPointer, ValType),
23 Struct(Vec<Arc<WasmPlaceRepr>>),
24 Class(WasmPointer, Vec<Arc<WasmPlaceRepr>>),
25 Nowhere,
26}
27
28impl<'db> ExprCodegen<'_, 'db> {
29 pub(super) fn next_stack_frame(&self) -> WasmPointer {
32 WasmPointer {
33 base_variable: self.wasm_stack_pointer,
34 offset: self.wasm_stack_frame_size,
35 }
36 }
37
38 pub(super) fn insert_variable(&mut self, lv: SymVariable<'db>, ty: SymTy<'db>) {
42 let ty_repr = self.wasm_repr_of_type(ty);
43 let emplaced_repr = self.emplace_local(&ty_repr);
44 self.variables.insert(lv, emplaced_repr);
45 }
46
47 pub(super) fn place_for_local(&self, local_variable: SymVariable<'db>) -> Arc<WasmPlaceRepr> {
49 self.variables[&local_variable].clone()
50 }
51
52 pub(super) fn place(&self, place: SymPlaceExpr<'db>) -> Arc<WasmPlaceRepr> {
54 let db = self.cx.db;
55 match *place.kind(db) {
56 SymPlaceExprKind::Var(v) => self.place_for_local(v),
57 SymPlaceExprKind::Error(_) => Arc::new(WasmPlaceRepr::Nowhere),
58 SymPlaceExprKind::Field(owner, field) => {
59 let owner_place = self.place(owner);
60 self.field_place(owner_place, owner.ty(db), field)
61 }
62 }
63 }
64
65 pub(super) fn push_from(&mut self, place: &WasmPlaceRepr) {
67 match *place {
68 WasmPlaceRepr::Local(wasm_local, val_type) => {
69 self.push_from_local(val_type, wasm_local)
70 }
71 WasmPlaceRepr::Heap(wasm_pointer, val_type) => {
72 self.push_from_memory(val_type, wasm_pointer)
73 }
74 WasmPlaceRepr::Struct(ref fields) => {
75 fields.iter().for_each(|r| self.push_from(r));
76 }
77 WasmPlaceRepr::Class(flags, ref fields) => {
78 self.push_from_memory(ValType::I32, flags);
79 fields.iter().for_each(|r| self.push_from(r));
80 }
81 WasmPlaceRepr::Nowhere => (),
82 }
83 }
84
85 pub(super) fn push_shared_from(&mut self, place: &WasmPlaceRepr) {
87 match *place {
88 WasmPlaceRepr::Struct(ref fields) => {
89 fields.iter().for_each(|r| self.push_shared_from(r));
90 }
91 WasmPlaceRepr::Class(flags, ref fields) => {
92 self.instructions.push(Instruction::I32Const(0));
94
95 self.push_from_memory(ValType::I32, flags);
96 fields.iter().for_each(|r| self.push_shared_from(r));
97 }
98 WasmPlaceRepr::Local(..) | WasmPlaceRepr::Heap(..) | WasmPlaceRepr::Nowhere => {
99 self.push_from(place);
100 }
101 }
102 }
103
104 pub(super) fn push_leased_from(&mut self, place: &WasmPlaceRepr) {
106 match *place {
107 WasmPlaceRepr::Class(flags, _) => {
108 self.push_pointer(flags);
109 }
110 _ => panic!("can only lease classes"),
111 }
112 }
113
114 pub(super) fn pop_and_store(&mut self, to_place: &WasmPlaceRepr) {
116 match *to_place {
117 WasmPlaceRepr::Local(wasm_local, val_type) => self.pop_to_local(val_type, wasm_local),
118 WasmPlaceRepr::Heap(wasm_pointer, val_type) => {
119 self.pop_to_memory(val_type, wasm_pointer)
120 }
121 WasmPlaceRepr::Struct(ref fields) => {
122 fields.iter().rev().for_each(|r| self.pop_and_store(r));
123 }
124 WasmPlaceRepr::Class(flags, ref fields) => {
125 fields.iter().rev().for_each(|r| self.pop_and_store(r));
126 self.pop_to_memory(ValType::I32, flags);
127 }
128 WasmPlaceRepr::Nowhere => (),
129 }
130 }
131
132 fn field_place(
135 &self,
136 owner_place_repr: Arc<WasmPlaceRepr>,
137 owner_ty: SymTy<'db>,
138 field: SymField<'db>,
139 ) -> Arc<WasmPlaceRepr> {
140 let db = self.cx.db;
141 match owner_ty.kind(db) {
142 SymTyKind::Var(sym_variable) => self.field_place(
143 owner_place_repr,
144 self.generics[sym_variable].assert_type(db),
145 field,
146 ),
147 SymTyKind::Infer(_) => panic!("unresolved inference variable"),
148 SymTyKind::Never | SymTyKind::Error(_) => match &*owner_place_repr {
149 WasmPlaceRepr::Nowhere => owner_place_repr,
150 _ => panic!("unexpeced place for {owner_ty:?}: {owner_place_repr:?}"),
151 },
152 SymTyKind::Named(ty_name, _) => match *ty_name {
153 SymTyName::Future => match &*owner_place_repr {
154 WasmPlaceRepr::Class(_, vec) => vec[0].clone(),
155 WasmPlaceRepr::Nowhere => owner_place_repr,
156 _ => panic!("unexpeced place for {owner_ty:?}: {owner_place_repr:?}"),
157 },
158 SymTyName::Primitive(_) => panic!("primitive types do not have fields"),
159 SymTyName::Tuple { arity: _ } => todo!(),
160 SymTyName::Aggregate(aggr) => {
161 match &*owner_place_repr {
163 WasmPlaceRepr::Struct(fields) | WasmPlaceRepr::Class(_, fields) => {
164 let field_index = aggr
165 .fields(db)
166 .take_while(|f: &SymField<'_>| *f != field)
167 .count();
168 fields[field_index].clone()
169 }
170 WasmPlaceRepr::Nowhere => owner_place_repr,
171 _ => panic!("unexpeced place for {owner_ty:?}: {owner_place_repr:?}"),
172 }
173 }
174 },
175 #[expect(unused_variables)]
176 SymTyKind::Perm(sym_perm, sym_ty) => todo!(),
177 }
178 }
179
180 fn emplace_local(&mut self, repr: &WasmRepr) -> Arc<WasmPlaceRepr> {
183 match repr {
184 WasmRepr::Val(val_type) => {
185 let local = self.fresh_local_index(*val_type);
186 Arc::new(WasmPlaceRepr::Local(local, *val_type))
187 }
188 WasmRepr::Struct(vec) => Arc::new(WasmPlaceRepr::Struct(
189 vec.iter().map(|r| self.emplace_local(r)).collect(),
190 )),
191 WasmRepr::Class(_) => self.emplace_memory(repr),
192 WasmRepr::Nothing => Arc::new(WasmPlaceRepr::Nowhere),
193 }
194 }
195
196 fn emplace_memory(&mut self, repr: &WasmRepr) -> Arc<WasmPlaceRepr> {
199 match repr {
200 WasmRepr::Val(val_type) => {
201 let pointer = self.fresh_memory_slot(*val_type);
202 Arc::new(WasmPlaceRepr::Heap(pointer, *val_type))
203 }
204 WasmRepr::Struct(vec) => Arc::new(WasmPlaceRepr::Struct(
205 vec.iter().map(|r| self.emplace_memory(r)).collect(),
206 )),
207 WasmRepr::Class(vec) => {
208 let flag_word = self.fresh_memory_slot(ValType::I32);
209 Arc::new(WasmPlaceRepr::Class(
210 flag_word,
211 vec.iter().map(|r| self.emplace_memory(r)).collect(),
212 ))
213 }
214 WasmRepr::Nothing => Arc::new(WasmPlaceRepr::Nowhere),
215 }
216 }
217
218 fn fresh_local_index(&mut self, v: ValType) -> WasmLocal {
220 let index = u32::try_from(self.wasm_locals.len()).expect("too many locals");
221 self.wasm_locals.push(v);
222 WasmLocal { index }
223 }
224
225 fn fresh_memory_slot(&mut self, v: ValType) -> WasmPointer {
227 let offset = self.wasm_stack_frame_size;
228 self.wasm_stack_frame_size += val_type_size_in_bytes(v);
229 WasmPointer {
230 base_variable: self.wasm_stack_pointer,
231 offset,
232 }
233 }
234
235 fn push_from_local(&mut self, val_type: wasm_encoder::ValType, local: WasmLocal) {
237 assert_eq!(self.wasm_locals[local.index as usize], val_type);
238 self.instructions.push(Instruction::LocalGet(local.index));
239 }
240
241 fn pop_to_local(&mut self, v: ValType, local: WasmLocal) {
243 assert_eq!(self.wasm_locals[local.index as usize], v);
244 self.instructions.push(Instruction::LocalSet(local.index));
245 }
246
247 fn push_from_memory(
249 &mut self,
250 v: ValType,
251 WasmPointer {
252 base_variable,
253 offset,
254 }: WasmPointer,
255 ) {
256 let offset = offset as u64;
257 self.instructions.push(match v {
258 ValType::I32 => Instruction::I32Load(wasm_encoder::MemArg {
259 offset,
260 align: 4,
261 memory_index: base_variable.index,
262 }),
263 ValType::I64 => Instruction::I64Load(wasm_encoder::MemArg {
264 offset,
265 align: 4,
266 memory_index: base_variable.index,
267 }),
268 ValType::F32 => Instruction::F32Load(wasm_encoder::MemArg {
269 offset,
270 align: 4,
271 memory_index: base_variable.index,
272 }),
273 ValType::F64 => Instruction::F64Load(wasm_encoder::MemArg {
274 offset,
275 align: 4,
276 memory_index: base_variable.index,
277 }),
278 ValType::V128 | ValType::Ref(_) => panic!("unexpected val type {v:?}"),
279 });
280 }
281
282 pub(super) fn push_pointer(
284 &mut self,
285 WasmPointer {
286 base_variable,
287 offset,
288 }: WasmPointer,
289 ) {
290 self.push_from_local(ValType::I32, base_variable);
291 self.instructions.push(Instruction::I32Const(offset as i32));
292 self.instructions.push(Instruction::I32Add);
293 }
294
295 fn pop_to_memory(
297 &mut self,
298 v: ValType,
299 WasmPointer {
300 base_variable,
301 offset,
302 }: WasmPointer,
303 ) {
304 let offset = offset as u64;
305 self.instructions.push(match v {
306 ValType::I32 => Instruction::I32Store(wasm_encoder::MemArg {
307 offset,
308 align: 4,
309 memory_index: base_variable.index,
310 }),
311 ValType::I64 => Instruction::I64Store(wasm_encoder::MemArg {
312 offset,
313 align: 4,
314 memory_index: base_variable.index,
315 }),
316 ValType::F32 => Instruction::F32Store(wasm_encoder::MemArg {
317 offset,
318 align: 4,
319 memory_index: base_variable.index,
320 }),
321 ValType::F64 => Instruction::F64Store(wasm_encoder::MemArg {
322 offset,
323 align: 4,
324 memory_index: base_variable.index,
325 }),
326 ValType::V128 | ValType::Ref(_) => panic!("unexpected val type {v:?}"),
327 });
328 }
329}
330
331impl WasmRepr {
332 pub fn flatten(&self) -> Vec<ValType> {
334 match self {
335 WasmRepr::Val(val_type) => vec![*val_type],
336
337 WasmRepr::Struct(fields) => fields.iter().flat_map(|r| r.local_val_tys()).collect(),
339
340 WasmRepr::Class(fields) => std::iter::once(ValType::I32)
342 .chain(fields.iter().flat_map(|r| r.local_val_tys()))
343 .collect(),
344
345 WasmRepr::Nothing => vec![],
346 }
347 }
348
349 pub fn local_val_tys(&self) -> Vec<ValType> {
352 match self {
353 WasmRepr::Val(val_type) => vec![*val_type],
354 WasmRepr::Struct(fields) => fields.iter().flat_map(|r| r.local_val_tys()).collect(),
355 WasmRepr::Class(_) => vec![],
356 WasmRepr::Nothing => vec![],
357 }
358 }
359}
360
361fn val_type_size_in_bytes(v: ValType) -> u32 {
362 match v {
363 ValType::I32 => 4,
364 ValType::I64 => 8,
365 ValType::F32 => 4,
366 ValType::F64 => 8,
367 ValType::V128 => 16,
368 ValType::Ref(_) => panic!("ref values do not have a size in bytes"),
369 }
370}
371
372#[derive(Copy, Clone, Debug)]
373pub struct WasmLocal {
374 pub index: u32,
375}
376
377#[derive(Copy, Clone, Debug)]
378pub struct WasmPointer {
379 base_variable: WasmLocal,
380 offset: u32,
381}