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