swamp_code_gen/
iter.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::FlagStateKind;
6use crate::code_bld::CodeBuilder;
7use crate::ctx::Context;
8use crate::transformer::{Collection, Transformer, TransformerResult};
9use source_map_node::Node;
10use swamp_semantic::{Expression, VariableRef};
11use swamp_vm_isa::{InstructionPosition, MemoryOffset};
12use swamp_vm_types::types::{
13    BasicTypeKind, BasicTypeRef, Place, TypedRegister, VmType, pointer_type, u8_type, u32_type,
14};
15use swamp_vm_types::{MemoryLocation, PatchPosition};
16use tracing::error;
17
18impl CodeBuilder<'_> {
19    /// Generates code to iterate over a collection using a transformer (e.g., map, filter, `filter_map`)
20    /// and a lambda expression. Handles creation of result vectors, iterator setup, lambda invocation,
21    /// early exit logic, and result collection.
22    ///
23    /// Steps:
24    /// 1. (Optional) Initialize a target vector for the result, if the transformer produces one.
25    /// 2. Initialize the iterator for the collection.
26    /// 3. Generate code to fetch the next element from the iterator.
27    /// 4. Spill lambda variables to protect them from being clobbered by function calls
28    /// 5. Inline the lambda code for the current element.
29    /// 6. Restore lambda variables from stack after lambda execution
30    /// 7. If the transformer supports early exit (e.g., filter, find), set the P flag based on the lambda result.
31    /// 8. Conditionally skip result insertion if early exit is triggered.
32    /// 9. (Optional) If applicable, insert the (possibly unwrapped) result into the target vector.
33    /// 10. Loop back to fetch the next element.
34    /// 11. Finalize iteration, handling any post-processing (e.g., normalizing boolean results).
35    ///
36    /// # Parameters
37    /// - `node`: The AST node for error reporting and code location.
38    /// - `collection_type`: The type of collection being iterated.
39    /// - `transformer`: The transformer operation (map, filter, find, fold, etc.).
40    /// - `collection_self_region`: Memory region of the collection.
41    /// - `lambda_expression`: The lambda expression to apply.
42    /// - `ctx`: Code generation context. Contains the result target.
43    ///
44    /// # Returns
45    /// - `Ok(())` on success, or an error if code generation fails.
46    ///
47    /// # Errors
48    /// // TODO:
49    /// # Panics
50    /// - If the lambda expression or its kind is not as expected (internal error).
51    #[allow(clippy::too_many_lines)]
52    #[allow(clippy::too_many_arguments)]
53    pub fn emit_iterate_over_collection_with_lambda(
54        &mut self,
55        target_destination: &Place,
56        node: &Node,
57        source_collection_type: Collection,
58        transformer: Transformer,
59        source_collection_reg: &TypedRegister,
60        lambda_tuple: (Vec<VariableRef>, &Expression),
61        ctx: &Context,
62    ) {
63        let (lambda_variables, lambda_expr) = lambda_tuple;
64        let maybe_primary_element_gen_type = source_collection_reg.ty.basic_type.element();
65
66        let target_variables: Vec<_> = lambda_variables
67            .iter()
68            .map(|x| {
69                self.variable_registers
70                    .get(&x.unique_id_within_function)
71                    .unwrap()
72                    .clone()
73            })
74            .collect();
75
76        // Primary is the right most variable
77        let primary_variable = &target_variables[target_variables.len() - 1];
78
79        let hwm = self.temp_registers.save_mark();
80
81        // 1. Initialize the target collection if needed and compute collection pointer
82        let maybe_target_collection_pointer = match transformer.return_type() {
83            TransformerResult::VecFromSourceCollection | TransformerResult::VecWithLambdaResult => {
84                // For transformers that create collections (filter, map, filterMap),
85                // we need to initialize the target vector before adding elements
86                let target_memory_location = target_destination.memory_location_or_pointer_reg();
87
88                self.emit_initialize_memory_for_any_type(
89                    &target_memory_location,
90                    node,
91                    "initialize target collection for transformer",
92                );
93
94                // For VecFromSourceCollection, compute the collection pointer now to avoid
95                // it being clobbered during lambda execution
96                if matches!(
97                    transformer.return_type(),
98                    TransformerResult::VecFromSourceCollection
99                ) {
100                    let computed_pointer = self.emit_compute_effective_address_to_register(
101                        target_destination,
102                        node,
103                        "get pointer to collection (before lambda execution)",
104                    );
105
106                    Some(computed_pointer)
107                } else {
108                    None
109                }
110            }
111            _ => {
112                // Other transformers don't need target initialization
113                None
114            }
115        };
116
117        // 2. Initialize the iterator and generate code to fetch the next element.
118        let (continue_iteration_label, iteration_complete_patch_position) = self
119            .emit_iter_init_and_next(
120                node,
121                source_collection_type,
122                maybe_primary_element_gen_type,
123                source_collection_reg,
124                &target_variables,
125            );
126
127        // 3. Inline the lambda code for the current element(s).
128        let lambda_result = self.emit_scalar_rvalue(lambda_expr, ctx);
129
130        // 4. If the transformer supports early exit, set the P flag based on the lambda result.
131        let transformer_t_flag_state = self.check_if_transformer_sets_t_flag(transformer);
132
133        // 5. Conditionally skip result insertion if early exit is triggered.
134        let maybe_skip_early = if self.check_if_transformer_can_skip_early(transformer)
135            && matches!(
136                transformer_t_flag_state,
137                FlagStateKind::TFlagIsTrueWhenSet | FlagStateKind::TFlagIsTrueWhenClear
138            ) {
139            // The P flag is set so we can act on it
140            let skip_early = self.builder.add_jmp_if_not_equal_polarity_placeholder(
141                &lambda_result,
142                &transformer_t_flag_state.polarity(),
143                node,
144                "skip early",
145            );
146
147            Some(skip_early)
148        } else {
149            // P flag is not set, we have to iterate through the whole collection
150            None
151        };
152
153        // 6. If applicable, insert the (possibly unwrapped) result into the target vector.
154
155        match transformer.return_type() {
156            TransformerResult::Unit => {
157
158                // Only alternative is that it is a bool return, so no need to take any action here
159            }
160            TransformerResult::Bool => {
161                // Only alternative is that it is a bool return, so no need to take any action here
162            }
163            TransformerResult::WrappedValueFromSourceCollection => {
164                // Handled elsewhere
165            }
166            TransformerResult::VecWithLambdaResult => {
167                self.transformer_add_to_collection(
168                    &lambda_result,
169                    transformer.needs_tag_removed(),
170                    source_collection_type,
171                    target_destination.register().unwrap(),
172                    node,
173                );
174            }
175            TransformerResult::VecFromSourceCollection => {
176                let skip_if_false_patch_position = self.builder.add_jmp_if_not_true_placeholder(
177                    &lambda_result,
178                    node,
179                    "skip the result if it is false",
180                );
181
182                let absolute_pointer = maybe_target_collection_pointer
183                    .as_ref()
184                    .expect("collection pointer should have been computed before lambda execution");
185
186                self.add_to_collection(
187                    node,
188                    source_collection_type,
189                    absolute_pointer,
190                    primary_variable,
191                );
192
193                self.builder.patch_jump_here(skip_if_false_patch_position);
194            }
195        }
196
197        // 7. Loop back to fetch the next element.
198        self.builder.add_jmp(
199            continue_iteration_label,
200            &lambda_expr.debug_last_expression().node,
201            "jump to iter_next",
202        );
203
204        self.builder
205            .patch_jump_here(iteration_complete_patch_position);
206
207        // 8. Finalize iteration, handling any post-processing (e.g., normalizing boolean results).
208        if let Some(found_skip_early) = maybe_skip_early {
209            self.builder.patch_jump_here(found_skip_early);
210        }
211
212        match transformer.return_type() {
213            TransformerResult::Bool => {
214                // It is a transformer that returns a bool, lets store P flag as bool it
215            }
216            TransformerResult::WrappedValueFromSourceCollection => {
217                let destination_type = target_destination.ty();
218                let BasicTypeKind::Optional(tagged_union) = &destination_type.kind else {
219                    panic!("expected optional {destination_type:?}");
220                };
221
222                let tag_some_value_reg = self.temp_registers.allocate(
223                    VmType::new_unknown_placement(u8_type()),
224                    "iterate over collection target",
225                );
226
227                self.builder.add_mov8_immediate(
228                    tag_some_value_reg.register(),
229                    1,
230                    node,
231                    "mark tag as Some",
232                );
233
234                let result_location = target_destination.memory_location_or_pointer_reg();
235
236                self.builder.add_st8_using_ptr_with_offset(
237                    &result_location.unsafe_add_offset(tagged_union.tag_offset),
238                    tag_some_value_reg.register(),
239                    node,
240                    "store Tag value of (1) into memory",
241                );
242
243                // TODO: let payload_memory_location = result_location.unsafe_add_offset(tagged_union.payload_offset);
244                let payload_memory_location = result_location.unsafe_add_offset(MemoryOffset(4));
245                let payload_destination = Place::Memory(payload_memory_location);
246
247                let source_location = Place::Register(primary_variable.clone());
248
249                self.emit_store_value_to_memory_place(
250                    &payload_destination,
251                    &source_location,
252                    node,
253                    "copy result in place",
254                );
255            }
256            _ => {}
257        }
258
259        self.temp_registers.restore_to_mark(hwm);
260    }
261
262    #[allow(clippy::unnecessary_wraps)]
263    #[allow(clippy::too_many_lines)]
264    fn emit_iter_init_and_next(
265        &mut self,
266        node: &Node,
267        collection_type: Collection,
268        maybe_element_type: Option<BasicTypeRef>,
269        collection_self_addr: &TypedRegister,
270        target_variables: &[TypedRegister],
271    ) -> (InstructionPosition, PatchPosition) {
272        let iterator_gen_type = collection_type.iterator_gen_type();
273
274        let target_iterator_header_reg = self.allocate_frame_space_and_return_absolute_pointer_reg(
275            &iterator_gen_type,
276            node,
277            "allocate iterator header space",
278        );
279
280        let primary_register = if target_variables.len() == 2 {
281            &target_variables[1]
282        } else {
283            &target_variables[0]
284        };
285
286        let iter_next_position = InstructionPosition(self.builder.position().0 + 1);
287        let placeholder = match collection_type {
288            Collection::Range => {
289                self.builder.add_range_iter_init(
290                    &target_iterator_header_reg,
291                    collection_self_addr,
292                    node,
293                    "range init",
294                );
295
296                assert_eq!(target_variables.len(), 1);
297                self.builder.add_range_iter_next_placeholder(
298                    &target_iterator_header_reg,
299                    &target_variables[0],
300                    node,
301                    "range iter next single",
302                )
303            }
304
305            Collection::Map => {
306                // For maps, we need to handle both key and value potentially being scalars
307                // Map iteration returns pointers to both key and value, so we need to load scalar values
308                // TODO: Clean this up a bit to be more easy to read
309                // TODO: Also, scalar values that are mutated should be stored back to the value register
310                // Keys are by design not allowed to be mutated in iteration.
311                let (temp_registers, target_variables_to_use) = if target_variables.len() == 2 {
312                    // Map iteration with key-value pairs
313                    let key_register = &target_variables[0];
314                    let value_register = &target_variables[1];
315
316                    let (key_is_scalar, value_is_scalar) =
317                        match &collection_self_addr.ty.basic_type.kind {
318                            BasicTypeKind::MapStorage {
319                                key_type,
320                                value_type,
321                                ..
322                            } => (key_type.is_scalar(), value_type.is_scalar()),
323                            BasicTypeKind::DynamicLengthMapView(key_item, value_item) => {
324                                (key_item.ty.is_scalar(), value_item.ty.is_scalar())
325                            }
326                            _ => (false, false),
327                        };
328
329                    let mut temp_regs = Vec::new();
330                    let mut target_vars = Vec::new();
331
332                    // Handle key
333                    if key_is_scalar {
334                        let temp_key_addr = self
335                            .temp_registers
336                            .allocate(key_register.ty.clone(), "temp address for key");
337                        temp_regs.push((temp_key_addr.register().clone(), key_register.clone()));
338                        target_vars.push(temp_key_addr.register);
339                    } else {
340                        target_vars.push(key_register.clone());
341                    }
342
343                    // Handle value
344                    if value_is_scalar {
345                        let temp_value_addr = self
346                            .temp_registers
347                            .allocate(value_register.ty.clone(), "temp address for value");
348                        temp_regs
349                            .push((temp_value_addr.register().clone(), value_register.clone()));
350                        target_vars.push(temp_value_addr.register);
351                    } else {
352                        target_vars.push(value_register.clone());
353                    }
354
355                    (Some(temp_regs), target_vars)
356                } else {
357                    // Single value iteration (values only)
358                    let value_register = &target_variables[0];
359                    let value_is_scalar = match &collection_self_addr.ty.basic_type.kind {
360                        BasicTypeKind::MapStorage { value_type, .. } => value_type.is_scalar(),
361                        BasicTypeKind::DynamicLengthMapView(_, value_item) => {
362                            value_item.ty.is_scalar()
363                        }
364                        _ => false,
365                    };
366
367                    if value_is_scalar {
368                        let temp_value_addr = self
369                            .temp_registers
370                            .allocate(value_register.ty.clone(), "temp address for value");
371                        (
372                            Some(vec![(
373                                temp_value_addr.register().clone(),
374                                value_register.clone(),
375                            )]),
376                            vec![temp_value_addr.register.clone()],
377                        )
378                    } else {
379                        (None, target_variables.to_vec())
380                    }
381                };
382
383                let patch_position = self.emit_iter_init_and_next_to_memory(
384                    &target_iterator_header_reg,
385                    node,
386                    collection_type,
387                    maybe_element_type,
388                    collection_self_addr,
389                    &target_variables_to_use,
390                );
391
392                // Load scalar values from the addresses returned by the map iterator
393                if let Some(temp_regs) = temp_registers {
394                    for (temp_addr_reg, target_scalar_reg) in temp_regs {
395                        let source_memory_location =
396                            MemoryLocation::new_copy_over_whole_type_with_zero_offset(
397                                temp_addr_reg,
398                            );
399                        self.emit_load_value_from_memory_source(
400                            &target_scalar_reg,
401                            &source_memory_location,
402                            node,
403                            "load scalar value from map iterator address",
404                        );
405                    }
406                }
407
408                patch_position
409            }
410            _ => {
411                let (temp_registers, target_variables_to_use) =
412                    if primary_register.ty.basic_type.is_scalar() {
413                        // For primitives, create temp register to hold the address, since they do not want
414                        // the address
415                        let temp_addr = self.temp_registers.allocate(
416                            VmType::new_contained_in_register(pointer_type()),
417                            "temp address for value",
418                        );
419
420                        if target_variables.len() == 2 {
421                            (
422                                Some(temp_addr.register().clone()),
423                                vec![target_variables[0].clone(), temp_addr.register.clone()],
424                            )
425                        } else {
426                            (
427                                Some(temp_addr.register().clone()),
428                                vec![temp_addr.register.clone()],
429                            )
430                        }
431                    } else {
432                        (None, target_variables.to_vec())
433                    };
434
435                let patch_position = self.emit_iter_init_and_next_to_memory(
436                    &target_iterator_header_reg,
437                    node,
438                    collection_type,
439                    maybe_element_type,
440                    collection_self_addr,
441                    &target_variables_to_use,
442                );
443
444                if let Some(temp_regs) = temp_registers {
445                    if collection_type == Collection::String {
446                        self.builder.add_mov_reg(
447                            primary_register,
448                            &temp_regs,
449                            node,
450                            "copy from temporary to primary scalar register",
451                        );
452                        //self.emit_copy_register(&temp_regs, primary_register, node, "comment");
453                    } else {
454                        let source_memory_location =
455                            MemoryLocation::new_copy_over_whole_type_with_zero_offset(temp_regs);
456                        self.emit_load_value_from_memory_source(
457                            primary_register,
458                            &source_memory_location,
459                            node,
460                            "load primitive from element address",
461                        );
462                    }
463                }
464
465                patch_position
466            }
467        };
468
469        (iter_next_position, placeholder)
470    }
471
472    fn emit_iter_init_and_next_to_memory(
473        &mut self,
474        target_iterator_header_reg: &TypedRegister,
475        node: &Node,
476        collection_type: Collection,
477        maybe_element_type: Option<BasicTypeRef>,
478        collection_self_addr: &TypedRegister,
479        target_variables: &[TypedRegister],
480    ) -> PatchPosition {
481        let primary_register = if target_variables.len() == 2 {
482            &target_variables[1]
483        } else {
484            &target_variables[0]
485        };
486        let is_pair = target_variables.len() == 2;
487        match collection_type {
488            // Low  prio
489            Collection::String => {
490                if maybe_element_type.is_none() {
491                    error!(
492                        ?collection_self_addr,
493                        "can no start iterating with this strange vec collection"
494                    );
495                }
496
497                self.builder.add_string_iter_init(
498                    target_iterator_header_reg,
499                    collection_self_addr,
500                    maybe_element_type.unwrap().total_size,
501                    node,
502                    "vec iter init",
503                );
504
505                let is_pair = target_variables.len() == 2;
506
507                if is_pair {
508                    // For non-primitives, use target directly
509                    self.builder.add_string_iter_next_pair_placeholder(
510                        target_iterator_header_reg,
511                        &target_variables[0],
512                        &target_variables[1],
513                        node,
514                        "vec iter next pair",
515                    )
516                } else {
517                    // For non-primitives, use target directly
518                    self.builder.add_string_iter_next_placeholder(
519                        target_iterator_header_reg,
520                        primary_register,
521                        node,
522                        "vec iter next single",
523                    )
524                }
525            }
526            Collection::Vec => {
527                if maybe_element_type.is_none() {
528                    error!(
529                        ?collection_self_addr,
530                        "can no start iterating with this strange vec collection"
531                    );
532                }
533
534                self.builder.add_vec_iter_init(
535                    target_iterator_header_reg,
536                    collection_self_addr,
537                    node,
538                    "vec iter init",
539                );
540
541                let is_pair = target_variables.len() == 2;
542
543                if is_pair {
544                    // For non-primitives, use target directly
545                    self.builder.add_vec_iter_next_pair_placeholder(
546                        target_iterator_header_reg,
547                        &target_variables[0],
548                        &target_variables[1],
549                        node,
550                        "vec iter next pair",
551                    )
552                } else {
553                    // For non-primitives, use target directly
554                    self.builder.add_vec_iter_next_placeholder(
555                        target_iterator_header_reg,
556                        primary_register,
557                        node,
558                        "vec iter next single",
559                    )
560                }
561            }
562            Collection::Sparse => {
563                self.builder.add_sparse_iter_init(
564                    target_iterator_header_reg,
565                    collection_self_addr,
566                    node,
567                    "sparse init",
568                );
569
570                if is_pair {
571                    self.builder.add_sparse_iter_next_pair_placeholder(
572                        target_iterator_header_reg,
573                        &target_variables[0],
574                        &target_variables[1],
575                        node,
576                        "sparse next_pair",
577                    )
578                } else {
579                    self.builder.add_sparse_iter_next_placeholder(
580                        target_iterator_header_reg,
581                        primary_register,
582                        node,
583                        "sparse next_single",
584                    )
585                }
586            }
587
588            Collection::Map => {
589                self.builder.add_map_iter_init(
590                    target_iterator_header_reg,
591                    collection_self_addr,
592                    node,
593                    "map init",
594                );
595
596                if target_variables.len() == 2 {
597                    self.builder.add_map_iter_next_pair_placeholder(
598                        target_iterator_header_reg,
599                        &target_variables[0],
600                        &target_variables[1],
601                        node,
602                        "map next_pair",
603                    )
604                } else {
605                    self.builder.add_map_iter_next_placeholder(
606                        target_iterator_header_reg,
607                        &target_variables[0],
608                        node,
609                        "map next_single",
610                    )
611                }
612            }
613            Collection::Grid => todo!(),
614            _ => panic!("unknown collection"),
615        }
616    }
617
618    const fn check_if_transformer_sets_t_flag(
619        &mut self,
620        transformer: Transformer,
621    ) -> FlagStateKind {
622        match transformer {
623            Transformer::For => FlagStateKind::TFlagIsIndeterminate,
624            Transformer::While => FlagStateKind::TFlagIsTrueWhenSet,
625            Transformer::Filter => {
626                // TODO: Bring this back //assert_eq!(in_value.size().0, 1); // bool
627                FlagStateKind::TFlagIsTrueWhenSet
628            }
629            Transformer::Find => {
630                // TODO: Bring this back //assert_eq!(in_value.size().0, 1); // bool
631                FlagStateKind::TFlagIsTrueWhenClear
632            }
633            Transformer::Map => FlagStateKind::TFlagIsIndeterminate,
634            Transformer::Any => FlagStateKind::TFlagIsTrueWhenClear,
635            Transformer::All => FlagStateKind::TFlagIsTrueWhenSet,
636            Transformer::FilterMap => FlagStateKind::TFlagIsTrueWhenSet,
637        }
638    }
639
640    fn add_to_collection(
641        &mut self,
642        node: &Node,
643        collection: Collection,
644        mut_collection: &TypedRegister,
645        value: &TypedRegister,
646    ) {
647        match collection {
648            Collection::Vec => {
649                let target_element_addr_reg = self.temp_registers.allocate(
650                    VmType::new_contained_in_register(u32_type()),
651                    "address for the element entry",
652                );
653
654                self.builder.add_vec_push_addr(
655                    target_element_addr_reg.register(),
656                    mut_collection,
657                    node,
658                    "add_to_collection for transformer",
659                );
660
661                let vec_entry_destination =
662                    Place::Memory(MemoryLocation::new_copy_over_whole_type_with_zero_offset(
663                        target_element_addr_reg.register().clone(),
664                    ));
665
666                // Initialize the allocated space first (like variable definition)
667                if value.ty.basic_type.is_aggregate() {
668                    self.emit_initialize_memory_for_any_type(
669                        vec_entry_destination.grab_memory_location(),
670                        node,
671                        "initialize vec.push allocated space for transformer",
672                    );
673                }
674
675                let source_destination = if value.ty.is_scalar() {
676                    Place::Register(value.clone())
677                } else {
678                    Place::Memory(MemoryLocation::new_copy_over_whole_type_with_zero_offset(
679                        value.clone(),
680                    ))
681                };
682
683                self.emit_store_value_to_memory_place(
684                    &vec_entry_destination,
685                    &source_destination,
686                    node,
687                    "store value in the new vec entry slot",
688                );
689            }
690            Collection::Sparse => panic!("not supported by sparse"),
691            Collection::Map => todo!(),
692            Collection::Grid => todo!(),
693            Collection::String => todo!(),
694            Collection::Range => todo!(),
695        }
696    }
697
698    fn transformer_add_to_collection(
699        &mut self,
700        in_value: &TypedRegister,
701        should_unwrap_value: bool,
702        collection_type: Collection,
703        mut_collection: &TypedRegister,
704        node: &Node,
705    ) {
706        let (register_to_be_inserted_in_collection, _maybe_temp) = if should_unwrap_value {
707            let tagged_union = in_value.underlying().optional_info().unwrap().clone();
708            let some_variant = tagged_union.get_variant_by_index(1);
709            let payload_vm_type = VmType::new_unknown_placement(some_variant.ty.clone());
710            let temp_reg = self
711                .temp_registers
712                .allocate(payload_vm_type, "transform add to collection");
713            let (_tag_offset, _tag_size, _payload_offset, _) =
714                in_value.underlying().unwrap_info().unwrap();
715
716            /*
717            in_value.unsafe_add_offset(payload_offset);
718
719            self.emit_load_from_memory_internal(
720                temp_reg.register(),
721                in_value,
722                payload_offset,
723                &payload_vm_type,
724                node,
725                "transformer add to collection",
726            );
727
728             */
729            (temp_reg.register.clone(), Some(temp_reg))
730        } else {
731            (in_value.clone(), None)
732        };
733
734        self.add_to_collection(
735            node,
736            collection_type,
737            mut_collection,
738            &register_to_be_inserted_in_collection,
739        );
740    }
741
742    const fn check_if_transformer_can_skip_early(&self, transformer: Transformer) -> bool {
743        match transformer {
744            Transformer::For => false,
745            Transformer::Filter => false,
746            Transformer::Find => true,
747            Transformer::While => true,
748            Transformer::Map => false,
749            Transformer::Any => true,
750            Transformer::All => true,
751            Transformer::FilterMap => false,
752        }
753    }
754}