swamp_code_gen/
map.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;
7use source_map_node::Node;
8use swamp_semantic::{Expression, MapType};
9use swamp_vm_types::types::{BasicTypeRef, Destination, VmType};
10use swamp_vm_types::{MemoryLocation, MemoryOffset, PointerLocation};
11
12impl CodeBuilder<'_> {
13    /// Emits Swamp VM opcodes to calculate the memory address of an element within a map.
14    pub fn map_subscript_helper(
15        &mut self,
16        map_header_location: &Destination,
17        map_type: &MapType,
18        key_expression: &Expression,
19        should_create_if_needed: bool,
20        ctx: &Context,
21    ) -> Destination {
22        let map_header_ptr_reg = self.emit_compute_effective_address_to_register(
23            map_header_location,
24            &key_expression.node,
25            "get map header absolute pointer",
26        );
27
28        let pointer_location = PointerLocation::new(map_header_ptr_reg);
29
30        // We have to get the key materialized in a temporary storage, so the map can calculate the hash for it.
31        let key_temp_storage_reg =
32            self.emit_aggregate_pointer_or_pointer_to_scalar_memory(key_expression, ctx);
33
34        let gen_value_type = self.state.layout_cache.layout(&map_type.value);
35        let map_entry_reg = self.temp_registers.allocate(
36            VmType::new_unknown_placement(gen_value_type.clone()),
37            "map entry temp",
38        );
39
40        if should_create_if_needed {
41            self.builder.add_map_get_or_reserve_entry_location(
42                map_entry_reg.register(),
43                &pointer_location,
44                &key_temp_storage_reg,
45                &key_expression.node,
46                "lookup the entry for this key, or create one, in the map",
47            );
48            // HACK: Initialize capacity
49            let map_value_pointer = MemoryLocation::new_copy_over_whole_type_with_zero_offset(
50                map_entry_reg.register.clone(),
51            );
52            self.emit_initialize_memory_for_any_type(
53                &map_value_pointer,
54                &key_expression.node,
55                "initialize the map value entry, just to be safe",
56            );
57        } else {
58            self.builder.add_map_get_entry_location(
59                map_entry_reg.register(),
60                &pointer_location,
61                &key_temp_storage_reg,
62                &key_expression.node,
63                "lookup the entry for this key in the map",
64            );
65        }
66
67        // a map subscript is always a memory location
68        Destination::Memory(MemoryLocation {
69            base_ptr_reg: map_entry_reg.register,
70            offset: MemoryOffset(0),
71            ty: VmType::new_unknown_placement(gen_value_type),
72        })
73    }
74
75    pub(crate) fn emit_map_storage_init_from_initializer_pair_list(
76        &mut self,
77        target_map_header_ptr_reg: &PointerLocation, // Points to MapStorage
78        initializer_pair_list_expressions: &[(Expression, Expression)],
79        key_type: &BasicTypeRef,
80        value_type: &BasicTypeRef,
81        logical_limit: usize,
82        node: &Node,
83        comment: &str,
84        ctx: &Context,
85    ) {
86        let hwm = self.temp_registers.save_mark();
87
88        let safe_output_register = self.temp_registers.allocate(
89            VmType::new_contained_in_register(
90                target_map_header_ptr_reg.ptr_reg.ty.basic_type().clone(),
91            ),
92            "safe output destination for map initialization list",
93        );
94        self.builder.add_mov_reg(
95            &safe_output_register.register,
96            &target_map_header_ptr_reg.ptr_reg,
97            node,
98            "safe output register destination for map initialization",
99        );
100        let safe_output_map_header_pointer_location =
101            PointerLocation::new(safe_output_register.register);
102
103        let key_frame_location = self.allocate_frame_space_and_return_destination_to_it(
104            key_type,
105            node,
106            "key temporary storage",
107        );
108        let key_frame_place = key_frame_location
109            .vm_type()
110            .unwrap()
111            .frame_placed_type()
112            .unwrap();
113
114        let value_target_register = self.temp_registers.allocate(
115            VmType::new_unknown_placement(value_type.clone()),
116            "key temp",
117        );
118
119        for (key_expr, value_expr) in initializer_pair_list_expressions {
120            let initializer_pair_node = &key_expr.node;
121
122            if key_frame_location.ty().total_size.0 > 1 {
123                self.builder.add_frame_memory_clear(
124                    key_frame_place.region(),
125                    initializer_pair_node,
126                    "clear key area each time, to make sure hash is calculated correctly",
127                );
128            }
129
130            self.emit_expression_into_target_memory(
131                key_frame_location.grab_memory_location(),
132                key_expr,
133                "store key to memory",
134                ctx,
135            );
136
137            self.builder.add_map_get_or_reserve_entry_location(
138                value_target_register.register(),
139                &safe_output_map_header_pointer_location,
140                &key_frame_location.grab_memory_location().base_ptr_reg,
141                initializer_pair_node,
142                "find existing or create a map entry to write into",
143            );
144
145            let value_memory_location = MemoryLocation {
146                base_ptr_reg: value_target_register.register().clone(),
147                offset: MemoryOffset(0),
148                ty: VmType::new_unknown_placement(value_type.clone()),
149            };
150
151            // Initialize the allocated space first (like variable definition)
152            if value_type.is_aggregate() {
153                self.emit_initialize_memory_for_any_type(
154                    &value_memory_location,
155                    initializer_pair_node,
156                    "initialize map entry completely",
157                );
158            }
159
160            self.emit_expression_into_target_memory(
161                &value_memory_location,
162                value_expr,
163                "put value into map entry value section",
164                ctx,
165            );
166        }
167
168        self.temp_registers.restore_to_mark(hwm);
169    }
170}