swamp_code_gen/
logical.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 */
5//! Logical helper functions for the code gen emitter
6use crate::code_bld::CodeBuilder;
7use crate::ctx::Context;
8use crate::{FlagState, FlagStateKind};
9use source_map_node::Node;
10use swamp_semantic::{
11    BinaryOperator, BinaryOperatorKind, BooleanExpression, Expression, ExpressionKind,
12    UnaryOperator, UnaryOperatorKind,
13};
14use swamp_types::TypeKind;
15use swamp_vm_types::PatchPosition;
16use swamp_vm_types::types::{Place, TypedRegister, VmType, b8_type};
17
18impl CodeBuilder<'_> {
19    pub(crate) fn force_normalized_bool_reg_if_needed(
20        &mut self,
21        target: &TypedRegister,
22        t_flag_state: FlagState,
23        node: &Node,
24    ) {
25        match t_flag_state.kind {
26            FlagStateKind::TFlagIsIndeterminate => {
27                // intentionally do nothing
28            }
29            FlagStateKind::TFlagIsTrueWhenSet => {
30                // self.builder
31                //   .add_stz(target, node, "materialize positive P flag");
32            }
33            FlagStateKind::TFlagIsTrueWhenClear => {
34                self.builder
35                    .add_meqz(target, target, node, "materialize inverse boolean");
36            }
37        }
38    }
39    pub fn emit_unary_operator_logical(
40        &mut self,
41        dest_bool_reg: &TypedRegister,
42        unary_operator: &UnaryOperator,
43        ctx: &Context,
44    ) -> FlagState {
45        match &unary_operator.kind {
46            UnaryOperatorKind::Not => match &*unary_operator.left.ty.kind {
47                TypeKind::Bool => {
48                    let bool_result =
49                        self.emit_expression_to_boolean(dest_bool_reg, &unary_operator.left, ctx);
50                    bool_result.invert_polarity()
51                }
52                _ => panic!("unknown not"),
53            },
54            _ => panic!("unary operator do not provide P flag"),
55        }
56    }
57
58    pub(crate) fn emit_condition_context(
59        &mut self,
60        condition: &BooleanExpression,
61        ctx: &Context,
62    ) -> PatchPosition {
63        let hwm = self.temp_registers.save_mark();
64        let temp_truth_reg = self.temp_registers.allocate(
65            VmType::new_contained_in_register(b8_type()),
66            "true-register for condition",
67        );
68        let result =
69            self.emit_expression_to_boolean(temp_truth_reg.register(), &condition.expression, ctx);
70
71        let patch = self.builder.add_jmp_if_not_equal_polarity_placeholder(
72            temp_truth_reg.register(),
73            &result.polarity(),
74            &condition.expression.node,
75            "jump boolean condition false",
76        );
77
78        self.temp_registers.restore_to_mark(hwm);
79
80        patch
81    }
82
83    pub(crate) fn emit_expression_to_boolean(
84        &mut self,
85        dest_bool_reg: &TypedRegister,
86        condition: &Expression,
87        ctx: &Context,
88    ) -> FlagState {
89        match &condition.kind {
90            ExpressionKind::CoerceOptionToBool(option_union_expr) => {
91                let region = self.emit_scalar_rvalue(option_union_expr, ctx);
92
93                let tag_reg = self.temp_registers.allocate(
94                    VmType::new_unknown_placement(swamp_vm_types::types::b8_type()),
95                    "temp for option tag",
96                );
97
98                let (tag_offset, ..) = region.ty.basic_type.unwrap_info().unwrap();
99                self.builder.add_ld8_from_pointer_with_offset(
100                    tag_reg.register(),
101                    &region,
102                    tag_offset,
103                    &option_union_expr.node,
104                    "load option tag",
105                );
106
107                self.builder.add_mov_reg(
108                    dest_bool_reg,
109                    tag_reg.register(),
110                    &option_union_expr.node,
111                    "test option tag",
112                );
113
114                return FlagState {
115                    kind: FlagStateKind::TFlagIsTrueWhenSet,
116                };
117            }
118            ExpressionKind::BinaryOp(operator) => match &operator.kind {
119                BinaryOperatorKind::LogicalAnd | BinaryOperatorKind::LogicalOr => {
120                    return self.emit_binary_operator_logical_to_boolean(
121                        dest_bool_reg,
122                        operator,
123                        ctx,
124                    );
125                }
126                BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => {
127                    let left = self.emit_scalar_rvalue(&operator.left, ctx);
128                    let right = self.emit_scalar_rvalue(&operator.right, ctx);
129                    let is_equal_polarity = matches!(operator.kind, BinaryOperatorKind::Equal);
130                    return self.emit_equality_to_bool_target(
131                        dest_bool_reg,
132                        &left,
133                        is_equal_polarity,
134                        &right,
135                        &operator.node,
136                        ctx,
137                    );
138                }
139                BinaryOperatorKind::GreaterEqual
140                | BinaryOperatorKind::GreaterThan
141                | BinaryOperatorKind::LessThan
142                | BinaryOperatorKind::LessEqual => {
143                    let left_source = self.emit_scalar_rvalue(&operator.left, ctx);
144                    let right_source = self.emit_scalar_rvalue(&operator.right, ctx);
145
146                    return self.emit_binary_operator_relational(
147                        dest_bool_reg,
148                        &left_source,
149                        operator,
150                        &right_source,
151                    );
152                }
153                _ => panic!(
154                    "binary operator does not provide us with P flag {:?}",
155                    condition.kind
156                ),
157            },
158            ExpressionKind::UnaryOp(operator) => match &operator.kind {
159                UnaryOperatorKind::Not => {
160                    return self.emit_unary_operator_logical(dest_bool_reg, operator, ctx);
161                }
162                _ => panic!("unary operator does not provide us with P flag"),
163            },
164            _ => {
165                let destination = Place::Register(dest_bool_reg.clone());
166                self.emit_expression(&destination, condition, ctx);
167            }
168        }
169
170        FlagState {
171            kind: FlagStateKind::TFlagIsTrueWhenSet,
172        }
173    }
174
175    fn emit_expression_to_normalized_t_flag(
176        &mut self,
177        dest_bool_reg: &TypedRegister,
178        condition: &Expression,
179        ctx: &Context,
180    ) {
181        let result = self.emit_expression_to_boolean(dest_bool_reg, condition, ctx);
182        assert_ne!(result.kind, FlagStateKind::TFlagIsIndeterminate);
183
184        if result.kind == FlagStateKind::TFlagIsTrueWhenClear {
185            self.builder.add_meqz(
186                dest_bool_reg,
187                dest_bool_reg,
188                &condition.node,
189                "normalized z is required",
190            );
191        }
192    }
193    pub(crate) fn emit_binary_operator_logical_to_boolean(
194        &mut self,
195        dest_bool_reg: &TypedRegister,
196        binary_operator: &BinaryOperator,
197        context: &Context,
198    ) -> FlagState {
199        let node = &binary_operator.node;
200
201        // the logical is always normalized
202        let kind = FlagStateKind::TFlagIsTrueWhenSet;
203
204        match binary_operator.kind {
205            BinaryOperatorKind::LogicalOr => {
206                self.emit_expression_to_normalized_t_flag(
207                    dest_bool_reg,
208                    &binary_operator.left,
209                    context,
210                );
211
212                let jump_after_patch = self.builder.add_jmp_if_true_placeholder(
213                    dest_bool_reg,
214                    node,
215                    "OR: skip rhs because lhs is true",
216                );
217
218                self.emit_expression_to_normalized_t_flag(
219                    dest_bool_reg,
220                    &binary_operator.right,
221                    context,
222                );
223
224                self.builder.patch_jump_here(jump_after_patch);
225            }
226            BinaryOperatorKind::LogicalAnd => {
227                self.emit_expression_to_normalized_t_flag(
228                    dest_bool_reg,
229                    &binary_operator.left,
230                    context,
231                );
232
233                let jump_after_patch = self.builder.add_jmp_if_not_true_placeholder(
234                    dest_bool_reg,
235                    node,
236                    "AND: skip rhs because lhs is false",
237                );
238
239                self.emit_expression_to_normalized_t_flag(
240                    dest_bool_reg,
241                    &binary_operator.right,
242                    context,
243                );
244
245                self.builder.patch_jump_here(jump_after_patch);
246            }
247
248            _ => {
249                panic!("unknown operator {binary_operator:?}");
250            }
251        }
252
253        FlagState { kind }
254    }
255}