1use super::*;
2
3pub(crate) fn emit_stmt_tokens(
4 function: &MirFunction,
5 stmt: &MirStmt,
6 source_file: &str,
7 source_line: usize,
8 mut_locals: &HashSet<MirLocalId>,
9 read_locals: &HashSet<MirLocalId>,
10 ref_ro_locals: &HashSet<MirLocalId>,
11 ref_mut_locals: &HashSet<MirLocalId>,
12 slice_locals: &HashSet<MirLocalId>,
13 function_names: &FunctionNames,
14 program_names: &ProgramNames,
15) -> rust::Tokens {
16 let mut tokens = rust::Tokens::new();
17 match stmt {
18 MirStmt::Instr(instr) => {
19 let instr_tokens = emit_instr_tokens(
20 function,
21 instr,
22 source_file,
23 source_line,
24 mut_locals,
25 read_locals,
26 ref_ro_locals,
27 ref_mut_locals,
28 slice_locals,
29 function_names,
30 program_names,
31 );
32 quote_in!(tokens => $instr_tokens);
33 }
34 MirStmt::If {
35 cond,
36 then_body,
37 else_body,
38 } => {
39 let cond = function_names.local_ident(*cond);
40 let then_tokens = emit_stmt_list_tokens(
41 function,
42 then_body,
43 source_file,
44 source_line,
45 mut_locals,
46 read_locals,
47 ref_ro_locals,
48 ref_mut_locals,
49 slice_locals,
50 function_names,
51 program_names,
52 );
53 let else_tokens = emit_stmt_list_tokens(
54 function,
55 else_body,
56 source_file,
57 source_line,
58 mut_locals,
59 read_locals,
60 ref_ro_locals,
61 ref_mut_locals,
62 slice_locals,
63 function_names,
64 program_names,
65 );
66 quote_in!(tokens =>
67 if $cond {
68 $then_tokens
69 } else {
70 $else_tokens
71 }
72 );
73 }
74 MirStmt::While {
75 loop_params,
76 init_args,
77 cond_body,
78 cond,
79 body,
80 step_args,
81 } => {
82 let mut init_tokens = rust::Tokens::new();
83 for (loop_param, init_arg) in loop_params.iter().zip(init_args.iter()) {
84 let loop_param_name = function_names.local_ident(*loop_param);
85 let init_arg_name = function_names.local_ident(*init_arg);
86 if read_locals.contains(loop_param) {
87 quote_in!(init_tokens => let mut $loop_param_name = $init_arg_name;);
88 } else {
89 quote_in!(init_tokens => let _ = $init_arg_name;);
90 }
91 }
92
93 let cond_tokens = emit_stmt_list_tokens(
94 function,
95 cond_body,
96 source_file,
97 source_line,
98 mut_locals,
99 read_locals,
100 ref_ro_locals,
101 ref_mut_locals,
102 slice_locals,
103 function_names,
104 program_names,
105 );
106 let cond = function_names.local_ident(*cond);
107 let body_tokens = emit_stmt_list_tokens(
108 function,
109 body,
110 source_file,
111 source_line,
112 mut_locals,
113 read_locals,
114 ref_ro_locals,
115 ref_mut_locals,
116 slice_locals,
117 function_names,
118 program_names,
119 );
120
121 let mut step_tokens = rust::Tokens::new();
122 if !loop_params.is_empty() {
123 if loop_params.len() == 1 {
124 let loop_param_name =
125 function_names.local_ident(*loop_params.first().expect("loop param"));
126 let step_arg_name =
127 function_names.local_ident(*step_args.first().expect("step arg"));
128 if read_locals.contains(loop_params.first().expect("loop param")) {
129 quote_in!(step_tokens => $loop_param_name = $step_arg_name;);
130 } else {
131 quote_in!(step_tokens => let _ = $step_arg_name;);
132 }
133 } else {
134 let used_loop_param_indexes = loop_params
135 .iter()
136 .enumerate()
137 .filter_map(|(index, local)| {
138 if read_locals.contains(local) {
139 Some(index)
140 } else {
141 None
142 }
143 })
144 .collect::<Vec<_>>();
145
146 if used_loop_param_indexes.is_empty() {
147 for step_arg in step_args {
148 let step_arg_name = function_names.local_ident(*step_arg);
149 quote_in!(step_tokens => let _ = $step_arg_name;);
150 }
151 } else {
152 let tuple_name =
153 synthetic_temp_ident(&format!("loop_next_{}", loop_params[0].index()));
154 let mut tuple_items = rust::Tokens::new();
155 for (index, step_arg) in step_args.iter().enumerate() {
156 if index > 0 {
157 quote_in!(tuple_items => ,);
158 }
159 let step_arg_name = function_names.local_ident(*step_arg);
160 quote_in!(tuple_items => $step_arg_name);
161 }
162 let tuple_name_decl = tuple_name.clone();
163 quote_in!(step_tokens => let $tuple_name_decl = ($tuple_items););
164 for index in used_loop_param_indexes {
165 let loop_param_name = function_names.local_ident(loop_params[index]);
166 let idx = index;
167 let tuple_name_ref = tuple_name.clone();
168 quote_in!(step_tokens => $loop_param_name = $tuple_name_ref.$idx;);
169 }
170 }
171 }
172 }
173
174 quote_in!(tokens =>
175 $init_tokens
176 loop {
177 $cond_tokens
178 if $cond {
179 $body_tokens
180 $step_tokens
181 } else {
182 break;
183 }
184 }
185 );
186 }
187 MirStmt::Return { value } => {
188 let value = function_names.local_ident(*value);
189 quote_in!(tokens => return $value;);
190 }
191 }
192 tokens
193}
194
195pub(crate) fn emit_instr_tokens(
196 function: &MirFunction,
197 instr: &MirInstr,
198 source_file: &str,
199 source_line: usize,
200 mut_locals: &HashSet<MirLocalId>,
201 read_locals: &HashSet<MirLocalId>,
202 ref_ro_locals: &HashSet<MirLocalId>,
203 ref_mut_locals: &HashSet<MirLocalId>,
204 slice_locals: &HashSet<MirLocalId>,
205 function_names: &FunctionNames,
206 program_names: &ProgramNames,
207) -> rust::Tokens {
208 if let Some(tokens) = emit_pointer_instr_tokens(
209 function,
210 instr,
211 source_file,
212 source_line,
213 mut_locals,
214 read_locals,
215 function_names,
216 ) {
217 return tokens;
218 }
219 if let Some(tokens) = emit_call_like_instr_tokens(
220 function,
221 instr,
222 mut_locals,
223 read_locals,
224 function_names,
225 program_names,
226 ) {
227 return tokens;
228 }
229 if let Some(tokens) =
230 emit_int_binary_instr_tokens(instr, mut_locals, read_locals, function_names)
231 {
232 return tokens;
233 }
234 if let Some(tokens) =
235 emit_collection_borrow_instr_tokens(instr, mut_locals, read_locals, function_names)
236 {
237 return tokens;
238 }
239
240 let mut tokens = rust::Tokens::new();
241
242 match instr {
243 MirInstr::ConstUnit { dst } => {
244 emit_local_binding(
245 &mut tokens,
246 *dst,
247 quote!(()),
248 quote!(()),
249 true,
250 mut_locals,
251 read_locals,
252 function_names,
253 );
254 }
255 MirInstr::ConstInt { dst, value } => {
256 let int_value = *value;
257 let value = match function.locals[*dst].ty {
258 Ty::U8 => quote!($int_value as u8),
259 Ty::U16 => quote!($int_value as u16),
260 Ty::U32 => quote!($int_value as u32),
261 Ty::U64 => quote!($int_value as u64),
262 Ty::I8 => quote!($int_value as i8),
263 Ty::I16 => quote!($int_value as i16),
264 Ty::I32 => quote!($int_value as i32),
265 Ty::I64 => quote!($int_value),
266 Ty::F32 => quote!($int_value as f32),
267 Ty::F64 => quote!($int_value as f64),
268 _ => quote!($int_value),
269 };
270 emit_local_binding(
271 &mut tokens,
272 *dst,
273 value.clone(),
274 value,
275 true,
276 mut_locals,
277 read_locals,
278 function_names,
279 );
280 }
281 MirInstr::ConstFloat { dst, value } => {
282 let float_value = *value;
283 let value = match function.locals[*dst].ty {
284 Ty::F32 => {
285 let bits = (float_value as f32).to_bits();
286 quote!(f32::from_bits($bits))
287 }
288 _ => {
289 let bits = float_value.to_bits();
290 quote!(f64::from_bits($bits))
291 }
292 };
293 emit_local_binding(
294 &mut tokens,
295 *dst,
296 value.clone(),
297 value,
298 true,
299 mut_locals,
300 read_locals,
301 function_names,
302 );
303 }
304 MirInstr::ConstString { dst, value } => {
305 let value = value.clone();
306 let string_value = quote!(String::from($(quoted(value.as_str()))));
307 emit_local_binding(
308 &mut tokens,
309 *dst,
310 string_value.clone(),
311 string_value,
312 true,
313 mut_locals,
314 read_locals,
315 function_names,
316 );
317 }
318 MirInstr::ConstBool { dst, value } => {
319 let value = if *value { quote!(true) } else { quote!(false) };
320 emit_local_binding(
321 &mut tokens,
322 *dst,
323 value.clone(),
324 value,
325 true,
326 mut_locals,
327 read_locals,
328 function_names,
329 );
330 }
331 MirInstr::ConstNull { dst } => {
332 emit_local_binding(
333 &mut tokens,
334 *dst,
335 quote!(None),
336 quote!(None::<()>),
337 true,
338 mut_locals,
339 read_locals,
340 function_names,
341 );
342 }
343 MirInstr::MakeArray { items, dst, .. } => {
344 let dst = *dst;
345 let array_items = emit_array_items(items, function_names);
346 let value_tokens = if slice_locals.contains(&dst) {
347 emit_boxed_slice_items(items, function_names)
348 } else {
349 array_items
350 };
351 emit_local_binding(
352 &mut tokens,
353 dst,
354 value_tokens.clone(),
355 value_tokens,
356 true,
357 mut_locals,
358 read_locals,
359 function_names,
360 );
361 }
362 MirInstr::MakeTuple { items, dst, .. } => {
363 let dst = *dst;
364 let mut tuple_items = rust::Tokens::new();
365 for (index, item) in items.iter().enumerate() {
366 if index > 0 {
367 quote_in!(tuple_items => ,);
368 }
369 let local = function_names.local_ident(item.local);
370 quote_in!(tuple_items => $local);
371 }
372 if items.len() == 1 {
373 quote_in!(tuple_items => ,);
374 }
375 let value = quote!(($tuple_items));
376 emit_local_binding(
377 &mut tokens,
378 dst,
379 value.clone(),
380 value,
381 true,
382 mut_locals,
383 read_locals,
384 function_names,
385 );
386 }
387 MirInstr::MakeStruct {
388 name, fields, dst, ..
389 } => {
390 let dst = *dst;
391 let struct_name = mangle_struct_ident(name);
392 let mut field_tokens = rust::Tokens::new();
393 for (index, (field_name, local)) in fields.iter().enumerate() {
394 if index > 0 {
395 quote_in!(field_tokens => ,);
396 }
397 let field_name = field_name.clone();
398 let local = function_names.local_ident(*local);
399 quote_in!(field_tokens => $field_name: $local);
400 }
401 let value = quote!($struct_name { $field_tokens });
402 emit_local_binding(
403 &mut tokens,
404 dst,
405 value.clone(),
406 value,
407 true,
408 mut_locals,
409 read_locals,
410 function_names,
411 );
412 }
413 MirInstr::MakeEnum {
414 enum_name,
415 variant,
416 payload,
417 dst,
418 ..
419 } => {
420 let enum_name = if enum_name == "Option" {
421 "Option".to_owned()
422 } else {
423 mangle_struct_ident(enum_name)
424 };
425 let variant = variant.clone();
426 let dst = *dst;
427 let payload_tokens = emit_enum_payload_items(payload, function_names);
428 let value = if payload.is_empty() {
429 quote!($enum_name::$variant)
430 } else {
431 quote!($enum_name::$variant($payload_tokens))
432 };
433 emit_local_binding(
434 &mut tokens,
435 dst,
436 value.clone(),
437 value,
438 true,
439 mut_locals,
440 read_locals,
441 function_names,
442 );
443 }
444 MirInstr::ReadField { base, field, dst } => {
445 let base_local = *base;
446 let base = function_names.local_ident(base_local);
447 let field = field.clone();
448 let dst = *dst;
449 let value_expr = if let Ok(index) = field.parse::<usize>() {
450 if ref_ro_locals.contains(&base_local) || ref_mut_locals.contains(&base_local) {
451 quote!((*$base).$index.clone())
452 } else {
453 quote!($base.$index.clone())
454 }
455 } else if ref_ro_locals.contains(&base_local) || ref_mut_locals.contains(&base_local) {
456 quote!((*$base).$field.clone())
457 } else {
458 quote!($base.$field.clone())
459 };
460 emit_local_binding(
461 &mut tokens,
462 dst,
463 value_expr.clone(),
464 value_expr,
465 true,
466 mut_locals,
467 read_locals,
468 function_names,
469 );
470 }
471 MirInstr::EnumIsVariant {
472 value,
473 enum_name,
474 variant,
475 payload_len,
476 dst,
477 } => {
478 let value = function_names.local_ident(*value);
479 let enum_name = if enum_name == "Option" {
480 "Option".to_owned()
481 } else {
482 mangle_struct_ident(enum_name)
483 };
484 let variant = variant.clone();
485 let pattern = emit_enum_variant_wildcard_pattern(&enum_name, &variant, *payload_len);
486 let check_expr = quote!(matches!(&$value, $pattern));
487 emit_local_binding(
488 &mut tokens,
489 *dst,
490 check_expr.clone(),
491 check_expr,
492 true,
493 mut_locals,
494 read_locals,
495 function_names,
496 );
497 }
498 MirInstr::EnumGetField {
499 value,
500 enum_name,
501 variant,
502 index,
503 dst,
504 } => {
505 let value = function_names.local_ident(*value);
506 let enum_name = if enum_name == "Option" {
507 "Option".to_owned()
508 } else {
509 mangle_struct_ident(enum_name)
510 };
511 let variant = variant.clone();
512 let extract_expr = emit_enum_get_field_expr(&value, &enum_name, &variant, *index);
513 emit_local_binding(
514 &mut tokens,
515 *dst,
516 extract_expr.clone(),
517 extract_expr,
518 true,
519 mut_locals,
520 read_locals,
521 function_names,
522 );
523 }
524 MirInstr::Copy { src, dst } => {
525 let src_local_id = *src;
526 let src = function_names.local_ident(src_local_id);
527 let value = quote!($src.clone());
528 emit_local_binding(
529 &mut tokens,
530 *dst,
531 value.clone(),
532 value,
533 true,
534 mut_locals,
535 read_locals,
536 function_names,
537 );
538 }
539 MirInstr::Move { src, dst } => {
540 let src = function_names.local_ident(*src);
541 let value = quote!($src);
542 emit_local_binding(
543 &mut tokens,
544 *dst,
545 value.clone(),
546 value,
547 true,
548 mut_locals,
549 read_locals,
550 function_names,
551 );
552 }
553 MirInstr::AssignLocal { src, dst } => {
554 let src = function_names.local_ident(*src);
555 let dst = function_names.local_ident(*dst);
556 quote_in!(tokens => $dst = $src;);
557 }
558 MirInstr::StoreRef { src, dst_ref } => {
559 let src = function_names.local_ident(*src);
560 let dst_ref = function_names.local_ident(*dst_ref);
561 quote_in!(tokens => *$dst_ref = $src;);
562 }
563 MirInstr::NumCast { src, dst } => {
564 let src = function_names.local_ident(*src);
565 let cast_ty = emit_ty_tokens(&function.locals[*dst].ty);
566 let value = quote!($src as $cast_ty);
567 emit_local_binding(
568 &mut tokens,
569 *dst,
570 value.clone(),
571 value,
572 true,
573 mut_locals,
574 read_locals,
575 function_names,
576 );
577 }
578 MirInstr::CheckedIntCast { src, dst } => {
579 let src_name = function_names.local_ident(*src);
580 let cast_ty = emit_ty_tokens(&function.locals[*dst].ty);
581 let (target_min, target_max) = int_ty_range_i128(&function.locals[*dst].ty)
582 .expect("checked int cast destination should be integer");
583 let value = quote!({
584 let source_value_i128 = $src_name as i128;
585 if source_value_i128 < $target_min || source_value_i128 > $target_max {
586 panic!("checked integer cast overflow");
587 }
588 $src_name as $cast_ty
589 });
590 emit_local_binding(
591 &mut tokens,
592 *dst,
593 value.clone(),
594 value,
595 true,
596 mut_locals,
597 read_locals,
598 function_names,
599 );
600 }
601 MirInstr::AssignFieldPath { base, fields, src } => {
602 let base_name = function_names.local_ident(*base);
603 let src = function_names.local_ident(*src);
604 let mut path = rust::Tokens::new();
605 for field in fields {
606 if let Ok(index) = field.parse::<usize>() {
607 quote_in!(path => .$index);
608 } else {
609 let field = field.clone();
610 quote_in!(path => .$field);
611 }
612 }
613 if ref_mut_locals.contains(base) {
614 quote_in!(tokens => (*$base_name)$path = $src;);
615 } else {
616 quote_in!(tokens => $base_name$path = $src;);
617 }
618 }
619 MirInstr::ReleaseHeap { local } => {
620 let local = function_names.local_ident(*local);
621 quote_in!(tokens => let _ = &$local;);
622 }
623 MirInstr::PointerNew { .. }
624 | MirInstr::CollectionLen { .. }
625 | MirInstr::IndexBorrowRo { .. }
626 | MirInstr::IndexBorrowMut { .. }
627 | MirInstr::SliceBorrowRo { .. }
628 | MirInstr::SliceBorrowMut { .. }
629 | MirInstr::FieldBorrowRo { .. }
630 | MirInstr::FieldBorrowMut { .. }
631 | MirInstr::PointerIsSome { .. }
632 | MirInstr::PointerBorrowRo { .. }
633 | MirInstr::PointerBorrowMut { .. }
634 | MirInstr::DerefCopy { .. }
635 | MirInstr::Call { .. }
636 | MirInstr::CallExtern { .. }
637 | MirInstr::CallIntrinsic { .. }
638 | MirInstr::IntLt { .. }
639 | MirInstr::IntGt { .. }
640 | MirInstr::IntLtEq { .. }
641 | MirInstr::IntGtEq { .. }
642 | MirInstr::IntEq { .. }
643 | MirInstr::IntNeq { .. }
644 | MirInstr::IntAdd { .. }
645 | MirInstr::IntSub { .. }
646 | MirInstr::IntMul { .. }
647 | MirInstr::IntDiv { .. }
648 | MirInstr::IntMod { .. } => {
649 unreachable!("instruction should be emitted by helper before match")
650 }
651 }
652
653 tokens
654}
655
656pub(crate) fn int_ty_range_i128(ty: &Ty) -> Option<(rust::Tokens, rust::Tokens)> {
657 match ty {
658 Ty::U8 => Some((quote!(u8::MIN as i128), quote!(u8::MAX as i128))),
659 Ty::U16 => Some((quote!(u16::MIN as i128), quote!(u16::MAX as i128))),
660 Ty::U32 => Some((quote!(u32::MIN as i128), quote!(u32::MAX as i128))),
661 Ty::U64 => Some((quote!(u64::MIN as i128), quote!(u64::MAX as i128))),
662 Ty::I8 => Some((quote!(i8::MIN as i128), quote!(i8::MAX as i128))),
663 Ty::I16 => Some((quote!(i16::MIN as i128), quote!(i16::MAX as i128))),
664 Ty::I32 => Some((quote!(i32::MIN as i128), quote!(i32::MAX as i128))),
665 Ty::I64 => Some((quote!(i64::MIN as i128), quote!(i64::MAX as i128))),
666 _ => None,
667 }
668}
669
670pub(crate) fn emit_call_like_instr_tokens(
671 function: &MirFunction,
672 instr: &MirInstr,
673 mut_locals: &HashSet<MirLocalId>,
674 read_locals: &HashSet<MirLocalId>,
675 function_names: &FunctionNames,
676 program_names: &ProgramNames,
677) -> Option<rust::Tokens> {
678 let mut tokens = rust::Tokens::new();
679 match instr {
680 MirInstr::Call { callee, args, dst } => {
681 let callee = program_names.function_ident(*callee);
682 let args = emit_internal_call_args(function, args, function_names);
683 let value = quote!($callee($args));
684 emit_local_binding(
685 &mut tokens,
686 *dst,
687 value.clone(),
688 value,
689 true,
690 mut_locals,
691 read_locals,
692 function_names,
693 );
694 Some(tokens)
695 }
696 MirInstr::CallExtern { symbol, args, dst } => {
697 let expr = emit_extern_call_expr(function, symbol, args, function_names);
698 emit_local_binding(
699 &mut tokens,
700 *dst,
701 expr.clone(),
702 expr,
703 true,
704 mut_locals,
705 read_locals,
706 function_names,
707 );
708 Some(tokens)
709 }
710 MirInstr::CallIntrinsic {
711 intrinsic,
712 args,
713 dst,
714 ..
715 } => {
716 let expr = emit_intrinsic_call_expr(function, *intrinsic, args, function_names);
717 emit_local_binding(
718 &mut tokens,
719 *dst,
720 expr.clone(),
721 expr,
722 true,
723 mut_locals,
724 read_locals,
725 function_names,
726 );
727 Some(tokens)
728 }
729 _ => None,
730 }
731}
732
733pub(crate) fn emit_pointer_instr_tokens(
734 function: &MirFunction,
735 instr: &MirInstr,
736 source_file: &str,
737 source_line: usize,
738 mut_locals: &HashSet<MirLocalId>,
739 read_locals: &HashSet<MirLocalId>,
740 function_names: &FunctionNames,
741) -> Option<rust::Tokens> {
742 let mut tokens = rust::Tokens::new();
743 match instr {
744 MirInstr::PointerNew { src, dst } => {
745 let src = function_names.local_ident(*src);
746 let site = format!(
747 "{}:{} fn {} pointer_new local {}",
748 source_file,
749 source_line,
750 function.name,
751 dst.index()
752 );
753 let value = quote!(ruka_runtime::ptr::Ptr::new_tracked($src, $(quoted(site.as_str()))));
754 emit_local_binding(
755 &mut tokens,
756 *dst,
757 value.clone(),
758 value,
759 true,
760 mut_locals,
761 read_locals,
762 function_names,
763 );
764 Some(tokens)
765 }
766 MirInstr::PointerIsSome { pointer, dst } => {
767 let pointer = function_names.local_ident(*pointer);
768 let value = quote!($pointer.is_some());
769 emit_local_binding(
770 &mut tokens,
771 *dst,
772 value.clone(),
773 value,
774 true,
775 mut_locals,
776 read_locals,
777 function_names,
778 );
779 Some(tokens)
780 }
781 MirInstr::PointerBorrowRo { pointer, dst } => {
782 let pointer = function_names.local_ident(*pointer);
783 let value = quote!($pointer.borrow());
784 emit_local_binding(
785 &mut tokens,
786 *dst,
787 value.clone(),
788 value,
789 false,
790 mut_locals,
791 read_locals,
792 function_names,
793 );
794 Some(tokens)
795 }
796 MirInstr::PointerBorrowMut { pointer, dst } => {
797 let pointer = function_names.local_ident(*pointer);
798 let value = quote!($pointer.borrow_mut());
799 emit_local_binding(
800 &mut tokens,
801 *dst,
802 value.clone(),
803 value,
804 false,
805 mut_locals,
806 read_locals,
807 function_names,
808 );
809 Some(tokens)
810 }
811 MirInstr::DerefCopy { src, dst } => {
812 let src = function_names.local_ident(*src);
813 let value = quote!((*$src).clone());
814 emit_local_binding(
815 &mut tokens,
816 *dst,
817 value.clone(),
818 value,
819 true,
820 mut_locals,
821 read_locals,
822 function_names,
823 );
824 Some(tokens)
825 }
826 _ => None,
827 }
828}