swamp_code_gen/
rvalue.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 swamp_semantic::{Expression, ExpressionKind};
8use swamp_vm_types::types::{Place, TypedRegister, VmType};
9
10impl CodeBuilder<'_> {
11    /// Make sure we have a pointer to something, no matter if it is a scalar or aggregate
12    ///
13    /// Mostly (only?) used for making sure we have a key value memory region for
14    /// `Map` to calculate a hash for (and copy this memory for inserting)
15    pub fn emit_aggregate_pointer_or_pointer_to_scalar_memory(
16        &mut self,
17        aggregate_or_scalar_expr: &Expression,
18        ctx: &Context,
19    ) -> TypedRegister {
20        let gen_key_type = self.state.layout_cache.layout(&aggregate_or_scalar_expr.ty);
21        if gen_key_type.is_aggregate() {
22            self.emit_scalar_rvalue(aggregate_or_scalar_expr, ctx)
23        } else {
24            // for scalar values, we need to materialize in a temp memory
25            let memory_location = self.allocate_frame_space_and_return_memory_location(
26                &gen_key_type,
27                &aggregate_or_scalar_expr.node,
28                "temp space for scalar key",
29            );
30            self.emit_expression_into_target_memory(
31                &memory_location,
32                aggregate_or_scalar_expr,
33                "temp space for scalar",
34                ctx,
35            );
36            memory_location.pointer_location().unwrap().ptr_reg
37        }
38    }
39
40    /// Emits code to evaluate an expression and return a pointer register.
41    ///
42    /// This function handles both regular expressions that can be materialized as scalar rvalues
43    /// and expressions that need temporary memory storage (like initializer lists).
44    ///
45    /// For expressions that need memory materialization:
46    /// - If `allow_temporary` is true: Allocates temporary frame space, initializes it, and returns pointer
47    /// - If `allow_temporary` is false: Falls back to `emit_scalar_rvalue` (may fail for some expressions)
48    ///
49    /// For regular expressions:
50    /// - Uses `emit_scalar_rvalue` to get the pointer directly
51    ///
52    /// # Parameters
53    /// - `expr`: The expression to evaluate
54    /// - `ctx`: Code generation context
55    /// - `allow_temporary`: Whether temporary storage is acceptable (false for mutable iteration)
56    pub fn emit_scalar_rvalue_or_pointer_to_temporary(
57        &mut self,
58        expr: &Expression,
59        ctx: &Context,
60        allow_temporary: bool,
61    ) -> TypedRegister {
62        if allow_temporary
63            && Self::rvalue_needs_memory_location_to_materialize_in(
64                &mut self.state.layout_cache,
65                expr,
66            )
67        {
68            // Expression needs temporary storage (like initializer lists)
69            let expr_basic_type = self.state.layout_cache.layout(&expr.ty);
70            let temp_memory = self.allocate_frame_space_and_return_destination_to_it(
71                &expr_basic_type,
72                &expr.node,
73                "temporary storage for expression that needs memory materialization",
74            );
75
76            // Initialize the temporary memory for collections (vectors, etc.)
77            if let Place::Memory(ref memory_location) = temp_memory {
78                self.emit_initialize_memory_for_any_type(
79                    memory_location,
80                    &expr.node,
81                    "initialize temporary storage for expression",
82                );
83            }
84
85            // Materialize the expression into the temporary memory
86            self.emit_expression(&temp_memory, expr, ctx);
87
88            // Return the pointer to the temporary memory
89            self.emit_compute_effective_address_to_register(
90                &temp_memory,
91                &expr.node,
92                "get pointer to temporary storage",
93            )
94        } else {
95            // Regular case: expression can be materialized as scalar rvalue
96            // Note: This may fail for expressions that need memory materialization
97            // when allow_temporary is false, but that's intentional behavior
98            self.emit_scalar_rvalue(expr, ctx)
99        }
100    }
101
102    /// Emits code to evaluate an expression into a scalar rvalue.
103    ///
104    /// In compiler terminology:
105    /// - "emit" means to generate the machine code
106    /// - "scalar" refers to single-value types (numbers, booleans, pointers) as opposed to aggregates
107    /// - "rvalue" is a value-producing expression that can appear on the right side of an assignment
108    ///
109    /// This method provides an optimized path for scalar values, avoiding the complexity
110    /// of aggregate type handling in the general `emit_expression`. It's particularly
111    /// important for efficient code generation of expressions that must produce values
112    /// (rvalues) rather than storage locations (lvalues).
113    ///
114    /// Basically, for aggregate types, it shouldn't to materialize (copy) the aggregate value
115    /// to a destination, but instead just provide a register pointing to the source location.
116    ///
117    /// # Direct Register Access
118    ///
119    /// The following cases can return a register without materialization:
120    ///
121    /// - Variable access (returns the existing register)
122    /// - Constants (the memory location can be materialized into a register)
123    /// - Some scalar literals (can be materialized directly into a register)
124    ///
125    /// # Register Allocation
126    ///
127    /// For other expressions that can't provide a direct register,
128    /// a temporary register is allocated and the expression is evaluated into it.
129    ///
130    /// # Examples in Compiler Terms
131    ///
132    /// ```ignore
133    /// // Binary operations need rvalues for both operands
134    /// let left_reg = emit_scalar_rvalue(&binary_op.left);   // rvalue needed
135    /// let right_reg = emit_scalar_rvalue(&binary_op.right); // rvalue needed
136    ///
137    /// // Assignment needs an lvalue on left, rvalue on right
138    /// let target = emit_lvalue(&assign.left);        // lvalue needed
139    /// let value = emit_scalar_rvalue(&assign.right); // rvalue needed
140    /// ```
141    pub fn emit_scalar_rvalue(&mut self, expr: &Expression, ctx: &Context) -> TypedRegister {
142        match &expr.kind {
143            ExpressionKind::VariableAccess(variable_ref) => {
144                return self.get_variable_register(variable_ref).clone();
145            }
146            ExpressionKind::ConstantAccess(constant_ref) => {
147                let constant_type = self.state.layout_cache.layout(&constant_ref.resolved_type);
148                if constant_type.is_aggregate() {
149                    let temp_target_reg = self.temp_registers.allocate(
150                        VmType::new_unknown_placement(constant_type),
151                        "temporary for constant access",
152                    );
153                    let constant_gen = self.state.constant_offsets.get(&constant_ref.id).unwrap();
154                    self.builder.add_mov_32_immediate_value(
155                        temp_target_reg.register(),
156                        constant_gen.addr().0,
157                        &expr.node,
158                        "load in the constant pointer",
159                    );
160                    return temp_target_reg.register;
161                }
162            }
163            _ => {}
164        }
165        {
166            let ty = self.state.layout_cache.layout(&expr.ty);
167            let temp_target_reg = self.temp_registers.allocate(
168                VmType::new_unknown_placement(ty),
169                "to produce a rvalue, we have to allocate a temporary variable",
170            );
171
172            self.emit_expression_into_register(
173                temp_target_reg.register(),
174                expr,
175                "emit_rvalue",
176                ctx,
177            );
178
179            temp_target_reg.register
180        }
181    }
182}