ruka_codegen_rust/
analysis_and_calls.rs

1use super::*;
2
3pub(crate) fn emit_collection_borrow_instr_tokens(
4    instr: &MirInstr,
5    mut_locals: &HashSet<MirLocalId>,
6    read_locals: &HashSet<MirLocalId>,
7    function_names: &FunctionNames,
8) -> Option<rust::Tokens> {
9    let mut tokens = rust::Tokens::new();
10    match instr {
11        MirInstr::CollectionLen { collection, dst } => {
12            let collection = function_names.local_ident(*collection);
13            let value = quote!($collection.len() as i64);
14            emit_local_binding(
15                &mut tokens,
16                *dst,
17                value.clone(),
18                value,
19                true,
20                mut_locals,
21                read_locals,
22                function_names,
23            );
24            Some(tokens)
25        }
26        MirInstr::IndexBorrowRo {
27            collection,
28            index,
29            dst,
30        } => {
31            let collection = function_names.local_ident(*collection);
32            let index = function_names.local_ident(*index);
33            let value = quote!(&$collection[$index as usize]);
34            emit_local_binding(
35                &mut tokens,
36                *dst,
37                value.clone(),
38                value,
39                false,
40                mut_locals,
41                read_locals,
42                function_names,
43            );
44            Some(tokens)
45        }
46        MirInstr::IndexBorrowMut {
47            collection,
48            index,
49            dst,
50        } => {
51            let collection = function_names.local_ident(*collection);
52            let index = function_names.local_ident(*index);
53            let value = quote!(&mut $collection[$index as usize]);
54            emit_local_binding(
55                &mut tokens,
56                *dst,
57                value.clone(),
58                value,
59                false,
60                mut_locals,
61                read_locals,
62                function_names,
63            );
64            Some(tokens)
65        }
66        MirInstr::SliceBorrowRo {
67            collection,
68            start,
69            end,
70            dst,
71        } => {
72            let collection = function_names.local_ident(*collection);
73            let slice_expr =
74                emit_slice_expr_tokens(&collection, *start, *end, function_names, false);
75            emit_local_binding(
76                &mut tokens,
77                *dst,
78                slice_expr.clone(),
79                slice_expr,
80                false,
81                mut_locals,
82                read_locals,
83                function_names,
84            );
85            Some(tokens)
86        }
87        MirInstr::SliceBorrowMut {
88            collection,
89            start,
90            end,
91            dst,
92        } => {
93            let collection = function_names.local_ident(*collection);
94            let slice_expr =
95                emit_slice_expr_tokens(&collection, *start, *end, function_names, true);
96            emit_local_binding(
97                &mut tokens,
98                *dst,
99                slice_expr.clone(),
100                slice_expr,
101                false,
102                mut_locals,
103                read_locals,
104                function_names,
105            );
106            Some(tokens)
107        }
108        MirInstr::FieldBorrowRo { base, field, dst } => {
109            let base = function_names.local_ident(*base);
110            let field_expr = if let Ok(index) = field.parse::<usize>() {
111                quote!(#$index)
112            } else {
113                let field = field.clone();
114                quote!($field)
115            };
116            let value = quote!(&$base.$field_expr);
117            emit_local_binding(
118                &mut tokens,
119                *dst,
120                value.clone(),
121                value,
122                false,
123                mut_locals,
124                read_locals,
125                function_names,
126            );
127            Some(tokens)
128        }
129        MirInstr::FieldBorrowMut { base, field, dst } => {
130            let base = function_names.local_ident(*base);
131            let field_expr = if let Ok(index) = field.parse::<usize>() {
132                quote!(#$index)
133            } else {
134                let field = field.clone();
135                quote!($field)
136            };
137            let value = quote!(&mut $base.$field_expr);
138            emit_local_binding(
139                &mut tokens,
140                *dst,
141                value.clone(),
142                value,
143                false,
144                mut_locals,
145                read_locals,
146                function_names,
147            );
148            Some(tokens)
149        }
150        _ => None,
151    }
152}
153
154pub(crate) fn emit_int_binary_instr_tokens(
155    instr: &MirInstr,
156    mut_locals: &HashSet<MirLocalId>,
157    read_locals: &HashSet<MirLocalId>,
158    function_names: &FunctionNames,
159) -> Option<rust::Tokens> {
160    let mut tokens = rust::Tokens::new();
161    let (lhs, rhs, dst, value) = match instr {
162        MirInstr::IntLt { lhs, rhs, dst } => (*lhs, *rhs, *dst, "lt"),
163        MirInstr::IntGt { lhs, rhs, dst } => (*lhs, *rhs, *dst, "gt"),
164        MirInstr::IntLtEq { lhs, rhs, dst } => (*lhs, *rhs, *dst, "lteq"),
165        MirInstr::IntGtEq { lhs, rhs, dst } => (*lhs, *rhs, *dst, "gteq"),
166        MirInstr::IntEq { lhs, rhs, dst } => (*lhs, *rhs, *dst, "eq"),
167        MirInstr::IntNeq { lhs, rhs, dst } => (*lhs, *rhs, *dst, "neq"),
168        MirInstr::IntAdd { lhs, rhs, dst } => (*lhs, *rhs, *dst, "add"),
169        MirInstr::IntSub { lhs, rhs, dst } => (*lhs, *rhs, *dst, "sub"),
170        MirInstr::IntMul { lhs, rhs, dst } => (*lhs, *rhs, *dst, "mul"),
171        MirInstr::IntDiv { lhs, rhs, dst } => (*lhs, *rhs, *dst, "div"),
172        MirInstr::IntMod { lhs, rhs, dst } => (*lhs, *rhs, *dst, "mod"),
173        _ => return None,
174    };
175    let lhs = function_names.local_ident(lhs);
176    let rhs = function_names.local_ident(rhs);
177    let expr = match value {
178        "lt" => quote!($lhs < $rhs),
179        "gt" => quote!($lhs > $rhs),
180        "lteq" => quote!($lhs <= $rhs),
181        "gteq" => quote!($lhs >= $rhs),
182        "eq" => quote!($lhs == $rhs),
183        "neq" => quote!($lhs != $rhs),
184        "add" => quote!($lhs + $rhs),
185        "sub" => quote!($lhs - $rhs),
186        "mul" => quote!($lhs * $rhs),
187        "div" => quote!($lhs / $rhs),
188        "mod" => quote!($lhs % $rhs),
189        _ => unreachable!("unsupported int op"),
190    };
191    emit_local_binding(
192        &mut tokens,
193        dst,
194        expr.clone(),
195        expr,
196        true,
197        mut_locals,
198        read_locals,
199        function_names,
200    );
201    Some(tokens)
202}
203
204pub(crate) fn emit_local_binding(
205    mut tokens: &mut rust::Tokens,
206    dst: MirLocalId,
207    binding_value: rust::Tokens,
208    discard_value: rust::Tokens,
209    allow_mut: bool,
210    mut_locals: &HashSet<MirLocalId>,
211    read_locals: &HashSet<MirLocalId>,
212    function_names: &FunctionNames,
213) {
214    if read_locals.contains(&dst) {
215        let dst_name = function_names.local_ident(dst);
216        if allow_mut && mut_locals.contains(&dst) {
217            quote_in!(tokens => let mut $dst_name = $binding_value;);
218        } else {
219            quote_in!(tokens => let $dst_name = $binding_value;);
220        }
221    } else {
222        quote_in!(tokens => let _ = $discard_value;);
223    }
224}
225
226pub(crate) fn collect_mutable_locals(stmts: &[MirStmt], mutable_locals: &mut HashSet<MirLocalId>) {
227    for stmt in stmts {
228        match stmt {
229            MirStmt::Instr(MirInstr::Call { args, .. }) => {
230                for arg in args {
231                    if arg.is_mutable_borrow() {
232                        let _ = mutable_locals.insert(arg.local);
233                    }
234                }
235            }
236            MirStmt::Instr(MirInstr::CallExtern { args, .. }) => {
237                for arg in args {
238                    if arg.is_mutable_borrow() {
239                        let _ = mutable_locals.insert(arg.local);
240                    }
241                }
242            }
243            MirStmt::Instr(MirInstr::CallIntrinsic { args, .. }) => {
244                for arg in args {
245                    if arg.is_mutable_borrow() {
246                        let _ = mutable_locals.insert(arg.local);
247                    }
248                }
249            }
250            MirStmt::Instr(MirInstr::IndexBorrowMut { collection, .. }) => {
251                let _ = mutable_locals.insert(*collection);
252            }
253            MirStmt::Instr(MirInstr::SliceBorrowMut { collection, .. }) => {
254                let _ = mutable_locals.insert(*collection);
255            }
256            MirStmt::Instr(MirInstr::PointerBorrowMut { pointer, .. }) => {
257                let _ = mutable_locals.insert(*pointer);
258            }
259            MirStmt::Instr(MirInstr::FieldBorrowMut { base, .. }) => {
260                let _ = mutable_locals.insert(*base);
261            }
262            MirStmt::Instr(MirInstr::AssignLocal { dst, .. }) => {
263                let _ = mutable_locals.insert(*dst);
264            }
265            MirStmt::Instr(MirInstr::AssignFieldPath { base, .. }) => {
266                let _ = mutable_locals.insert(*base);
267            }
268            MirStmt::Instr(_) => {}
269            MirStmt::If {
270                then_body,
271                else_body,
272                ..
273            } => {
274                collect_mutable_locals(then_body, mutable_locals);
275                collect_mutable_locals(else_body, mutable_locals);
276            }
277            MirStmt::While {
278                loop_params,
279                cond_body,
280                body,
281                ..
282            } => {
283                for local in loop_params {
284                    let _ = mutable_locals.insert(*local);
285                }
286                collect_mutable_locals(cond_body, mutable_locals);
287                collect_mutable_locals(body, mutable_locals);
288            }
289            MirStmt::Return { .. } => {}
290        }
291    }
292}
293
294pub(crate) fn collect_read_locals(stmts: &[MirStmt], read_locals: &mut HashSet<MirLocalId>) {
295    for stmt in stmts {
296        match stmt {
297            MirStmt::Instr(instr) => collect_instr_reads(instr, read_locals),
298            MirStmt::If {
299                cond,
300                then_body,
301                else_body,
302            } => {
303                let _ = read_locals.insert(*cond);
304                collect_read_locals(then_body, read_locals);
305                collect_read_locals(else_body, read_locals);
306            }
307            MirStmt::While {
308                init_args,
309                cond_body,
310                cond,
311                body,
312                step_args,
313                ..
314            } => {
315                for local in init_args {
316                    let _ = read_locals.insert(*local);
317                }
318                collect_read_locals(cond_body, read_locals);
319                let _ = read_locals.insert(*cond);
320                collect_read_locals(body, read_locals);
321                for local in step_args {
322                    let _ = read_locals.insert(*local);
323                }
324            }
325            MirStmt::Return { value } => {
326                let _ = read_locals.insert(*value);
327            }
328        }
329    }
330}
331
332pub(crate) fn collect_instr_reads(instr: &MirInstr, read_locals: &mut HashSet<MirLocalId>) {
333    match instr {
334        MirInstr::ConstUnit { .. }
335        | MirInstr::ConstInt { .. }
336        | MirInstr::ConstFloat { .. }
337        | MirInstr::ConstString { .. }
338        | MirInstr::ConstBool { .. }
339        | MirInstr::ConstNull { .. } => {}
340        MirInstr::PointerNew { src, .. } => {
341            let _ = read_locals.insert(*src);
342        }
343        MirInstr::MakeArray { items, .. } => {
344            for item in items {
345                let _ = read_locals.insert(item.local);
346            }
347        }
348        MirInstr::MakeTuple { items, .. } => {
349            for item in items {
350                let _ = read_locals.insert(item.local);
351            }
352        }
353        MirInstr::MakeStruct { fields, .. } => {
354            for (_, value) in fields {
355                let _ = read_locals.insert(*value);
356            }
357        }
358        MirInstr::MakeEnum { payload, .. } => {
359            for item in payload {
360                let _ = read_locals.insert(item.local);
361            }
362        }
363        MirInstr::ReadField { base, .. }
364        | MirInstr::FieldBorrowRo { base, .. }
365        | MirInstr::FieldBorrowMut { base, .. } => {
366            let _ = read_locals.insert(*base);
367        }
368        MirInstr::EnumIsVariant { value, .. } | MirInstr::EnumGetField { value, .. } => {
369            let _ = read_locals.insert(*value);
370        }
371        MirInstr::Copy { src, .. }
372        | MirInstr::Move { src, .. }
373        | MirInstr::NumCast { src, .. }
374        | MirInstr::CheckedIntCast { src, .. }
375        | MirInstr::DerefCopy { src, .. } => {
376            let _ = read_locals.insert(*src);
377        }
378        MirInstr::AssignLocal { src, dst } => {
379            let _ = read_locals.insert(*src);
380            let _ = read_locals.insert(*dst);
381        }
382        MirInstr::StoreRef { src, dst_ref } => {
383            let _ = read_locals.insert(*src);
384            let _ = read_locals.insert(*dst_ref);
385        }
386        MirInstr::AssignFieldPath { base, src, .. } => {
387            let _ = read_locals.insert(*base);
388            let _ = read_locals.insert(*src);
389        }
390        MirInstr::Call { args, .. } => {
391            for arg in args {
392                let _ = read_locals.insert(arg.local);
393            }
394        }
395        MirInstr::CallExtern { args, .. } => {
396            for arg in args {
397                let _ = read_locals.insert(arg.local);
398            }
399        }
400        MirInstr::CallIntrinsic { args, .. } => {
401            for arg in args {
402                let _ = read_locals.insert(arg.local);
403            }
404        }
405        MirInstr::CollectionLen { collection, .. } => {
406            let _ = read_locals.insert(*collection);
407        }
408        MirInstr::IndexBorrowRo {
409            collection, index, ..
410        }
411        | MirInstr::IndexBorrowMut {
412            collection, index, ..
413        } => {
414            let _ = read_locals.insert(*collection);
415            let _ = read_locals.insert(*index);
416        }
417        MirInstr::SliceBorrowRo {
418            collection,
419            start,
420            end,
421            ..
422        }
423        | MirInstr::SliceBorrowMut {
424            collection,
425            start,
426            end,
427            ..
428        } => {
429            let _ = read_locals.insert(*collection);
430            if let Some(start) = start {
431                let _ = read_locals.insert(*start);
432            }
433            if let Some(end) = end {
434                let _ = read_locals.insert(*end);
435            }
436        }
437        MirInstr::PointerIsSome { pointer, .. }
438        | MirInstr::PointerBorrowRo { pointer, .. }
439        | MirInstr::PointerBorrowMut { pointer, .. } => {
440            let _ = read_locals.insert(*pointer);
441        }
442        MirInstr::ReleaseHeap { local } => {
443            let _ = read_locals.insert(*local);
444        }
445        MirInstr::IntLt { lhs, rhs, .. }
446        | MirInstr::IntGt { lhs, rhs, .. }
447        | MirInstr::IntLtEq { lhs, rhs, .. }
448        | MirInstr::IntGtEq { lhs, rhs, .. }
449        | MirInstr::IntEq { lhs, rhs, .. }
450        | MirInstr::IntNeq { lhs, rhs, .. }
451        | MirInstr::IntAdd { lhs, rhs, .. }
452        | MirInstr::IntSub { lhs, rhs, .. }
453        | MirInstr::IntMul { lhs, rhs, .. }
454        | MirInstr::IntDiv { lhs, rhs, .. }
455        | MirInstr::IntMod { lhs, rhs, .. } => {
456            let _ = read_locals.insert(*lhs);
457            let _ = read_locals.insert(*rhs);
458        }
459    }
460}
461
462pub(crate) fn emit_slice_expr_tokens(
463    collection: &str,
464    start: Option<MirLocalId>,
465    end: Option<MirLocalId>,
466    function_names: &FunctionNames,
467    mutable: bool,
468) -> rust::Tokens {
469    let start_tokens = start.map(|value| function_names.local_ident(value));
470    let end_tokens = end.map(|value| function_names.local_ident(value));
471    match (start_tokens, end_tokens, mutable) {
472        (Some(start), Some(end), false) => {
473            quote!(&$collection[$start as usize..$end as usize])
474        }
475        (Some(start), None, false) => quote!(&$collection[$start as usize..]),
476        (None, Some(end), false) => quote!(&$collection[..$end as usize]),
477        (None, None, false) => quote!(&$collection[..]),
478        (Some(start), Some(end), true) => {
479            quote!(&mut $collection[$start as usize..$end as usize])
480        }
481        (Some(start), None, true) => quote!(&mut $collection[$start as usize..]),
482        (None, Some(end), true) => quote!(&mut $collection[..$end as usize]),
483        (None, None, true) => quote!(&mut $collection[..]),
484    }
485}
486
487pub(crate) fn collect_ref_locals(
488    function: &MirFunction,
489) -> (HashSet<MirLocalId>, HashSet<MirLocalId>) {
490    let mut ro_refs = HashSet::new();
491    let mut mut_refs = HashSet::new();
492    for (local_id, info) in function.locals.iter() {
493        if info.is_place() {
494            if info.is_mutable_place() {
495                let _ = mut_refs.insert(local_id);
496            } else {
497                let _ = ro_refs.insert(local_id);
498            }
499        }
500    }
501    (ro_refs, mut_refs)
502}
503
504pub(crate) fn collect_slice_locals(function: &MirFunction) -> HashSet<MirLocalId> {
505    let mut slices = HashSet::new();
506    for (local_id, info) in function.locals.iter() {
507        if matches!(info.ty, Ty::Slice(_)) {
508            let _ = slices.insert(local_id);
509        }
510    }
511    slices
512}
513
514pub(crate) fn emit_array_items(
515    items: &[MirAggregateArg],
516    function_names: &FunctionNames,
517) -> rust::Tokens {
518    let mut out = rust::Tokens::new();
519    for (index, item) in items.iter().enumerate() {
520        let local = function_names.local_ident(item.local);
521        if index > 0 {
522            quote_in!(out => ,);
523        }
524        if item.is_owned_move() {
525            quote_in!(out => $local)
526        } else if item.is_owned_copy() {
527            quote_in!(out => $local.clone())
528        } else {
529            panic!("borrowed aggregate arg mode is not valid for array literals")
530        }
531    }
532    quote!([$out])
533}
534
535pub(crate) fn emit_boxed_slice_items(
536    items: &[MirAggregateArg],
537    function_names: &FunctionNames,
538) -> rust::Tokens {
539    let mut out = rust::Tokens::new();
540    for (index, item) in items.iter().enumerate() {
541        let local = function_names.local_ident(item.local);
542        if index > 0 {
543            quote_in!(out => ,);
544        }
545        if item.is_owned_move() {
546            quote_in!(out => $local)
547        } else if item.is_owned_copy() {
548            quote_in!(out => $local.clone())
549        } else {
550            panic!("borrowed aggregate arg mode is not valid for slice literals")
551        }
552    }
553    quote!(vec![$out])
554}
555
556pub(crate) fn emit_enum_payload_items(
557    items: &[MirAggregateArg],
558    function_names: &FunctionNames,
559) -> rust::Tokens {
560    let mut out = rust::Tokens::new();
561    for (index, item) in items.iter().enumerate() {
562        let local = function_names.local_ident(item.local);
563        if index > 0 {
564            quote_in!(out => ,);
565        }
566        if item.is_owned_move() {
567            quote_in!(out => $local)
568        } else if item.is_owned_copy() {
569            quote_in!(out => $local.clone())
570        } else {
571            panic!("borrowed aggregate arg mode is not valid for enum constructors")
572        }
573    }
574    out
575}
576
577pub(crate) fn emit_enum_get_field_expr(
578    value: &str,
579    enum_name: &str,
580    variant: &str,
581    index: usize,
582) -> rust::Tokens {
583    let mut bindings = rust::Tokens::new();
584    for field_index in 0..=index {
585        if field_index > 0 {
586            quote_in!(bindings => ,);
587        }
588        if field_index == index {
589            quote_in!(bindings => value);
590        } else {
591            quote_in!(bindings => _);
592        }
593    }
594    quote!(match &$value {
595        $enum_name::$variant($bindings) => value.clone(),
596        _ => unreachable!("enum_get_field matched wrong variant"),
597    })
598}
599
600pub(crate) fn emit_enum_variant_wildcard_pattern(
601    enum_name: &str,
602    variant: &str,
603    payload_len: usize,
604) -> rust::Tokens {
605    if payload_len == 0 {
606        quote!($enum_name::$variant)
607    } else {
608        let mut wildcards = rust::Tokens::new();
609        for index in 0..payload_len {
610            if index > 0 {
611                quote_in!(wildcards => ,);
612            }
613            quote_in!(wildcards => _);
614        }
615        quote!($enum_name::$variant($wildcards))
616    }
617}
618
619pub(crate) fn emit_intrinsic_call_expr(
620    function: &MirFunction,
621    intrinsic: MirIntrinsic,
622    args: &[MirCallArg],
623    function_names: &FunctionNames,
624) -> rust::Tokens {
625    match intrinsic {
626        MirIntrinsic::Ptr => {
627            let args = emit_extern_call_args(function, args, function_names);
628            quote!(ruka_runtime::ptr::Ptr::new($args))
629        }
630        MirIntrinsic::Array => {
631            let init = emit_extern_call_args(function, &args[0..1], function_names);
632            let len = emit_extern_call_args(function, &args[1..2], function_names);
633            quote!(vec![$init; $len as usize])
634        }
635    }
636}
637
638pub(crate) fn emit_internal_call_args(
639    function: &MirFunction,
640    args: &[MirCallArg],
641    function_names: &FunctionNames,
642) -> rust::Tokens {
643    let mut items = rust::Tokens::new();
644    for (index, arg) in args.iter().enumerate() {
645        if index > 0 {
646            quote_in!(items => ,);
647        }
648        let local = function_names.local_ident(arg.local);
649        let binding = function.call_arg_binding(arg);
650        if binding.is_borrowed() {
651            if binding.requires_deref_read() {
652                quote_in!(items => &*$local)
653            } else {
654                quote_in!(items => &$local)
655            }
656        } else if binding.is_mutable_borrow() {
657            if binding.is_mutable_place() {
658                quote_in!(items => &mut *$local)
659            } else {
660                quote_in!(items => &mut $local)
661            }
662        } else if binding.is_owned_move() {
663            if binding.requires_deref_read() {
664                let cloned = emit_place_read_clone_expr(binding, &local);
665                quote_in!(items => $cloned)
666            } else {
667                quote_in!(items => $local)
668            }
669        } else {
670            debug_assert!(binding.is_owned_copy());
671            if binding.requires_deref_read() {
672                let cloned = emit_place_read_clone_expr(binding, &local);
673                quote_in!(items => $cloned)
674            } else {
675                quote_in!(items => $local.clone())
676            }
677        }
678    }
679    items
680}
681
682pub(crate) fn emit_extern_call_args(
683    function: &MirFunction,
684    args: &[MirCallArg],
685    function_names: &FunctionNames,
686) -> rust::Tokens {
687    let mut items = rust::Tokens::new();
688    for (index, arg) in args.iter().enumerate() {
689        if index > 0 {
690            quote_in!(items => ,);
691        }
692        let local = function_names.local_ident(arg.local);
693        let binding = function.call_arg_binding(arg);
694        if binding.is_borrowed() {
695            if binding.requires_deref_read() {
696                let cloned = emit_place_read_clone_expr(binding, &local);
697                quote_in!(items => $cloned)
698            } else {
699                quote_in!(items => $local.clone())
700            }
701        } else if binding.is_mutable_borrow() {
702            if binding.is_mutable_place() {
703                quote_in!(items => &mut *$local)
704            } else {
705                quote_in!(items => &mut $local)
706            }
707        } else if binding.is_owned_move() {
708            if binding.requires_deref_read() {
709                let cloned = emit_place_read_clone_expr(binding, &local);
710                quote_in!(items => $cloned)
711            } else {
712                quote_in!(items => $local)
713            }
714        } else {
715            debug_assert!(binding.is_owned_copy());
716            if binding.requires_deref_read() {
717                let cloned = emit_place_read_clone_expr(binding, &local);
718                quote_in!(items => $cloned)
719            } else {
720                quote_in!(items => $local.clone())
721            }
722        }
723    }
724    items
725}
726
727/// Emit one cloned owned-value expression from one place local read.
728fn emit_place_read_clone_expr(
729    binding: ruka_mir::MirCallArgBinding<'_>,
730    local: &str,
731) -> rust::Tokens {
732    if matches!(binding.place_item_ty(), Some(Ty::Slice(_))) {
733        quote!($local.to_vec())
734    } else {
735        quote!((*$local).clone())
736    }
737}
738
739pub(crate) fn emit_extern_call_expr(
740    function: &MirFunction,
741    symbol: &str,
742    args: &[MirCallArg],
743    function_names: &FunctionNames,
744) -> rust::Tokens {
745    let runtime_symbol = symbol.strip_prefix("std::").unwrap_or(symbol);
746    let callee = format!("ruka_runtime::{runtime_symbol}");
747    let args = emit_extern_call_args(function, args, function_names);
748    quote!($callee($args))
749}
750
751pub(crate) fn emit_function_params(
752    function: &MirFunction,
753    function_names: &FunctionNames,
754    read_locals: &HashSet<MirLocalId>,
755) -> rust::Tokens {
756    let mut params = rust::Tokens::new();
757    for binding in function.param_bindings() {
758        if binding.index > 0 {
759            quote_in!(params => ,);
760        }
761        let name = incoming_param_ident(
762            function_names,
763            binding.local_id,
764            binding.index,
765            read_locals.contains(&binding.local_id),
766        );
767        let ty_tokens = if binding.expects_view() || binding.expects_mut_borrow() {
768            emit_borrowed_param_ty_tokens(binding.semantic_ty())
769        } else {
770            emit_ty_tokens(binding.semantic_ty())
771        };
772        if binding.expects_view() {
773            quote_in!(params => $name: &$ty_tokens)
774        } else if binding.expects_mut_borrow() {
775            quote_in!(params => $name: &mut $ty_tokens)
776        } else {
777            quote_in!(params => $name: $ty_tokens)
778        }
779    }
780    params
781}