1use super::*;
2
3mod call_family;
4mod numeric;
5
6pub(crate) use call_family::lower_call_family_instr;
7pub(crate) use numeric::*;
8
9pub(crate) fn lower_instr(
10 instr: &ruka_mir::MirInstr,
11 body: &mut walrus::InstrSeqBuilder,
12 ctx: &LowerCtx<'_>,
13) -> Result<(), LowerError> {
14 if lower_call_family_instr(instr, body, ctx)? {
15 return Ok(());
16 }
17 if lower_numeric_instr(instr, body, ctx)? {
18 return Ok(());
19 }
20 if lower_aggregate_instr(instr, body, ctx)? {
21 return Ok(());
22 }
23
24 match instr {
25 ruka_mir::MirInstr::ConstUnit { .. } => Ok(()),
26 ruka_mir::MirInstr::ReleaseHeap { local } => {
27 let ownership = local_heap_ownership(
28 ctx.local_heap_ownership,
29 local.as_u32(),
30 "release heap ownership",
31 )?;
32 if !ownership.uses_heap_ops() {
33 return Err(LowerError::UnsupportedInstruction(
34 "release heap for non-owned local",
35 ));
36 }
37 let value_ty = local_ty(ctx.local_tys, local.as_u32(), "release heap ty")?;
38 let value_local =
39 runtime_local_index(ctx.local_indices, local.as_u32(), "release heap")?;
40 match value_ty {
41 Ty::Pointer(item) => emit_pointer_release(
42 body,
43 ctx.runtime,
44 ctx.memory_id,
45 value_local,
46 item.as_ref(),
47 ctx.structs,
48 ctx.enums,
49 ctx.pointer_drop_functions,
50 ctx.scratch_i32_local,
51 ctx.scratch_i32_local_b,
52 ctx.scratch_i32_local_d,
53 ctx.scratch_i32_local_c,
54 ctx.scratch_i64_local,
55 ),
56 Ty::String => emit_string_release(
57 body,
58 ctx.runtime,
59 ctx.memory_id,
60 value_local,
61 ctx.scratch_i32_local,
62 ctx.scratch_i32_local_b,
63 ctx.scratch_i32_local_c,
64 ),
65 Ty::Array { item, .. } | Ty::Slice(item) => {
66 if matches!(ownership, ruka_mir::MirHeapOwnership::OwnedShallow) {
67 emit_array_release_shallow(
68 body,
69 ctx.runtime,
70 ctx.memory_id,
71 value_local,
72 ctx.scratch_i32_local,
73 ctx.scratch_i32_local_b,
74 ctx.scratch_i32_local_c,
75 ctx.scratch_i32_local_d,
76 ctx.scratch_i64_local,
77 )
78 } else {
79 emit_array_release(
80 body,
81 ctx.runtime,
82 ctx.memory_id,
83 value_local,
84 item.as_ref(),
85 ctx.structs,
86 ctx.enums,
87 ctx.pointer_drop_functions,
88 ctx.scratch_i32_local,
89 ctx.scratch_i32_local_b,
90 ctx.scratch_i32_local_c,
91 ctx.scratch_i32_local_d,
92 ctx.scratch_i64_local,
93 )
94 }
95 }
96 Ty::Enum { .. } => emit_enum_release(
97 body,
98 ctx.runtime,
99 ctx.memory_id,
100 value_local,
101 value_ty,
102 ctx.structs,
103 ctx.enums,
104 ctx.pointer_drop_functions,
105 ctx.scratch_i32_local,
106 ctx.scratch_i32_local_b,
107 ctx.scratch_i32_local_c,
108 ctx.scratch_i32_local_d,
109 ctx.scratch_i64_local,
110 ),
111 _ => Err(LowerError::UnsupportedInstruction(
112 "release heap for unsupported type",
113 )),
114 }
115 }
116 ruka_mir::MirInstr::ConstInt { dst, value } => {
117 let dst_id = dst.as_u32();
118 if let Some(dst) = runtime_local(ctx.local_indices, dst_id)? {
119 let dst_ty =
120 runtime_local_valtype(ctx.local_runtime_types, dst_id, "const int dst")?;
121 match dst_ty {
122 ValType::I32 => {
123 body.i32_const(*value as i32).local_set(dst);
124 }
125 ValType::I64 => {
126 body.i64_const(*value).local_set(dst);
127 }
128 ValType::F32 => {
129 body.f32_const(*value as f32).local_set(dst);
130 }
131 ValType::F64 => {
132 body.f64_const(*value as f64).local_set(dst);
133 }
134 _ => {
135 return Err(LowerError::UnsupportedInstruction(
136 "unsupported const int dst type",
137 ));
138 }
139 }
140 }
141 Ok(())
142 }
143 ruka_mir::MirInstr::ConstFloat { dst, value } => {
144 let dst_id = dst.as_u32();
145 if let Some(dst) = runtime_local(ctx.local_indices, dst_id)? {
146 let dst_ty =
147 runtime_local_valtype(ctx.local_runtime_types, dst_id, "const float dst")?;
148 match dst_ty {
149 ValType::F32 => {
150 body.f32_const(*value as f32).local_set(dst);
151 }
152 ValType::F64 => {
153 body.f64_const(*value).local_set(dst);
154 }
155 _ => {
156 return Err(LowerError::UnsupportedInstruction(
157 "unsupported const float dst type",
158 ));
159 }
160 }
161 }
162 Ok(())
163 }
164 ruka_mir::MirInstr::ConstString { dst, value } => {
165 if let Some(dst) = runtime_local(ctx.local_indices, dst.as_u32())? {
166 let string_ptr = *ctx
167 .string_literal_offsets
168 .get(value)
169 .ok_or_else(|| LowerError::UnknownStringLiteral(value.clone()))?;
170 body.i32_const(int32_from_u32(string_ptr, "string literal offset")?)
171 .local_set(dst);
172 }
173 Ok(())
174 }
175 ruka_mir::MirInstr::ConstBool { dst, value } => {
176 if let Some(dst) = runtime_local(ctx.local_indices, dst.as_u32())? {
177 body.i32_const(if *value { 1 } else { 0 }).local_set(dst);
178 }
179 Ok(())
180 }
181 ruka_mir::MirInstr::ConstNull { dst } => {
182 if let Some(dst) = runtime_local(ctx.local_indices, dst.as_u32())? {
183 body.i32_const(0).local_set(dst);
184 }
185 Ok(())
186 }
187 ruka_mir::MirInstr::PointerNew { src, dst } => {
188 let src_local = runtime_local_index(ctx.local_indices, src.as_u32(), "pointer src")?;
189 let src_ty =
190 runtime_local_valtype(ctx.local_runtime_types, src.as_u32(), "pointer src ty")?;
191 let dst_local = runtime_local_index(ctx.local_indices, dst.as_u32(), "pointer dst")?;
192 emit_pointer_alloc(
193 body,
194 ctx.runtime,
195 ctx.memory_id,
196 None,
197 ctx.function_line,
198 src_local,
199 src_ty,
200 dst_local,
201 )?;
202 Ok(())
203 }
204 ruka_mir::MirInstr::PointerIsSome { pointer, dst } => {
205 let pointer_local =
206 runtime_local_index(ctx.local_indices, pointer.as_u32(), "pointer is some")?;
207 let dst_local = runtime_local_index(ctx.local_indices, dst.as_u32(), "pointer bool")?;
208 body.local_get(pointer_local)
209 .i32_const(0)
210 .binop(BinaryOp::I32Ne)
211 .local_set(dst_local);
212 Ok(())
213 }
214 ruka_mir::MirInstr::PointerBorrowRo { pointer, dst }
215 | ruka_mir::MirInstr::PointerBorrowMut { pointer, dst } => {
216 let pointer_local =
217 runtime_local_index(ctx.local_indices, pointer.as_u32(), "pointer borrow src")?;
218 let dst_local =
219 runtime_local_index(ctx.local_indices, dst.as_u32(), "pointer borrow dst")?;
220 body.local_get(pointer_local)
221 .i32_const(POINTER_VALUE_OFFSET as i32)
222 .binop(BinaryOp::I32Add)
223 .local_set(dst_local);
224 Ok(())
225 }
226 ruka_mir::MirInstr::Copy { src, dst }
227 | ruka_mir::MirInstr::Move { src, dst }
228 | ruka_mir::MirInstr::AssignLocal { src, dst } => {
229 match (
230 runtime_local(ctx.local_indices, src.as_u32())?,
231 runtime_local(ctx.local_indices, dst.as_u32())?,
232 ) {
233 (Some(src_local), Some(dst_local)) => {
234 let src_mir_ty = local_ty(ctx.local_tys, src.as_u32(), "copy src mir ty")?;
235 let dst_mir_ty = local_ty(ctx.local_tys, dst.as_u32(), "copy dst mir ty")?;
236 if matches!(instr, ruka_mir::MirInstr::Copy { .. }) {
237 let ownership = local_heap_ownership(
238 ctx.local_heap_ownership,
239 src.as_u32(),
240 "copy src ownership",
241 )?;
242 if ownership.uses_heap_ops() {
243 emit_clone_for_type(
244 body,
245 ctx.runtime,
246 ctx.memory_id,
247 None,
248 ctx.function_line,
249 src_local,
250 src_mir_ty,
251 ctx.structs,
252 ctx.enums,
253 ctx.scratch_i32_local,
254 ctx.scratch_i32_local_b,
255 ctx.scratch_i32_local_c,
256 ctx.scratch_i32_local_d,
257 ctx.scratch_i32_local_e,
258 ctx.scratch_i64_local,
259 dst_local,
260 )?;
261 return Ok(());
262 }
263 }
264 let src_ty = runtime_local_valtype(
265 ctx.local_runtime_types,
266 src.as_u32(),
267 "copy src ty",
268 )?;
269 let dst_ty = runtime_local_valtype(
270 ctx.local_runtime_types,
271 dst.as_u32(),
272 "copy dst ty",
273 )?;
274 body.local_get(src_local);
275 match (src_ty, dst_ty) {
276 (ValType::I64, ValType::I64)
277 | (ValType::I32, ValType::I32)
278 | (ValType::F32, ValType::F32)
279 | (ValType::F64, ValType::F64) => {}
280 (ValType::I32, ValType::I64) => {
281 body.unop(UnaryOp::I64ExtendUI32);
282 }
283 (ValType::I64, ValType::I32) => {
284 body.unop(UnaryOp::I32WrapI64);
285 }
286 (ValType::F32, ValType::F64) => {
287 body.unop(UnaryOp::F64PromoteF32);
288 }
289 (ValType::F64, ValType::F32) => {
290 body.unop(UnaryOp::F32DemoteF64);
291 }
292 _ => {
293 return Err(LowerError::UnsupportedInstruction(
294 "unsupported copy runtime type conversion",
295 ));
296 }
297 }
298 emit_normalize_integer_top_of_stack(body, dst_mir_ty, dst_ty)?;
299 body.local_set(dst_local);
300 if matches!(instr, ruka_mir::MirInstr::Move { .. }) {
301 let ownership = local_heap_ownership(
302 ctx.local_heap_ownership,
303 src.as_u32(),
304 "move src ownership",
305 )?;
306 if ownership.uses_heap_ops() {
307 body.i32_const(0).local_set(src_local);
308 }
309 }
310 }
311 (None, None) => {}
312 (Some(_), None) => {}
313 (None, Some(_)) => {
314 return Err(LowerError::UnsupportedInstruction("copy from unit local"));
315 }
316 }
317 Ok(())
318 }
319 ruka_mir::MirInstr::NumCast { src, dst } => {
320 let src_local = runtime_local_index(ctx.local_indices, src.as_u32(), "num cast src")?;
321 let dst_local = runtime_local_index(ctx.local_indices, dst.as_u32(), "num cast dst")?;
322 let src_ty =
323 runtime_local_valtype(ctx.local_runtime_types, src.as_u32(), "num cast src ty")?;
324 let dst_ty =
325 runtime_local_valtype(ctx.local_runtime_types, dst.as_u32(), "num cast dst ty")?;
326 let semantic_src_ty = local_ty(ctx.local_tys, src.as_u32(), "num cast src semantic")?;
327 let semantic_dst_ty = local_ty(ctx.local_tys, dst.as_u32(), "num cast dst semantic")?;
328 body.local_get(src_local);
329 match (src_ty, dst_ty) {
330 (ValType::I32, ValType::I64) => {
331 if is_unsigned_integer_ty(semantic_src_ty) {
332 body.unop(UnaryOp::I64ExtendUI32);
333 } else {
334 body.unop(UnaryOp::I64ExtendSI32);
335 }
336 }
337 (ValType::I64, ValType::I32) => {
338 body.unop(UnaryOp::I32WrapI64);
339 }
340 (ValType::I32, ValType::F32) => {
341 if is_unsigned_integer_ty(semantic_src_ty) {
342 body.unop(UnaryOp::F32ConvertUI32);
343 } else {
344 body.unop(UnaryOp::F32ConvertSI32);
345 }
346 }
347 (ValType::I32, ValType::F64) => {
348 if is_unsigned_integer_ty(semantic_src_ty) {
349 body.unop(UnaryOp::F64ConvertUI32);
350 } else {
351 body.unop(UnaryOp::F64ConvertSI32);
352 }
353 }
354 (ValType::I64, ValType::F32) => {
355 if is_unsigned_integer_ty(semantic_src_ty) {
356 body.unop(UnaryOp::F32ConvertUI64);
357 } else {
358 body.unop(UnaryOp::F32ConvertSI64);
359 }
360 }
361 (ValType::I64, ValType::F64) => {
362 if is_unsigned_integer_ty(semantic_src_ty) {
363 body.unop(UnaryOp::F64ConvertUI64);
364 } else {
365 body.unop(UnaryOp::F64ConvertSI64);
366 }
367 }
368 (ValType::F32, ValType::F64) => {
369 body.unop(UnaryOp::F64PromoteF32);
370 }
371 (ValType::F64, ValType::F32) => {
372 body.unop(UnaryOp::F32DemoteF64);
373 }
374 (ValType::F32, ValType::I32) => {
375 if is_unsigned_integer_ty(semantic_dst_ty) {
376 body.unop(UnaryOp::I32TruncUF32);
377 } else {
378 body.unop(UnaryOp::I32TruncSF32);
379 }
380 }
381 (ValType::F32, ValType::I64) => {
382 if is_unsigned_integer_ty(semantic_dst_ty) {
383 body.unop(UnaryOp::I64TruncUF32);
384 } else {
385 body.unop(UnaryOp::I64TruncSF32);
386 }
387 }
388 (ValType::F64, ValType::I32) => {
389 if is_unsigned_integer_ty(semantic_dst_ty) {
390 body.unop(UnaryOp::I32TruncUF64);
391 } else {
392 body.unop(UnaryOp::I32TruncSF64);
393 }
394 }
395 (ValType::F64, ValType::I64) => {
396 if is_unsigned_integer_ty(semantic_dst_ty) {
397 body.unop(UnaryOp::I64TruncUF64);
398 } else {
399 body.unop(UnaryOp::I64TruncSF64);
400 }
401 }
402 _ => {}
403 }
404 emit_normalize_integer_top_of_stack(body, semantic_dst_ty, dst_ty)?;
405 body.local_set(dst_local);
406 Ok(())
407 }
408 ruka_mir::MirInstr::CheckedIntCast { src, dst } => {
409 let src_local =
410 runtime_local_index(ctx.local_indices, src.as_u32(), "checked cast src")?;
411 let dst_local =
412 runtime_local_index(ctx.local_indices, dst.as_u32(), "checked cast dst")?;
413 let src_ty = runtime_local_valtype(
414 ctx.local_runtime_types,
415 src.as_u32(),
416 "checked cast src ty",
417 )?;
418 let dst_ty = runtime_local_valtype(
419 ctx.local_runtime_types,
420 dst.as_u32(),
421 "checked cast dst ty",
422 )?;
423 let semantic_src_ty =
424 local_ty(ctx.local_tys, src.as_u32(), "checked cast src semantic")?;
425 let semantic_dst_ty =
426 local_ty(ctx.local_tys, dst.as_u32(), "checked cast dst semantic")?;
427 if !(semantic_src_ty.is_integer() && semantic_dst_ty.is_integer()) {
428 return Err(LowerError::UnsupportedInstruction(
429 "checked int cast requires integer locals",
430 ));
431 }
432
433 body.local_get(src_local);
434 match src_ty {
435 ValType::I32 => {
436 if is_unsigned_integer_ty(semantic_src_ty) {
437 body.unop(UnaryOp::I64ExtendUI32);
438 } else {
439 body.unop(UnaryOp::I64ExtendSI32);
440 }
441 }
442 ValType::I64 => {}
443 _ => {
444 return Err(LowerError::UnsupportedInstruction(
445 "checked int cast only supports i32/i64 runtime values",
446 ));
447 }
448 }
449 body.local_set(ctx.scratch_i64_local);
450
451 if is_unsigned_integer_ty(semantic_src_ty) {
452 if is_unsigned_integer_ty(semantic_dst_ty) {
453 if let Some(max) = unsigned_integer_max_u64(semantic_dst_ty)
454 && max != u64::MAX
455 {
456 emit_i64_check_unsigned_lte(body, ctx.scratch_i64_local, max as i64);
457 }
458 } else if let Some(max) = signed_integer_max_i64(semantic_dst_ty) {
459 emit_i64_check_unsigned_lte(body, ctx.scratch_i64_local, max);
460 }
461 } else if is_unsigned_integer_ty(semantic_dst_ty) {
462 emit_i64_check_signed_gte(body, ctx.scratch_i64_local, 0);
463 if let Some(max) = unsigned_integer_max_u64(semantic_dst_ty)
464 && max != u64::MAX
465 {
466 emit_i64_check_signed_lte(body, ctx.scratch_i64_local, max as i64);
467 }
468 } else {
469 let min = signed_integer_min_i64(semantic_dst_ty)
470 .ok_or(LowerError::UnsupportedInstruction("checked cast dst min"))?;
471 let max = signed_integer_max_i64(semantic_dst_ty)
472 .ok_or(LowerError::UnsupportedInstruction("checked cast dst max"))?;
473 emit_i64_check_signed_gte(body, ctx.scratch_i64_local, min);
474 emit_i64_check_signed_lte(body, ctx.scratch_i64_local, max);
475 }
476
477 body.local_get(ctx.scratch_i64_local);
478 match dst_ty {
479 ValType::I64 => {}
480 ValType::I32 => {
481 body.unop(UnaryOp::I32WrapI64);
482 }
483 _ => {
484 return Err(LowerError::UnsupportedInstruction(
485 "checked int cast runtime type mismatch",
486 ));
487 }
488 }
489 body.local_set(dst_local);
490 Ok(())
491 }
492 ruka_mir::MirInstr::DerefCopy { src, dst } => {
493 let src_ref = runtime_local_index(ctx.local_indices, src.as_u32(), "deref src")?;
494 let dst_local = runtime_local_index(ctx.local_indices, dst.as_u32(), "deref dst")?;
495 let src_ty =
496 runtime_local_valtype(ctx.local_runtime_types, src.as_u32(), "deref src ty")?;
497 let dst_ty =
498 runtime_local_valtype(ctx.local_runtime_types, dst.as_u32(), "deref dst ty")?;
499 if is_passthrough_place_local(ctx, *src) {
500 body.local_get(src_ref);
501 match (src_ty, dst_ty) {
502 (ValType::I64, ValType::I64) | (ValType::I32, ValType::I32) => {}
503 (ValType::I32, ValType::I64) => {
504 body.unop(UnaryOp::I64ExtendUI32);
505 }
506 (ValType::I64, ValType::I32) => {
507 body.unop(UnaryOp::I32WrapI64);
508 }
509 _ => {
510 return Err(LowerError::UnsupportedInstruction(
511 "unsupported passthrough deref runtime type conversion",
512 ));
513 }
514 }
515 } else {
516 body.local_get(src_ref).instr(Load {
517 memory: ctx.memory_id,
518 kind: LoadKind::I64 { atomic: false },
519 arg: MemArg {
520 align: 8,
521 offset: 0,
522 },
523 });
524 match dst_ty {
525 ValType::I64 => {}
526 ValType::I32 => {
527 body.unop(UnaryOp::I32WrapI64);
528 }
529 _ => {
530 return Err(LowerError::UnsupportedInstruction(
531 "unsupported deref destination type",
532 ));
533 }
534 }
535 }
536 body.local_set(dst_local);
537 Ok(())
538 }
539 ruka_mir::MirInstr::StoreRef { src, dst_ref } => {
540 let src_local = runtime_local_index(ctx.local_indices, src.as_u32(), "store ref src")?;
541 let src_ty =
542 runtime_local_valtype(ctx.local_runtime_types, src.as_u32(), "store ref src ty")?;
543 let dst_ref_id = *dst_ref;
544 let dst_ref =
545 runtime_local_index(ctx.local_indices, dst_ref_id.as_u32(), "store ref dst")?;
546 if is_passthrough_place_local(ctx, dst_ref_id) {
547 let dst_ty = runtime_local_valtype(
548 ctx.local_runtime_types,
549 dst_ref_id.as_u32(),
550 "store ref dst ty",
551 )?;
552 body.local_get(src_local);
553 match (src_ty, dst_ty) {
554 (ValType::I64, ValType::I64)
555 | (ValType::I32, ValType::I32)
556 | (ValType::F32, ValType::F32)
557 | (ValType::F64, ValType::F64) => {}
558 (ValType::I32, ValType::I64) => {
559 body.unop(UnaryOp::I64ExtendUI32);
560 }
561 (ValType::I64, ValType::I32) => {
562 body.unop(UnaryOp::I32WrapI64);
563 }
564 (ValType::F32, ValType::F64) => {
565 body.unop(UnaryOp::F64PromoteF32);
566 }
567 (ValType::F64, ValType::F32) => {
568 body.unop(UnaryOp::F32DemoteF64);
569 }
570 _ => {
571 return Err(LowerError::UnsupportedInstruction(
572 "unsupported store ref passthrough conversion",
573 ));
574 }
575 }
576 body.local_set(dst_ref);
577 return Ok(());
578 }
579 body.local_get(dst_ref).local_get(src_local);
580 match src_ty {
581 ValType::I64 => {}
582 ValType::I32 => {
583 body.unop(UnaryOp::I64ExtendUI32);
584 }
585 _ => {
586 return Err(LowerError::UnsupportedInstruction(
587 "unsupported store ref source type",
588 ));
589 }
590 }
591 body.instr(Store {
592 memory: ctx.memory_id,
593 kind: StoreKind::I64 { atomic: false },
594 arg: MemArg {
595 align: 8,
596 offset: 0,
597 },
598 });
599 Ok(())
600 }
601 ruka_mir::MirInstr::Call { .. }
602 | ruka_mir::MirInstr::CallIntrinsic { .. }
603 | ruka_mir::MirInstr::CallExtern { .. }
604 | ruka_mir::MirInstr::IntAdd { .. }
605 | ruka_mir::MirInstr::IntSub { .. }
606 | ruka_mir::MirInstr::IntMul { .. }
607 | ruka_mir::MirInstr::IntDiv { .. }
608 | ruka_mir::MirInstr::IntMod { .. }
609 | ruka_mir::MirInstr::IntLt { .. }
610 | ruka_mir::MirInstr::IntGt { .. }
611 | ruka_mir::MirInstr::IntLtEq { .. }
612 | ruka_mir::MirInstr::IntGtEq { .. }
613 | ruka_mir::MirInstr::IntEq { .. }
614 | ruka_mir::MirInstr::IntNeq { .. }
615 | ruka_mir::MirInstr::MakeArray { .. }
616 | ruka_mir::MirInstr::CollectionLen { .. }
617 | ruka_mir::MirInstr::IndexBorrowRo { .. }
618 | ruka_mir::MirInstr::IndexBorrowMut { .. }
619 | ruka_mir::MirInstr::MakeStruct { .. }
620 | ruka_mir::MirInstr::ReadField { .. }
621 | ruka_mir::MirInstr::FieldBorrowRo { .. }
622 | ruka_mir::MirInstr::FieldBorrowMut { .. }
623 | ruka_mir::MirInstr::MakeEnum { .. }
624 | ruka_mir::MirInstr::EnumIsVariant { .. }
625 | ruka_mir::MirInstr::EnumGetField { .. } => {
626 unreachable!("instruction should be lowered by helper before main match")
627 }
628 _ => Err(LowerError::UnsupportedInstruction(instr_name(instr))),
629 }
630}