use super::Llvm;
use crate::{ir, report::throw_llvm_error};
use inkwell::types::StructType;
type Pointer<'ll> = inkwell::values::PointerValue<'ll>;
impl<'ll> Llvm<'ll> {
pub fn llvm_get_field(&mut self, get_field_instr: &ir::GetFieldInstr) {
let self_value = get_field_instr.self_value;
let field = get_field_instr.field;
let struct_value = *self.stack.get_value(self_value);
if !struct_value.is_pointer_value() {
let struct_value = struct_value.into_struct_value();
let struct_type = struct_value.get_type(); let struct_name = self.get_struct_name(&struct_type, self_value);
let field_index = self.get_field_index(struct_name.as_str(), field) as u32;
let temp = self.stack.temp_register();
let field_value = match self.builder.build_extract_value(struct_value, field_index, &temp) {
Ok(value) => value,
Err(e) => throw_llvm_error(format!("failed to get field value: {}", e)),
};
self.stack.set_value(get_field_instr.dest, field_value);
return;
}
let struct_ptr = struct_value.into_pointer_value();
let struct_type = self.get_struct_type(self_value);
let struct_name = self.get_struct_name(&struct_type, self_value);
let field_index = self.get_field_index(struct_name.as_str(), field);
let field_ptr = self.build_field_pointer(struct_type, struct_ptr, field_index);
self.stack.set_value(get_field_instr.dest, field_ptr.into());
}
pub fn get_struct_type(&mut self, self_value: ir::Register) -> StructType<'ll> {
self.stack.get_register_type(self_value).copied().unwrap_or_else(|| {
let register = self_value.as_string();
throw_llvm_error(format!("register '{}' not found in struct table", register))
})
}
pub fn get_struct_name(
&mut self,
struct_type: &StructType<'ll>,
self_reg: ir::Register,
) -> String {
let struct_name = match struct_type.get_name() {
Some(name) => name.to_str().unwrap_or_else(|_| throw_llvm_error("invalid struct name")),
None => throw_llvm_error(format!("struct type '{}' not found", self_reg.as_string())),
};
struct_name.to_string()
}
pub fn get_field_index(&mut self, struct_name: &str, field: ir::Register) -> usize {
self.stack.get_struct_field(struct_name, field).unwrap_or_else(|| {
let error = format!("field '{}' not found in struct '{}'", field.as_string(), struct_name);
throw_llvm_error(error)
})
}
#[rustfmt::skip]
pub fn build_field_pointer(&mut self, ll_type: StructType<'ll>, ptr: Pointer<'ll>, idx: usize) -> Pointer<'ll> {
let i32_type = self.ctx.i32_type();
let zero = i32_type.const_zero();
let position = i32_type.const_int(idx as u64, false);
let temp = self.stack.temp_register();
let params = [zero, position];
unsafe {
match self.builder.build_gep(ll_type, ptr, ¶ms, &temp) {
Ok(value) => value,
Err(e) => throw_llvm_error(format!("failed to get field ptr: {}", e)),
}
}
}
}