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    Destination, TypedRegister, VmType, float_type, int_type, pointer_type, u8_type, u16_type,
14    u32_type,
15};
16use swamp_vm_types::{
17    AggregateMemoryLocation, COLLECTION_CAPACITY_OFFSET, COLLECTION_ELEMENT_COUNT_OFFSET,
18    GRID_HEADER_HEIGHT_OFFSET, GRID_HEADER_WIDTH_OFFSET, MemoryLocation, MemoryOffset,
19    PointerLocation,
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            IntrinsicFunction::VecRemoveIndex => {
406                let index_region_expr = &arguments[0];
407                let index_region = self.emit_scalar_rvalue(index_region_expr, ctx);
408
409                let element_type = self_basic_type.element().unwrap();
410
411                self.builder.add_vec_remove_index(
412                    &self_ptr_reg.ptr_reg,
413                    &index_region,
414                    node,
415                    "remove index",
416                );
417            }
418            IntrinsicFunction::VecRemoveIndexGetValue => {
419                let key_expr = &arguments[0];
420                let key_region = self.emit_scalar_rvalue(key_expr, ctx);
421                let element_type = self_basic_type.element().unwrap();
422
423                // Handle both register and memory destinations
424                if let Some(target_reg) = output_destination.register() {
425                    // Direct register destination
426                    self.builder.add_vec_remove_index_get_value(
427                        target_reg,
428                        &self_ptr_reg.ptr_reg, // mut self
429                        &key_region,
430                        node,
431                        "vec remove index get value to register",
432                    );
433                } else {
434                    // Memory destination or other
435                    let temp_reg = self.temp_registers.allocate(
436                        VmType::new_contained_in_register(element_type),
437                        "temp for vec remove index get value",
438                    );
439
440                    self.builder.add_vec_remove_index_get_value(
441                        &temp_reg.register,
442                        &self_ptr_reg.ptr_reg,
443                        &key_region,
444                        node,
445                        "vec remove index get value to temp",
446                    );
447
448                    // Copy from temporary register to destination
449                    let source = Destination::Register(temp_reg.register);
450                    self.emit_copy_value_between_destinations(
451                        output_destination,
452                        &source,
453                        node,
454                        "copy vec element to destination",
455                    );
456                }
457            }
458            IntrinsicFunction::VecRemoveFirstIndexGetValue => {
459                let zero_reg = self.temp_registers.allocate(
460                    VmType::new_contained_in_register(u8_type()),
461                    "vec remove first. set index 0",
462                );
463                self.builder
464                    .add_mov8_immediate(zero_reg.register(), 0, node, "zero index");
465                let value_addr_reg = self.temp_registers.allocate(
466                    VmType::new_contained_in_register(u32_type()),
467                    "vec entry addr to copy from",
468                );
469                let element_type = self_basic_type.element().unwrap();
470                self.builder.add_vec_subscript(
471                    value_addr_reg.register(),
472                    &self_ptr_reg.ptr_reg,
473                    zero_reg.register(),
474                    element_type.total_size,
475                    node,
476                    "lookup first entry in vec",
477                );
478
479                let source_memory_location = MemoryLocation {
480                    base_ptr_reg: value_addr_reg.register,
481                    offset: MemoryOffset(0),
482                    ty: VmType::new_unknown_placement(element_type),
483                };
484
485                self.emit_copy_value_from_memory_location(
486                    output_destination,
487                    &source_memory_location,
488                    node,
489                    "load the vec entry to target register",
490                );
491
492                self.builder.add_vec_remove_index(
493                    &self_ptr_reg.ptr_reg, // mut self
494                    zero_reg.register(),
495                    node,
496                    "vec remove first index",
497                );
498            }
499            IntrinsicFunction::VecClear => {
500                let temp_element_count_reg = self.temp_registers.allocate(
501                    VmType::new_contained_in_register(u16_type()),
502                    "vec_clear zero",
503                );
504                self.builder.add_mov_16_immediate_value(
505                    temp_element_count_reg.register(),
506                    0,
507                    node,
508                    "set to zero",
509                );
510                self.builder.add_st16_using_ptr_with_offset(
511                    output_destination.grab_memory_location(),
512                    temp_element_count_reg.register(),
513                    node,
514                    "set element_count to zero",
515                );
516            }
517            IntrinsicFunction::VecGet => {
518                let key_expr = &arguments[0];
519                let key_region = self.emit_scalar_rvalue(key_expr, ctx);
520                let element_type = self_ptr_reg.ptr_reg.ty.basic_type.element().unwrap();
521
522                // Similar approach as GridGet - get pointer to element and use copy helpers
523                let temp_element_ptr = self.temp_registers.allocate(
524                    VmType::new_contained_in_register(pointer_type()),
525                    "temp for vec element address",
526                );
527
528                // Get the address of the vector element
529                self.builder.add_vec_subscript(
530                    temp_element_ptr.register(),
531                    &self_ptr_reg.ptr_reg,
532                    &key_region,
533                    element_type.total_size,
534                    node,
535                    "get vec element address",
536                );
537
538                // Create a memory location for the element
539                let element_memory_location = MemoryLocation {
540                    base_ptr_reg: temp_element_ptr.register,
541                    offset: MemoryOffset(0),
542                    ty: VmType::new_unknown_placement(element_type),
543                };
544
545                // Copy from memory location to destination (works for both register and memory)
546                self.emit_copy_value_from_memory_location(
547                    output_destination,
548                    &element_memory_location,
549                    node,
550                    "copy vec element to destination",
551                );
552            }
553            _ => todo!("Vec {intrinsic_fn}"),
554        }
555
556        /*
557                   IntrinsicFunction::VecSwap => {
558               let index_a = self
559                   .emit_for_access_or_location(&arguments[0], ctx)
560                   .grab_rvalue()
561                   .clone();
562               let index_b = self
563                   .emit_for_access_or_location(&arguments[1], ctx)
564                   .grab_rvalue()
565                   .clone();
566               self.builder
567                   .add_vec_swap(self_addr.unwrap(), &index_a, &index_b, node, "vec swap");
568           }
569
570           IntrinsicFunction::VecInsert => { // Low prio
571           }
572           IntrinsicFunction::VecFirst => { // Low prio
573           }
574           IntrinsicFunction::VecLast => { // Low prio
575           }
576
577        */
578    }
579
580    fn emit_intrinsic_call_int(
581        &mut self,
582        target_reg: &TypedRegister,
583        intrinsic_fn: &IntrinsicFunction,
584        arguments: &[TypedRegister],
585        node: &Node,
586    ) {
587        let first_argument = &arguments[0];
588
589        // Intrinsics can operate on any register directly, no need for register protection
590        match intrinsic_fn {
591            IntrinsicFunction::IntAbs => {
592                self.builder
593                    .add_int_abs(target_reg, first_argument, node, "int abs");
594            }
595
596            IntrinsicFunction::IntRnd => {
597                self.builder
598                    .add_int_rnd(target_reg, first_argument, node, "int pseudo random");
599            }
600            IntrinsicFunction::IntMax => {
601                let int_register = &arguments[1];
602
603                self.builder
604                    .add_int_max(target_reg, first_argument, int_register, node, "int max");
605            }
606            IntrinsicFunction::IntMin => {
607                let int_register = &arguments[1];
608
609                self.builder
610                    .add_int_min(target_reg, first_argument, int_register, node, "int min");
611            }
612            IntrinsicFunction::IntClamp => {
613                let min_reg = &arguments[1];
614                let max_reg = &arguments[2];
615                self.builder.add_int_clamp(
616                    target_reg,
617                    first_argument,
618                    min_reg,
619                    max_reg,
620                    node,
621                    "int clamp",
622                );
623            }
624            IntrinsicFunction::IntToFloat => {
625                self.builder.add_int_to_float(
626                    target_reg,
627                    first_argument,
628                    node,
629                    &format!("int to float {}", first_argument.comment()),
630                );
631            }
632            IntrinsicFunction::IntToString => {
633                self.builder
634                    .add_int_to_string(target_reg, first_argument, node, "int_to_string");
635            }
636            _ => {}
637        }
638        // No need to copy from a temporary register as we're using target_reg directly
639    }
640
641    #[allow(clippy::too_many_lines)]
642    fn emit_intrinsic_call_fixed(
643        &mut self,
644        target_reg: &TypedRegister,
645        intrinsic_fn: &IntrinsicFunction,
646        arguments: &[TypedRegister],
647        node: &Node,
648    ) {
649        // Intrinsics can operate directly on any register, no need for temporary registers
650        let first_argument_reg = &arguments[0];
651        match intrinsic_fn {
652            IntrinsicFunction::FloatRound => {
653                self.builder
654                    .add_float_round(target_reg, first_argument_reg, node, "float round");
655            }
656            IntrinsicFunction::FloatFloor => {
657                self.builder
658                    .add_float_floor(target_reg, first_argument_reg, node, "float floor");
659            }
660            IntrinsicFunction::FloatSqrt => {
661                self.builder
662                    .add_float_sqrt(target_reg, first_argument_reg, node, "float sqr");
663            }
664            IntrinsicFunction::FloatSign => {
665                self.builder
666                    .add_float_sign(target_reg, first_argument_reg, node, "float sign");
667            }
668            IntrinsicFunction::FloatAbs => {
669                self.builder
670                    .add_float_abs(target_reg, first_argument_reg, node, "float abs");
671            }
672            IntrinsicFunction::FloatRnd => {
673                self.builder.add_float_prnd(
674                    target_reg,
675                    first_argument_reg,
676                    node,
677                    "float pseudo random",
678                );
679            }
680            IntrinsicFunction::FloatCos => {
681                self.builder
682                    .add_float_cos(target_reg, first_argument_reg, node, "float cos");
683            }
684            IntrinsicFunction::FloatSin => {
685                self.builder
686                    .add_float_sin(target_reg, first_argument_reg, node, "float sin");
687            }
688            IntrinsicFunction::FloatAcos => {
689                self.builder
690                    .add_float_acos(target_reg, first_argument_reg, node, "float acos");
691            }
692            IntrinsicFunction::FloatAsin => {
693                self.builder
694                    .add_float_asin(target_reg, first_argument_reg, node, "float asin");
695            }
696            IntrinsicFunction::FloatAtan2 => {
697                self.builder
698                    .add_float_atan2(target_reg, first_argument_reg, node, "float atan2");
699            }
700            IntrinsicFunction::FloatMin => {
701                let float_region = &arguments[1];
702                self.builder.add_float_min(
703                    target_reg,
704                    first_argument_reg,
705                    float_region,
706                    node,
707                    "float min",
708                );
709            }
710            IntrinsicFunction::FloatMax => {
711                let float_region = &arguments[1];
712                self.builder.add_float_max(
713                    target_reg,
714                    first_argument_reg,
715                    float_region,
716                    node,
717                    "float max",
718                );
719            }
720            IntrinsicFunction::FloatClamp => {
721                let float_region = &arguments[1];
722                let float_b_region = &arguments[2];
723
724                self.builder.add_float_clamp(
725                    target_reg,
726                    float_region,
727                    first_argument_reg,
728                    float_b_region,
729                    node,
730                    "float round",
731                );
732            }
733            IntrinsicFunction::FloatToString => self.builder.float_to_string(
734                target_reg,
735                first_argument_reg,
736                node,
737                "float_to_string",
738            ),
739            _ => panic!("wasn't a fixed operation"),
740        }
741        // No need to copy from temp register to target as we're using target_reg directly
742    }
743
744    pub fn emit_intrinsic_transformer(
745        &mut self,
746        target_destination: &Destination,
747        intrinsic_fn: &IntrinsicFunction,
748        self_addr: &PointerLocation,
749        lambda: (Vec<VariableRef>, &Expression),
750        node: &Node,
751        ctx: &Context,
752    ) {
753        match intrinsic_fn {
754            IntrinsicFunction::TransformerFold => { // Low prio
755            }
756            IntrinsicFunction::TransformerFilter => {
757                self.emit_iterate_over_collection_with_lambda(
758                    target_destination,
759                    node,
760                    Collection::Vec,
761                    Transformer::Filter,
762                    &self_addr.ptr_reg,
763                    lambda,
764                    ctx,
765                );
766            }
767
768            IntrinsicFunction::TransformerFor => {
769                self.emit_iterate_over_collection_with_lambda(
770                    target_destination,
771                    node,
772                    Collection::Vec,
773                    Transformer::For,
774                    &self_addr.ptr_reg,
775                    lambda,
776                    ctx,
777                );
778            }
779            IntrinsicFunction::TransformerWhile => {
780                self.emit_iterate_over_collection_with_lambda(
781                    target_destination,
782                    node,
783                    Collection::Vec,
784                    Transformer::While,
785                    &self_addr.ptr_reg,
786                    lambda,
787                    ctx,
788                );
789            }
790
791            IntrinsicFunction::TransformerFind => {
792                self.emit_iterate_over_collection_with_lambda(
793                    target_destination,
794                    node,
795                    Collection::Vec,
796                    Transformer::Find,
797                    &self_addr.ptr_reg,
798                    lambda,
799                    ctx,
800                );
801            }
802            _ => todo!("{intrinsic_fn}"),
803        }
804    }
805
806    #[allow(clippy::too_many_lines)]
807    #[allow(clippy::too_many_arguments)]
808    pub fn emit_single_intrinsic_call_with_self_destination(
809        &mut self,
810        target_destination: &Destination,
811        node: &Node,
812        intrinsic_fn: &IntrinsicFunction,
813        self_destination: Option<&Destination>,
814        arguments: &[ArgumentExpression],
815        ctx: &Context,
816        comment: &str,
817    ) {
818        // Use the helper function to properly materialize the self argument
819        let self_reg = if let Some(self_dest) = self_destination {
820            self.emit_load_scalar_or_absolute_aggregate_pointer(self_dest, node, comment)
821        } else {
822            None
823        };
824
825        // Delegate to the existing function
826        self.emit_single_intrinsic_call_with_self(
827            target_destination,
828            node,
829            intrinsic_fn,
830            self_reg.as_ref(),
831            arguments,
832            ctx,
833            comment,
834        );
835    }
836
837    #[allow(clippy::too_many_lines)]
838    #[allow(clippy::too_many_arguments)]
839    pub fn emit_single_intrinsic_call_with_self(
840        &mut self,
841        target_destination: &Destination,
842        node: &Node,
843        intrinsic_fn: &IntrinsicFunction,
844        self_reg: Option<&TypedRegister>,
845        arguments: &[ArgumentExpression],
846        ctx: &Context,
847        comment: &str,
848    ) {
849        let maybe_target = target_destination.register();
850
851        match intrinsic_fn {
852            IntrinsicFunction::Float2Magnitude
853            | IntrinsicFunction::FloatAbs
854            | IntrinsicFunction::FloatRound
855            | IntrinsicFunction::FloatFloor
856            | IntrinsicFunction::FloatSqrt
857            | IntrinsicFunction::FloatSign
858            | IntrinsicFunction::FloatRnd
859            | IntrinsicFunction::FloatCos
860            | IntrinsicFunction::FloatSin
861            | IntrinsicFunction::FloatAcos
862            | IntrinsicFunction::FloatAsin
863            | IntrinsicFunction::FloatAtan2
864            | IntrinsicFunction::FloatMin
865            | IntrinsicFunction::FloatMax
866            | IntrinsicFunction::FloatClamp
867            | IntrinsicFunction::FloatToString => {
868                // Float
869                let (temp_reg, dest_reg) = if target_destination.is_register() {
870                    (None, target_destination.register().unwrap().clone())
871                } else {
872                    let temp_reg = self.temp_registers.allocate(
873                        VmType::new_contained_in_register(float_type()),
874                        "temporary destination for low level intrinsic",
875                    );
876
877                    (Some(temp_reg.register.clone()), temp_reg.register)
878                };
879
880                // Materialize self to ensure we have the actual scalar value
881                let mut converted_regs = vec![self_reg.unwrap().clone()];
882                for arg in arguments {
883                    let ArgumentExpression::Expression(found_expression) = arg else {
884                        panic!("must be expression");
885                    };
886                    let materialized_arg = self.emit_scalar_rvalue(found_expression, ctx);
887                    converted_regs.push(materialized_arg);
888                }
889
890                self.emit_intrinsic_call_fixed(&dest_reg, intrinsic_fn, &converted_regs, node);
891
892                if let Some(temp_reg) = temp_reg {
893                    self.emit_store_scalar_to_memory_offset_instruction(
894                        target_destination.grab_memory_location(),
895                        &temp_reg,
896                        node,
897                        "store the fixed point value into memory",
898                    );
899                }
900            }
901
902            IntrinsicFunction::IntToFloat => {
903                // IntToFloat - special case because it returns a float, not an int
904                let (temp_reg, dest_reg) = if target_destination.is_register() {
905                    (None, target_destination.register().unwrap().clone())
906                } else {
907                    let temp_reg = self.temp_registers.allocate(
908                        VmType::new_contained_in_register(float_type()),
909                        "temporary destination for int to float intrinsic",
910                    );
911
912                    (Some(temp_reg.register.clone()), temp_reg.register)
913                };
914
915                // Self is already materialized as a register
916                let int_value_reg = self_reg.unwrap();
917
918                // Now convert the materialized integer value to float
919                self.builder.add_int_to_float(
920                    &dest_reg,
921                    int_value_reg,
922                    node,
923                    &format!("int to float {}", int_value_reg.comment()),
924                );
925
926                if let Some(temp_reg) = temp_reg {
927                    self.emit_store_scalar_to_memory_offset_instruction(
928                        target_destination.grab_memory_location(),
929                        &temp_reg,
930                        node,
931                        "store the float result from int to float conversion",
932                    );
933                }
934            }
935
936            IntrinsicFunction::IntAbs
937            | IntrinsicFunction::IntRnd
938            | IntrinsicFunction::IntMax
939            | IntrinsicFunction::IntMin
940            | IntrinsicFunction::IntClamp
941            | IntrinsicFunction::IntToString => {
942                // Int
943                let (temp_reg, dest_reg) = if target_destination.is_register() {
944                    let target_reg = target_destination.register().unwrap();
945                    // Intrinsics can operate on any register directly, no special treatment needed
946                    (None, target_reg.clone())
947                } else {
948                    let temp_reg = self.temp_registers.allocate(
949                        VmType::new_contained_in_register(u32_type()),
950                        "temporary destination for low level intrinsic",
951                    );
952
953                    (Some(temp_reg.register.clone()), temp_reg.register)
954                };
955
956                // Materialize additional arguments (self is already materialized)
957                let mut converted_regs = vec![self_reg.unwrap().clone()];
958                for arg in arguments {
959                    let ArgumentExpression::Expression(found_expression) = arg else {
960                        panic!("must be expression");
961                    };
962                    let materialized_arg = self.emit_scalar_rvalue(found_expression, ctx);
963                    converted_regs.push(materialized_arg);
964                }
965
966                self.emit_intrinsic_call_int(&dest_reg, intrinsic_fn, &converted_regs, node);
967
968                if let Some(temp_reg) = temp_reg {
969                    if target_destination.is_register() {
970                        // Copy from temp to target register
971                        self.builder.add_mov_reg(
972                            target_destination.register().unwrap(),
973                            &temp_reg,
974                            node,
975                            "copy intrinsic result from temp to target register",
976                        );
977                    } else {
978                        // Store to memory location
979                        self.emit_store_scalar_to_memory_offset_instruction(
980                            target_destination.grab_memory_location(),
981                            &temp_reg,
982                            node,
983                            "put the low level intrinsic fixed (int) back to memory",
984                        );
985                    }
986                }
987            }
988
989            IntrinsicFunction::VecPush
990            | IntrinsicFunction::VecPop
991            | IntrinsicFunction::VecRemoveIndex
992            | IntrinsicFunction::VecRemoveIndexGetValue
993            | IntrinsicFunction::VecRemoveFirstIndexGetValue
994            | IntrinsicFunction::VecClear
995            | IntrinsicFunction::VecSwap
996            | IntrinsicFunction::VecInsert
997            | IntrinsicFunction::VecFirst
998            | IntrinsicFunction::VecGet
999            | IntrinsicFunction::VecLast => {
1000                // Vec
1001                // Self is assumed to be a flattened pointer:
1002                let vec_self_ptr_reg = PointerLocation {
1003                    ptr_reg: self_reg.unwrap().clone(),
1004                };
1005                let converted_to_expressions: Vec<_> = arguments
1006                    .iter()
1007                    .map(|arg| {
1008                        let ArgumentExpression::Expression(found_expression) = arg else {
1009                            panic!("must be expression");
1010                        };
1011                        found_expression.clone()
1012                    })
1013                    .collect();
1014
1015                self.emit_intrinsic_call_vec(
1016                    target_destination,
1017                    intrinsic_fn,
1018                    &vec_self_ptr_reg,
1019                    &converted_to_expressions,
1020                    node,
1021                    ctx,
1022                );
1023            }
1024
1025            IntrinsicFunction::GridGet
1026            | IntrinsicFunction::GridSet
1027            | IntrinsicFunction::GridWidth
1028            | IntrinsicFunction::GridHeight => {
1029                // Grid
1030                // Self is assumed to be a flattened pointer:
1031                let grid_self_ptr_reg = PointerLocation {
1032                    ptr_reg: self_reg.unwrap().clone(),
1033                };
1034                let converted_to_expressions: Vec<_> = arguments
1035                    .iter()
1036                    .map(|arg| {
1037                        let ArgumentExpression::Expression(found_expression) = arg else {
1038                            panic!("must be expression");
1039                        };
1040                        found_expression.clone()
1041                    })
1042                    .collect();
1043                self.emit_intrinsic_grid(
1044                    target_destination,
1045                    intrinsic_fn,
1046                    &grid_self_ptr_reg,
1047                    &converted_to_expressions,
1048                    node,
1049                    comment,
1050                    ctx,
1051                );
1052            }
1053
1054            IntrinsicFunction::SparseIsAlive
1055            | IntrinsicFunction::SparseRemove
1056            | IntrinsicFunction::SparseAdd => {
1057                // Sparse
1058                // Self is assumed to be a flattened pointer:
1059                let grid_self_ptr_reg = PointerLocation {
1060                    ptr_reg: self_reg.unwrap().clone(),
1061                };
1062                let converted_to_expressions: Vec<_> = arguments
1063                    .iter()
1064                    .map(|arg| {
1065                        let ArgumentExpression::Expression(found_expression) = arg else {
1066                            panic!("must be expression");
1067                        };
1068                        found_expression.clone()
1069                    })
1070                    .collect();
1071                self.emit_intrinsic_sparse(
1072                    target_destination,
1073                    intrinsic_fn,
1074                    &grid_self_ptr_reg,
1075                    &converted_to_expressions,
1076                    node,
1077                    comment,
1078                    ctx,
1079                );
1080            }
1081
1082            IntrinsicFunction::TransformerFor
1083            | IntrinsicFunction::TransformerWhile
1084            | IntrinsicFunction::TransformerFindMap
1085            | IntrinsicFunction::TransformerAny
1086            | IntrinsicFunction::TransformerAll
1087            | IntrinsicFunction::TransformerMap
1088            | IntrinsicFunction::TransformerFilter
1089            | IntrinsicFunction::TransformerFilterMap
1090            | IntrinsicFunction::TransformerFind
1091            | IntrinsicFunction::TransformerFold => {
1092                // Self is assumed to be a flattened pointer:
1093                let collection_self_ptr_reg = PointerLocation {
1094                    ptr_reg: self_reg.unwrap().clone(),
1095                };
1096
1097                let lambda_expression = &arguments[0];
1098
1099                // Take out lambda and other lookups before generating the code
1100                let ArgumentExpression::Expression(expr) = lambda_expression else {
1101                    panic!("err");
1102                };
1103
1104                let ExpressionKind::Lambda(lambda_variables, lambda_expr) = &expr.kind else {
1105                    panic!("must have lambda for transformers");
1106                };
1107
1108                self.emit_intrinsic_transformer(
1109                    target_destination,
1110                    intrinsic_fn,
1111                    &collection_self_ptr_reg,
1112                    (lambda_variables.clone(), lambda_expr),
1113                    node,
1114                    ctx,
1115                );
1116            }
1117
1118            IntrinsicFunction::RuntimePanic => {
1119                self.builder
1120                    .add_panic(self_reg.unwrap(), node, "intrinsic panic");
1121            }
1122
1123            IntrinsicFunction::RuntimeHalt => {
1124                self.builder.add_halt(node, "intrinsic halt");
1125            }
1126
1127            IntrinsicFunction::RuntimeStep => {
1128                self.builder.add_step(node, "intrinsic step");
1129            }
1130
1131            IntrinsicFunction::RangeInit => {
1132                let start_reg = self_reg.unwrap();
1133                // let MutRefOrImmutableExpression::Expression(start_arg_expr) = start_arg else {
1134                //    panic!();
1135                //};
1136                // let start_reg = self.emit_scalar_rvalue(start_arg_expr, ctx);
1137
1138                let end_arg = &arguments[0];
1139                let ArgumentExpression::Expression(end_arg_expr) = end_arg else {
1140                    panic!();
1141                };
1142                let end_reg = self.emit_scalar_rvalue(end_arg_expr, ctx);
1143
1144                let is_inclusive = &arguments[1];
1145                let ArgumentExpression::Expression(is_inclusive_expr) = is_inclusive else {
1146                    panic!();
1147                };
1148                let is_inclusive_reg = self.emit_scalar_rvalue(is_inclusive_expr, ctx);
1149                let absolute_range_pointer = self.emit_compute_effective_address_to_register(
1150                    target_destination,
1151                    node,
1152                    "create range target pointer",
1153                );
1154                self.builder.add_range_init(
1155                    &absolute_range_pointer,
1156                    start_reg,
1157                    &end_reg,
1158                    &is_inclusive_reg,
1159                    node,
1160                    "create a range",
1161                );
1162            }
1163
1164            // Bool
1165            IntrinsicFunction::CharToString => {
1166                if maybe_target.is_none() {
1167                    eprintln!("problem");
1168                }
1169                self.builder.char_to_string(
1170                    maybe_target.unwrap(),
1171                    self_reg.unwrap(),
1172                    node,
1173                    "char_to_string",
1174                );
1175            }
1176
1177            IntrinsicFunction::CharToInt => {
1178                if maybe_target.is_none() {
1179                    eprintln!("problem");
1180                }
1181                self.builder.add_mov_reg(
1182                    maybe_target.unwrap(),
1183                    self_reg.unwrap(),
1184                    node,
1185                    "char_to_int",
1186                );
1187            }
1188
1189            // Bool
1190            IntrinsicFunction::ByteToString => {
1191                if maybe_target.is_none() {
1192                    eprintln!("problem");
1193                }
1194                self.builder.byte_to_string(
1195                    maybe_target.unwrap(),
1196                    self_reg.unwrap(),
1197                    node,
1198                    "byte_to_string",
1199                );
1200            }
1201
1202            IntrinsicFunction::ByteToInt => {
1203                if maybe_target.is_none() {
1204                    eprintln!("problem");
1205                }
1206                // It is safe to "upcast" to an i32 from a u8, so just copy the register
1207                // TODO: Make something smarter so we don't have to copy
1208                self.builder.add_mov_reg(
1209                    maybe_target.unwrap(),
1210                    self_reg.unwrap(),
1211                    node,
1212                    "byte_to_int",
1213                );
1214            }
1215
1216            // Bool
1217            IntrinsicFunction::BoolToString => {
1218                if maybe_target.is_none() {
1219                    eprintln!("problem");
1220                }
1221                self.builder.bool_to_string(
1222                    maybe_target.unwrap(),
1223                    self_reg.unwrap(),
1224                    node,
1225                    "bool_to_string",
1226                );
1227            }
1228
1229            IntrinsicFunction::StringToString => {
1230                self.builder.string_to_string(
1231                    maybe_target.unwrap(),
1232                    self_reg.unwrap(),
1233                    node,
1234                    "string_to_string",
1235                );
1236            }
1237
1238            // Common Collection
1239            IntrinsicFunction::MapIsEmpty | IntrinsicFunction::VecIsEmpty => {
1240                let collection_pointer = PointerLocation {
1241                    ptr_reg: self_reg.unwrap().clone(),
1242                };
1243                self.emit_collection_is_empty(
1244                    maybe_target.unwrap().clone(),
1245                    &collection_pointer,
1246                    node,
1247                    "vec empty",
1248                );
1249            }
1250
1251            IntrinsicFunction::StringLen
1252            | IntrinsicFunction::MapLen
1253            | IntrinsicFunction::VecLen => {
1254                let collection_pointer = PointerLocation {
1255                    ptr_reg: self_reg.unwrap().clone(),
1256                };
1257                self.emit_collection_len(
1258                    maybe_target.unwrap(),
1259                    &collection_pointer,
1260                    node,
1261                    "get the collection element_count",
1262                );
1263            }
1264            IntrinsicFunction::MapCapacity | IntrinsicFunction::VecCapacity => {
1265                let collection_pointer = PointerLocation {
1266                    ptr_reg: self_reg.unwrap().clone(),
1267                };
1268                self.emit_collection_capacity(
1269                    maybe_target.unwrap(),
1270                    &collection_pointer,
1271                    node,
1272                    "get the collection element_count",
1273                );
1274            }
1275
1276            IntrinsicFunction::MapRemove | IntrinsicFunction::MapHas => {
1277                // Map
1278                // Self is assumed to be a flattened pointer:
1279                let grid_self_ptr_reg = PointerLocation {
1280                    ptr_reg: self_reg.unwrap().clone(),
1281                };
1282                let converted_to_expressions: Vec<_> = arguments
1283                    .iter()
1284                    .map(|arg| {
1285                        let ArgumentExpression::Expression(found_expression) = arg else {
1286                            panic!("must be expression");
1287                        };
1288                        found_expression.clone()
1289                    })
1290                    .collect();
1291                self.emit_intrinsic_map(
1292                    target_destination,
1293                    intrinsic_fn,
1294                    &grid_self_ptr_reg,
1295                    &converted_to_expressions,
1296                    node,
1297                    comment,
1298                    ctx,
1299                );
1300            } // All intrinsic cases are now handled above
1301        }
1302    }
1303
1304    fn emit_intrinsic_map_remove(
1305        &mut self,
1306        map_header_reg: &PointerLocation,
1307        key_expression: &Expression,
1308        ctx: &Context,
1309    ) {
1310        let key_register =
1311            self.emit_aggregate_pointer_or_pointer_to_scalar_memory(key_expression, ctx);
1312
1313        self.builder
1314            .add_map_remove(map_header_reg, &key_register, &key_expression.node, "");
1315    }
1316
1317    fn emit_collection_capacity(
1318        &mut self,
1319        output_reg: &TypedRegister,
1320        collection_addr: &PointerLocation,
1321        node: &Node,
1322        comment: &str,
1323    ) {
1324        self.builder.add_ld16_from_pointer_with_offset_u16(
1325            output_reg,
1326            &collection_addr.ptr_reg,
1327            COLLECTION_CAPACITY_OFFSET,
1328            node,
1329            comment,
1330        );
1331    }
1332
1333    fn emit_collection_len(
1334        &mut self,
1335        output_reg: &TypedRegister,
1336        collection_addr: &PointerLocation,
1337        node: &Node,
1338        comment: &str,
1339    ) {
1340        self.builder.add_ld16_from_pointer_with_offset_u16(
1341            output_reg,
1342            &collection_addr.ptr_reg,
1343            COLLECTION_ELEMENT_COUNT_OFFSET,
1344            node,
1345            &format!("{comment} - collection element_count"),
1346        );
1347    }
1348
1349    fn emit_collection_is_empty(
1350        &mut self,
1351        output_reg: TypedRegister,
1352        collection_addr: &PointerLocation,
1353        node: &Node,
1354        _comment: &str,
1355    ) {
1356        self.builder.add_ld16_from_pointer_with_offset_u16(
1357            &output_reg,
1358            &collection_addr.ptr_reg,
1359            COLLECTION_ELEMENT_COUNT_OFFSET,
1360            node,
1361            "get the map length for testing if it is empty",
1362        );
1363        self.builder.add_seqz(
1364            &output_reg,
1365            &output_reg,
1366            node,
1367            "convert the map length to inverted bool",
1368        );
1369    }
1370}