use super::super::{CodeGen, CodeGenError, VirtualValue};
use std::fmt::Write as _;
impl CodeGen {
pub(in crate::codegen) fn codegen_inline_comparison(
&mut self,
stack_var: &str,
icmp_op: &str,
) -> Result<Option<String>, CodeGenError> {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let (ptr_a, val_a, val_b) = self.emit_load_two_int_operands(stack_var)?;
let cmp_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp {} i64 %{}, %{}",
cmp_result, icmp_op, val_a, val_b
)?;
let zext = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = zext i1 %{} to i64",
zext, cmp_result
)?;
self.emit_store_bool(&ptr_a, &zext)?;
let result_var = self.emit_stack_gep(stack_var, -1)?;
Ok(Some(result_var))
}
pub(in crate::codegen) fn codegen_inline_binary_op(
&mut self,
stack_var: &str,
llvm_op: &str,
_adjust_op: &str, ) -> Result<Option<String>, CodeGenError> {
if self.virtual_stack.len() >= 2
&& let Some(result) = self.codegen_binary_op_virtual(stack_var, llvm_op)?
{
return Ok(Some(result));
}
self.codegen_binary_op_memory(stack_var, llvm_op)
}
pub(in crate::codegen) fn codegen_binary_op_virtual(
&mut self,
stack_var: &str,
llvm_op: &str,
) -> Result<Option<String>, CodeGenError> {
let val_b = self.virtual_stack.pop().unwrap();
let val_a = self.virtual_stack.pop().unwrap();
let (ssa_a, ssa_b) = match (&val_a, &val_b) {
(VirtualValue::Int { ssa_var: a, .. }, VirtualValue::Int { ssa_var: b, .. }) => {
(a.clone(), b.clone())
}
_ => {
self.virtual_stack.push(val_a);
self.virtual_stack.push(val_b);
return Ok(None);
}
};
let op_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = {} i64 %{}, %{}",
op_result, llvm_op, ssa_a, ssa_b
)?;
let result = VirtualValue::Int {
ssa_var: op_result,
value: 0, };
Ok(Some(self.push_virtual(result, stack_var)?))
}
pub(in crate::codegen) fn codegen_binary_op_memory(
&mut self,
stack_var: &str,
llvm_op: &str,
) -> Result<Option<String>, CodeGenError> {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let (ptr_a, val_a, val_b) = self.emit_load_two_int_operands(stack_var)?;
let op_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = {} i64 %{}, %{}",
op_result, llvm_op, val_a, val_b
)?;
self.emit_store_int_result_in_place(&ptr_a, &op_result)?;
let result_var = self.emit_stack_gep(stack_var, -1)?;
Ok(Some(result_var))
}
pub(in crate::codegen) fn codegen_inline_float_binary_op(
&mut self,
_stack_var: &str,
_llvm_op: &str,
) -> Result<Option<String>, CodeGenError> {
Ok(None)
}
pub(in crate::codegen) fn codegen_inline_float_comparison(
&mut self,
_stack_var: &str,
_fcmp_op: &str,
) -> Result<Option<String>, CodeGenError> {
Ok(None)
}
pub(in crate::codegen) fn codegen_inline_int_bitwise_binary(
&mut self,
stack_var: &str,
llvm_op: &str, ) -> Result<Option<String>, CodeGenError> {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let (ptr_a, val_a, val_b) = self.emit_load_two_int_operands(stack_var)?;
let op_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = {} i64 %{}, %{}",
op_result, llvm_op, val_a, val_b
)?;
self.emit_store_int_result_in_place(&ptr_a, &op_result)?;
let result_var = self.emit_stack_gep(stack_var, -1)?;
Ok(Some(result_var))
}
pub(in crate::codegen) fn codegen_inline_shift(
&mut self,
stack_var: &str,
is_left: bool, ) -> Result<Option<String>, CodeGenError> {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let (ptr_a, val_a, val_b) = self.emit_load_two_int_operands(stack_var)?;
let op_result = self.codegen_shift_compute(&val_a, &val_b, is_left)?;
self.emit_store_int_result_in_place(&ptr_a, &op_result)?;
let result_var = self.emit_stack_gep(stack_var, -1)?;
Ok(Some(result_var))
}
pub(in crate::codegen) fn codegen_shift_compute(
&mut self,
val_a: &str,
val_b: &str,
is_left: bool,
) -> Result<String, CodeGenError> {
let is_neg = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp slt i64 %{}, 0",
is_neg, val_b
)?;
let is_overflow = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp sge i64 %{}, 64",
is_overflow, val_b
)?;
let is_invalid = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = or i1 %{}, %{}",
is_invalid, is_neg, is_overflow
)?;
let safe_count = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = select i1 %{}, i64 0, i64 %{}",
safe_count, is_invalid, val_b
)?;
let shift_result = self.fresh_temp();
let op = if is_left { "shl" } else { "lshr" };
writeln!(
&mut self.output,
" %{} = {} i64 %{}, %{}",
shift_result, op, val_a, safe_count
)?;
let op_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = select i1 %{}, i64 0, i64 %{}",
op_result, is_invalid, shift_result
)?;
Ok(op_result)
}
pub(in crate::codegen) fn codegen_inline_int_unary_intrinsic(
&mut self,
stack_var: &str,
intrinsic: &str, ) -> Result<Option<String>, CodeGenError> {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let (top_ptr, val) = self.emit_load_top_int(stack_var)?;
let result = self.fresh_temp();
if intrinsic == "llvm.ctpop.i64" {
writeln!(
&mut self.output,
" %{} = call i64 @{}(i64 %{})",
result, intrinsic, val
)?;
} else {
writeln!(
&mut self.output,
" %{} = call i64 @{}(i64 %{}, i1 false)",
result, intrinsic, val
)?;
}
self.emit_store_int_result_in_place(&top_ptr, &result)?;
Ok(Some(stack_var.to_string()))
}
}