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::{info, 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                if !output.is_register() {
335                    self.debug_expression(expr, "emitting expression");
336                }
337                self.emit_borrow_mutable_reference(
338                    output.grab_register(),
339                    &expr.node,
340                    expression,
341                    ctx,
342                ); // todo:
343            }
344            ExpressionKind::BinaryOp(operator) => match operator.kind {
345                BinaryOperatorKind::NoneCoalesce => self.emit_none_coalesce_operator(
346                    output,
347                    &operator.left,
348                    &operator.right,
349                    &operator.node,
350                    ctx,
351                ),
352                _ => match output {
353                    Place::Register(reg) => {
354                        self.emit_binary_operator(reg, operator, ctx);
355                    }
356                    Place::Memory(mem_loc) => {
357                        let temp_reg = self
358                            .temp_registers
359                            .allocate(mem_loc.ty.clone(), "binary_op_temp");
360                        self.emit_binary_operator(temp_reg.register(), operator, ctx);
361
362                        self.builder.add_st32_using_ptr_with_offset(
363                            mem_loc,
364                            temp_reg.register(),
365                            node,
366                            "store binary op result directly to memory with offset",
367                        );
368                    }
369                    Place::Discard => {
370                        panic!("binary operator always returns a value")
371                    }
372                },
373            },
374            ExpressionKind::UnaryOp(operator) => match output {
375                Place::Register(reg) => {
376                    self.emit_unary_operator(reg, operator, ctx);
377                }
378                Place::Memory(mem_loc) => {
379                    let temp_reg = self
380                        .temp_registers
381                        .allocate(mem_loc.ty.clone(), "unary_op_temp");
382                    self.emit_unary_operator(temp_reg.register(), operator, ctx);
383
384                    self.builder.add_st32_using_ptr_with_offset(
385                        mem_loc,
386                        temp_reg.register(),
387                        node,
388                        "store unary op result directly to memory with offset",
389                    );
390                }
391                Place::Discard => {
392                    warn!("unary operator always returns a value")
393                }
394            },
395            ExpressionKind::PostfixChain(start, chain) => {
396                self.emit_postfix_chain(output, start, chain, ctx);
397            }
398            ExpressionKind::Match(match_expr) => match &*match_expr.expression.ty.kind {
399                TypeKind::Enum(_enum_type) => {
400                    self.emit_match_enum(output, match_expr, ctx);
401                }
402                _ => {
403                    self.emit_match_literal(output, match_expr, ctx);
404                }
405            },
406            ExpressionKind::Guard(guards) => self.emit_guard(output, guards, ctx),
407            ExpressionKind::When(bindings, true_expr, false_expr) => {
408                self.emit_when(output, bindings, true_expr, false_expr.as_deref(), ctx);
409            }
410            ExpressionKind::IntrinsicCallEx(intrinsic_fn, arguments) => {
411                self.emit_single_intrinsic_call(output, &expr.node, intrinsic_fn, arguments, ctx);
412            }
413            ExpressionKind::CoerceToAny(a) => {
414                self.emit_coerce_to_any(output, a, ctx);
415            }
416            ExpressionKind::CoerceOptionToBool(a) => {
417                self.emit_coerce_option_to_bool(output.grab_register(), a, ctx);
418            }
419            ExpressionKind::CoerceIntToChar(a) => {
420                self.emit_coerce_int_to_char(output.grab_register(), a, ctx);
421            }
422            ExpressionKind::CoerceIntToByte(a) => {
423                self.emit_coerce_int_to_byte(output, a, ctx);
424            }
425            ExpressionKind::CoerceIntToShort(a) => {
426                self.emit_coerce_int_to_short(output, a, ctx);
427            }
428            ExpressionKind::CoerceIntToPointer(a) => {
429                todo!("not supporting pointers yet");
430            }
431            ExpressionKind::InternalCall(internal, arguments) => {
432                self.emit_internal_call(output, &expr.node, internal, arguments, ctx);
433            }
434            ExpressionKind::HostCall(host_fn, arguments) => {
435                self.emit_host_call(output, &expr.node, host_fn, arguments, ctx);
436            }
437
438            // Statements - can not return anything, so should assert that output is unit (nothing)
439            ExpressionKind::TupleDestructuring(variables, tuple_type, tuple_expression) => {
440                debug_assert!(output.is_unit());
441                self.emit_tuple_destructuring(variables, tuple_type, tuple_expression, ctx);
442            }
443            ExpressionKind::Assignment(target_mut_location_expr, source_expr) => {
444                debug_assert!(output.is_unit());
445                self.emit_assignment(target_mut_location_expr, source_expr, "", ctx);
446            }
447            ExpressionKind::VariableDefinition(variable, expression) => {
448                debug_assert!(output.is_unit());
449                self.emit_variable_definition(variable, expression, ctx);
450            }
451            ExpressionKind::VariableDefinitionLValue(variable, location_expr) => {
452                // For lvalue definitions, the variable should be initialized with the memory address
453                // This is used in `with` statements, e.g. `mut var = &some.field`
454                match output {
455                    Place::Discard => {
456                        let lvalue_destination = self.emit_lvalue_address(location_expr, ctx);
457
458                        let alias_register = self.get_variable_register(variable).clone();
459
460                        self.emit_compute_effective_address_to_target_register(
461                            &alias_register,
462                            &lvalue_destination,
463                            &variable.name,
464                            "initialize alias variable with lvalue address",
465                        );
466                    }
467                    _ => {
468                        panic!("VariableDefinitionLValue should only be used with Unit destination")
469                    }
470                }
471            }
472            ExpressionKind::VariableReassignment(variable, expression) => {
473                assert!(output.is_unit());
474                self.emit_variable_reassignment(variable, expression, ctx);
475            }
476            ExpressionKind::CompoundAssignment(target_location, operator_kind, source_expr) => {
477                assert!(output.is_unit());
478                self.emit_compound_assignment(target_location, operator_kind, source_expr, ctx);
479            }
480            ExpressionKind::ForLoop(for_pattern, collection, lambda_expr) => {
481                if !output.is_unit() {
482                    let x = 3;
483                    eprintln!("problem");
484                }
485                assert!(output.is_unit());
486                self.emit_for_loop(
487                    output,
488                    &expr.node,
489                    for_pattern,
490                    collection,
491                    lambda_expr,
492                    ctx,
493                );
494            }
495            ExpressionKind::WhileLoop(condition, expression) => {
496                assert!(output.is_unit());
497                self.emit_while_loop(condition, expression, ctx);
498            }
499
500            // Illegal
501            ExpressionKind::Lambda(_vec, _x) => {
502                panic!("something went wrong. non-capturing lambdas can not be evaluated")
503            }
504        }
505
506        self.temp_registers.restore_to_mark(hwm);
507    }
508
509    pub(crate) fn rvalue_needs_memory_location_to_materialize_in(
510        layout_cache: &mut LayoutCache,
511        expr: &Expression,
512    ) -> bool {
513        // TODO: Should have more robust check here
514        match &expr.kind {
515            // Why does it break if we check for is_reg_copy/scalar for these?
516            ExpressionKind::EnumVariantLiteral(_, _)
517            | ExpressionKind::TupleLiteral(_)
518            | ExpressionKind::InitializerList(_, _)
519            | ExpressionKind::InitializerPairList(_, _)
520            | ExpressionKind::Option(_)
521            | ExpressionKind::AnonymousStructLiteral(_)
522            | ExpressionKind::CoerceToAny(_) => true,
523
524            ExpressionKind::InternalCall(_, _)
525            | ExpressionKind::HostCall(_, _)
526            | ExpressionKind::IntrinsicCallEx(_, _) => {
527                let basic_type = layout_cache.layout(&expr.ty);
528                !basic_type.is_reg_copy()
529            }
530
531            ExpressionKind::PostfixChain(_chain, postfixes) => {
532                // TODO: this seems to work, but it feels all kind of wrong
533                let last_is_member_call = matches!(
534                    postfixes[postfixes.len() - 1].kind,
535                    PostfixKind::MemberCall(..)
536                );
537                let basic_type = layout_cache.layout(&expr.ty);
538                last_is_member_call && !basic_type.is_reg_copy()
539            }
540
541            _ => false,
542        }
543    }
544
545    pub(crate) fn emit_expression_into_target_memory(
546        &mut self,
547        memory_location: &MemoryLocation,
548        expr: &Expression,
549        comment: &str,
550        ctx: &Context,
551    ) {
552        // Only initialize if it's a direct collection type (not inside a tuple/struct)
553        if matches!(
554            expr.kind,
555            ExpressionKind::InitializerList(_, _) | ExpressionKind::InitializerPairList(_, _)
556        ) {
557            self.emit_initialize_memory_for_any_type(
558                memory_location,
559                &expr.node,
560                &format!("{comment} - initialize memory for collection"),
561            );
562        }
563
564        let output = Place::new_location(memory_location.clone());
565        self.emit_expression(&output, expr, ctx);
566    }
567
568    pub(crate) fn emit_expression_into_register(
569        &mut self,
570        target_register: &TypedRegister,
571        expr: &Expression,
572        comment: &str,
573        ctx: &Context,
574    ) {
575        let output = Place::new_reg(target_register.clone());
576
577        self.emit_expression(&output, expr, &ctx.clone().with_comment(comment));
578    }
579}