Skip to main content

dada_codegen/cx/
wasm_repr.rs

1use dada_ir_sym::{
2    ir::{
3        classes::{SymAggregate, SymAggregateStyle},
4        primitive::SymPrimitiveKind,
5        types::{SymGenericTerm, SymPerm, SymPermKind, SymPlace, SymTy, SymTyKind, SymTyName},
6        variables::SymVariable,
7    },
8    prelude::CheckedFieldTy,
9};
10use dada_util::Map;
11use wasm_encoder::ValType;
12
13/// The WASM representation for a Dada value independent of the place in which it is stored.
14/// This isn't really a specific representation, in some sense,
15/// but rather enough information to determine how to represent
16/// an instance of this value in any of the places it could appear:
17///
18/// * On the WebAssembly stack or memory, in which case all the
19///   [flattened values](`WasmRepr::flatten`) would be pushed/stored one after the other.
20/// * In WebAssembly local variables, in which case a [subset of the values](`WasmRepr::local_val_tys`)
21///   would be stored in subsequent variables. Note that the data for classes
22///   never appears in locals, and so a single value can be partially stored in locals and
23///   partially in memory.
24///
25/// # See also
26///
27/// The [`WasmPlaceRepr`][] type describes the representation of
28/// particular Dada place (which in turn has an associated Dada type).
29///
30/// [`WasmPlaceRepr`]: `crate::cx::generate_expr::wasm_place_repr::WasmPlaceRepr`
31#[derive(Debug)]
32pub(crate) enum WasmRepr {
33    /// Indicates a single primitive value. This may appear on the WASM stack,
34    /// a local value, or the memory, depending on the context in which it appears.
35    Val(ValType),
36
37    /// An aggregate value type. The values needed to represent its fields
38    /// are found in the `Vec<WasmRepr>` argument.
39    Struct(Vec<WasmRepr>),
40
41    /// A class. The data for classes is always stored in WASM memory.
42    /// It begins with an (implicit) I32 flag word and then contains
43    /// whatever values are needed to represent the fields, stored as a `Vec<WasmRepr>`.
44    Class(Vec<WasmRepr>),
45
46    /// No data at all (something zero-sized).
47    Nothing,
48}
49
50type Generics<'db> = Map<SymVariable<'db>, SymGenericTerm<'db>>;
51
52pub(super) struct WasmReprCx<'g, 'db> {
53    db: &'db dyn crate::Db,
54    generics: &'g Generics<'db>,
55}
56
57impl<'g, 'db> WasmReprCx<'g, 'db> {
58    pub(super) fn new(db: &'db dyn crate::Db, generics: &'g Generics<'db>) -> Self {
59        Self { db, generics }
60    }
61
62    /// Returns the [`WasmRepr`][] that describes how `of_type` will be represented in WASM.
63    pub(super) fn wasm_repr_of_type(&mut self, of_type: SymTy<'db>) -> WasmRepr {
64        let db = self.db;
65        match *of_type.kind(db) {
66            SymTyKind::Named(ty_name, ref ty_args) => {
67                self.wasm_repr_of_named_type(ty_name, ty_args)
68            }
69            SymTyKind::Var(sym_variable) => self.wasm_repr_of_variable(sym_variable),
70            SymTyKind::Infer(_) => panic!("unexpected inference variable"),
71            SymTyKind::Never | SymTyKind::Error(_) => WasmRepr::Nothing,
72            SymTyKind::Perm(sym_perm, sym_ty) => self.wasm_repr_of_perm_type(sym_perm, sym_ty),
73        }
74    }
75
76    fn wasm_repr_of_variable(&mut self, sym_variable: SymVariable<'db>) -> WasmRepr {
77        let result = self
78            .generics
79            .get(&sym_variable)
80            .expect("expected value for each generic type")
81            .assert_type(self.db);
82        self.wasm_repr_of_type(result)
83    }
84
85    fn wasm_repr_of_perm_type(&mut self, sym_perm: SymPerm<'db>, sym_ty: SymTy<'db>) -> WasmRepr {
86        let db = self.db;
87        match *sym_perm.kind(db) {
88            SymPermKind::Mutable(_) => self.wasm_pointer(),
89            SymPermKind::My | SymPermKind::Our | SymPermKind::Referenced(_) => {
90                self.wasm_repr_of_type(sym_ty)
91            }
92            SymPermKind::Var(sym_variable) => {
93                let result = self
94                    .generics
95                    .get(&sym_variable)
96                    .expect("expected value for each generic type")
97                    .assert_perm(db);
98                self.wasm_repr_of_perm_type(result, sym_ty)
99            }
100            SymPermKind::Error(_) => WasmRepr::Nothing,
101            SymPermKind::Apply(left, _) => self.wasm_repr_of_perm_type(left, sym_ty),
102            SymPermKind::Infer(_infer_var_index) => unreachable!(),
103            SymPermKind::Or(perm_l, _perm_r) => {
104                // the type check should ensure `perm_l` and `perm_r` are compatible
105                self.wasm_repr_of_perm_type(perm_l, sym_ty)
106            }
107        }
108    }
109
110    /// Returns the [`WasmRepr`][] for a Dada named type.
111    fn wasm_repr_of_named_type(
112        &mut self,
113        ty_name: SymTyName<'db>,
114        ty_args: &Vec<SymGenericTerm<'db>>,
115    ) -> WasmRepr {
116        let db = self.db;
117        match ty_name {
118            SymTyName::Primitive(sym_primitive) => {
119                WasmRepr::Val(self.wasm_valtype_for_primitive_kind(sym_primitive.kind(db)))
120            }
121            SymTyName::Aggregate(aggr) => match aggr.style(db) {
122                // structs  have the fields inlined
123                SymAggregateStyle::Struct => {
124                    WasmRepr::Struct(self.wasm_repr_of_aggr_fields(aggr, ty_args))
125                }
126
127                SymAggregateStyle::Class => {
128                    WasmRepr::Class(self.wasm_repr_of_aggr_fields(aggr, ty_args))
129                }
130            },
131            SymTyName::Future => {
132                assert_eq!(ty_args.len(), 1);
133                let ty_arg = ty_args[0].assert_type(db);
134                WasmRepr::Class(vec![self.wasm_repr_of_type(ty_arg)])
135            }
136            SymTyName::Tuple { arity } => {
137                assert_eq!(ty_args.len(), arity);
138                WasmRepr::Struct(
139                    ty_args
140                        .iter()
141                        .map(|term| self.wasm_repr_of_type(term.assert_type(db)))
142                        .collect(),
143                )
144            }
145        }
146    }
147
148    /// The WASM [`ValType`][] for a Dada primtive. Note that small Dada values like `i16` or whatever
149    /// are just promoted up to `I32` because we are lazy.
150    fn wasm_valtype_for_primitive_kind(&self, primitive: SymPrimitiveKind) -> ValType {
151        match primitive {
152            SymPrimitiveKind::Bool => ValType::I32,
153            SymPrimitiveKind::Char => ValType::I32,
154            SymPrimitiveKind::Int { bits } | SymPrimitiveKind::Uint { bits } => match bits {
155                0..=32 => ValType::I32,
156                33..=64 => ValType::I64,
157                _ => panic!("unexpectedly large number of integer bits {bits}"),
158            },
159            SymPrimitiveKind::Usize | SymPrimitiveKind::Isize => self.pointer_val_type(),
160            SymPrimitiveKind::Float { bits } => match bits {
161                32 => ValType::F32,
162                64 => ValType::F64,
163                _ => panic!("unexpected number of floating point bits {bits}"),
164            },
165        }
166    }
167
168    /// The WASM representations for the fields of some aggregate type
169    /// (could be a struct or a class).
170    fn wasm_repr_of_aggr_fields(
171        &mut self,
172        aggr: SymAggregate<'db>,
173        ty_args: &Vec<SymGenericTerm<'db>>,
174    ) -> Vec<WasmRepr> {
175        self.aggr_field_tys(aggr, ty_args)
176            .iter()
177            .map(|ty| self.wasm_repr_of_type(*ty))
178            .collect()
179    }
180
181    /// The types of each field of some aggregate type given the values `ty_args` for its generic arguments.
182    fn aggr_field_tys<'a>(
183        &self,
184        aggr: SymAggregate<'db>,
185        ty_args: &'a Vec<SymGenericTerm<'db>>,
186    ) -> Vec<SymTy<'db>> {
187        let db = self.db;
188        aggr.fields(db)
189            .map(|f| f.checked_field_ty(db))
190            .map(|ty| {
191                let ty = ty.substitute(db, ty_args);
192                ty.substitute(db, &[SymGenericTerm::Place(SymPlace::erased(db))])
193            })
194            .collect()
195    }
196
197    /// The WASM representation for a pointer value.
198    fn wasm_pointer(&self) -> WasmRepr {
199        WasmRepr::Val(self.pointer_val_type())
200    }
201
202    /// The [`ValType`][] for a pointer value.
203    /// For now, hardcoded to [`ValType::I32`][] but if/when 64-bit wasm exists this could change.
204    fn pointer_val_type(&self) -> ValType {
205        ValType::I32
206    }
207}