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