1use super::*;
2
3pub(crate) fn emit_clone_for_type(
4 body: &mut walrus::InstrSeqBuilder,
5 runtime: &RuntimeFunctions,
6 memory_id: MemoryId,
7 source_pos: Option<ruka_mir::MirSourcePos>,
8 fallback_line: i32,
9 source_local: LocalId,
10 ty: &Ty,
11 structs: &[ruka_mir::MirStructDecl],
12 enums: &[ruka_mir::MirEnumDecl],
13 scratch_i32_local: LocalId,
14 scratch_i32_local_b: LocalId,
15 scratch_i32_local_c: LocalId,
16 scratch_i32_local_d: LocalId,
17 scratch_i32_local_e: LocalId,
18 scratch_i64_local: LocalId,
19 dst_local: LocalId,
20) -> Result<(), LowerError> {
21 let plan = build_release_plan(ty, structs, enums)?;
22 emit_clone_plan_value(
23 body,
24 runtime,
25 memory_id,
26 source_pos,
27 fallback_line,
28 source_local,
29 &plan,
30 scratch_i32_local,
31 scratch_i32_local_b,
32 scratch_i32_local_c,
33 scratch_i32_local_d,
34 scratch_i32_local_e,
35 scratch_i64_local,
36 dst_local,
37 )
38}
39
40pub(crate) fn emit_clone_plan_value(
41 body: &mut walrus::InstrSeqBuilder,
42 runtime: &RuntimeFunctions,
43 memory_id: MemoryId,
44 source_pos: Option<ruka_mir::MirSourcePos>,
45 fallback_line: i32,
46 source_local: LocalId,
47 plan: &ReleasePlan,
48 scratch_i32_local: LocalId,
49 scratch_i32_local_b: LocalId,
50 scratch_i32_local_c: LocalId,
51 scratch_i32_local_d: LocalId,
52 scratch_i32_local_e: LocalId,
53 scratch_i64_local: LocalId,
54 dst_local: LocalId,
55) -> Result<(), LowerError> {
56 match plan {
57 ReleasePlan::None => {
58 body.local_get(source_local).local_set(dst_local);
59 Ok(())
60 }
61 ReleasePlan::Pointer { pointee, .. } => emit_clone_pointer(
62 body,
63 runtime,
64 memory_id,
65 source_pos,
66 fallback_line,
67 source_local,
68 pointee.as_ref(),
69 scratch_i32_local,
70 scratch_i32_local_b,
71 scratch_i32_local_c,
72 scratch_i32_local_d,
73 scratch_i32_local_e,
74 scratch_i64_local,
75 dst_local,
76 ),
77 ReleasePlan::String => emit_clone_string(
78 body,
79 runtime,
80 memory_id,
81 source_pos,
82 fallback_line,
83 source_local,
84 scratch_i32_local,
85 scratch_i32_local_b,
86 scratch_i32_local_c,
87 dst_local,
88 ),
89 ReleasePlan::Array(item_plan) => emit_clone_array(
90 body,
91 runtime,
92 memory_id,
93 source_pos,
94 fallback_line,
95 source_local,
96 item_plan.as_ref(),
97 scratch_i32_local,
98 scratch_i32_local_b,
99 scratch_i32_local_c,
100 scratch_i32_local_d,
101 scratch_i32_local_e,
102 scratch_i64_local,
103 dst_local,
104 ),
105 ReleasePlan::Aggregate {
106 payload_bytes,
107 fields,
108 } => emit_clone_aggregate(
109 body,
110 runtime,
111 memory_id,
112 source_pos,
113 fallback_line,
114 source_local,
115 *payload_bytes,
116 fields,
117 scratch_i32_local,
118 scratch_i32_local_b,
119 scratch_i32_local_c,
120 scratch_i32_local_d,
121 scratch_i32_local_e,
122 scratch_i64_local,
123 dst_local,
124 ),
125 ReleasePlan::Enum {
126 payload_bytes,
127 variants,
128 } => emit_clone_enum(
129 body,
130 runtime,
131 memory_id,
132 source_pos,
133 fallback_line,
134 source_local,
135 *payload_bytes,
136 variants,
137 scratch_i32_local,
138 scratch_i32_local_b,
139 scratch_i32_local_c,
140 scratch_i32_local_d,
141 scratch_i32_local_e,
142 scratch_i64_local,
143 dst_local,
144 ),
145 }
146}
147
148fn pick_clone_temp_local(
149 first: LocalId,
150 second: LocalId,
151 third: LocalId,
152 avoid_a: LocalId,
153 avoid_b: LocalId,
154 avoid_c: LocalId,
155) -> LocalId {
156 if first != avoid_a && first != avoid_b && first != avoid_c {
157 return first;
158 }
159 if second != avoid_a && second != avoid_b && second != avoid_c {
160 return second;
161 }
162 if third != avoid_a && third != avoid_b && third != avoid_c {
163 return third;
164 }
165 first
166}
167
168fn pick_clone_temp_local_wide(
169 first: LocalId,
170 second: LocalId,
171 third: LocalId,
172 fourth: LocalId,
173 fifth: LocalId,
174 avoid_a: LocalId,
175 avoid_b: LocalId,
176 avoid_c: LocalId,
177 avoid_d: LocalId,
178) -> LocalId {
179 for candidate in [first, second, third, fourth, fifth] {
180 if candidate != avoid_a
181 && candidate != avoid_b
182 && candidate != avoid_c
183 && candidate != avoid_d
184 {
185 return candidate;
186 }
187 }
188 first
189}
190
191fn emit_clone_pointer(
192 body: &mut walrus::InstrSeqBuilder,
193 runtime: &RuntimeFunctions,
194 memory_id: MemoryId,
195 source_pos: Option<ruka_mir::MirSourcePos>,
196 fallback_line: i32,
197 source_local: LocalId,
198 pointee_plan: &ReleasePlan,
199 scratch_i32_local: LocalId,
200 scratch_i32_local_b: LocalId,
201 scratch_i32_local_c: LocalId,
202 scratch_i32_local_d: LocalId,
203 scratch_i32_local_e: LocalId,
204 scratch_i64_local: LocalId,
205 dst_local: LocalId,
206) -> Result<(), LowerError> {
207 let alloc = runtime_tracked_alloc_function(runtime)?;
208 let (kind_id, file_id, line, column) =
209 alloc_site_parts(ALLOC_SITE_POINTER_CLONE, source_pos, fallback_line);
210 let nested_result_local = pick_clone_temp_local(
211 scratch_i32_local_d,
212 scratch_i32_local_c,
213 scratch_i32_local_e,
214 scratch_i32_local_b,
215 dst_local,
216 source_local,
217 );
218 let mut nested_error = None;
219 body.local_get(source_local)
220 .local_set(scratch_i32_local)
221 .local_get(scratch_i32_local)
222 .i32_const(0)
223 .binop(BinaryOp::I32Eq)
224 .if_else(
225 None,
226 |null_src| {
227 null_src.i32_const(0).local_set(dst_local);
228 },
229 |non_null| {
230 non_null
231 .i32_const(POINTER_CELL_BYTES)
232 .i32_const(kind_id)
233 .i32_const(file_id)
234 .i32_const(line)
235 .i32_const(column)
236 .call(alloc.function_id)
237 .local_set(dst_local)
238 .local_get(dst_local)
239 .i32_const(1)
240 .instr(Store {
241 memory: memory_id,
242 kind: StoreKind::I32 { atomic: false },
243 arg: MemArg {
244 align: 4,
245 offset: ARRAY_HEADER_OFFSET,
246 },
247 })
248 .local_get(scratch_i32_local)
249 .instr(Load {
250 memory: memory_id,
251 kind: LoadKind::I64 { atomic: false },
252 arg: MemArg {
253 align: 8,
254 offset: POINTER_VALUE_OFFSET,
255 },
256 })
257 .local_set(scratch_i64_local);
258 if matches!(pointee_plan, ReleasePlan::None) {
259 non_null
260 .local_get(dst_local)
261 .local_get(scratch_i64_local)
262 .instr(Store {
263 memory: memory_id,
264 kind: StoreKind::I64 { atomic: false },
265 arg: MemArg {
266 align: 8,
267 offset: POINTER_VALUE_OFFSET,
268 },
269 });
270 return;
271 }
272 non_null
273 .local_get(scratch_i64_local)
274 .unop(UnaryOp::I32WrapI64)
275 .local_set(scratch_i32_local_b);
276 if let Err(err) = emit_clone_plan_value(
277 non_null,
278 runtime,
279 memory_id,
280 source_pos,
281 fallback_line,
282 scratch_i32_local_b,
283 pointee_plan,
284 scratch_i32_local,
285 scratch_i32_local_c,
286 scratch_i32_local_d,
287 scratch_i32_local_e,
288 scratch_i32_local_b,
289 scratch_i64_local,
290 nested_result_local,
291 ) {
292 nested_error = Some(err);
293 return;
294 }
295 non_null
296 .local_get(dst_local)
297 .local_get(nested_result_local)
298 .unop(UnaryOp::I64ExtendUI32)
299 .instr(Store {
300 memory: memory_id,
301 kind: StoreKind::I64 { atomic: false },
302 arg: MemArg {
303 align: 8,
304 offset: POINTER_VALUE_OFFSET,
305 },
306 });
307 },
308 );
309 if let Some(err) = nested_error {
310 return Err(err);
311 }
312 Ok(())
313}
314
315fn emit_clone_string(
316 body: &mut walrus::InstrSeqBuilder,
317 runtime: &RuntimeFunctions,
318 memory_id: MemoryId,
319 source_pos: Option<ruka_mir::MirSourcePos>,
320 fallback_line: i32,
321 source_local: LocalId,
322 scratch_i32_local: LocalId,
323 scratch_i32_local_b: LocalId,
324 scratch_i32_local_c: LocalId,
325 dst_local: LocalId,
326) -> Result<(), LowerError> {
327 let alloc = runtime_tracked_alloc_function(runtime)?;
328 let (kind_id, file_id, line, column) =
329 alloc_site_parts(ALLOC_SITE_AGGREGATE_NEW, source_pos, fallback_line);
330 body.local_get(source_local)
331 .local_set(scratch_i32_local)
332 .local_get(scratch_i32_local)
333 .i32_const(0)
334 .binop(BinaryOp::I32Eq)
335 .if_else(
336 None,
337 |null_src| {
338 null_src.i32_const(0).local_set(dst_local);
339 },
340 |non_null| {
341 non_null
342 .local_get(scratch_i32_local)
343 .instr(Load {
344 memory: memory_id,
345 kind: LoadKind::I32 { atomic: false },
346 arg: MemArg {
347 align: 4,
348 offset: STRING_HEADER_OFFSET,
349 },
350 })
351 .local_set(scratch_i32_local_b)
352 .local_get(scratch_i32_local_b)
353 .i32_const(0)
354 .binop(BinaryOp::I32Eq)
355 .if_else(
356 None,
357 |static_literal| {
358 static_literal
359 .local_get(scratch_i32_local)
360 .local_set(dst_local);
361 },
362 |heap_string| {
363 heap_string
364 .local_get(scratch_i32_local)
365 .instr(Load {
366 memory: memory_id,
367 kind: LoadKind::I32 { atomic: false },
368 arg: MemArg {
369 align: 4,
370 offset: STRING_LEN_OFFSET,
371 },
372 })
373 .local_set(scratch_i32_local_b)
374 .local_get(scratch_i32_local_b)
375 .i32_const(STRING_DATA_OFFSET as i32)
376 .binop(BinaryOp::I32Add)
377 .i32_const(kind_id)
378 .i32_const(file_id)
379 .i32_const(line)
380 .i32_const(column)
381 .call(alloc.function_id)
382 .local_set(dst_local)
383 .local_get(dst_local)
384 .i32_const(1)
385 .instr(Store {
386 memory: memory_id,
387 kind: StoreKind::I32 { atomic: false },
388 arg: MemArg {
389 align: 4,
390 offset: STRING_HEADER_OFFSET,
391 },
392 })
393 .local_get(dst_local)
394 .local_get(scratch_i32_local_b)
395 .instr(Store {
396 memory: memory_id,
397 kind: StoreKind::I32 { atomic: false },
398 arg: MemArg {
399 align: 4,
400 offset: STRING_LEN_OFFSET,
401 },
402 });
403 heap_string
404 .i32_const(0)
405 .local_set(scratch_i32_local_c)
406 .block(None, |done| {
407 let done_id = done.id();
408 done.loop_(None, |loop_| {
409 let loop_id = loop_.id();
410 loop_
411 .local_get(scratch_i32_local_c)
412 .local_get(scratch_i32_local)
413 .instr(Load {
414 memory: memory_id,
415 kind: LoadKind::I32 { atomic: false },
416 arg: MemArg {
417 align: 4,
418 offset: STRING_LEN_OFFSET,
419 },
420 })
421 .binop(BinaryOp::I32GeU)
422 .br_if(done_id)
423 .local_get(scratch_i32_local)
424 .i32_const(STRING_DATA_OFFSET as i32)
425 .binop(BinaryOp::I32Add)
426 .local_get(scratch_i32_local_c)
427 .binop(BinaryOp::I32Add)
428 .instr(Load {
429 memory: memory_id,
430 kind: LoadKind::I32_8 {
431 kind: walrus::ir::ExtendedLoad::ZeroExtend,
432 },
433 arg: MemArg {
434 align: 1,
435 offset: 0,
436 },
437 })
438 .local_set(scratch_i32_local_b)
439 .local_get(dst_local)
440 .i32_const(STRING_DATA_OFFSET as i32)
441 .binop(BinaryOp::I32Add)
442 .local_get(scratch_i32_local_c)
443 .binop(BinaryOp::I32Add)
444 .local_get(scratch_i32_local_b)
445 .instr(Store {
446 memory: memory_id,
447 kind: StoreKind::I32_8 { atomic: false },
448 arg: MemArg {
449 align: 1,
450 offset: 0,
451 },
452 })
453 .local_get(scratch_i32_local_c)
454 .i32_const(1)
455 .binop(BinaryOp::I32Add)
456 .local_set(scratch_i32_local_c)
457 .br(loop_id);
458 });
459 });
460 },
461 );
462 },
463 );
464 Ok(())
465}
466
467fn emit_clone_array(
468 body: &mut walrus::InstrSeqBuilder,
469 runtime: &RuntimeFunctions,
470 memory_id: MemoryId,
471 source_pos: Option<ruka_mir::MirSourcePos>,
472 fallback_line: i32,
473 source_local: LocalId,
474 item_plan: &ReleasePlan,
475 scratch_i32_local: LocalId,
476 scratch_i32_local_b: LocalId,
477 scratch_i32_local_c: LocalId,
478 scratch_i32_local_d: LocalId,
479 scratch_i32_local_e: LocalId,
480 scratch_i64_local: LocalId,
481 dst_local: LocalId,
482) -> Result<(), LowerError> {
483 let alloc = runtime_tracked_alloc_function(runtime)?;
484 let (kind_id, file_id, line, column) =
485 alloc_site_parts(ALLOC_SITE_ARRAY_NEW, source_pos, fallback_line);
486 let mut nested_error = None;
487 let nested_result_local = pick_clone_temp_local(
488 scratch_i32_local_d,
489 scratch_i32_local_c,
490 scratch_i32_local_e,
491 scratch_i32_local_d,
492 dst_local,
493 source_local,
494 );
495 body.local_get(source_local)
496 .local_set(scratch_i32_local)
497 .local_get(scratch_i32_local)
498 .i32_const(0)
499 .binop(BinaryOp::I32Eq)
500 .if_else(
501 None,
502 |null_src| {
503 null_src.i32_const(0).local_set(dst_local);
504 },
505 |non_null| {
506 non_null
507 .local_get(scratch_i32_local)
508 .instr(Load {
509 memory: memory_id,
510 kind: LoadKind::I32 { atomic: false },
511 arg: MemArg {
512 align: 4,
513 offset: ARRAY_CAP_OFFSET,
514 },
515 })
516 .i32_const(ARRAY_SLOT_BYTES)
517 .binop(BinaryOp::I32Mul)
518 .i32_const(ARRAY_DATA_OFFSET as i32)
519 .binop(BinaryOp::I32Add)
520 .i32_const(kind_id)
521 .i32_const(file_id)
522 .i32_const(line)
523 .i32_const(column)
524 .call(alloc.function_id)
525 .local_set(dst_local)
526 .local_get(dst_local)
527 .i32_const(1)
528 .instr(Store {
529 memory: memory_id,
530 kind: StoreKind::I32 { atomic: false },
531 arg: MemArg {
532 align: 4,
533 offset: ARRAY_HEADER_OFFSET,
534 },
535 })
536 .local_get(dst_local)
537 .local_get(scratch_i32_local)
538 .instr(Load {
539 memory: memory_id,
540 kind: LoadKind::I32 { atomic: false },
541 arg: MemArg {
542 align: 4,
543 offset: ARRAY_LEN_OFFSET,
544 },
545 })
546 .instr(Store {
547 memory: memory_id,
548 kind: StoreKind::I32 { atomic: false },
549 arg: MemArg {
550 align: 4,
551 offset: ARRAY_LEN_OFFSET,
552 },
553 })
554 .local_get(dst_local)
555 .local_get(scratch_i32_local)
556 .instr(Load {
557 memory: memory_id,
558 kind: LoadKind::I32 { atomic: false },
559 arg: MemArg {
560 align: 4,
561 offset: ARRAY_CAP_OFFSET,
562 },
563 })
564 .instr(Store {
565 memory: memory_id,
566 kind: StoreKind::I32 { atomic: false },
567 arg: MemArg {
568 align: 4,
569 offset: ARRAY_CAP_OFFSET,
570 },
571 })
572 .local_get(dst_local)
573 .local_get(scratch_i32_local)
574 .instr(Load {
575 memory: memory_id,
576 kind: LoadKind::I32 { atomic: false },
577 arg: MemArg {
578 align: 4,
579 offset: ARRAY_LEN_OFFSET,
580 },
581 })
582 .instr(Store {
583 memory: memory_id,
584 kind: StoreKind::I32 { atomic: false },
585 arg: MemArg {
586 align: 4,
587 offset: ARRAY_HEADER_OFFSET,
588 },
589 })
590 .block(None, |done| {
591 let done_id = done.id();
592 done.loop_(None, |loop_| {
593 let loop_id = loop_.id();
594 loop_
595 .local_get(dst_local)
596 .instr(Load {
597 memory: memory_id,
598 kind: LoadKind::I32 { atomic: false },
599 arg: MemArg {
600 align: 4,
601 offset: ARRAY_HEADER_OFFSET,
602 },
603 })
604 .local_set(scratch_i32_local_b)
605 .local_get(scratch_i32_local_b)
606 .i32_const(0)
607 .binop(BinaryOp::I32Eq)
608 .br_if(done_id)
609 .local_get(scratch_i32_local_b)
610 .i32_const(1)
611 .binop(BinaryOp::I32Sub)
612 .local_set(scratch_i32_local_b)
613 .local_get(dst_local)
614 .local_get(scratch_i32_local_b)
615 .instr(Store {
616 memory: memory_id,
617 kind: StoreKind::I32 { atomic: false },
618 arg: MemArg {
619 align: 4,
620 offset: ARRAY_HEADER_OFFSET,
621 },
622 })
623 .local_get(scratch_i32_local)
624 .i32_const(ARRAY_DATA_OFFSET as i32)
625 .binop(BinaryOp::I32Add)
626 .local_get(scratch_i32_local_b)
627 .i32_const(ARRAY_SLOT_BYTES)
628 .binop(BinaryOp::I32Mul)
629 .binop(BinaryOp::I32Add)
630 .instr(Load {
631 memory: memory_id,
632 kind: LoadKind::I64 { atomic: false },
633 arg: MemArg {
634 align: 8,
635 offset: 0,
636 },
637 })
638 .local_set(scratch_i64_local);
639 if matches!(item_plan, ReleasePlan::None) {
640 loop_
641 .local_get(dst_local)
642 .i32_const(ARRAY_DATA_OFFSET as i32)
643 .binop(BinaryOp::I32Add)
644 .local_get(scratch_i32_local_b)
645 .i32_const(ARRAY_SLOT_BYTES)
646 .binop(BinaryOp::I32Mul)
647 .binop(BinaryOp::I32Add)
648 .local_get(scratch_i64_local)
649 .instr(Store {
650 memory: memory_id,
651 kind: StoreKind::I64 { atomic: false },
652 arg: MemArg {
653 align: 8,
654 offset: 0,
655 },
656 })
657 .br(loop_id);
658 return;
659 }
660 loop_
661 .local_get(scratch_i64_local)
662 .unop(UnaryOp::I32WrapI64)
663 .local_set(scratch_i32_local_c)
664 .local_get(scratch_i32_local_c)
665 .local_set(scratch_i32_local_d);
666 if let Err(err) = emit_clone_plan_value(
667 loop_,
668 runtime,
669 memory_id,
670 source_pos,
671 fallback_line,
672 scratch_i32_local_d,
673 item_plan,
674 scratch_i32_local,
675 scratch_i32_local_b,
676 scratch_i32_local_c,
677 scratch_i32_local_d,
678 scratch_i32_local_e,
679 scratch_i64_local,
680 nested_result_local,
681 ) {
682 nested_error = Some(err);
683 return;
684 }
685 loop_
686 .local_get(dst_local)
687 .i32_const(ARRAY_DATA_OFFSET as i32)
688 .binop(BinaryOp::I32Add)
689 .local_get(scratch_i32_local_b)
690 .i32_const(ARRAY_SLOT_BYTES)
691 .binop(BinaryOp::I32Mul)
692 .binop(BinaryOp::I32Add)
693 .local_get(nested_result_local)
694 .unop(UnaryOp::I64ExtendUI32)
695 .instr(Store {
696 memory: memory_id,
697 kind: StoreKind::I64 { atomic: false },
698 arg: MemArg {
699 align: 8,
700 offset: 0,
701 },
702 })
703 .br(loop_id);
704 });
705 })
706 .local_get(dst_local)
707 .i32_const(1)
708 .instr(Store {
709 memory: memory_id,
710 kind: StoreKind::I32 { atomic: false },
711 arg: MemArg {
712 align: 4,
713 offset: ARRAY_HEADER_OFFSET,
714 },
715 });
716 },
717 );
718 if let Some(err) = nested_error {
719 return Err(err);
720 }
721 Ok(())
722}
723
724fn emit_clone_aggregate(
725 body: &mut walrus::InstrSeqBuilder,
726 runtime: &RuntimeFunctions,
727 memory_id: MemoryId,
728 source_pos: Option<ruka_mir::MirSourcePos>,
729 fallback_line: i32,
730 source_local: LocalId,
731 payload_bytes: u32,
732 fields: &[(u32, bool, ReleasePlan)],
733 scratch_i32_local: LocalId,
734 scratch_i32_local_b: LocalId,
735 scratch_i32_local_c: LocalId,
736 scratch_i32_local_d: LocalId,
737 scratch_i32_local_e: LocalId,
738 scratch_i64_local: LocalId,
739 dst_local: LocalId,
740) -> Result<(), LowerError> {
741 let total_bytes = ARRAY_DATA_OFFSET.saturating_add(payload_bytes);
742 emit_clone_blob(
743 body,
744 runtime,
745 memory_id,
746 source_pos,
747 fallback_line,
748 source_local,
749 total_bytes,
750 ALLOC_SITE_AGGREGATE_NEW,
751 scratch_i32_local,
752 scratch_i32_local_b,
753 dst_local,
754 )?;
755 emit_clone_aggregate_inline_fields(
756 body,
757 runtime,
758 memory_id,
759 source_pos,
760 fallback_line,
761 source_local,
762 dst_local,
763 fields,
764 scratch_i32_local,
765 scratch_i32_local_b,
766 scratch_i32_local_c,
767 scratch_i32_local_d,
768 scratch_i32_local_e,
769 scratch_i64_local,
770 )
771}
772
773fn emit_clone_enum(
774 body: &mut walrus::InstrSeqBuilder,
775 runtime: &RuntimeFunctions,
776 memory_id: MemoryId,
777 source_pos: Option<ruka_mir::MirSourcePos>,
778 fallback_line: i32,
779 source_local: LocalId,
780 payload_bytes: u32,
781 variants: &[Vec<(u32, bool, ReleasePlan)>],
782 scratch_i32_local: LocalId,
783 scratch_i32_local_b: LocalId,
784 scratch_i32_local_c: LocalId,
785 scratch_i32_local_d: LocalId,
786 scratch_i32_local_e: LocalId,
787 scratch_i64_local: LocalId,
788 dst_local: LocalId,
789) -> Result<(), LowerError> {
790 let total_bytes = ARRAY_DATA_OFFSET.saturating_add(payload_bytes);
791 emit_clone_blob(
792 body,
793 runtime,
794 memory_id,
795 source_pos,
796 fallback_line,
797 source_local,
798 total_bytes,
799 ALLOC_SITE_ENUM_NEW,
800 scratch_i32_local,
801 scratch_i32_local_b,
802 dst_local,
803 )?;
804 let mut nested_error = None;
805 body.local_get(source_local)
806 .i32_const(0)
807 .binop(BinaryOp::I32Ne)
808 .if_else(
809 None,
810 |non_null| {
811 non_null
812 .local_get(source_local)
813 .i32_const(ARRAY_DATA_OFFSET as i32)
814 .binop(BinaryOp::I32Add)
815 .instr(Load {
816 memory: memory_id,
817 kind: LoadKind::I32 { atomic: false },
818 arg: MemArg {
819 align: 4,
820 offset: ENUM_TAG_OFFSET,
821 },
822 })
823 .local_set(scratch_i32_local);
824 for (variant_index, field_plans) in variants.iter().enumerate() {
825 non_null
826 .local_get(scratch_i32_local)
827 .i32_const(variant_index as i32)
828 .binop(BinaryOp::I32Eq)
829 .if_else(
830 None,
831 |variant_block| {
832 if let Err(err) = emit_clone_aggregate_inline_fields(
833 variant_block,
834 runtime,
835 memory_id,
836 source_pos,
837 fallback_line,
838 source_local,
839 dst_local,
840 field_plans,
841 scratch_i32_local,
842 scratch_i32_local_b,
843 scratch_i32_local_c,
844 scratch_i32_local_d,
845 scratch_i32_local_e,
846 scratch_i64_local,
847 ) {
848 nested_error = Some(err);
849 }
850 },
851 |_else_| {},
852 );
853 }
854 },
855 |_null| {},
856 );
857 if let Some(err) = nested_error {
858 return Err(err);
859 }
860 Ok(())
861}
862
863fn emit_clone_aggregate_inline_fields(
864 body: &mut walrus::InstrSeqBuilder,
865 runtime: &RuntimeFunctions,
866 memory_id: MemoryId,
867 source_pos: Option<ruka_mir::MirSourcePos>,
868 fallback_line: i32,
869 src_aggregate_local: LocalId,
870 dst_aggregate_local: LocalId,
871 fields: &[(u32, bool, ReleasePlan)],
872 scratch_i32_local: LocalId,
873 scratch_i32_local_b: LocalId,
874 scratch_i32_local_c: LocalId,
875 scratch_i32_local_d: LocalId,
876 scratch_i32_local_e: LocalId,
877 scratch_i64_local: LocalId,
878) -> Result<(), LowerError> {
879 let mut nested_error = None;
880 for (offset, inline, plan) in fields {
881 if *inline {
882 if let ReleasePlan::Aggregate { fields, .. } = plan {
883 body.local_get(src_aggregate_local)
884 .i32_const(ARRAY_DATA_OFFSET as i32)
885 .binop(BinaryOp::I32Add)
886 .i32_const(*offset as i32)
887 .binop(BinaryOp::I32Add)
888 .local_set(scratch_i32_local_d)
889 .local_get(dst_aggregate_local)
890 .i32_const(ARRAY_DATA_OFFSET as i32)
891 .binop(BinaryOp::I32Add)
892 .i32_const(*offset as i32)
893 .binop(BinaryOp::I32Add)
894 .local_set(scratch_i32_local_e);
895 if let Err(err) = emit_clone_aggregate_inline_fields(
896 body,
897 runtime,
898 memory_id,
899 source_pos,
900 fallback_line,
901 scratch_i32_local_d,
902 scratch_i32_local_e,
903 fields,
904 scratch_i32_local,
905 scratch_i32_local_b,
906 scratch_i32_local_c,
907 scratch_i32_local_d,
908 scratch_i32_local_e,
909 scratch_i64_local,
910 ) {
911 nested_error = Some(err);
912 break;
913 }
914 }
915 continue;
916 }
917 if matches!(plan, ReleasePlan::None) {
918 continue;
919 }
920 body.local_get(src_aggregate_local)
921 .i32_const(ARRAY_DATA_OFFSET as i32)
922 .binop(BinaryOp::I32Add)
923 .i32_const(*offset as i32)
924 .binop(BinaryOp::I32Add)
925 .instr(Load {
926 memory: memory_id,
927 kind: LoadKind::I32 { atomic: false },
928 arg: MemArg {
929 align: 4,
930 offset: 0,
931 },
932 })
933 .local_set(scratch_i32_local);
934 let nested_source_local = pick_clone_temp_local_wide(
935 scratch_i32_local_d,
936 scratch_i32_local_c,
937 scratch_i32_local_e,
938 scratch_i32_local_b,
939 scratch_i32_local,
940 src_aggregate_local,
941 dst_aggregate_local,
942 scratch_i32_local,
943 scratch_i64_local,
944 );
945 body.local_get(scratch_i32_local)
946 .local_set(nested_source_local);
947 let nested_result_local = pick_clone_temp_local_wide(
948 scratch_i32_local_e,
949 scratch_i32_local_c,
950 scratch_i32_local_d,
951 scratch_i32_local_b,
952 scratch_i32_local,
953 src_aggregate_local,
954 dst_aggregate_local,
955 nested_source_local,
956 scratch_i64_local,
957 );
958 if let Err(err) = emit_clone_plan_value(
959 body,
960 runtime,
961 memory_id,
962 source_pos,
963 fallback_line,
964 nested_source_local,
965 plan,
966 scratch_i32_local,
967 scratch_i32_local_b,
968 scratch_i32_local_c,
969 scratch_i32_local_d,
970 scratch_i32_local_e,
971 scratch_i64_local,
972 nested_result_local,
973 ) {
974 nested_error = Some(err);
975 break;
976 }
977 body.local_get(dst_aggregate_local)
978 .i32_const(ARRAY_DATA_OFFSET as i32)
979 .binop(BinaryOp::I32Add)
980 .i32_const(*offset as i32)
981 .binop(BinaryOp::I32Add)
982 .local_get(nested_result_local)
983 .instr(Store {
984 memory: memory_id,
985 kind: StoreKind::I32 { atomic: false },
986 arg: MemArg {
987 align: 4,
988 offset: 0,
989 },
990 });
991 }
992 if let Some(err) = nested_error {
993 return Err(err);
994 }
995 Ok(())
996}
997
998fn emit_clone_blob(
999 body: &mut walrus::InstrSeqBuilder,
1000 runtime: &RuntimeFunctions,
1001 memory_id: MemoryId,
1002 source_pos: Option<ruka_mir::MirSourcePos>,
1003 fallback_line: i32,
1004 source_local: LocalId,
1005 total_bytes: u32,
1006 alloc_site: i32,
1007 scratch_i32_local: LocalId,
1008 scratch_i32_local_b: LocalId,
1009 dst_local: LocalId,
1010) -> Result<(), LowerError> {
1011 let total_i32 = int32_from_u32(total_bytes, "clone bytes")?;
1012 let mut nested_error = None;
1013 body.local_get(source_local)
1014 .i32_const(0)
1015 .binop(BinaryOp::I32Eq)
1016 .if_else(
1017 None,
1018 |null_src| {
1019 null_src.i32_const(0).local_set(dst_local);
1020 },
1021 |non_null| {
1022 if let Err(err) = emit_heap_alloc(
1023 non_null,
1024 runtime,
1025 total_i32,
1026 alloc_site,
1027 source_pos,
1028 fallback_line,
1029 dst_local,
1030 ) {
1031 nested_error = Some(err);
1032 return;
1033 }
1034 if let Err(err) = emit_copy_bytes(
1035 non_null,
1036 memory_id,
1037 source_local,
1038 dst_local,
1039 total_bytes,
1040 scratch_i32_local,
1041 scratch_i32_local_b,
1042 ) {
1043 nested_error = Some(err);
1044 return;
1045 }
1046 non_null.local_get(dst_local).i32_const(1).instr(Store {
1047 memory: memory_id,
1048 kind: StoreKind::I32 { atomic: false },
1049 arg: MemArg {
1050 align: 4,
1051 offset: ARRAY_HEADER_OFFSET,
1052 },
1053 });
1054 },
1055 );
1056 if let Some(err) = nested_error {
1057 return Err(err);
1058 }
1059 Ok(())
1060}