swamp_code_gen/
expr.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::{BinaryOperatorKind, Expression, ExpressionKind};
8use swamp_types::TypeKind;
9use swamp_vm_layout::LayoutCache;
10use swamp_vm_types::MemoryLocation;
11use swamp_vm_types::types::{Place, TypedRegister, VmType, int_type};
12
13impl CodeBuilder<'_> {
14    /// The expression materializer! Transforms high-level expressions into their code representation,
15    /// making sure each value finds its proper home in either a register or memory location.
16    ///
17    /// # Optimization Magic
18    ///
19    /// Uses Destination-Passing Style (DPS) to optimize code generation. Instead of creating
20    /// temporary values and copying them around, we tell each expression exactly where its
21    /// result should end up. This means we can often construct values directly in their
22    /// final location, avoiding unnecessary copies and temporary allocations.
23    ///
24    /// # Arguments
25    ///
26    /// * `output` - Where should our expression's result live? (register or memory location)
27    /// * `expr` - The expression we're bringing to life
28    /// * `ctx` - The context with all our compilation knowledge
29    ///
30    /// If something needs temporary storage, we'll handle that too.
31    #[allow(clippy::too_many_lines)]
32    pub fn emit_expression(&mut self, output: &Place, expr: &Expression, ctx: &Context) {
33        let node = &expr.node;
34
35        if self.options.should_show_debug {
36            self.debug_expression(expr, "emitting expression");
37        }
38
39        // Must check for initializer first, since they do not require temporary memory
40        // or similar, should be handled first and then return.
41        match &expr.kind {
42            ExpressionKind::InitializerList(_element_type, expressions) => {
43                self.emit_vec_like_collection_init_from_initialization_list(
44                    output,
45                    expressions,
46                    &expr.node,
47                    ctx,
48                );
49                return;
50            }
51            ExpressionKind::InitializerPairList(_element_type, expressions) => {
52                self.emit_map_like_init_from_initialization_pair_list(
53                    output,
54                    expressions,
55                    &expr.node,
56                    ctx,
57                );
58                return;
59            }
60            _ => {}
61        }
62
63        // If the expression needs a memory target, and the current output is not a memory target, create temp memory to materialize in
64        // and return a pointer in the register instead and hopefully it works out.
65        if !matches!(output, Place::Memory(_))
66            && Self::rvalue_needs_memory_location_to_materialize_in(
67                &mut self.state.layout_cache,
68                expr,
69            )
70        {
71            let expr_basic_type = self.state.layout_cache.layout(&expr.ty);
72            let temp_materialization_target = self
73                .allocate_frame_space_and_return_destination_to_it(
74                    &expr_basic_type,
75                    &expr.node,
76                    "rvalue temporary materialization",
77                );
78
79            self.emit_expression(&temp_materialization_target, expr, ctx);
80
81            self.builder.add_mov_reg(
82                output.grab_register(),
83                &temp_materialization_target
84                    .grab_memory_location()
85                    .base_ptr_reg,
86                node,
87                "copy temp materialization memory pointer reg",
88            );
89
90            return;
91        }
92
93        let hwm = self.temp_registers.save_mark();
94
95        match &expr.kind {
96            &ExpressionKind::InitializerList(_, _) | &ExpressionKind::InitializerPairList(_, _) => {
97                panic!("handled earlier")
98            }
99            ExpressionKind::Error(_) => {
100                return;
101            }
102
103            ExpressionKind::StringLiteral(str) => {
104                self.emit_string_literal(output, node, str, ctx);
105            }
106            ExpressionKind::IntLiteral(int) => match output {
107                Place::Register(target_reg) => {
108                    self.builder.add_mov_32_immediate_value(
109                        target_reg,
110                        *int as u32,
111                        node,
112                        "int literal",
113                    );
114                }
115                Place::Memory(location) => {
116                    let temp_int_literal_reg = self.temp_registers.allocate(
117                        VmType::new_contained_in_register(int_type()),
118                        "temporary for int literal",
119                    );
120                    self.builder.add_mov_32_immediate_value(
121                        temp_int_literal_reg.register(),
122                        *int as u32,
123                        node,
124                        "int literal",
125                    );
126                    self.builder.add_st32_using_ptr_with_offset(
127                        location,
128                        temp_int_literal_reg.register(),
129                        node,
130                        &format!("copy int literal into destination memory {location} <- {temp_int_literal_reg}"),
131                    );
132                }
133                Place::Discard => {
134                    panic!("int can not materialize into nothing")
135                }
136            },
137            ExpressionKind::ByteLiteral(byte) => match output {
138                Place::Register(target_reg) => {
139                    self.builder
140                        .add_mov8_immediate(target_reg, *byte, node, "int literal");
141                }
142                Place::Memory(location) => {
143                    let temp_byte_literal_reg = self.temp_registers.allocate(
144                        VmType::new_contained_in_register(int_type()),
145                        "temporary for byte literal",
146                    );
147                    self.builder.add_mov8_immediate(
148                        temp_byte_literal_reg.register(),
149                        *byte,
150                        node,
151                        "byte literal",
152                    );
153                    self.builder.add_st8_using_ptr_with_offset(
154                        location,
155                        temp_byte_literal_reg.register(),
156                        node,
157                        &format!("copy byte literal into destination memory {location} <- {temp_byte_literal_reg}"),
158                    );
159                }
160                Place::Discard => {
161                    panic!("byte can not materialize into nothing")
162                }
163            },
164            ExpressionKind::FloatLiteral(fixed_point) => match output {
165                Place::Register(target_reg) => {
166                    self.builder.add_mov_32_immediate_value(
167                        target_reg,
168                        fixed_point.inner() as u32,
169                        node,
170                        "float literal",
171                    );
172                }
173                Place::Memory(location) => {
174                    let temp_fixed_point_temp_reg = self.temp_registers.allocate(
175                        VmType::new_contained_in_register(int_type()),
176                        "temporary for float literal",
177                    );
178                    self.builder.add_mov_32_immediate_value(
179                        temp_fixed_point_temp_reg.register(),
180                        fixed_point.inner() as u32,
181                        node,
182                        "float literal",
183                    );
184                    self.builder.add_st32_using_ptr_with_offset(
185                        location,
186                        temp_fixed_point_temp_reg.register(),
187                        node,
188                        "copy float literal into destination memory",
189                    );
190                }
191                Place::Discard => {
192                    panic!("int can not materialize into nothing")
193                }
194            },
195            ExpressionKind::NoneLiteral => {
196                //let union_info = output.ty().unwrap_info().unwrap();
197
198                match output {
199                    Place::Register(target_reg) => {
200                        self.builder
201                            .add_mov8_immediate(target_reg, 0, node, "none literal");
202                    }
203                    Place::Memory(location) => {
204                        let temp_none_literal_reg = self.temp_registers.allocate(
205                            VmType::new_contained_in_register(int_type()),
206                            "temporary for none literal",
207                        );
208                        self.builder.add_mov8_immediate(
209                            temp_none_literal_reg.register(),
210                            0,
211                            node,
212                            "none literal",
213                        );
214
215                        self.builder.add_st8_using_ptr_with_offset(
216                            location,
217                            temp_none_literal_reg.register(),
218                            node,
219                            "copy none literal into destination memory",
220                        );
221                    }
222                    Place::Discard => {
223                        panic!("none can not materialize into nothing")
224                    }
225                }
226            }
227            ExpressionKind::BoolLiteral(truthy) => match output {
228                Place::Register(target_reg) => {
229                    self.builder.add_mov8_immediate(
230                        target_reg,
231                        u8::from(*truthy),
232                        node,
233                        "bool literal",
234                    );
235                }
236                Place::Memory(location) => {
237                    let temp_bool_literal_reg = self.temp_registers.allocate(
238                        VmType::new_contained_in_register(int_type()),
239                        "temporary for bool literal",
240                    );
241                    self.builder.add_mov8_immediate(
242                        temp_bool_literal_reg.register(),
243                        u8::from(*truthy),
244                        node,
245                        "bool literal",
246                    );
247
248                    self.builder.add_st8_using_ptr_with_offset(
249                        location,
250                        temp_bool_literal_reg.register(),
251                        node,
252                        "copy bool literal into destination memory",
253                    );
254                }
255                Place::Discard => {
256                    panic!("int can not materialize into nothing")
257                }
258            },
259
260            ExpressionKind::EnumVariantLiteral(enum_variant, expressions) => {
261                // A enum variant literal can not be represented as a register, not even a pointer to it, it needs materialization into memory
262                self.emit_enum_variant_to_memory_location(
263                    &output.grab_aggregate_memory_location(),
264                    &expr.ty,
265                    enum_variant,
266                    expressions,
267                    node,
268                    ctx,
269                );
270            }
271            ExpressionKind::TupleLiteral(expressions) => {
272                // A tuple literal can not be represented as a register, not even a pointer to it, it needs materialization into memory
273                self.emit_tuple_literal_into_memory(
274                    &output.grab_aggregate_memory_location(),
275                    &expr.ty,
276                    expressions,
277                    ctx,
278                    node,
279                );
280            }
281
282            ExpressionKind::If(condition, true_expression, maybe_false_expression) => {
283                self.emit_if(
284                    output,
285                    condition,
286                    true_expression,
287                    maybe_false_expression.as_deref(),
288                    ctx,
289                );
290            }
291            ExpressionKind::Block(expressions) => {
292                self.emit_block(output, expressions, ctx);
293            }
294            ExpressionKind::NamedStructLiteral(inner) => {
295                self.emit_expression(output, inner, ctx);
296            }
297            ExpressionKind::AnonymousStructLiteral(anon_struct) => {
298                // Literals can not have pointers to them, they need to materialize into a memory location
299                self.emit_anonymous_struct_literal_into_memory_location(
300                    &output.grab_aggregate_memory_location(),
301                    anon_struct,
302                    &expr.ty,
303                    node,
304                    "struct literal",
305                    ctx,
306                );
307            }
308            ExpressionKind::Option(maybe_option) => self
309                .emit_option_expression_into_target_memory_location(
310                    output,
311                    node,
312                    maybe_option.as_deref(),
313                    ctx,
314                ),
315            ExpressionKind::ConstantAccess(constant_ref) => {
316                self.emit_constant_access(output, constant_ref, &expr.node, ctx);
317            }
318            ExpressionKind::VariableAccess(variable_ref) => {
319                let variable_register = self.get_variable_register(variable_ref).clone();
320                let source_destination = Place::Register(variable_register);
321                self.emit_copy_value_between_places(
322                    output,
323                    &source_destination,
324                    node,
325                    &format!(
326                        "copy variable '{}' to destination",
327                        variable_ref.assigned_name
328                    ),
329                );
330            }
331            ExpressionKind::BorrowMutRef(expression) => {
332                self.emit_borrow_mutable_reference(
333                    output.grab_register(),
334                    &expr.node,
335                    expression,
336                    ctx,
337                ); // todo:
338            }
339            ExpressionKind::BinaryOp(operator) => match operator.kind {
340                BinaryOperatorKind::NoneCoalesce => self.emit_none_coalesce_operator(
341                    output,
342                    &operator.left,
343                    &operator.right,
344                    &operator.node,
345                    ctx,
346                ),
347                _ => match output {
348                    Place::Register(reg) => {
349                        self.emit_binary_operator(reg, operator, ctx);
350                    }
351                    Place::Memory(mem_loc) => {
352                        let temp_reg = self
353                            .temp_registers
354                            .allocate(mem_loc.ty.clone(), "binary_op_temp");
355                        self.emit_binary_operator(temp_reg.register(), operator, ctx);
356
357                        self.builder.add_st32_using_ptr_with_offset(
358                            mem_loc,
359                            temp_reg.register(),
360                            node,
361                            "store binary op result directly to memory with offset",
362                        );
363                    }
364                    Place::Discard => {
365                        panic!("binary operator always returns a value")
366                    }
367                },
368            },
369            ExpressionKind::UnaryOp(operator) => match output {
370                Place::Register(reg) => {
371                    self.emit_unary_operator(reg, operator, ctx);
372                }
373                Place::Memory(mem_loc) => {
374                    let temp_reg = self
375                        .temp_registers
376                        .allocate(mem_loc.ty.clone(), "unary_op_temp");
377                    self.emit_unary_operator(temp_reg.register(), operator, ctx);
378
379                    self.builder.add_st32_using_ptr_with_offset(
380                        mem_loc,
381                        temp_reg.register(),
382                        node,
383                        "store unary op result directly to memory with offset",
384                    );
385                }
386                Place::Discard => {
387                    panic!("unary operator always returns a value")
388                }
389            },
390            ExpressionKind::PostfixChain(start, chain) => {
391                self.emit_postfix_chain(output, start, chain, ctx);
392            }
393            ExpressionKind::Match(match_expr) => match &*match_expr.expression.ty.kind {
394                TypeKind::Enum(_enum_type) => {
395                    self.emit_match_enum(output, match_expr, ctx);
396                }
397                _ => {
398                    self.emit_match_literal(output, match_expr, ctx);
399                }
400            },
401            ExpressionKind::Guard(guards) => self.emit_guard(output, guards, ctx),
402            ExpressionKind::When(bindings, true_expr, false_expr) => {
403                self.emit_when(output, bindings, true_expr, false_expr.as_deref(), ctx);
404            }
405            ExpressionKind::IntrinsicCallEx(intrinsic_fn, arguments) => {
406                self.emit_single_intrinsic_call(output, &expr.node, intrinsic_fn, arguments, ctx);
407            }
408            ExpressionKind::CoerceToAny(a) => {
409                self.emit_coerce_to_any(output, a, ctx);
410            }
411            ExpressionKind::CoerceOptionToBool(a) => {
412                self.emit_coerce_option_to_bool(output.grab_register(), a, ctx);
413            }
414            ExpressionKind::CoerceIntToChar(a) => {
415                self.emit_coerce_int_to_char(output.grab_register(), a, ctx);
416            }
417            ExpressionKind::CoerceIntToByte(a) => {
418                self.emit_coerce_int_to_byte(output, a, ctx);
419            }
420            ExpressionKind::InternalCall(internal, arguments) => {
421                self.emit_internal_call(output, &expr.node, internal, arguments, ctx);
422            }
423            ExpressionKind::HostCall(host_fn, arguments) => {
424                self.emit_host_call(output, &expr.node, host_fn, arguments, ctx);
425            }
426
427            // Statements - can not return anything, so should assert that output is unit (nothing)
428            ExpressionKind::TupleDestructuring(variables, tuple_type, tuple_expression) => {
429                debug_assert!(output.is_unit());
430                self.emit_tuple_destructuring(variables, tuple_type, tuple_expression, ctx);
431            }
432            ExpressionKind::Assignment(target_mut_location_expr, source_expr) => {
433                debug_assert!(output.is_unit());
434                self.emit_assignment(target_mut_location_expr, source_expr, "", ctx);
435            }
436            ExpressionKind::VariableDefinition(variable, expression) => {
437                debug_assert!(output.is_unit());
438                self.emit_variable_definition(variable, expression, ctx);
439            }
440            ExpressionKind::VariableDefinitionLValue(variable, location_expr) => {
441                // For lvalue definitions, the variable should be initialized with the memory address
442                // This is used in `with` statements, e.g. `mut var = &some.field`
443                match output {
444                    Place::Discard => {
445                        let lvalue_destination = self.emit_lvalue_address(location_expr, ctx);
446
447                        let alias_register = self.get_variable_register(variable).clone();
448
449                        self.emit_compute_effective_address_to_target_register(
450                            &alias_register,
451                            &lvalue_destination,
452                            &variable.name,
453                            "initialize alias variable with lvalue address",
454                        );
455                    }
456                    _ => {
457                        panic!("VariableDefinitionLValue should only be used with Unit destination")
458                    }
459                }
460            }
461            ExpressionKind::VariableReassignment(variable, expression) => {
462                assert!(output.is_unit());
463                self.emit_variable_reassignment(variable, expression, ctx);
464            }
465            ExpressionKind::CompoundAssignment(target_location, operator_kind, source_expr) => {
466                assert!(output.is_unit());
467                self.emit_compound_assignment(target_location, operator_kind, source_expr, ctx);
468            }
469            ExpressionKind::ForLoop(for_pattern, collection, lambda_expr) => {
470                if !output.is_unit() {
471                    let x = 3;
472                    eprintln!("problem");
473                }
474                assert!(output.is_unit());
475                self.emit_for_loop(
476                    output,
477                    &expr.node,
478                    for_pattern,
479                    collection,
480                    lambda_expr,
481                    ctx,
482                );
483            }
484            ExpressionKind::WhileLoop(condition, expression) => {
485                assert!(output.is_unit());
486                self.emit_while_loop(condition, expression, ctx);
487            }
488
489            // Illegal
490            ExpressionKind::Lambda(_vec, _x) => {
491                panic!("something went wrong. non-capturing lambdas can not be evaluated")
492            }
493        }
494
495        self.temp_registers.restore_to_mark(hwm);
496    }
497
498    pub(crate) fn rvalue_needs_memory_location_to_materialize_in(
499        layout_cache: &mut LayoutCache,
500        expr: &Expression,
501    ) -> bool {
502        let specific_kind_of_expression_needs_memory_target = match &expr.kind {
503            // TODO: Should have more robust check here. maybe check primitives instead and invert?
504            ExpressionKind::EnumVariantLiteral(_, _)
505            | ExpressionKind::TupleLiteral(_)
506            | ExpressionKind::InitializerList(_, _)
507            | ExpressionKind::InitializerPairList(_, _) => true,
508            ExpressionKind::Option(_)
509            | ExpressionKind::AnonymousStructLiteral(_)
510            | ExpressionKind::CoerceToAny(_) => true,
511            _ => false,
512        };
513
514        if specific_kind_of_expression_needs_memory_target {
515            true
516        } else {
517            // Easy to forget that you should also check if it's a function call with a return type requiring memory allocation
518            match &expr.kind {
519                ExpressionKind::InternalCall(_, _)
520                | ExpressionKind::HostCall(_, _)
521                | ExpressionKind::IntrinsicCallEx(_, _) => {
522                    let basic_type = layout_cache.layout(&expr.ty);
523                    !basic_type.is_reg_copy()
524                }
525                _ => false,
526            }
527        }
528    }
529
530    pub(crate) fn emit_expression_into_target_memory(
531        &mut self,
532        memory_location: &MemoryLocation,
533        expr: &Expression,
534        comment: &str,
535        ctx: &Context,
536    ) {
537        // Only initialize if it's a direct collection type (not inside a tuple/struct)
538        if matches!(
539            expr.kind,
540            ExpressionKind::InitializerList(_, _) | ExpressionKind::InitializerPairList(_, _)
541        ) {
542            self.emit_initialize_memory_for_any_type(
543                memory_location,
544                &expr.node,
545                &format!("{comment} - initialize memory for collection"),
546            );
547        }
548
549        let output = Place::new_location(memory_location.clone());
550        self.emit_expression(&output, expr, ctx);
551    }
552
553    pub(crate) fn emit_expression_into_register(
554        &mut self,
555        target_register: &TypedRegister,
556        expr: &Expression,
557        comment: &str,
558        ctx: &Context,
559    ) {
560        let output = Place::new_reg(target_register.clone());
561
562        self.emit_expression(&output, expr, ctx);
563    }
564}