1use std::collections::BTreeMap;
2
3use cranelift_entity::PrimaryMap;
4use ruka_types::Ty;
5
6use crate::{
7 LocalInfo, LocalKind, MirBlockId, MirCallArg, MirCallArgBinding, MirEnumDecl, MirFuncId,
8 MirHeapOwnership, MirInstr, MirLocalId, MirLocalRepr, MirOwnershipMode, MirStructDecl,
9};
10
11#[derive(Debug, Clone)]
16pub struct MirProgram {
17 pub functions: PrimaryMap<MirFuncId, MirFunction>,
19 pub function_names: BTreeMap<String, MirFuncId>,
21 pub structs: Vec<MirStructDecl>,
23 pub enums: Vec<MirEnumDecl>,
25}
26
27impl MirProgram {
28 pub fn function_count(&self) -> usize {
30 self.functions.len()
31 }
32}
33
34#[derive(Debug, Clone)]
40pub struct MirFunction {
41 pub name: String,
43 pub arity: usize,
45 pub return_ty: Ty,
47 pub locals: PrimaryMap<MirLocalId, LocalInfo>,
49 pub params: Vec<MirLocalId>,
51 pub param_modes: Vec<MirOwnershipMode>,
53 pub entry: MirBlockId,
55 pub blocks: PrimaryMap<MirBlockId, MirBlock>,
57}
58
59#[derive(Debug, Clone, Copy)]
61pub struct MirParamBinding<'a> {
62 pub index: usize,
64 pub local_id: MirLocalId,
66 pub mode: MirOwnershipMode,
68 pub local: &'a LocalInfo,
70}
71
72impl<'a> MirParamBinding<'a> {
73 pub fn expects_view(self) -> bool {
75 matches!(self.mode, MirOwnershipMode::View)
76 }
77
78 pub fn expects_mut_borrow(self) -> bool {
80 matches!(self.mode, MirOwnershipMode::MutBorrow)
81 }
82
83 pub fn expects_owned(self) -> bool {
85 matches!(self.mode, MirOwnershipMode::Owned)
86 }
87
88 pub fn semantic_ty(self) -> &'a Ty {
90 match self.mode {
91 MirOwnershipMode::View | MirOwnershipMode::MutBorrow => {
92 self.local.place_item_ty().unwrap_or(&self.local.ty)
93 }
94 MirOwnershipMode::Owned => &self.local.ty,
95 }
96 }
97
98 pub fn source_repr(self) -> MirLocalRepr {
100 match self.mode {
101 MirOwnershipMode::View => MirLocalRepr::SharedPlace,
102 MirOwnershipMode::MutBorrow => MirLocalRepr::MutablePlace,
103 MirOwnershipMode::Owned => MirLocalRepr::Value,
104 }
105 }
106
107 pub fn local_repr(self) -> MirLocalRepr {
109 self.local.repr
110 }
111
112 pub fn requires_materialization(self) -> bool {
114 self.source_repr() != self.local_repr()
115 }
116
117 pub fn materializes_view_from_owned(self) -> bool {
119 self.expects_view() && self.requires_materialization()
120 }
121}
122
123impl MirFunction {
124 pub fn param_binding(&self, index: usize) -> MirParamBinding<'_> {
126 let local_id = self.params[index];
127 MirParamBinding {
128 index,
129 local_id,
130 mode: self.param_modes[index],
131 local: &self.locals[local_id],
132 }
133 }
134
135 pub fn param_bindings(&self) -> impl Iterator<Item = MirParamBinding<'_>> + '_ {
137 self.params
138 .iter()
139 .enumerate()
140 .map(|(index, _)| self.param_binding(index))
141 }
142
143 pub fn local_info(&self, local_id: MirLocalId) -> &LocalInfo {
145 &self.locals[local_id]
146 }
147
148 pub fn call_arg_binding<'a>(&'a self, arg: &'a MirCallArg) -> MirCallArgBinding<'a> {
150 MirCallArgBinding {
151 arg,
152 local: &self.locals[arg.local],
153 }
154 }
155
156 pub fn assert_valid(&self) {
158 assert_eq!(
159 self.params.len(),
160 self.param_modes.len(),
161 "function `{}` has mismatched params and param modes",
162 self.name,
163 );
164
165 for binding in self.param_bindings() {
166 let info = binding.local;
167 assert!(
168 matches!(info.kind, LocalKind::Param),
169 "function `{}` has non-param local {:?} in params list",
170 self.name,
171 binding.local_id,
172 );
173 match binding.mode {
174 MirOwnershipMode::View => assert!(
175 !info.repr.is_mutable_place(),
176 "function `{}` has mutable view param {:?}",
177 self.name,
178 binding.local_id,
179 ),
180 MirOwnershipMode::MutBorrow => assert!(
181 info.repr.is_mutable_place(),
182 "function `{}` has non-mutable borrowed param {:?}",
183 self.name,
184 binding.local_id,
185 ),
186 MirOwnershipMode::Owned => assert!(
187 !info.repr.is_place(),
188 "function `{}` has place-shaped owned param {:?}",
189 self.name,
190 binding.local_id,
191 ),
192 }
193 }
194
195 for (local_id, info) in self.locals.iter() {
196 match info.heap_ownership {
197 MirHeapOwnership::None => {}
198 MirHeapOwnership::Owned | MirHeapOwnership::OwnedShallow => assert!(
199 !info.repr.is_place(),
200 "function `{}` marks place local {:?} as heap-owned",
201 self.name,
202 local_id,
203 ),
204 MirHeapOwnership::BorrowedView => {
205 assert!(
206 info.repr.is_place(),
207 "function `{}` marks non-place local {:?} as BorrowedView",
208 self.name,
209 local_id,
210 );
211 assert!(
212 matches!(info.place_item_ty(), Some(Ty::Slice(_))),
213 "function `{}` marks non-slice place local {:?} as BorrowedView",
214 self.name,
215 local_id,
216 );
217 }
218 }
219 }
220
221 for (_, block) in self.blocks.iter() {
222 for local_id in &block.params {
223 let _ = &self.locals[*local_id];
224 }
225 for instr in &block.instrs {
226 self.assert_instr_valid(instr);
227 }
228 match &block.terminator {
229 MirTerminator::Jump { target, args } => {
230 self.assert_edge_args_compatible(
231 self.blocks[*target].params.as_slice(),
232 args.as_slice(),
233 "jump",
234 );
235 }
236 MirTerminator::Branch {
237 cond,
238 then_target,
239 then_args,
240 else_target,
241 else_args,
242 ..
243 } => {
244 assert!(
245 !self.locals[*cond].repr.is_place(),
246 "function `{}` branches on place local {:?}",
247 self.name,
248 cond,
249 );
250 let then_params = &self.blocks[*then_target].params;
251 self.assert_edge_args_compatible(
252 then_params.as_slice(),
253 then_args.as_slice(),
254 "then",
255 );
256 let else_params = &self.blocks[*else_target].params;
257 self.assert_edge_args_compatible(
258 else_params.as_slice(),
259 else_args.as_slice(),
260 "else",
261 );
262 }
263 MirTerminator::Return { value } => {
264 assert!(
265 !self.locals[*value].repr.is_place(),
266 "function `{}` returns place local {:?}",
267 self.name,
268 value,
269 );
270 assert_eq!(
271 self.locals[*value].ty, self.return_ty,
272 "function `{}` returns local {:?} with mismatched type",
273 self.name, value,
274 );
275 }
276 }
277 }
278 }
279
280 fn assert_edge_args_compatible(
282 &self,
283 params: &[MirLocalId],
284 args: &[MirLocalId],
285 edge_kind: &'static str,
286 ) {
287 assert_eq!(
288 params.len(),
289 args.len(),
290 "function `{}` has {edge_kind} edge with mismatched block arg count",
291 self.name,
292 );
293 for (param, arg) in params.iter().zip(args.iter()) {
294 let param_info = &self.locals[*param];
295 let arg_info = &self.locals[*arg];
296 assert_eq!(
297 param_info.repr, arg_info.repr,
298 "function `{}` has {edge_kind} edge with repr mismatch into block param {:?}",
299 self.name, param,
300 );
301 assert_eq!(
302 param_info.ty, arg_info.ty,
303 "function `{}` has {edge_kind} edge with type mismatch into block param {:?}",
304 self.name, param,
305 );
306 }
307 }
308
309 fn assert_instr_valid(&self, instr: &MirInstr) {
311 match instr {
312 MirInstr::StoreRef { dst_ref, .. } => assert!(
313 self.locals[*dst_ref].repr.is_mutable_place(),
314 "function `{}` stores through non-mutable place {:?}",
315 self.name,
316 dst_ref,
317 ),
318 MirInstr::ReleaseHeap { local } => {
319 assert!(
320 !self.locals[*local].repr.is_place(),
321 "function `{}` applies heap ownership op to place local {:?}",
322 self.name,
323 local,
324 );
325 assert!(
326 self.locals[*local].uses_heap_ops(),
327 "function `{}` applies heap ownership op to non-heap local {:?}",
328 self.name,
329 local,
330 );
331 }
332 MirInstr::DerefCopy { src, dst } => {
333 assert!(
334 self.locals[*src].repr.is_place(),
335 "function `{}` dereferences non-place local {:?}",
336 self.name,
337 src,
338 );
339 assert!(
340 !self.locals[*dst].repr.is_place(),
341 "function `{}` deref-copies into place local {:?}",
342 self.name,
343 dst,
344 );
345 }
346 MirInstr::Call { args, dst, .. }
347 | MirInstr::CallExtern { args, dst, .. }
348 | MirInstr::CallIntrinsic { args, dst, .. } => {
349 assert!(
350 !self.locals[*dst].repr.is_place(),
351 "function `{}` writes call result into place {:?}",
352 self.name,
353 dst,
354 );
355 self.assert_call_args_valid(args.as_slice());
356 }
357 MirInstr::EnumIsVariant { value, dst, .. }
358 | MirInstr::EnumGetField { value, dst, .. } => {
359 assert!(
360 !self.locals[*value].repr.is_place(),
361 "function `{}` reads enum metadata from place local {:?}",
362 self.name,
363 value,
364 );
365 assert!(
366 !self.locals[*dst].repr.is_place(),
367 "function `{}` writes enum metadata into place {:?}",
368 self.name,
369 dst,
370 );
371 }
372 MirInstr::NumCast { src, dst } | MirInstr::CheckedIntCast { src, dst } => {
373 assert!(
374 !self.locals[*src].repr.is_place(),
375 "function `{}` uses place source {:?} in numeric cast",
376 self.name,
377 src,
378 );
379 assert!(
380 !self.locals[*dst].repr.is_place(),
381 "function `{}` writes numeric cast into place {:?}",
382 self.name,
383 dst,
384 );
385 }
386 MirInstr::IntLt { lhs, rhs, dst }
387 | MirInstr::IntGt { lhs, rhs, dst }
388 | MirInstr::IntLtEq { lhs, rhs, dst }
389 | MirInstr::IntGtEq { lhs, rhs, dst }
390 | MirInstr::IntEq { lhs, rhs, dst }
391 | MirInstr::IntNeq { lhs, rhs, dst }
392 | MirInstr::IntAdd { lhs, rhs, dst }
393 | MirInstr::IntSub { lhs, rhs, dst }
394 | MirInstr::IntMul { lhs, rhs, dst }
395 | MirInstr::IntDiv { lhs, rhs, dst }
396 | MirInstr::IntMod { lhs, rhs, dst } => {
397 assert!(
398 !self.locals[*lhs].repr.is_place(),
399 "function `{}` uses place lhs {:?} in scalar op",
400 self.name,
401 lhs,
402 );
403 assert!(
404 !self.locals[*rhs].repr.is_place(),
405 "function `{}` uses place rhs {:?} in scalar op",
406 self.name,
407 rhs,
408 );
409 assert!(
410 !self.locals[*dst].repr.is_place(),
411 "function `{}` writes scalar op into place {:?}",
412 self.name,
413 dst,
414 );
415 }
416 _ => {}
417 }
418 }
419
420 fn assert_call_args_valid(&self, args: &[MirCallArg]) {
422 for arg in args {
423 let binding = self.call_arg_binding(arg);
424 if binding.is_mutable_borrow() {
425 assert!(
426 !matches!(binding.local.repr, MirLocalRepr::SharedPlace),
427 "function `{}` mutably borrows shared place {:?}",
428 self.name,
429 binding.local_id(),
430 );
431 }
432 }
433 }
434}
435
436#[derive(Debug, Clone)]
440pub struct MirBlock {
441 pub params: Vec<MirLocalId>,
443 pub instrs: Vec<MirInstr>,
445 pub terminator: MirTerminator,
447}
448
449#[derive(Debug, Clone)]
451pub enum MirTerminator {
452 Jump {
454 target: MirBlockId,
456 args: Vec<MirLocalId>,
458 },
459 Branch {
461 cond: MirLocalId,
463 then_target: MirBlockId,
465 then_args: Vec<MirLocalId>,
467 else_target: MirBlockId,
469 else_args: Vec<MirLocalId>,
471 },
472 Return {
475 value: MirLocalId,
477 },
478}