use crate::FlagStateKind;
use crate::code_bld::CodeBuilder;
use crate::ctx::Context;
use source_map_node::Node;
use swamp_semantic::{BinaryOperator, BinaryOperatorKind, Expression};
use swamp_types::TypeKind;
use swamp_vm_types::AggregateMemoryLocation;
use swamp_vm_types::types::{Place, TypedRegister, VmType, u8_type};
impl CodeBuilder<'_> {
pub(crate) fn emit_binary_operator(
&mut self,
target_reg: &TypedRegister,
binary_operator: &BinaryOperator,
ctx: &Context,
) {
match &binary_operator.kind {
BinaryOperatorKind::LogicalOr | BinaryOperatorKind::LogicalAnd => {
let t_flag_result =
self.emit_binary_operator_logical_to_boolean(target_reg, binary_operator, ctx);
self.force_normalized_bool_reg_if_needed(
target_reg,
t_flag_result,
&binary_operator.node,
);
}
_ => self.emit_binary_operator_normal(target_reg, binary_operator, ctx),
}
}
fn emit_binary_operator_normal(
&mut self,
dest_bool_reg: &TypedRegister,
binary_operator: &BinaryOperator,
ctx: &Context,
) {
let hwm = self.temp_registers.save_mark();
let left_source = self.emit_scalar_rvalue(&binary_operator.left, ctx);
let right_source = self.emit_scalar_rvalue(&binary_operator.right, ctx);
match &binary_operator.kind {
BinaryOperatorKind::NoneCoalesce => {
panic!("handled elsewhere")
}
BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => {
let is_equal_polarity = matches!(binary_operator.kind, BinaryOperatorKind::Equal);
let t_flag = self.emit_equality_to_bool_target(
dest_bool_reg,
&left_source,
is_equal_polarity,
&right_source,
&binary_operator.node,
ctx,
);
self.force_normalized_bool_reg_if_needed(
dest_bool_reg,
t_flag,
&binary_operator.node,
);
}
BinaryOperatorKind::GreaterEqual
| BinaryOperatorKind::GreaterThan
| BinaryOperatorKind::LessThan
| BinaryOperatorKind::LessEqual => {
let t_flag = self.emit_binary_operator_relational(
dest_bool_reg,
&left_source,
binary_operator,
&right_source,
);
self.force_normalized_bool_reg_if_needed(
dest_bool_reg,
t_flag,
&binary_operator.node,
);
}
_ => match (
&*binary_operator.left.ty.kind,
&*binary_operator.right.ty.kind,
) {
(TypeKind::Int, TypeKind::Int) => self.emit_binary_operator_i32(
dest_bool_reg,
&left_source,
&binary_operator.node,
&binary_operator.kind,
&right_source,
ctx,
),
(TypeKind::Float, TypeKind::Float) => self.emit_binary_operator_f32(
dest_bool_reg,
&left_source,
&binary_operator.node,
&binary_operator.kind,
&right_source,
ctx,
),
(
TypeKind::StringView(..) | TypeKind::StringStorage(..),
TypeKind::StringView(..) | TypeKind::StringStorage(..),
) => self.emit_binary_operator_string(
dest_bool_reg,
&left_source,
&binary_operator.node,
&binary_operator.kind,
&right_source,
ctx,
),
(TypeKind::StringView(..) | TypeKind::StringStorage(..), TypeKind::Int) => self
.emit_binary_operator_string(
dest_bool_reg,
&left_source,
&binary_operator.node,
&binary_operator.kind,
&right_source,
ctx,
),
_ => todo!(),
},
}
self.temp_registers.restore_to_mark(hwm);
}
fn emit_binary_operator_i32(
&mut self,
target_reg: &TypedRegister,
left_source: &TypedRegister,
node: &Node,
binary_operator_kind: &BinaryOperatorKind,
right_source: &TypedRegister,
ctx: &Context,
) {
match binary_operator_kind {
BinaryOperatorKind::Add => {
self.builder.add_add_u32(
target_reg,
left_source,
right_source,
node,
"i32 add",
);
}
BinaryOperatorKind::Subtract => {
self.builder.add_sub_u32(
target_reg,
left_source,
right_source,
node,
&format!("i32 sub {target_reg:?} = {left_source:?} - {right_source:?}"),
);
}
BinaryOperatorKind::Multiply => {
self.builder
.add_mul_i32(target_reg, left_source, right_source, node, "i32 mul");
}
BinaryOperatorKind::Divide => {
self.builder
.add_div_i32(target_reg, left_source, right_source, node, "i32 div");
}
BinaryOperatorKind::Modulo => {
self.builder
.add_mod_i32(target_reg, left_source, right_source, node, "i32 mod");
}
BinaryOperatorKind::LogicalOr => todo!(),
BinaryOperatorKind::LogicalAnd => todo!(),
BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => todo!(),
_ => todo!(),
}
}
#[allow(clippy::unnecessary_wraps)]
fn emit_binary_operator_f32(
&mut self,
target_reg: &TypedRegister,
left_source: &TypedRegister,
node: &Node,
binary_operator_kind: &BinaryOperatorKind,
right_source: &TypedRegister,
ctx: &Context,
) {
let kind = FlagStateKind::TFlagIsIndeterminate;
match binary_operator_kind {
BinaryOperatorKind::Add => {
self.builder
.add_add_f32(target_reg, left_source, right_source, node, "f32 add");
}
BinaryOperatorKind::Subtract => {
self.builder
.add_sub_f32(target_reg, left_source, right_source, node, "f32 sub");
}
BinaryOperatorKind::Multiply => {
self.builder
.add_mul_f32(target_reg, left_source, right_source, node, "f32 mul");
}
BinaryOperatorKind::Divide => {
self.builder
.add_div_f32(target_reg, left_source, right_source, node, "f32 div");
}
BinaryOperatorKind::Modulo => {
self.builder
.add_mod_i32(target_reg, left_source, right_source, node, "f32 mod");
}
BinaryOperatorKind::LogicalOr => panic!("not supported"),
BinaryOperatorKind::LogicalAnd => panic!("not supported"),
BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => panic!("handled elsewhere"),
_ => panic!("unknown"),
}
}
fn emit_binary_operator_string(
&mut self,
target_reg: &TypedRegister,
left_source: &TypedRegister,
node: &Node,
binary_operator_kind: &BinaryOperatorKind,
right_source: &TypedRegister,
ctx: &Context,
) {
match binary_operator_kind {
BinaryOperatorKind::Add => {
self.builder.add_string_append(
target_reg,
left_source,
right_source,
node,
"string append",
);
}
BinaryOperatorKind::Multiply => {
self.builder.add_string_multiply(
target_reg,
left_source,
right_source,
node,
"string multiply",
);
}
BinaryOperatorKind::Equal => todo!(),
BinaryOperatorKind::NotEqual => todo!(),
_ => panic!("illegal string operator"),
}
}
pub(crate) fn emit_none_coalesce_operator(
&mut self,
dest: &Place,
left: &Expression,
right: &Expression,
node: &Node,
ctx: &Context,
) {
let both_are_optionals = matches!(&*left.ty.kind, TypeKind::Optional(_))
&& matches!(&*right.ty.kind, TypeKind::Optional(_));
let left_optional_basic_type = self.state.layout_cache.layout(&left.ty);
let (tag_offset, _, payload_offset, _) = left_optional_basic_type.unwrap_info().unwrap();
let lhs_destination_storage_for_optional: Place; let destination_to_use_for_left_side = if both_are_optionals {
dest
} else {
lhs_destination_storage_for_optional = self
.allocate_frame_space_and_return_destination_to_it(
&left_optional_basic_type,
false,
node,
"?? left temp storage for tag + payload",
);
let left_payload_type_some_variant = left_optional_basic_type.get_variant(1);
if left_payload_type_some_variant.ty.is_aggregate() {
let lhs_memory_location = lhs_destination_storage_for_optional
.memory_location()
.unwrap();
let lhs_aggregate_pointer =
AggregateMemoryLocation::new(lhs_memory_location.clone());
let payload_memory_location = lhs_aggregate_pointer
.offset(payload_offset, left_payload_type_some_variant.ty.clone());
self.emit_initialize_memory_for_any_type(
&payload_memory_location.location,
node,
"initialize lhs temporary optional payload",
);
}
&lhs_destination_storage_for_optional
};
self.emit_expression(destination_to_use_for_left_side, left, ctx);
let tag_location = destination_to_use_for_left_side
.add_offset(tag_offset, VmType::new_contained_in_register(u8_type()));
let tag_memory_location = tag_location.memory_location().unwrap();
let temp_tag_reg = self.temp_registers.allocate(
VmType::new_contained_in_register(u8_type()),
"?? temporary tag register",
);
self.builder.add_ld8_from_pointer_with_offset(
temp_tag_reg.register(),
tag_memory_location.reg(),
tag_memory_location.offset,
node,
"?? load temporary register from tag_memory",
);
let jump_if_none = self.builder.add_jmp_if_not_true_placeholder(
&temp_tag_reg.register,
node,
"?? jump if None ",
);
if both_are_optionals {
} else {
let some_payload_basic_type = left_optional_basic_type.get_variant(1);
let payload_source_location = destination_to_use_for_left_side.add_offset(
payload_offset,
VmType::new_unknown_placement(some_payload_basic_type.ty.clone()),
);
self.emit_copy_value_between_places(dest, &payload_source_location, node, "?? right hand side is NOT optional. must copy from payload area to output destination. unwrap was needed because of different types");
}
let jump_to_after_whole_thing = self
.builder
.add_jump_placeholder(node, "jump over payload ");
self.builder.patch_jump_here(jump_if_none);
self.emit_expression(dest, right, ctx);
self.builder.patch_jump_here(jump_to_after_whole_thing);
}
}