use super::CodeGen;
use super::context::RegisterContext;
use super::types::RegisterType;
use crate::codegen::CodeGenError;
use std::fmt::Write as _;
impl CodeGen {
pub(super) fn emit_specialized_safe_div(
&mut self,
ctx: &mut RegisterContext,
op: &str, ) -> Result<(), CodeGenError> {
let (b, _) = ctx.pop().unwrap(); let (a, _) = ctx.pop().unwrap();
let is_zero = self.fresh_temp();
writeln!(&mut self.output, " %{} = icmp eq i64 %{}, 0", is_zero, b)?;
let (check_overflow, is_overflow) = if op == "sdiv" {
let is_int_min = self.fresh_temp();
let is_neg_one = self.fresh_temp();
let is_overflow = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp eq i64 %{}, -9223372036854775808",
is_int_min, a
)?;
writeln!(
&mut self.output,
" %{} = icmp eq i64 %{}, -1",
is_neg_one, b
)?;
writeln!(
&mut self.output,
" %{} = and i1 %{}, %{}",
is_overflow, is_int_min, is_neg_one
)?;
(true, is_overflow)
} else {
(false, String::new())
};
let ok_label = self.fresh_block("div_ok");
let fail_label = self.fresh_block("div_fail");
let merge_label = self.fresh_block("div_merge");
let overflow_label = if check_overflow {
self.fresh_block("div_overflow")
} else {
String::new()
};
writeln!(
&mut self.output,
" br i1 %{}, label %{}, label %{}",
is_zero,
fail_label,
if check_overflow {
&overflow_label
} else {
&ok_label
}
)?;
if check_overflow {
writeln!(&mut self.output, "{}:", overflow_label)?;
writeln!(
&mut self.output,
" br i1 %{}, label %{}, label %{}",
is_overflow, merge_label, ok_label
)?;
}
writeln!(&mut self.output, "{}:", ok_label)?;
let ok_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = {} i64 %{}, %{}",
ok_result, op, a, b
)?;
writeln!(&mut self.output, " br label %{}", merge_label)?;
writeln!(&mut self.output, "{}:", fail_label)?;
writeln!(&mut self.output, " br label %{}", merge_label)?;
writeln!(&mut self.output, "{}:", merge_label)?;
let result_phi = self.fresh_temp();
let success_phi = self.fresh_temp();
if check_overflow {
writeln!(
&mut self.output,
" %{} = phi i64 [ %{}, %{} ], [ 0, %{} ], [ -9223372036854775808, %{} ]",
result_phi, ok_result, ok_label, fail_label, overflow_label
)?;
writeln!(
&mut self.output,
" %{} = phi i64 [ 1, %{} ], [ 0, %{} ], [ 1, %{} ]",
success_phi, ok_label, fail_label, overflow_label
)?;
} else {
writeln!(
&mut self.output,
" %{} = phi i64 [ %{}, %{} ], [ 0, %{} ]",
result_phi, ok_result, ok_label, fail_label
)?;
writeln!(
&mut self.output,
" %{} = phi i64 [ 1, %{} ], [ 0, %{} ]",
success_phi, ok_label, fail_label
)?;
}
ctx.push(result_phi, RegisterType::I64);
ctx.push(success_phi, RegisterType::I64);
Ok(())
}
pub(super) fn emit_specialized_safe_shift(
&mut self,
ctx: &mut RegisterContext,
is_left: bool, ) -> Result<(), CodeGenError> {
let (b, _) = ctx.pop().unwrap(); let (a, _) = ctx.pop().unwrap();
let is_negative = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp slt i64 %{}, 0",
is_negative, b
)?;
let is_too_large = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp sge i64 %{}, 64",
is_too_large, b
)?;
let is_invalid = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = or i1 %{}, %{}",
is_invalid, is_negative, is_too_large
)?;
let safe_count = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = select i1 %{}, i64 0, i64 %{}",
safe_count, is_invalid, b
)?;
let shift_result = self.fresh_temp();
let op = if is_left { "shl" } else { "lshr" };
writeln!(
&mut self.output,
" %{} = {} i64 %{}, %{}",
shift_result, op, a, safe_count
)?;
let result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = select i1 %{}, i64 0, i64 %{}",
result, is_invalid, shift_result
)?;
ctx.push(result, RegisterType::I64);
Ok(())
}
}