swamp_code_gen/
intr.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::code_bld::CodeBuilder;
6use crate::ctx::Context;
7
8use crate::transformer::{Collection, Transformer};
9use source_map_node::Node;
10use swamp_semantic::intr::IntrinsicFunction;
11use swamp_semantic::{ArgumentExpression, Expression, ExpressionKind, VariableRef};
12use swamp_vm_types::types::{
13    float_type, int_type, pointer_type, u16_type, u32_type, u8_type, Destination, TypedRegister,
14    VmType,
15};
16use swamp_vm_types::{
17    AggregateMemoryLocation, MemoryLocation, MemoryOffset,
18    PointerLocation, COLLECTION_CAPACITY_OFFSET, COLLECTION_ELEMENT_COUNT_OFFSET, GRID_HEADER_HEIGHT_OFFSET,
19    GRID_HEADER_WIDTH_OFFSET,
20};
21
22impl CodeBuilder<'_> {
23    #[allow(clippy::too_many_lines)]
24    #[allow(clippy::single_match_else)]
25    pub fn emit_single_intrinsic_call(
26        &mut self,
27        target_reg: &Destination,
28        node: &Node,
29        intrinsic_fn: &IntrinsicFunction,
30        arguments: &[ArgumentExpression],
31        ctx: &Context,
32    ) {
33        {
34            // For primitive intrinsics, materialize the self argument to a register early
35            let self_reg = if arguments.is_empty() {
36                None
37            } else {
38                let ArgumentExpression::Expression(self_expr) = &arguments[0] else {
39                    panic!("Expected expression for self argument");
40                };
41                Some(self.emit_scalar_rvalue(self_expr, ctx))
42            };
43
44            let rest_args = if arguments.len() > 1 {
45                &arguments[1..]
46            } else {
47                &vec![]
48            };
49            self.emit_single_intrinsic_call_with_self(
50                target_reg,
51                node,
52                intrinsic_fn,
53                self_reg.as_ref(),
54                rest_args,
55                ctx,
56                "single intrinsic call",
57            );
58        }
59    }
60
61    pub fn emit_intrinsic_map(
62        &mut self,
63        output_destination: &Destination,
64        intrinsic_fn: &IntrinsicFunction,
65        self_ptr_reg: &PointerLocation,
66        arguments: &[Expression],
67        node: &Node,
68        comment: &str,
69        ctx: &Context,
70    ) {
71        match intrinsic_fn {
72            IntrinsicFunction::MapHas => {
73                let key_argument = &arguments[0];
74                // We have to get the key materialized in a temporary storage, so the map can calculate the hash for it.
75                let key_temp_storage_reg =
76                    self.emit_aggregate_pointer_or_pointer_to_scalar_memory(key_argument, ctx);
77
78                self.builder.add_map_has(
79                    output_destination.register().unwrap(),
80                    self_ptr_reg,
81                    &key_temp_storage_reg,
82                    node,
83                    "map_has",
84                );
85            }
86            IntrinsicFunction::MapRemove => {
87                let key_argument = &arguments[0];
88                self.emit_intrinsic_map_remove(self_ptr_reg, key_argument, ctx);
89            }
90            _ => todo!("missing intrinsic_map {intrinsic_fn}"),
91        }
92    }
93
94    pub fn emit_intrinsic_sparse(
95        &mut self,
96        output_destination: &Destination,
97        intrinsic_fn: &IntrinsicFunction,
98        self_ptr_reg: &PointerLocation,
99        arguments: &[Expression],
100        node: &Node,
101        comment: &str,
102        ctx: &Context,
103    ) {
104        match intrinsic_fn {
105            IntrinsicFunction::SparseAdd => {
106                let element_to_add_expression = &arguments[0];
107                self.emit_sparse_add(
108                    &output_destination.register().unwrap().clone(),
109                    self_ptr_reg,
110                    element_to_add_expression,
111                    node,
112                    ctx,
113                );
114            }
115
116            IntrinsicFunction::SparseRemove => {
117                let sparse_id_int_expression = &arguments[0];
118                self.emit_sparse_remove(self_ptr_reg, sparse_id_int_expression, node, ctx);
119            }
120
121            IntrinsicFunction::SparseIsAlive => {
122                let sparse_id_int_expression = &arguments[0];
123                self.emit_sparse_is_alive(
124                    &output_destination.register().unwrap().clone(),
125                    self_ptr_reg,
126                    sparse_id_int_expression,
127                    node,
128                    ctx,
129                );
130            }
131            _ => todo!("unknown sparse {intrinsic_fn}"),
132        }
133    }
134    pub fn emit_intrinsic_grid(
135        &mut self,
136        target_destination: &Destination,
137        intrinsic_fn: &IntrinsicFunction,
138        self_ptr_reg: &PointerLocation,
139        arguments: &[Expression],
140        node: &Node,
141        comment: &str,
142        ctx: &Context,
143    ) {
144        let (temp_reg, dest_reg) = if target_destination.is_register() {
145            (None, target_destination.register().unwrap().clone())
146        } else {
147            let temp_reg = self.temp_registers.allocate(
148                VmType::new_contained_in_register(float_type()),
149                "temporary destination for low level intrinsic",
150            );
151
152            (Some(temp_reg.register.clone()), temp_reg.register)
153        };
154        match intrinsic_fn {
155            IntrinsicFunction::GridSet => {
156                let x_expr = &arguments[0];
157                let y_expr = &arguments[1];
158                let value_expr = &arguments[2];
159
160                let x_reg = self.emit_scalar_rvalue(x_expr, ctx);
161                let y_reg = self.emit_scalar_rvalue(y_expr, ctx);
162                let element_gen_type = self_ptr_reg.ptr_reg.ty.basic_type.element().unwrap();
163
164                let temp_element_ptr = self.temp_registers.allocate(
165                    VmType::new_contained_in_register(element_gen_type.clone()),
166                    "temporary scalar",
167                );
168
169                self.builder.add_grid_get_entry_addr(
170                    &temp_element_ptr.register,
171                    self_ptr_reg,
172                    &x_reg,
173                    &y_reg,
174                    element_gen_type.total_size,
175                    node,
176                    comment,
177                );
178
179                let location = AggregateMemoryLocation {
180                    location: MemoryLocation {
181                        base_ptr_reg: temp_element_ptr.register,
182                        offset: MemoryOffset(0),
183                        ty: VmType::new_unknown_placement(element_gen_type.clone()),
184                    },
185                };
186
187                // Initialize the allocated space first (like variable definition)
188                if element_gen_type.is_aggregate() {
189                    self.emit_initialize_memory_for_any_type(
190                        &location.location,
191                        node,
192                        "initialize grid set allocated space",
193                    );
194                }
195
196                self.emit_expression_into_target_memory(
197                    &location.location,
198                    value_expr,
199                    "grid set",
200                    ctx,
201                );
202            }
203            IntrinsicFunction::GridGet => {
204                let x_expr = &arguments[0];
205                let y_expr = &arguments[1];
206
207                let x_reg = self.emit_scalar_rvalue(x_expr, ctx);
208                let y_reg = self.emit_scalar_rvalue(y_expr, ctx);
209
210                let element_type = self_ptr_reg.ptr_reg.ty.basic_type.element().unwrap();
211
212                // Allocate a temporary register to hold the address of the grid element
213                let temp_element_ptr = self.temp_registers.allocate(
214                    VmType::new_contained_in_register(pointer_type()),
215                    "temp for grid element address",
216                );
217
218                // Get the address of the grid element using the opcode
219                self.builder.add_grid_get_entry_addr(
220                    &temp_element_ptr.register,
221                    self_ptr_reg,
222                    &x_reg,
223                    &y_reg,
224                    element_type.total_size,
225                    node,
226                    comment,
227                );
228
229                // Create a memory location from the element address with correct type information
230                let element_memory_location = MemoryLocation {
231                    base_ptr_reg: temp_element_ptr.register,
232                    offset: MemoryOffset(0),
233                    ty: VmType::new_unknown_placement(element_type),
234                };
235
236                // Use emit_copy_value_from_memory_location to handle both register and memory destinations
237                // This will properly handle aggregates (like optionals) vs scalars
238                self.emit_copy_value_from_memory_location(
239                    target_destination,
240                    &element_memory_location,
241                    node,
242                    "copy grid element value to destination",
243                );
244            }
245
246            IntrinsicFunction::GridWidth => {
247                // Allocate a temporary register for the width value
248                let temp = self.temp_registers.allocate(
249                    VmType::new_contained_in_register(u16_type()),
250                    "temp for grid width",
251                );
252
253                // Get the memory location of the width field in the grid header
254                let self_memory_location = AggregateMemoryLocation::new(
255                    MemoryLocation::new_copy_over_whole_type_with_zero_offset(
256                        self_ptr_reg.ptr_reg.clone(),
257                    ),
258                );
259                let width_location =
260                    self_memory_location.offset(GRID_HEADER_WIDTH_OFFSET, int_type());
261
262                // Load the width value from the grid header into the temporary register
263                self.builder.add_ld16_from_pointer_from_memory_location(
264                    &temp.register,
265                    &width_location.location,
266                    node,
267                    comment,
268                );
269
270                // Create a source destination from the temporary register
271                let value_source = Destination::Register(temp.register);
272
273                // Use emit_copy_value_between_destinations to handle both register and memory destinations
274                self.emit_copy_value_between_destinations(
275                    target_destination,
276                    &value_source,
277                    node,
278                    "store grid width to destination",
279                );
280            }
281            IntrinsicFunction::GridHeight => {
282                // Allocate a temporary register for the height value
283                let temp = self.temp_registers.allocate(
284                    VmType::new_contained_in_register(u16_type()),
285                    "temp for grid height",
286                );
287
288                // Get the memory location of the height field in the grid header
289                let self_memory_location = AggregateMemoryLocation::new(
290                    MemoryLocation::new_copy_over_whole_type_with_zero_offset(
291                        self_ptr_reg.ptr_reg.clone(),
292                    ),
293                );
294                let height_location =
295                    self_memory_location.offset(GRID_HEADER_HEIGHT_OFFSET, int_type());
296
297                // Load the height value from the grid header into the temporary register
298                self.builder.add_ld16_from_pointer_from_memory_location(
299                    &temp.register,
300                    &height_location.location,
301                    node,
302                    comment,
303                );
304
305                // Create a source destination from the temporary register
306                let value_source = Destination::Register(temp.register);
307
308                // Use emit_copy_value_between_destinations to handle both register and memory destinations
309                self.emit_copy_value_between_destinations(
310                    target_destination,
311                    &value_source,
312                    node,
313                    "store grid height to destination",
314                );
315            }
316            _ => todo!("wrong grid {intrinsic_fn}"),
317        }
318    }
319
320    #[allow(clippy::too_many_lines)]
321    fn emit_intrinsic_call_vec(
322        &mut self,
323        output_destination: &Destination,
324        intrinsic_fn: &IntrinsicFunction,
325        self_ptr_reg: &PointerLocation,
326        arguments: &[Expression],
327        node: &Node,
328        ctx: &Context,
329    ) {
330        let self_basic_type = &self_ptr_reg.ptr_reg.ty.basic_type;
331        match intrinsic_fn {
332            IntrinsicFunction::VecPush => {
333                let element_expr = &arguments[0];
334
335                let element_gen_type = self.state.layout_cache.layout(&element_expr.ty);
336
337                let temp_element_ptr = self.temp_registers.allocate(
338                    VmType::new_contained_in_register(pointer_type()),
339                    "pointer to new element",
340                );
341
342                self.builder.add_vec_push_addr(
343                    temp_element_ptr.register(),
344                    &self_ptr_reg.ptr_reg,
345                    node,
346                    "set pointer to new element",
347                );
348
349                let location = AggregateMemoryLocation {
350                    location: MemoryLocation {
351                        base_ptr_reg: temp_element_ptr.register,
352                        offset: MemoryOffset(0),
353                        ty: VmType::new_unknown_placement(element_gen_type.clone()),
354                    },
355                };
356
357                // Initialize the allocated space first (like variable definition)
358                if element_gen_type.is_aggregate() {
359                    self.emit_initialize_memory_for_any_type(
360                        &location.location,
361                        node,
362                        "initialize vec.push allocated space",
363                    );
364                }
365
366                self.emit_expression_into_target_memory(
367                    &location.location,
368                    element_expr,
369                    "vec push",
370                    ctx,
371                );
372            }
373
374            IntrinsicFunction::VecPop => {
375                let element_type = self_basic_type.element().unwrap();
376                let pop_target_reg = if let Some(found_target_reg) = output_destination.register() {
377                    found_target_reg.clone()
378                } else {
379                    let temp = self.temp_registers.allocate(
380                        VmType::new_contained_in_register(element_type.clone()),
381                        "temp for vec pop",
382                    );
383                    temp.register
384                };
385                self.builder.add_vec_pop(
386                    &pop_target_reg,
387                    &self_ptr_reg.ptr_reg, // mut self
388                    element_type.total_size,
389                    node,
390                    "vec pop",
391                );
392                let source_memory_location = MemoryLocation {
393                    base_ptr_reg: pop_target_reg,
394                    offset: MemoryOffset(0),
395                    ty: VmType::new_unknown_placement(element_type),
396                };
397
398                self.emit_copy_value_from_memory_location(
399                    output_destination,
400                    &source_memory_location,
401                    node,
402                    "copy from vec pop",
403                );
404            }
405
406            IntrinsicFunction::VecSlice => {
407                let range_expr = &arguments[0];
408                let range_region = self.emit_scalar_rvalue(range_expr, ctx);
409
410                self.builder.add_vec_copy_range(&output_destination.grab_memory_location().pointer_location().unwrap(), self_ptr_reg, &range_region, node, "vec slice");
411            }
412
413            IntrinsicFunction::VecRemoveIndex => {
414                let index_region_expr = &arguments[0];
415                let index_region = self.emit_scalar_rvalue(index_region_expr, ctx);
416
417                let element_type = self_basic_type.element().unwrap();
418
419                self.builder.add_vec_remove_index(
420                    &self_ptr_reg.ptr_reg,
421                    &index_region,
422                    node,
423                    "remove index",
424                );
425            }
426            IntrinsicFunction::VecRemoveIndexGetValue => {
427                let key_expr = &arguments[0];
428                let key_region = self.emit_scalar_rvalue(key_expr, ctx);
429                let element_type = self_basic_type.element().unwrap();
430
431                // Handle both register and memory destinations
432                if let Some(target_reg) = output_destination.register() {
433                    // Direct register destination
434                    self.builder.add_vec_remove_index_get_value(
435                        target_reg,
436                        &self_ptr_reg.ptr_reg, // mut self
437                        &key_region,
438                        node,
439                        "vec remove index get value to register",
440                    );
441                } else {
442                    // Memory destination or other
443                    let temp_reg = self.temp_registers.allocate(
444                        VmType::new_contained_in_register(element_type),
445                        "temp for vec remove index get value",
446                    );
447
448                    self.builder.add_vec_remove_index_get_value(
449                        &temp_reg.register,
450                        &self_ptr_reg.ptr_reg,
451                        &key_region,
452                        node,
453                        "vec remove index get value to temp",
454                    );
455
456                    // Copy from temporary register to destination
457                    let source = Destination::Register(temp_reg.register);
458                    self.emit_copy_value_between_destinations(
459                        output_destination,
460                        &source,
461                        node,
462                        "copy vec element to destination",
463                    );
464                }
465            }
466            IntrinsicFunction::VecRemoveFirstIndexGetValue => {
467                let zero_reg = self.temp_registers.allocate(
468                    VmType::new_contained_in_register(u8_type()),
469                    "vec remove first. set index 0",
470                );
471                self.builder
472                    .add_mov8_immediate(zero_reg.register(), 0, node, "zero index");
473                let value_addr_reg = self.temp_registers.allocate(
474                    VmType::new_contained_in_register(u32_type()),
475                    "vec entry addr to copy from",
476                );
477                let element_type = self_basic_type.element().unwrap();
478                self.builder.add_vec_subscript(
479                    value_addr_reg.register(),
480                    &self_ptr_reg.ptr_reg,
481                    zero_reg.register(),
482                    element_type.total_size,
483                    node,
484                    "lookup first entry in vec",
485                );
486
487                let source_memory_location = MemoryLocation {
488                    base_ptr_reg: value_addr_reg.register,
489                    offset: MemoryOffset(0),
490                    ty: VmType::new_unknown_placement(element_type),
491                };
492
493                self.emit_copy_value_from_memory_location(
494                    output_destination,
495                    &source_memory_location,
496                    node,
497                    "load the vec entry to target register",
498                );
499
500                self.builder.add_vec_remove_index(
501                    &self_ptr_reg.ptr_reg, // mut self
502                    zero_reg.register(),
503                    node,
504                    "vec remove first index",
505                );
506            }
507            IntrinsicFunction::VecClear => {
508                let temp_element_count_reg = self.temp_registers.allocate(
509                    VmType::new_contained_in_register(u16_type()),
510                    "vec_clear zero",
511                );
512                self.builder.add_mov_16_immediate_value(
513                    temp_element_count_reg.register(),
514                    0,
515                    node,
516                    "set to zero",
517                );
518                self.builder.add_st16_using_ptr_with_offset(
519                    output_destination.grab_memory_location(),
520                    temp_element_count_reg.register(),
521                    node,
522                    "set element_count to zero",
523                );
524            }
525
526            IntrinsicFunction::VecGet => {
527                let key_expr = &arguments[0];
528                let key_region = self.emit_scalar_rvalue(key_expr, ctx);
529                let element_type = self_ptr_reg.ptr_reg.ty.basic_type.element().unwrap();
530
531                // Similar approach as GridGet - get pointer to element and use copy helpers
532                let temp_element_ptr = self.temp_registers.allocate(
533                    VmType::new_contained_in_register(pointer_type()),
534                    "temp for vec element address",
535                );
536
537                // Get the address of the vector element
538                self.builder.add_vec_subscript(
539                    temp_element_ptr.register(),
540                    &self_ptr_reg.ptr_reg,
541                    &key_region,
542                    element_type.total_size,
543                    node,
544                    "get vec element address",
545                );
546
547                // Create a memory location for the element
548                let element_memory_location = MemoryLocation {
549                    base_ptr_reg: temp_element_ptr.register,
550                    offset: MemoryOffset(0),
551                    ty: VmType::new_unknown_placement(element_type),
552                };
553
554                // Copy from memory location to destination (works for both register and memory)
555                self.emit_copy_value_from_memory_location(
556                    output_destination,
557                    &element_memory_location,
558                    node,
559                    "copy vec element to destination",
560                );
561            }
562            _ => todo!("Vec {intrinsic_fn}"),
563        }
564
565        /*
566                   IntrinsicFunction::VecSwap => {
567               let index_a = self
568                   .emit_for_access_or_location(&arguments[0], ctx)
569                   .grab_rvalue()
570                   .clone();
571               let index_b = self
572                   .emit_for_access_or_location(&arguments[1], ctx)
573                   .grab_rvalue()
574                   .clone();
575               self.builder
576                   .add_vec_swap(self_addr.unwrap(), &index_a, &index_b, node, "vec swap");
577           }
578
579           IntrinsicFunction::VecInsert => { // Low prio
580           }
581           IntrinsicFunction::VecFirst => { // Low prio
582           }
583           IntrinsicFunction::VecLast => { // Low prio
584           }
585
586        */
587    }
588
589    fn emit_intrinsic_call_int(
590        &mut self,
591        target_reg: &TypedRegister,
592        intrinsic_fn: &IntrinsicFunction,
593        arguments: &[TypedRegister],
594        node: &Node,
595    ) {
596        let first_argument = &arguments[0];
597
598        // Intrinsics can operate on any register directly, no need for register protection
599        match intrinsic_fn {
600            IntrinsicFunction::IntAbs => {
601                self.builder
602                    .add_int_abs(target_reg, first_argument, node, "int abs");
603            }
604
605            IntrinsicFunction::IntRnd => {
606                self.builder
607                    .add_int_rnd(target_reg, first_argument, node, "int pseudo random");
608            }
609            IntrinsicFunction::IntMax => {
610                let int_register = &arguments[1];
611
612                self.builder
613                    .add_int_max(target_reg, first_argument, int_register, node, "int max");
614            }
615            IntrinsicFunction::IntMin => {
616                let int_register = &arguments[1];
617
618                self.builder
619                    .add_int_min(target_reg, first_argument, int_register, node, "int min");
620            }
621            IntrinsicFunction::IntClamp => {
622                let min_reg = &arguments[1];
623                let max_reg = &arguments[2];
624                self.builder.add_int_clamp(
625                    target_reg,
626                    first_argument,
627                    min_reg,
628                    max_reg,
629                    node,
630                    "int clamp",
631                );
632            }
633            IntrinsicFunction::IntToFloat => {
634                self.builder.add_int_to_float(
635                    target_reg,
636                    first_argument,
637                    node,
638                    &format!("int to float {}", first_argument.comment()),
639                );
640            }
641            IntrinsicFunction::IntToString => {
642                self.builder
643                    .add_int_to_string(target_reg, first_argument, node, "int_to_string");
644            }
645            _ => {}
646        }
647        // No need to copy from a temporary register as we're using target_reg directly
648    }
649
650    #[allow(clippy::too_many_lines)]
651    fn emit_intrinsic_call_fixed(
652        &mut self,
653        target_reg: &TypedRegister,
654        intrinsic_fn: &IntrinsicFunction,
655        arguments: &[TypedRegister],
656        node: &Node,
657    ) {
658        // Intrinsics can operate directly on any register, no need for temporary registers
659        let first_argument_reg = &arguments[0];
660        match intrinsic_fn {
661            IntrinsicFunction::FloatRound => {
662                self.builder
663                    .add_float_round(target_reg, first_argument_reg, node, "float round");
664            }
665            IntrinsicFunction::FloatFloor => {
666                self.builder
667                    .add_float_floor(target_reg, first_argument_reg, node, "float floor");
668            }
669            IntrinsicFunction::FloatSqrt => {
670                self.builder
671                    .add_float_sqrt(target_reg, first_argument_reg, node, "float sqr");
672            }
673            IntrinsicFunction::FloatSign => {
674                self.builder
675                    .add_float_sign(target_reg, first_argument_reg, node, "float sign");
676            }
677            IntrinsicFunction::FloatAbs => {
678                self.builder
679                    .add_float_abs(target_reg, first_argument_reg, node, "float abs");
680            }
681            IntrinsicFunction::FloatRnd => {
682                self.builder.add_float_prnd(
683                    target_reg,
684                    first_argument_reg,
685                    node,
686                    "float pseudo random",
687                );
688            }
689            IntrinsicFunction::FloatCos => {
690                self.builder
691                    .add_float_cos(target_reg, first_argument_reg, node, "float cos");
692            }
693            IntrinsicFunction::FloatSin => {
694                self.builder
695                    .add_float_sin(target_reg, first_argument_reg, node, "float sin");
696            }
697            IntrinsicFunction::FloatAcos => {
698                self.builder
699                    .add_float_acos(target_reg, first_argument_reg, node, "float acos");
700            }
701            IntrinsicFunction::FloatAsin => {
702                self.builder
703                    .add_float_asin(target_reg, first_argument_reg, node, "float asin");
704            }
705            IntrinsicFunction::FloatAtan2 => {
706                self.builder
707                    .add_float_atan2(target_reg, first_argument_reg, node, "float atan2");
708            }
709            IntrinsicFunction::FloatMin => {
710                let float_region = &arguments[1];
711                self.builder.add_float_min(
712                    target_reg,
713                    first_argument_reg,
714                    float_region,
715                    node,
716                    "float min",
717                );
718            }
719            IntrinsicFunction::FloatMax => {
720                let float_region = &arguments[1];
721                self.builder.add_float_max(
722                    target_reg,
723                    first_argument_reg,
724                    float_region,
725                    node,
726                    "float max",
727                );
728            }
729            IntrinsicFunction::FloatClamp => {
730                let float_region = &arguments[1];
731                let float_b_region = &arguments[2];
732
733                self.builder.add_float_clamp(
734                    target_reg,
735                    float_region,
736                    first_argument_reg,
737                    float_b_region,
738                    node,
739                    "float round",
740                );
741            }
742            IntrinsicFunction::FloatToString => self.builder.float_to_string(
743                target_reg,
744                first_argument_reg,
745                node,
746                "float_to_string",
747            ),
748            _ => panic!("wasn't a fixed operation"),
749        }
750        // No need to copy from temp register to target as we're using target_reg directly
751    }
752
753    pub fn emit_intrinsic_transformer(
754        &mut self,
755        target_destination: &Destination,
756        intrinsic_fn: &IntrinsicFunction,
757        self_addr: &PointerLocation,
758        lambda: (Vec<VariableRef>, &Expression),
759        node: &Node,
760        ctx: &Context,
761    ) {
762        match intrinsic_fn {
763            IntrinsicFunction::TransformerFold => { // Low prio
764            }
765            IntrinsicFunction::TransformerFilter => {
766                self.emit_iterate_over_collection_with_lambda(
767                    target_destination,
768                    node,
769                    Collection::Vec,
770                    Transformer::Filter,
771                    &self_addr.ptr_reg,
772                    lambda,
773                    ctx,
774                );
775            }
776
777            IntrinsicFunction::TransformerFor => {
778                self.emit_iterate_over_collection_with_lambda(
779                    target_destination,
780                    node,
781                    Collection::Vec,
782                    Transformer::For,
783                    &self_addr.ptr_reg,
784                    lambda,
785                    ctx,
786                );
787            }
788            IntrinsicFunction::TransformerWhile => {
789                self.emit_iterate_over_collection_with_lambda(
790                    target_destination,
791                    node,
792                    Collection::Vec,
793                    Transformer::While,
794                    &self_addr.ptr_reg,
795                    lambda,
796                    ctx,
797                );
798            }
799
800            IntrinsicFunction::TransformerFind => {
801                self.emit_iterate_over_collection_with_lambda(
802                    target_destination,
803                    node,
804                    Collection::Vec,
805                    Transformer::Find,
806                    &self_addr.ptr_reg,
807                    lambda,
808                    ctx,
809                );
810            }
811            _ => todo!("{intrinsic_fn}"),
812        }
813    }
814
815    #[allow(clippy::too_many_lines)]
816    #[allow(clippy::too_many_arguments)]
817    pub fn emit_single_intrinsic_call_with_self_destination(
818        &mut self,
819        target_destination: &Destination,
820        node: &Node,
821        intrinsic_fn: &IntrinsicFunction,
822        self_destination: Option<&Destination>,
823        arguments: &[ArgumentExpression],
824        ctx: &Context,
825        comment: &str,
826    ) {
827        // Use the helper function to properly materialize the self argument
828        let self_reg = if let Some(self_dest) = self_destination {
829            self.emit_load_scalar_or_absolute_aggregate_pointer(self_dest, node, comment)
830        } else {
831            None
832        };
833
834        // Delegate to the existing function
835        self.emit_single_intrinsic_call_with_self(
836            target_destination,
837            node,
838            intrinsic_fn,
839            self_reg.as_ref(),
840            arguments,
841            ctx,
842            comment,
843        );
844    }
845
846    #[allow(clippy::too_many_lines)]
847    #[allow(clippy::too_many_arguments)]
848    pub fn emit_single_intrinsic_call_with_self(
849        &mut self,
850        target_destination: &Destination,
851        node: &Node,
852        intrinsic_fn: &IntrinsicFunction,
853        self_reg: Option<&TypedRegister>,
854        arguments: &[ArgumentExpression],
855        ctx: &Context,
856        comment: &str,
857    ) {
858        let maybe_target = target_destination.register();
859
860        match intrinsic_fn {
861            IntrinsicFunction::Float2Magnitude
862            | IntrinsicFunction::FloatAbs
863            | IntrinsicFunction::FloatRound
864            | IntrinsicFunction::FloatFloor
865            | IntrinsicFunction::FloatSqrt
866            | IntrinsicFunction::FloatSign
867            | IntrinsicFunction::FloatRnd
868            | IntrinsicFunction::FloatCos
869            | IntrinsicFunction::FloatSin
870            | IntrinsicFunction::FloatAcos
871            | IntrinsicFunction::FloatAsin
872            | IntrinsicFunction::FloatAtan2
873            | IntrinsicFunction::FloatMin
874            | IntrinsicFunction::FloatMax
875            | IntrinsicFunction::FloatClamp
876            | IntrinsicFunction::FloatToString => {
877                // Float
878                let (temp_reg, dest_reg) = if target_destination.is_register() {
879                    (None, target_destination.register().unwrap().clone())
880                } else {
881                    let temp_reg = self.temp_registers.allocate(
882                        VmType::new_contained_in_register(float_type()),
883                        "temporary destination for low level intrinsic",
884                    );
885
886                    (Some(temp_reg.register.clone()), temp_reg.register)
887                };
888
889                // Materialize self to ensure we have the actual scalar value
890                let mut converted_regs = vec![self_reg.unwrap().clone()];
891                for arg in arguments {
892                    let ArgumentExpression::Expression(found_expression) = arg else {
893                        panic!("must be expression");
894                    };
895                    let materialized_arg = self.emit_scalar_rvalue(found_expression, ctx);
896                    converted_regs.push(materialized_arg);
897                }
898
899                self.emit_intrinsic_call_fixed(&dest_reg, intrinsic_fn, &converted_regs, node);
900
901                if let Some(temp_reg) = temp_reg {
902                    self.emit_store_scalar_to_memory_offset_instruction(
903                        target_destination.grab_memory_location(),
904                        &temp_reg,
905                        node,
906                        "store the fixed point value into memory",
907                    );
908                }
909            }
910
911            IntrinsicFunction::IntToFloat => {
912                // IntToFloat - special case because it returns a float, not an int
913                let (temp_reg, dest_reg) = if target_destination.is_register() {
914                    (None, target_destination.register().unwrap().clone())
915                } else {
916                    let temp_reg = self.temp_registers.allocate(
917                        VmType::new_contained_in_register(float_type()),
918                        "temporary destination for int to float intrinsic",
919                    );
920
921                    (Some(temp_reg.register.clone()), temp_reg.register)
922                };
923
924                // Self is already materialized as a register
925                let int_value_reg = self_reg.unwrap();
926
927                // Now convert the materialized integer value to float
928                self.builder.add_int_to_float(
929                    &dest_reg,
930                    int_value_reg,
931                    node,
932                    &format!("int to float {}", int_value_reg.comment()),
933                );
934
935                if let Some(temp_reg) = temp_reg {
936                    self.emit_store_scalar_to_memory_offset_instruction(
937                        target_destination.grab_memory_location(),
938                        &temp_reg,
939                        node,
940                        "store the float result from int to float conversion",
941                    );
942                }
943            }
944
945            IntrinsicFunction::IntAbs
946            | IntrinsicFunction::IntRnd
947            | IntrinsicFunction::IntMax
948            | IntrinsicFunction::IntMin
949            | IntrinsicFunction::IntClamp
950            | IntrinsicFunction::IntToString => {
951                // Int
952                let (temp_reg, dest_reg) = if target_destination.is_register() {
953                    let target_reg = target_destination.register().unwrap();
954                    // Intrinsics can operate on any register directly, no special treatment needed
955                    (None, target_reg.clone())
956                } else {
957                    let temp_reg = self.temp_registers.allocate(
958                        VmType::new_contained_in_register(u32_type()),
959                        "temporary destination for low level intrinsic",
960                    );
961
962                    (Some(temp_reg.register.clone()), temp_reg.register)
963                };
964
965                // Materialize additional arguments (self is already materialized)
966                let mut converted_regs = vec![self_reg.unwrap().clone()];
967                for arg in arguments {
968                    let ArgumentExpression::Expression(found_expression) = arg else {
969                        panic!("must be expression");
970                    };
971                    let materialized_arg = self.emit_scalar_rvalue(found_expression, ctx);
972                    converted_regs.push(materialized_arg);
973                }
974
975                self.emit_intrinsic_call_int(&dest_reg, intrinsic_fn, &converted_regs, node);
976
977                if let Some(temp_reg) = temp_reg {
978                    if target_destination.is_register() {
979                        // Copy from temp to target register
980                        self.builder.add_mov_reg(
981                            target_destination.register().unwrap(),
982                            &temp_reg,
983                            node,
984                            "copy intrinsic result from temp to target register",
985                        );
986                    } else {
987                        // Store to memory location
988                        self.emit_store_scalar_to_memory_offset_instruction(
989                            target_destination.grab_memory_location(),
990                            &temp_reg,
991                            node,
992                            "put the low level intrinsic fixed (int) back to memory",
993                        );
994                    }
995                }
996            }
997
998            IntrinsicFunction::VecPush
999            | IntrinsicFunction::VecPop
1000            | IntrinsicFunction::VecRemoveIndex
1001            | IntrinsicFunction::VecRemoveIndexGetValue
1002            | IntrinsicFunction::VecRemoveFirstIndexGetValue
1003            | IntrinsicFunction::VecClear
1004            | IntrinsicFunction::VecSlice
1005            | IntrinsicFunction::VecSwap
1006            | IntrinsicFunction::VecInsert
1007            | IntrinsicFunction::VecFirst
1008            | IntrinsicFunction::VecGet
1009            | IntrinsicFunction::VecLast => {
1010                // Vec
1011                // Self is assumed to be a flattened pointer:
1012                let vec_self_ptr_reg = PointerLocation {
1013                    ptr_reg: self_reg.unwrap().clone(),
1014                };
1015                let converted_to_expressions: Vec<_> = arguments
1016                    .iter()
1017                    .map(|arg| {
1018                        let ArgumentExpression::Expression(found_expression) = arg else {
1019                            panic!("must be expression");
1020                        };
1021                        found_expression.clone()
1022                    })
1023                    .collect();
1024
1025                self.emit_intrinsic_call_vec(
1026                    target_destination,
1027                    intrinsic_fn,
1028                    &vec_self_ptr_reg,
1029                    &converted_to_expressions,
1030                    node,
1031                    ctx,
1032                );
1033            }
1034
1035            IntrinsicFunction::GridGet
1036            | IntrinsicFunction::GridSet
1037            | IntrinsicFunction::GridWidth
1038            | IntrinsicFunction::GridHeight => {
1039                // Grid
1040                // Self is assumed to be a flattened pointer:
1041                let grid_self_ptr_reg = PointerLocation {
1042                    ptr_reg: self_reg.unwrap().clone(),
1043                };
1044                let converted_to_expressions: Vec<_> = arguments
1045                    .iter()
1046                    .map(|arg| {
1047                        let ArgumentExpression::Expression(found_expression) = arg else {
1048                            panic!("must be expression");
1049                        };
1050                        found_expression.clone()
1051                    })
1052                    .collect();
1053                self.emit_intrinsic_grid(
1054                    target_destination,
1055                    intrinsic_fn,
1056                    &grid_self_ptr_reg,
1057                    &converted_to_expressions,
1058                    node,
1059                    comment,
1060                    ctx,
1061                );
1062            }
1063
1064            IntrinsicFunction::SparseIsAlive
1065            | IntrinsicFunction::SparseRemove
1066            | IntrinsicFunction::SparseAdd => {
1067                // Sparse
1068                // Self is assumed to be a flattened pointer:
1069                let grid_self_ptr_reg = PointerLocation {
1070                    ptr_reg: self_reg.unwrap().clone(),
1071                };
1072                let converted_to_expressions: Vec<_> = arguments
1073                    .iter()
1074                    .map(|arg| {
1075                        let ArgumentExpression::Expression(found_expression) = arg else {
1076                            panic!("must be expression");
1077                        };
1078                        found_expression.clone()
1079                    })
1080                    .collect();
1081                self.emit_intrinsic_sparse(
1082                    target_destination,
1083                    intrinsic_fn,
1084                    &grid_self_ptr_reg,
1085                    &converted_to_expressions,
1086                    node,
1087                    comment,
1088                    ctx,
1089                );
1090            }
1091
1092            IntrinsicFunction::TransformerFor
1093            | IntrinsicFunction::TransformerWhile
1094            | IntrinsicFunction::TransformerFindMap
1095            | IntrinsicFunction::TransformerAny
1096            | IntrinsicFunction::TransformerAll
1097            | IntrinsicFunction::TransformerMap
1098            | IntrinsicFunction::TransformerFilter
1099            | IntrinsicFunction::TransformerFilterMap
1100            | IntrinsicFunction::TransformerFind
1101            | IntrinsicFunction::TransformerFold => {
1102                // Self is assumed to be a flattened pointer:
1103                let collection_self_ptr_reg = PointerLocation {
1104                    ptr_reg: self_reg.unwrap().clone(),
1105                };
1106
1107                let lambda_expression = &arguments[0];
1108
1109                // Take out lambda and other lookups before generating the code
1110                let ArgumentExpression::Expression(expr) = lambda_expression else {
1111                    panic!("err");
1112                };
1113
1114                let ExpressionKind::Lambda(lambda_variables, lambda_expr) = &expr.kind else {
1115                    panic!("must have lambda for transformers");
1116                };
1117
1118                self.emit_intrinsic_transformer(
1119                    target_destination,
1120                    intrinsic_fn,
1121                    &collection_self_ptr_reg,
1122                    (lambda_variables.clone(), lambda_expr),
1123                    node,
1124                    ctx,
1125                );
1126            }
1127
1128            IntrinsicFunction::RuntimePanic => {
1129                self.builder
1130                    .add_panic(self_reg.unwrap(), node, "intrinsic panic");
1131            }
1132
1133            IntrinsicFunction::RuntimeHalt => {
1134                self.builder.add_halt(node, "intrinsic halt");
1135            }
1136
1137            IntrinsicFunction::RuntimeStep => {
1138                self.builder.add_step(node, "intrinsic step");
1139            }
1140
1141            IntrinsicFunction::RangeInit => {
1142                let start_reg = self_reg.unwrap();
1143                // let MutRefOrImmutableExpression::Expression(start_arg_expr) = start_arg else {
1144                //    panic!();
1145                //};
1146                // let start_reg = self.emit_scalar_rvalue(start_arg_expr, ctx);
1147
1148                let end_arg = &arguments[0];
1149                let ArgumentExpression::Expression(end_arg_expr) = end_arg else {
1150                    panic!();
1151                };
1152                let end_reg = self.emit_scalar_rvalue(end_arg_expr, ctx);
1153
1154                let is_inclusive = &arguments[1];
1155                let ArgumentExpression::Expression(is_inclusive_expr) = is_inclusive else {
1156                    panic!();
1157                };
1158                let is_inclusive_reg = self.emit_scalar_rvalue(is_inclusive_expr, ctx);
1159                let absolute_range_pointer = self.emit_compute_effective_address_to_register(
1160                    target_destination,
1161                    node,
1162                    "create range target pointer",
1163                );
1164                self.builder.add_range_init(
1165                    &absolute_range_pointer,
1166                    start_reg,
1167                    &end_reg,
1168                    &is_inclusive_reg,
1169                    node,
1170                    "create a range",
1171                );
1172            }
1173
1174            // Bool
1175            IntrinsicFunction::CodepointToString => {
1176                if maybe_target.is_none() {
1177                    eprintln!("problem");
1178                }
1179                self.builder.codepoint_to_string(
1180                    maybe_target.unwrap(),
1181                    self_reg.unwrap(),
1182                    node,
1183                    "char_to_string",
1184                );
1185            }
1186
1187            IntrinsicFunction::CodepointToInt => {
1188                if maybe_target.is_none() {
1189                    eprintln!("problem");
1190                }
1191                self.builder.add_mov_reg(
1192                    maybe_target.unwrap(),
1193                    self_reg.unwrap(),
1194                    node,
1195                    "char_to_int",
1196                );
1197            }
1198
1199            // Bool
1200            IntrinsicFunction::ByteToString => {
1201                if maybe_target.is_none() {
1202                    eprintln!("problem");
1203                }
1204                self.builder.byte_to_string(
1205                    maybe_target.unwrap(),
1206                    self_reg.unwrap(),
1207                    node,
1208                    "byte_to_string",
1209                );
1210            }
1211
1212            IntrinsicFunction::ByteToInt => {
1213                if maybe_target.is_none() {
1214                    eprintln!("problem");
1215                }
1216                // It is safe to "upcast" to an i32 from a u8, so just copy the register
1217                // TODO: Make something smarter so we don't have to copy
1218                self.builder.add_mov_reg(
1219                    maybe_target.unwrap(),
1220                    self_reg.unwrap(),
1221                    node,
1222                    "byte_to_int",
1223                );
1224            }
1225
1226            // Bool
1227            IntrinsicFunction::BoolToString => {
1228                if maybe_target.is_none() {
1229                    eprintln!("problem");
1230                }
1231                self.builder.bool_to_string(
1232                    maybe_target.unwrap(),
1233                    self_reg.unwrap(),
1234                    node,
1235                    "bool_to_string",
1236                );
1237            }
1238
1239            IntrinsicFunction::StringToString => {
1240                self.builder.string_to_string(
1241                    maybe_target.unwrap(),
1242                    self_reg.unwrap(),
1243                    node,
1244                    "string_to_string",
1245                );
1246            }
1247
1248            // Common Collection
1249            IntrinsicFunction::MapIsEmpty | IntrinsicFunction::VecIsEmpty => {
1250                let collection_pointer = PointerLocation {
1251                    ptr_reg: self_reg.unwrap().clone(),
1252                };
1253                self.emit_collection_is_empty(
1254                    maybe_target.unwrap().clone(),
1255                    &collection_pointer,
1256                    node,
1257                    "vec empty",
1258                );
1259            }
1260
1261            IntrinsicFunction::StringLen
1262            | IntrinsicFunction::MapLen
1263            | IntrinsicFunction::VecLen => {
1264                let collection_pointer = PointerLocation {
1265                    ptr_reg: self_reg.unwrap().clone(),
1266                };
1267                self.emit_collection_len(
1268                    maybe_target.unwrap(),
1269                    &collection_pointer,
1270                    node,
1271                    "get the collection element_count",
1272                );
1273            }
1274            IntrinsicFunction::MapCapacity | IntrinsicFunction::VecCapacity => {
1275                let collection_pointer = PointerLocation {
1276                    ptr_reg: self_reg.unwrap().clone(),
1277                };
1278                self.emit_collection_capacity(
1279                    maybe_target.unwrap(),
1280                    &collection_pointer,
1281                    node,
1282                    "get the collection element_count",
1283                );
1284            }
1285
1286            IntrinsicFunction::MapRemove | IntrinsicFunction::MapHas => {
1287                // Map
1288                // Self is assumed to be a flattened pointer:
1289                let grid_self_ptr_reg = PointerLocation {
1290                    ptr_reg: self_reg.unwrap().clone(),
1291                };
1292                let converted_to_expressions: Vec<_> = arguments
1293                    .iter()
1294                    .map(|arg| {
1295                        let ArgumentExpression::Expression(found_expression) = arg else {
1296                            panic!("must be expression");
1297                        };
1298                        found_expression.clone()
1299                    })
1300                    .collect();
1301                self.emit_intrinsic_map(
1302                    target_destination,
1303                    intrinsic_fn,
1304                    &grid_self_ptr_reg,
1305                    &converted_to_expressions,
1306                    node,
1307                    comment,
1308                    ctx,
1309                );
1310            } // All intrinsic cases are now handled above
1311        }
1312    }
1313
1314    fn emit_intrinsic_map_remove(
1315        &mut self,
1316        map_header_reg: &PointerLocation,
1317        key_expression: &Expression,
1318        ctx: &Context,
1319    ) {
1320        let key_register =
1321            self.emit_aggregate_pointer_or_pointer_to_scalar_memory(key_expression, ctx);
1322
1323        self.builder
1324            .add_map_remove(map_header_reg, &key_register, &key_expression.node, "");
1325    }
1326
1327    fn emit_collection_capacity(
1328        &mut self,
1329        output_reg: &TypedRegister,
1330        collection_addr: &PointerLocation,
1331        node: &Node,
1332        comment: &str,
1333    ) {
1334        self.builder.add_ld16_from_pointer_with_offset_u16(
1335            output_reg,
1336            &collection_addr.ptr_reg,
1337            COLLECTION_CAPACITY_OFFSET,
1338            node,
1339            comment,
1340        );
1341    }
1342
1343    fn emit_collection_len(
1344        &mut self,
1345        output_reg: &TypedRegister,
1346        collection_addr: &PointerLocation,
1347        node: &Node,
1348        comment: &str,
1349    ) {
1350        self.builder.add_ld16_from_pointer_with_offset_u16(
1351            output_reg,
1352            &collection_addr.ptr_reg,
1353            COLLECTION_ELEMENT_COUNT_OFFSET,
1354            node,
1355            &format!("{comment} - collection element_count"),
1356        );
1357    }
1358
1359    fn emit_collection_is_empty(
1360        &mut self,
1361        output_reg: TypedRegister,
1362        collection_addr: &PointerLocation,
1363        node: &Node,
1364        _comment: &str,
1365    ) {
1366        self.builder.add_ld16_from_pointer_with_offset_u16(
1367            &output_reg,
1368            &collection_addr.ptr_reg,
1369            COLLECTION_ELEMENT_COUNT_OFFSET,
1370            node,
1371            "get the map length for testing if it is empty",
1372        );
1373        self.builder.add_meqz(
1374            &output_reg,
1375            &output_reg,
1376            node,
1377            "convert the map length to inverted bool",
1378        );
1379    }
1380}