use super::super::{CodeGen, CodeGenError};
use std::fmt::Write as _;
impl CodeGen {
pub(in crate::codegen) fn try_codegen_inline_op(
&mut self,
stack_var: &str,
name: &str,
) -> Result<Option<String>, CodeGenError> {
match name {
"drop" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let result_var = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = call ptr @patch_seq_drop_op(ptr %{})",
result_var, stack_var
)?;
Ok(Some(result_var))
}
"dup" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let top_ptr = self.emit_stack_gep(stack_var, -1)?;
let use_fast_path = self.prev_stmt_is_trivial_literal
|| self.is_trivially_copyable_at_current_stmt();
if use_fast_path {
let val = self.emit_load_value(&top_ptr)?;
self.emit_store_value(stack_var, &val)?;
} else {
writeln!(
&mut self.output,
" call void @patch_seq_clone_value(ptr %{}, ptr %{})",
top_ptr, stack_var
)?;
}
let result_var = self.emit_stack_gep(stack_var, 1)?;
Ok(Some(result_var))
}
"swap" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let ptr_b = self.emit_stack_gep(stack_var, -1)?;
let ptr_a = self.emit_stack_gep(stack_var, -2)?;
let val_a = self.emit_load_value(&ptr_a)?;
let val_b = self.emit_load_value(&ptr_b)?;
self.emit_store_value(&ptr_a, &val_b)?;
self.emit_store_value(&ptr_b, &val_a)?;
Ok(Some(stack_var.to_string()))
}
"over" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let ptr_a = self.emit_stack_gep(stack_var, -2)?;
writeln!(
&mut self.output,
" call void @patch_seq_clone_value(ptr %{}, ptr %{})",
ptr_a, stack_var
)?;
let result_var = self.emit_stack_gep(stack_var, 1)?;
Ok(Some(result_var))
}
"i.add" | "i.+" => self.codegen_inline_binary_op(stack_var, "add", "sub"),
"i.subtract" | "i.-" => self.codegen_inline_binary_op(stack_var, "sub", "add"),
"i.multiply" | "i.*" => self.codegen_inline_binary_op(stack_var, "mul", "div"),
"i.=" | "i.eq" => self.codegen_inline_comparison(stack_var, "eq"),
"i.<>" | "i.neq" => self.codegen_inline_comparison(stack_var, "ne"),
"i.<" | "i.lt" => self.codegen_inline_comparison(stack_var, "slt"),
"i.>" | "i.gt" => self.codegen_inline_comparison(stack_var, "sgt"),
"i.<=" | "i.lte" => self.codegen_inline_comparison(stack_var, "sle"),
"i.>=" | "i.gte" => self.codegen_inline_comparison(stack_var, "sge"),
"f.add" | "f.+" => self.codegen_inline_float_binary_op(stack_var, "fadd"),
"f.subtract" | "f.-" => self.codegen_inline_float_binary_op(stack_var, "fsub"),
"f.multiply" | "f.*" => self.codegen_inline_float_binary_op(stack_var, "fmul"),
"f.divide" | "f./" => self.codegen_inline_float_binary_op(stack_var, "fdiv"),
"f.=" | "f.eq" => self.codegen_inline_float_comparison(stack_var, "oeq"),
"f.<>" | "f.neq" => self.codegen_inline_float_comparison(stack_var, "one"),
"f.<" | "f.lt" => self.codegen_inline_float_comparison(stack_var, "olt"),
"f.>" | "f.gt" => self.codegen_inline_float_comparison(stack_var, "ogt"),
"f.<=" | "f.lte" => self.codegen_inline_float_comparison(stack_var, "ole"),
"f.>=" | "f.gte" => self.codegen_inline_float_comparison(stack_var, "oge"),
"and" => {
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 and_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = and i64 %{}, %{}",
and_result, val_a, val_b
)?;
let bool_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp ne i64 %{}, 0",
bool_result, and_result
)?;
let zext = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = zext i1 %{} to i64",
zext, bool_result
)?;
self.emit_store_bool(&ptr_a, &zext)?;
let result_var = self.emit_stack_gep(stack_var, -1)?;
Ok(Some(result_var))
}
"or" => {
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 or_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = or i64 %{}, %{}",
or_result, val_a, val_b
)?;
let bool_result = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = icmp ne i64 %{}, 0",
bool_result, or_result
)?;
let zext = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = zext i1 %{} to i64",
zext, bool_result
)?;
self.emit_store_bool(&ptr_a, &zext)?;
let result_var = self.emit_stack_gep(stack_var, -1)?;
Ok(Some(result_var))
}
"not" => {
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 is_zero = self.fresh_temp();
writeln!(&mut self.output, " %{} = icmp eq i64 %{}, 0", is_zero, val)?;
let zext = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = zext i1 %{} to i64",
zext, is_zero
)?;
self.emit_store_bool(&top_ptr, &zext)?;
Ok(Some(stack_var.to_string()))
}
"band" => self.codegen_inline_int_bitwise_binary(stack_var, "and"),
"bor" => self.codegen_inline_int_bitwise_binary(stack_var, "or"),
"bxor" => self.codegen_inline_int_bitwise_binary(stack_var, "xor"),
"shl" => self.codegen_inline_shift(stack_var, true),
"shr" => self.codegen_inline_shift(stack_var, false),
"i.neg" | "negate" => {
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 neg_result = self.fresh_temp();
writeln!(&mut self.output, " %{} = sub i64 0, %{}", neg_result, val)?;
self.emit_store_int_result_in_place(&top_ptr, &neg_result)?;
Ok(Some(stack_var.to_string()))
}
"bnot" => {
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 not_result = self.fresh_temp();
writeln!(&mut self.output, " %{} = xor i64 %{}, -1", not_result, val)?;
self.emit_store_int_result_in_place(&top_ptr, ¬_result)?;
Ok(Some(stack_var.to_string()))
}
"popcount" => self.codegen_inline_int_unary_intrinsic(stack_var, "llvm.ctpop.i64"),
"clz" => self.codegen_inline_int_unary_intrinsic(stack_var, "llvm.ctlz.i64"),
"ctz" => self.codegen_inline_int_unary_intrinsic(stack_var, "llvm.cttz.i64"),
"rot" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let ptr_c = self.emit_stack_gep(stack_var, -1)?;
let ptr_b = self.emit_stack_gep(stack_var, -2)?;
let ptr_a = self.emit_stack_gep(stack_var, -3)?;
let val_a = self.emit_load_value(&ptr_a)?;
let val_b = self.emit_load_value(&ptr_b)?;
let val_c = self.emit_load_value(&ptr_c)?;
self.emit_store_value(&ptr_a, &val_b)?;
self.emit_store_value(&ptr_b, &val_c)?;
self.emit_store_value(&ptr_c, &val_a)?;
Ok(Some(stack_var.to_string()))
}
"nip" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let result_var = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = call ptr @patch_seq_nip(ptr %{})",
result_var, stack_var
)?;
Ok(Some(result_var))
}
"tuck" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let ptr_b = self.emit_stack_gep(stack_var, -1)?;
let ptr_a = self.emit_stack_gep(stack_var, -2)?;
let val_a = self.emit_load_value(&ptr_a)?;
let val_b = self.emit_load_value(&ptr_b)?;
writeln!(
&mut self.output,
" call void @patch_seq_clone_value(ptr %{}, ptr %{})",
ptr_b, stack_var
)?;
self.emit_store_value(&ptr_a, &val_b)?;
self.emit_store_value(&ptr_b, &val_a)?;
let result_var = self.emit_stack_gep(stack_var, 1)?;
Ok(Some(result_var))
}
"2dup" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let ptr_b = self.emit_stack_gep(stack_var, -1)?;
let ptr_a = self.emit_stack_gep(stack_var, -2)?;
writeln!(
&mut self.output,
" call void @patch_seq_clone_value(ptr %{}, ptr %{})",
ptr_a, stack_var
)?;
let new_ptr = self.emit_stack_gep(stack_var, 1)?;
writeln!(
&mut self.output,
" call void @patch_seq_clone_value(ptr %{}, ptr %{})",
ptr_b, new_ptr
)?;
let result_var = self.emit_stack_gep(stack_var, 2)?;
Ok(Some(result_var))
}
"3drop" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let drop1 = self.fresh_temp();
let drop2 = self.fresh_temp();
let drop3 = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = call ptr @patch_seq_drop_op(ptr %{})",
drop1, stack_var
)?;
writeln!(
&mut self.output,
" %{} = call ptr @patch_seq_drop_op(ptr %{})",
drop2, drop1
)?;
writeln!(
&mut self.output,
" %{} = call ptr @patch_seq_drop_op(ptr %{})",
drop3, drop2
)?;
Ok(Some(drop3))
}
"pick" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
if let Some(n) = self.prev_stmt_int_value
&& n >= 0
{
return self.codegen_pick_constant(stack_var, n as usize);
}
let n_ptr = self.emit_stack_gep(stack_var, -1)?;
let n_val = self.emit_load_int_payload(&n_ptr)?;
let offset = self.fresh_temp();
writeln!(&mut self.output, " %{} = add i64 %{}, 2", offset, n_val)?;
let neg_offset = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = sub i64 0, %{}",
neg_offset, offset
)?;
let src_ptr = self.emit_dynamic_stack_gep(stack_var, &neg_offset)?;
writeln!(
&mut self.output,
" call void @patch_seq_clone_value(ptr %{}, ptr %{})",
src_ptr, n_ptr
)?;
Ok(Some(stack_var.to_string()))
}
"roll" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
if let Some(n) = self.prev_stmt_int_value
&& n >= 0
{
return self.codegen_roll_constant(stack_var, n as usize);
}
let n_ptr = self.emit_stack_gep(stack_var, -1)?;
let n_val = self.emit_load_int_payload(&n_ptr)?;
let popped_sp = self.emit_stack_gep(stack_var, -1)?;
let offset = self.fresh_temp();
writeln!(&mut self.output, " %{} = add i64 %{}, 1", offset, n_val)?;
let neg_offset = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = sub i64 0, %{}",
neg_offset, offset
)?;
let src_ptr = self.emit_dynamic_stack_gep(&popped_sp, &neg_offset)?;
let rolled_val = self.emit_load_value(&src_ptr)?;
let src_plus_one = self.emit_stack_gep(&src_ptr, 1)?;
let value_size = self.value_size_bytes();
let size_bytes = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = mul i64 %{}, {}",
size_bytes, n_val, value_size
)?;
writeln!(
&mut self.output,
" call void @llvm.memmove.p0.p0.i64(ptr %{}, ptr %{}, i64 %{}, i1 false)",
src_ptr, src_plus_one, size_bytes
)?;
let top_ptr = self.emit_stack_gep(&popped_sp, -1)?;
self.emit_store_value(&top_ptr, &rolled_val)?;
Ok(Some(popped_sp))
}
">aux" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
let slot_idx = self.current_aux_sp;
let slot_name = &self.current_aux_slots[slot_idx].clone();
let top_ptr = self.emit_stack_gep(stack_var, -1)?;
let val = self.emit_load_value(&top_ptr)?;
self.emit_store_value(slot_name, &val)?;
self.current_aux_sp += 1;
Ok(Some(top_ptr))
}
"aux>" => {
let stack_var = self.spill_virtual_stack(stack_var)?;
let stack_var = stack_var.as_str();
debug_assert!(
self.current_aux_sp > 0,
"aux>: aux stack underflow (typechecker should have caught this)"
);
self.current_aux_sp -= 1;
let slot_idx = self.current_aux_sp;
let slot_name = &self.current_aux_slots[slot_idx].clone();
let val = self.emit_load_value(slot_name)?;
self.emit_store_value(stack_var, &val)?;
let result_var = self.emit_stack_gep(stack_var, 1)?;
Ok(Some(result_var))
}
_ => Ok(None),
}
}
pub(super) fn codegen_roll_constant(
&mut self,
stack_var: &str,
n: usize,
) -> Result<Option<String>, CodeGenError> {
let popped_sp = self.emit_stack_gep(stack_var, -1)?;
match n {
0 => Ok(Some(popped_sp)),
1 => {
let ptr_b = self.emit_stack_gep(&popped_sp, -1)?;
let ptr_a = self.emit_stack_gep(&popped_sp, -2)?;
let val_a = self.emit_load_value(&ptr_a)?;
let val_b = self.emit_load_value(&ptr_b)?;
self.emit_store_value(&ptr_a, &val_b)?;
self.emit_store_value(&ptr_b, &val_a)?;
Ok(Some(popped_sp))
}
2 => {
let ptr_c = self.emit_stack_gep(&popped_sp, -1)?;
let ptr_b = self.emit_stack_gep(&popped_sp, -2)?;
let ptr_a = self.emit_stack_gep(&popped_sp, -3)?;
let val_a = self.emit_load_value(&ptr_a)?;
let val_b = self.emit_load_value(&ptr_b)?;
let val_c = self.emit_load_value(&ptr_c)?;
self.emit_store_value(&ptr_a, &val_b)?;
self.emit_store_value(&ptr_b, &val_c)?;
self.emit_store_value(&ptr_c, &val_a)?;
Ok(Some(popped_sp))
}
_ => {
let neg_offset = -((n + 1) as i64);
let src_ptr = self.emit_stack_gep(&popped_sp, neg_offset)?;
let rolled_val = self.emit_load_value(&src_ptr)?;
let src_plus_one = self.emit_stack_gep(&src_ptr, 1)?;
let size_bytes = n as u64 * self.value_size_bytes();
writeln!(
&mut self.output,
" call void @llvm.memmove.p0.p0.i64(ptr %{}, ptr %{}, i64 {}, i1 false)",
src_ptr, src_plus_one, size_bytes
)?;
let top_ptr = self.emit_stack_gep(&popped_sp, -1)?;
self.emit_store_value(&top_ptr, &rolled_val)?;
Ok(Some(popped_sp))
}
}
}
pub(super) fn codegen_pick_constant(
&mut self,
stack_var: &str,
n: usize,
) -> Result<Option<String>, CodeGenError> {
let n_ptr = self.emit_stack_gep(stack_var, -1)?;
let neg_offset = -((n + 2) as i64);
let src_ptr = self.emit_stack_gep(stack_var, neg_offset)?;
writeln!(
&mut self.output,
" call void @patch_seq_clone_value(ptr %{}, ptr %{})",
src_ptr, n_ptr
)?;
Ok(Some(stack_var.to_string()))
}
}