use super::{CodeGen, CodeGenError};
use std::fmt::Write as _;
impl CodeGen {
pub(super) fn emit_value_type_def(&self, ir: &mut String) -> Result<(), CodeGenError> {
writeln!(ir, "; Value type (tagged pointer - 8 bytes)")?;
writeln!(ir, "%Value = type i64")?;
writeln!(ir)?;
Ok(())
}
pub(super) fn emit_stack_gep(
&mut self,
base: &str,
offset: i64,
) -> Result<String, CodeGenError> {
let tmp = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = getelementptr i64, ptr %{}, i64 {}",
tmp, base, offset
)?;
Ok(tmp)
}
pub(super) fn emit_dynamic_stack_gep(
&mut self,
base: &str,
offset_var: &str,
) -> Result<String, CodeGenError> {
let tmp = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = getelementptr i64, ptr %{}, i64 %{}",
tmp, base, offset_var
)?;
Ok(tmp)
}
pub(super) fn emit_load_int_payload(
&mut self,
value_ptr: &str,
) -> Result<String, CodeGenError> {
let tagged = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = load i64, ptr %{}",
tagged, value_ptr
)?;
let val = self.fresh_temp();
writeln!(&mut self.output, " %{} = ashr i64 %{}, 1", val, tagged)?;
Ok(val)
}
pub(super) fn emit_store_int(
&mut self,
value_ptr: &str,
int_var: &str,
) -> Result<(), CodeGenError> {
let shifted = self.fresh_temp();
writeln!(&mut self.output, " %{} = shl i64 %{}, 1", shifted, int_var)?;
let tagged = self.fresh_temp();
writeln!(&mut self.output, " %{} = or i64 %{}, 1", tagged, shifted)?;
writeln!(
&mut self.output,
" store i64 %{}, ptr %{}",
tagged, value_ptr
)?;
Ok(())
}
pub(super) fn emit_store_bool(
&mut self,
value_ptr: &str,
bool_var: &str,
) -> Result<(), CodeGenError> {
let tagged = self.fresh_temp();
writeln!(&mut self.output, " %{} = shl i64 %{}, 1", tagged, bool_var)?;
writeln!(
&mut self.output,
" store i64 %{}, ptr %{}",
tagged, value_ptr
)?;
Ok(())
}
pub(super) fn emit_load_two_int_operands(
&mut self,
stack_var: &str,
) -> Result<(String, String, String), CodeGenError> {
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_int_payload(&ptr_a)?;
let val_b = self.emit_load_int_payload(&ptr_b)?;
Ok((ptr_a, val_a, val_b))
}
pub(super) fn emit_load_top_int(
&mut self,
stack_var: &str,
) -> Result<(String, String), CodeGenError> {
let top_ptr = self.emit_stack_gep(stack_var, -1)?;
let val = self.emit_load_int_payload(&top_ptr)?;
Ok((top_ptr, val))
}
pub(super) fn emit_store_int_result_in_place(
&mut self,
top_ptr: &str,
result_var: &str,
) -> Result<(), CodeGenError> {
self.emit_store_int(top_ptr, result_var)
}
pub(super) fn emit_load_value(&mut self, ptr: &str) -> Result<String, CodeGenError> {
let val = self.fresh_temp();
writeln!(&mut self.output, " %{} = load i64, ptr %{}", val, ptr)?;
Ok(val)
}
pub(super) fn emit_store_value(&mut self, ptr: &str, val: &str) -> Result<(), CodeGenError> {
writeln!(&mut self.output, " store i64 %{}, ptr %{}", val, ptr)?;
Ok(())
}
pub(super) fn value_size_bytes(&self) -> u64 {
8
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_type_def() {
let cg = CodeGen::new();
let mut ir = String::new();
cg.emit_value_type_def(&mut ir).unwrap();
assert!(ir.contains("%Value = type i64"));
assert!(ir.contains("8 bytes"));
}
#[test]
fn test_stack_gep() {
let mut cg = CodeGen::new();
let tmp = cg.emit_stack_gep("sp", -1).unwrap();
assert!(cg.output.contains("getelementptr i64"));
assert!(!cg.output.contains("%Value"));
assert!(cg.output.contains(&format!("%{}", tmp)));
}
#[test]
fn test_load_int_payload() {
let mut cg = CodeGen::new();
let val = cg.emit_load_int_payload("ptr_a").unwrap();
assert!(cg.output.contains("load i64, ptr %ptr_a"));
assert!(cg.output.contains("ashr i64"));
assert!(!val.is_empty());
}
#[test]
fn test_store_int() {
let mut cg = CodeGen::new();
cg.emit_store_int("ptr_a", "val").unwrap();
assert!(cg.output.contains("shl i64 %val, 1"));
assert!(cg.output.contains("or i64"));
assert!(!cg.output.contains("store i64 0, ptr"));
}
#[test]
fn test_store_bool() {
let mut cg = CodeGen::new();
cg.emit_store_bool("ptr_a", "bval").unwrap();
assert!(cg.output.contains("shl i64 %bval, 1"));
assert!(!cg.output.contains("store i64 2"));
}
#[test]
fn test_value_size_bytes() {
assert_eq!(CodeGen::new().value_size_bytes(), 8);
}
#[test]
fn test_load_two_int_operands() {
let mut cg = CodeGen::new();
let (_ptr_a, val_a, val_b) = cg.emit_load_two_int_operands("sp").unwrap();
assert!(cg.output.contains("getelementptr i64"));
assert!(cg.output.contains("ashr i64"));
assert!(!val_a.is_empty());
assert!(!val_b.is_empty());
}
#[test]
fn test_load_value() {
let mut cg = CodeGen::new();
let val = cg.emit_load_value("ptr_a").unwrap();
assert!(cg.output.contains("load i64, ptr %ptr_a"));
assert!(!val.is_empty());
}
#[test]
fn test_store_value() {
let mut cg = CodeGen::new();
cg.emit_store_value("ptr_a", "val").unwrap();
assert!(cg.output.contains("store i64 %val, ptr %ptr_a"));
}
#[test]
fn test_dynamic_stack_gep() {
let mut cg = CodeGen::new();
let tmp = cg.emit_dynamic_stack_gep("sp", "offset").unwrap();
assert!(
cg.output
.contains("getelementptr i64, ptr %sp, i64 %offset")
);
assert!(!tmp.is_empty());
}
}