diff --git a/crates/mun_codegen/src/ir/array.rs b/crates/mun_codegen/src/ir/array.rs index c9b84105..58650fc6 100644 --- a/crates/mun_codegen/src/ir/array.rs +++ b/crates/mun_codegen/src/ir/array.rs @@ -1,127 +1,83 @@ -//! Defines a helper struct `RuntimeArrayValue` which wraps an inkwell value and -//! represents a pointer to a heap allocated Mun array struct. +//! Typed projections into heap-allocated Mun arrays. //! -//! Mun arrays are represented on the heap as: -//! -//! ```c -//! struct Obj { -//! ArrayValueT *value; -//! ... -//! } -//! -//! struct ArrayValueT { -//! usize_t len; -//! usize_t capacity; -//! T elements[capacity]; -//! } -//! ``` - -use std::ffi::CStr; +//! A runtime array handle points indirectly to `{ length, capacity, +//! first_element }`. This module keeps that aggregate type explicit so +//! projections do not depend on LLVM pointer element types. use inkwell::{ builder::Builder, types::{BasicTypeEnum, IntType, StructType}, - values::{BasicValueEnum, IntValue, PointerValue}, + values::{BasicValueEnum, PointerValue}, }; -use crate::ir::reference::RuntimeReferenceValue; +use crate::ir::{reference::RuntimeReferenceValue, value::PlaceValue}; -/// A helper struct that wraps a [`PointerValue`] which points to an in memory -/// Mun array value. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct RuntimeArrayValue<'ink>(RuntimeReferenceValue<'ink>); +/// A runtime array handle paired with the concrete array aggregate type. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) struct RuntimeArrayValue<'ink> { + reference: RuntimeReferenceValue<'ink>, + array_type: StructType<'ink>, +} impl<'ink> RuntimeArrayValue<'ink> { - /// Constructs a new `RuntimeArrayValue` from a reference pointer to a - /// specific array type. - /// - /// The pointer passed must be of type `**ArrayValueT`. - pub fn from_ptr(ptr: PointerValue<'ink>, array_type: StructType<'ink>) -> Result { - RuntimeReferenceValue::from_ptr(ptr, array_type).map(Self) - } - - /// Constructs a new instance from an inkwell [`PointerValue`] without - /// checking if this is actually a pointer to an array. - pub unsafe fn from_ptr_unchecked(ptr: PointerValue<'ink>) -> Self { - Self(RuntimeReferenceValue::from_ptr_unchecked(ptr)) - } - - /// Returns the name of the array - pub fn get_name(&self) -> &CStr { - self.0.get_name() + /// Associates a runtime handle with its concrete array aggregate type. + pub(crate) fn new(pointer: PointerValue<'ink>, array_type: StructType<'ink>) -> Self { + Self { + reference: RuntimeReferenceValue::new(pointer, array_type.into()), + array_type, + } } - /// Generate code to get to the array value. - fn get_array_ptr(&self, builder: &Builder<'ink>) -> PointerValue<'ink> { - self.0.get_data_ptr(builder) + fn get_array(&self, builder: &Builder<'ink>) -> PlaceValue<'ink> { + self.reference.get_data(builder) } - /// Generate code to fetch the length of the array. - pub fn get_length_ptr(&self, builder: &Builder<'ink>) -> PointerValue<'ink> { - let array_ptr = self.get_array_ptr(builder); - let value_name = array_ptr.get_name().to_string_lossy(); - builder - .build_struct_gep(array_ptr, 0, &format!("{value_name}->length")) - .expect("could not get `length` from array struct") - } - - /// Generate code to fetch the capacity of the array. - pub fn get_capacity(&self, builder: &Builder<'ink>) -> IntValue<'ink> { - let array_ptr = self.get_array_ptr(builder); - let value_name = array_ptr.get_name().to_string_lossy(); - let length_ptr = builder - .build_struct_gep(array_ptr, 1, &format!("{value_name}->capacity")) + /// Projects the array length field. + pub(crate) fn get_length(&self, builder: &Builder<'ink>) -> PlaceValue<'ink> { + let array = self.get_array(builder); + let pointer = array.pointer(); + let value_name = pointer.get_name().to_string_lossy(); + let pointer = builder + .build_struct_gep(pointer, 0, &format!("{value_name}->length")) .expect("could not get `length` from array struct"); - builder - .build_load(length_ptr, &format!("{value_name}.capacity")) - .into_int_value() + PlaceValue::new(pointer, self.length_ty().into()) } - /// Generate code to a pointer to the elements stored in the array. - pub fn get_elements(&self, builder: &Builder<'ink>) -> PointerValue<'ink> { - let array_ptr = self.get_array_ptr(builder); - let value_name = array_ptr.get_name().to_string_lossy(); - builder - .build_struct_gep(array_ptr, 2, &format!("{value_name}->elements")) - .expect("could not get `elements` from array struct") + /// Projects the first array element. + pub(crate) fn get_elements(&self, builder: &Builder<'ink>) -> PlaceValue<'ink> { + let array = self.get_array(builder); + let pointer = array.pointer(); + let value_name = pointer.get_name().to_string_lossy(); + let pointer = builder + .build_struct_gep(pointer, 2, &format!("{value_name}->elements")) + .expect("could not get `elements` from array struct"); + PlaceValue::new(pointer, self.element_ty()) } - /// Returns the type of the `length` field - pub fn length_ty(&self) -> IntType<'_> { - self.array_data_ty() + /// Returns the type of the length field. + pub(crate) fn length_ty(&self) -> IntType<'ink> { + self.array_type .get_field_type_at_index(0) - .expect("an array must have a second field") + .expect("an array must have a length field") .into_int_type() } - /// Returns the type of the `length` field - pub fn capacity_ty(&self) -> IntType<'_> { - self.array_data_ty() - .get_field_type_at_index(1) - .expect("an array must have a second field") - .into_int_type() - } - - /// Returns the type of the elements stored in this array - pub fn element_ty(&self) -> BasicTypeEnum<'ink> { - self.array_data_ty() + /// Returns the type of an array element. + pub(crate) fn element_ty(&self) -> BasicTypeEnum<'ink> { + self.array_type .get_field_type_at_index(2) - .expect("an array must have a second field") - } - - fn array_data_ty(&self) -> StructType<'ink> { - self.0.get_type().into_struct_type() + .expect("an array must have an element field") } } impl<'ink> From> for BasicValueEnum<'ink> { fn from(value: RuntimeArrayValue<'ink>) -> Self { - value.0.into() + value.reference.into() } } impl<'ink> From> for PointerValue<'ink> { fn from(value: RuntimeArrayValue<'ink>) -> Self { - value.0.into() + value.reference.into() } } diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs index d39aedd3..fd165335 100644 --- a/crates/mun_codegen/src/ir/body.rs +++ b/crates/mun_codegen/src/ir/body.rs @@ -4,6 +4,7 @@ use inkwell::{ basic_block::BasicBlock, builder::Builder, context::Context, + types::BasicTypeEnum, values::{ AggregateValueEnum, BasicMetadataValueEnum, BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, GlobalValue, IntValue, PointerValue, StructValue, @@ -172,7 +173,11 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { let param = self.fn_value.get_nth_param(idx as u32).unwrap(); if let Some(s) = ty.as_struct() { if s.data(self.db).memory_kind == abi::StructMemoryKind::Value { - deref_heap_value(&self.builder, param) + deref_heap_value( + &self.builder, + param, + self.hir_types.get_struct_type(s).into(), + ) } else { param } @@ -442,12 +447,12 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { .into_pointer_value(); // Construct a reference of the object - let reference = RuntimeReferenceValue::from_ptr(typed_reference, struct_ir_ty) - .expect("unable to construct mun reference type"); + let reference = RuntimeReferenceValue::new(typed_reference, struct_ir_ty.into()); // Store the struct value - let struct_ptr = reference.get_data_ptr(&self.builder); - self.builder.build_store(struct_ptr, struct_lit); + reference + .get_data(&self.builder) + .store(&self.builder, struct_lit.into()); reference.into() } @@ -609,19 +614,18 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { let ty = &self.infer[expr]; if let Some(s) = ty.as_struct() { if s.data(self.db).memory_kind == mun_hir::StructMemoryKind::Gc { - return deref_heap_value(&self.builder, value); + return deref_heap_value( + &self.builder, + value, + self.hir_types.get_struct_type(s).into(), + ); } } value } - /// Generates IR for looking up a certain path expression. - fn gen_path_place_expr( - &self, - path: &Path, - _expr: ExprId, - resolver: &Resolver, - ) -> inkwell::values::PointerValue<'ink> { + /// Generates the place associated with a local path. + fn gen_path_place_expr(&self, path: &Path, resolver: &Resolver) -> Place<'ink> { match resolver .resolve_path_as_value_fully(self.db, path) .expect("unknown path") @@ -632,8 +636,7 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { .pat_to_local .get(&pat) .expect("unresolved local binding") - .value() - .pointer(), + .clone(), ValueNs::FunctionId(_) | ValueNs::StructId(_) => { panic!("no support for module definitions") } @@ -1065,14 +1068,13 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { } } - /// Given an expression generate code that results in a memory address that - /// can be used for other place operations. + /// Generates a typed memory location for a place expression. fn gen_place_expr(&mut self, expr: ExprId) -> Option> { let body = self.body.clone(); - let pointer = match &body[expr] { - Expr::Path(p) => { + match &body[expr] { + Expr::Path(path) => { let resolver = mun_hir::resolver_for_expr(self.db, self.body.owner(), expr); - Some(self.gen_path_place_expr(p, expr, &resolver)) + Some(self.gen_path_place_expr(path, &resolver)) } Expr::Field { expr: receiver_expr, @@ -1080,13 +1082,7 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { } => self.gen_place_field(expr, *receiver_expr, name), Expr::Index { base, index } => self.gen_place_index(expr, *base, *index), _ => unreachable!("invalid place expression"), - }?; - let ty = self.infer[expr].clone(); - let pointee = self - .hir_types - .get_basic_type(&ty) - .expect("place must have a basic type"); - Some(Place::new(PlaceValue::new(pointer, pointee), ty)) + } } /// Returns true if the specified expression refers to an expression that @@ -1384,40 +1380,26 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { fn gen_field( &mut self, - _expr: ExprId, + expr: ExprId, receiver_expr: ExprId, name: &Name, ) -> Option> { let hir_struct = self.infer[receiver_expr] .as_struct() .expect("expected a struct"); - let hir_struct_name = hir_struct.name(self.db); - let field_idx = hir_struct .field(self.db, name) .expect("expected a struct field") .index(self.db); - let field_ir_name = &format!("{hir_struct_name}.{name}"); + if self.is_place_expr(receiver_expr) { - let receiver = self.gen_place_expr(receiver_expr)?; - let receiver_ptr = self - .opt_deref_value(receiver_expr, receiver.value().pointer().into()) - .into_pointer_value(); - let field_ptr = self - .builder - .build_struct_gep( - receiver_ptr, - field_idx, - &format!("{hir_struct_name}->{name}"), - ) - .unwrap_or_else(|_| { - panic!( - "could not get pointer to field `{hir_struct_name}::{name}` at index {field_idx}" - ) - }); - Some(self.builder.build_load(field_ptr, field_ir_name)) + Some( + self.gen_place_field(expr, receiver_expr, name)? + .load(&self.builder, field_ir_name) + .into_value(), + ) } else { let receiver_value = self.gen_expr(receiver_expr)?; let receiver_value = self.opt_deref_value(receiver_expr, receiver_value); @@ -1425,59 +1407,74 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { Some( self.builder .build_extract_value(receiver_struct, field_idx, field_ir_name) - .ok_or_else(|| { - format!( + .unwrap_or_else(|| { + panic!( "could not extract field {name} (index: {field_idx}) from struct {hir_struct_name}" ) - }) - .unwrap(), + }), ) } } fn gen_place_field( &mut self, - _expr: ExprId, + expr: ExprId, receiver_expr: ExprId, name: &Name, - ) -> Option> { + ) -> Option> { let hir_struct = self.infer[receiver_expr] .as_struct() .expect("expected a struct"); - let hir_struct_name = hir_struct.name(self.db); - let field_idx = hir_struct .field(self.db, name) .expect("expected a struct field") .index(self.db); - let receiver = self.gen_place_expr(receiver_expr)?; - let receiver_ptr = self - .opt_deref_value(receiver_expr, receiver.value().pointer().into()) - .into_pointer_value(); - Some( - self.builder - .build_struct_gep( - receiver_ptr, - field_idx, - &format!("{hir_struct_name}->{name}"), + let receiver_ptr = if hir_struct.data(self.db).memory_kind == mun_hir::StructMemoryKind::Gc + { + let handle_name = format!( + "{}->data", + receiver.value().pointer().get_name().to_string_lossy() + ); + let handle = receiver + .load(&self.builder, &handle_name) + .into_value() + .into_pointer_value(); + RuntimeReferenceValue::new(handle, self.hir_types.get_struct_type(hir_struct).into()) + .get_data_named(&self.builder, "deref") + .pointer() + } else { + receiver.value().pointer() + }; + let pointer = self + .builder + .build_struct_gep( + receiver_ptr, + field_idx, + &format!("{hir_struct_name}->{name}"), + ) + .unwrap_or_else(|_| { + panic!( + "could not get pointer to field `{hir_struct_name}::{name}` at index {field_idx}" ) - .unwrap_or_else(|_| { - panic!( - "could not get pointer to field `{hir_struct_name}::{name}` at index {field_idx}" - ) - }), - ) + }); + let ty = self.infer[expr].clone(); + let pointee = self + .hir_types + .get_basic_type(&ty) + .expect("field must have a basic type"); + Some(Place::new(PlaceValue::new(pointer, pointee), ty)) } /// Generates code to construct an array literal at runtime. Returns `None` /// if the code generation for the array literal never returns. fn gen_array(&mut self, expr: ExprId, exprs: &[ExprId]) -> Option> { - let array_ty = &self.infer[expr]; + let array_ty = self.infer[expr].clone(); let element_ty = array_ty .as_array() - .expect("the type of an array literal expression must be an Array"); + .expect("the type of an array literal expression must be an Array") + .clone(); let new_array_fn_ptr = self.dispatch_table.gen_intrinsic_lookup( self.external_globals.dispatch_table, @@ -1488,7 +1485,7 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { let type_info_ptr = self.type_table.gen_type_info_lookup( self.context, &self.builder, - &self.hir_types.type_id(array_ty), + &self.hir_types.type_id(&array_ty), self.external_globals.type_table, ); @@ -1527,7 +1524,7 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { .into_pointer_value(); // Cast the object pointer to the array struct type - let array_ty = self.hir_types.get_array_type(element_ty); + let array_ty = self.hir_types.get_array_type(&element_ty); let array_ptr = self .builder .build_bitcast( @@ -1539,63 +1536,74 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { ) .into_pointer_value(); - let array = RuntimeArrayValue::from_ptr(array_ptr, array_ty) - .expect("unable to convert pointer to typed reference"); + let array = RuntimeArrayValue::new(array_ptr, array_ty); let array_elements = array.get_elements(&self.builder); for (idx, expr) in exprs.iter().enumerate() { - let element_ptr = unsafe { + let pointer = unsafe { self.builder.build_gep( - array_elements, + array_elements.pointer(), &[self.context.i64_type().const_int(idx as u64, false)], - &format!("{}[{}]", array_elements.get_name().to_string_lossy(), idx), + &format!( + "{}[{}]", + array_elements.pointer().get_name().to_string_lossy(), + idx + ), ) }; - - let expr_value = self.gen_expr(*expr)?; - self.builder.build_store(element_ptr, expr_value); + let place = Place::new( + PlaceValue::new(pointer, array_elements.pointee()), + element_ty.clone(), + ); + let operand = self.gen_operand(*expr)?; + place.store(&self.builder, &operand); } - // Once all values have been stored in the array, update the length of the array let length = array.length_ty().const_int(exprs.len() as u64, false); - let array_length_ptr = array.get_length_ptr(&self.builder); - self.builder.build_store(array_length_ptr, length); + array + .get_length(&self.builder) + .store(&self.builder, length.into()); Some(array) } - /// Generates an index into an array + /// Generates an array element value. fn gen_index( &mut self, expr: ExprId, base: ExprId, index: ExprId, ) -> Option> { - let element_ptr = self.gen_place_index(expr, base, index)?; - Some(self.builder.build_load(element_ptr, "")) + let element = self.gen_place_index(expr, base, index)?; + Some(element.load(&self.builder, "").into_value()) } - /// Generates an index into an array + /// Generates a typed place for an array element. fn gen_place_index( &mut self, - _expr: ExprId, + expr: ExprId, base: ExprId, index: ExprId, - ) -> Option> { - // Safety: place expression can only be generated if the base expression is an - // array. - let base = unsafe { - RuntimeArrayValue::from_ptr_unchecked(self.gen_expr(base)?.into_pointer_value()) - }; + ) -> Option> { + let array_ty = self.infer[base].clone(); + let element_ty = array_ty + .as_array() + .expect("the base of an index expression must be an array") + .clone(); + let array_ir_ty = self.hir_types.get_array_type(&element_ty); + let base = RuntimeArrayValue::new(self.gen_expr(base)?.into_pointer_value(), array_ir_ty); let index = self.gen_expr(index)?.into_int_value(); - let elements = base.get_elements(&self.builder); - Some(unsafe { + let pointer = unsafe { self.builder.build_gep( - elements, + elements.pointer(), &[index], - &format!("{}+index", elements.get_name().to_string_lossy()), + &format!("{}+index", elements.pointer().get_name().to_string_lossy()), ) - }) + }; + Some(Place::new( + PlaceValue::new(pointer, elements.pointee()), + self.infer[expr].clone(), + )) } /// Returns a pointer to the allocator handle @@ -1612,16 +1620,13 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { } } -/// Derefs a heap-allocated value. As we introduce a layer of indirection for -/// hot reloading, we need to first load the pointer that points to the memory -/// block. +/// Dereferences a runtime handle using its explicit object type. fn deref_heap_value<'ink>( builder: &Builder<'ink>, value: BasicValueEnum<'ink>, + object_type: BasicTypeEnum<'ink>, ) -> BasicValueEnum<'ink> { - // Safety: we can assume that the input is a RuntimeReferenceValue - let mem_ptr = unsafe { RuntimeReferenceValue::from_ptr_unchecked(value.into_pointer_value()) } - .get_data_ptr(builder); - - builder.build_load(mem_ptr, "deref") + RuntimeReferenceValue::new(value.into_pointer_value(), object_type) + .get_data(builder) + .load(builder, "deref") } diff --git a/crates/mun_codegen/src/ir/reference.rs b/crates/mun_codegen/src/ir/reference.rs index f18c2aca..4839f5e6 100644 --- a/crates/mun_codegen/src/ir/reference.rs +++ b/crates/mun_codegen/src/ir/reference.rs @@ -1,5 +1,3 @@ -use std::ffi::CStr; - use inkwell::{ builder::Builder, types::{BasicType, BasicTypeEnum}, @@ -7,96 +5,55 @@ use inkwell::{ AddressSpace, }; -/// A helper struct that wraps an object on the heap. -/// -/// Objects on the heap are represented as an indirection. The stored pointer -/// points to an object on the heap where the first field points to the actual -/// data of the object: -/// -/// ```c -/// struct Obj { -/// ObjectData *data; -/// ... -/// } -/// ``` -/// -/// This enables the runtime to modify the contents of the object without having -/// to modify the references that point to it. +use crate::ir::value::PlaceValue; + +/// A stable runtime handle and the LLVM type of the heap object it references. /// -/// The `RuntimeReferenceValue` stores the indirection as `**T` (a pointer to a -/// pointer to `T`), where T is the type of the object stored on the heap. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct RuntimeReferenceValue<'ink>(PointerValue<'ink>); +/// The handle is stored as `**T`: the runtime may replace `*T` while generated +/// references remain valid. Keeping `T` explicitly avoids recovering semantic +/// information from LLVM pointer types. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) struct RuntimeReferenceValue<'ink> { + pointer: PointerValue<'ink>, + object_type: BasicTypeEnum<'ink>, +} impl<'ink> RuntimeReferenceValue<'ink> { - /// Constructs a new `RuntimeReferenceValue` from a reference pointer to a - /// specific type. - /// - /// The pointer passed must be of type `**T`. - pub fn from_ptr( - ptr: PointerValue<'ink>, - object_type: impl BasicType<'ink>, - ) -> Result { - let reference_type = object_type - .ptr_type(AddressSpace::default()) - .ptr_type(AddressSpace::default()); - if ptr.get_type() == reference_type { - Ok(Self(ptr)) - } else { - Err(format!( - "expected pointer of type {}, got {}", - reference_type.print_to_string().to_string_lossy(), - ptr.get_type().print_to_string().to_string_lossy() - )) + /// Associates a runtime handle with the object type it addresses. + pub(crate) fn new(pointer: PointerValue<'ink>, object_type: BasicTypeEnum<'ink>) -> Self { + debug_assert_eq!( + pointer.get_type(), + object_type + .ptr_type(AddressSpace::default()) + .ptr_type(AddressSpace::default()) + ); + Self { + pointer, + object_type, } } - /// Constructs a new instance from an inkwell `PointerValue` without - /// checking if this is actually a pointer to an object on the heap. - pub unsafe fn from_ptr_unchecked(ptr: PointerValue<'ink>) -> Self { - Self(ptr) - } - - /// Returns the name of the inkwell value - pub fn get_name(&self) -> &CStr { - self.0.get_name() - } - - /// Generates code to dereference the reference to get to the data of the - /// reference. - pub fn get_data_ptr(&self, builder: &Builder<'ink>) -> PointerValue<'ink> { - let value_name = self.0.get_name().to_string_lossy(); - - // Dereference the pointer to get the pointer to the data - // - // ```c - // data_ptr:*const T: = *data_ptr_ptr; - // ``` - builder - .build_load(self.0, &format!("{value_name}->data")) - .into_pointer_value() + /// Emits the runtime indirection and returns the resulting object place. + pub(crate) fn get_data(&self, builder: &Builder<'ink>) -> PlaceValue<'ink> { + let value_name = self.pointer.get_name().to_string_lossy(); + self.get_data_named(builder, &format!("{value_name}->data")) } - /// Returns the type of the object this instance points to - pub fn get_type(&self) -> BasicTypeEnum<'ink> { - self.0 - .get_type() - .get_element_type() - .into_pointer_type() - .get_element_type() - .try_into() - .expect("could not convert reference type to basic type") + /// Emits the runtime indirection with an explicit result name. + pub(crate) fn get_data_named(&self, builder: &Builder<'ink>, name: &str) -> PlaceValue<'ink> { + let pointer = builder.build_load(self.pointer, name).into_pointer_value(); + PlaceValue::new(pointer, self.object_type) } } impl<'ink> From> for BasicValueEnum<'ink> { fn from(value: RuntimeReferenceValue<'ink>) -> Self { - value.0.into() + value.pointer.into() } } impl<'ink> From> for PointerValue<'ink> { fn from(value: RuntimeReferenceValue<'ink>) -> Self { - value.0 + value.pointer } } diff --git a/crates/mun_codegen/src/ir/value.rs b/crates/mun_codegen/src/ir/value.rs index b3c94319..12b6ab6b 100644 --- a/crates/mun_codegen/src/ir/value.rs +++ b/crates/mun_codegen/src/ir/value.rs @@ -31,7 +31,7 @@ impl<'ink> Operand<'ink> { } /// The LLVM address and pointee type required to access a memory location. -#[derive(Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(crate) struct PlaceValue<'ink> { pointer: PointerValue<'ink>, pointee: BasicTypeEnum<'ink>, @@ -49,6 +49,18 @@ impl<'ink> PlaceValue<'ink> { pub(crate) fn pointee(self) -> BasicTypeEnum<'ink> { self.pointee } + pub(crate) fn load(self, builder: &Builder<'ink>, name: &str) -> BasicValueEnum<'ink> { + builder.build_load(self.pointer, name) + } + + pub(crate) fn store( + self, + builder: &Builder<'ink>, + value: BasicValueEnum<'ink>, + ) -> InstructionValue<'ink> { + debug_assert_eq!(self.pointee, value.get_type()); + builder.build_store(self.pointer, value) + } } /// A writable memory location paired with its Mun type. @@ -72,10 +84,7 @@ impl<'ink> Place<'ink> { } pub(crate) fn load(&self, builder: &Builder<'ink>, name: &str) -> Operand<'ink> { - Operand::new( - builder.build_load(self.value.pointer(), name), - self.ty.clone(), - ) + Operand::new(self.value.load(builder, name), self.ty.clone()) } pub(crate) fn store( @@ -84,8 +93,7 @@ impl<'ink> Place<'ink> { operand: &Operand<'ink>, ) -> InstructionValue<'ink> { debug_assert_eq!(self.ty(), operand.ty()); - debug_assert_eq!(self.value.pointee(), operand.value().get_type()); - builder.build_store(self.value.pointer(), operand.value()) + self.value.store(builder, operand.value()) } pub(crate) fn store_value( @@ -93,7 +101,6 @@ impl<'ink> Place<'ink> { builder: &Builder<'ink>, value: BasicValueEnum<'ink>, ) -> InstructionValue<'ink> { - debug_assert_eq!(self.value.pointee(), value.get_type()); - builder.build_store(self.value.pointer(), value) + self.value.store(builder, value) } }