Skip to content

Commit 303837f

Browse files
committed
fix: Handle SSA parameter values and pointer aggregates in LLVM backend
- Add handling for HirValueKind::Parameter in func.values iteration to map SSA-created parameter IDs to LLVM function parameters - Fix InsertValue for pointer aggregates (from Alloca) by using GEP + Store instead of direct InsertValue - Fix ExtractValue for pointer aggregates using GEP + Load - Track allocated types in type_map for proper GEP type handling - Clear value_map and type_map between function compilations This fixes function calls with parameters and struct field access.
1 parent 05afdc3 commit 303837f

1 file changed

Lines changed: 127 additions & 38 deletions

File tree

crates/compiler/src/llvm_backend.rs

Lines changed: 127 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@ impl<'ctx> LLVMBackend<'ctx> {
227227
// Clear block, value, and phi maps for this function
228228
self.block_map.clear();
229229
self.phi_map.clear();
230-
// Note: We keep function parameters in value_map from declare_function
230+
self.value_map.clear(); // Clear value_map between functions
231+
self.type_map.clear(); // Clear type_map between functions
231232

232233
// Map function parameters to HIR value IDs and store their types
233234
for (i, param) in func.signature.params.iter().enumerate() {
@@ -236,13 +237,21 @@ impl<'ctx> LLVMBackend<'ctx> {
236237
self.type_map.insert(param.id, param.ty.clone());
237238
}
238239

239-
// Map constant values and special instruction values to LLVM values
240+
// Map constant values, parameters, and special instruction values to LLVM values
240241
for (value_id, value) in &func.values {
241242
match &value.kind {
242243
HirValueKind::Constant(constant) => {
243244
let llvm_constant = self.compile_constant(constant)?;
244245
self.value_map.insert(*value_id, llvm_constant);
245246
}
247+
HirValueKind::Parameter(param_index) => {
248+
// SSA creates new value IDs for parameters with HirValueKind::Parameter
249+
// Map these to the actual LLVM function parameters
250+
if let Some(param_value) = fn_value.get_nth_param(*param_index) {
251+
self.value_map.insert(*value_id, param_value);
252+
self.type_map.insert(*value_id, value.ty.clone());
253+
}
254+
}
246255
HirValueKind::Instruction => {
247256
// For instruction values that appear in func.values (like undef structs),
248257
// create an undef value of the appropriate type
@@ -671,6 +680,8 @@ impl<'ctx> LLVMBackend<'ctx> {
671680
self.builder.build_alloca(llvm_ty, "alloca")?
672681
};
673682
self.value_map.insert(*result, alloca.into());
683+
// Record the type as a pointer to the allocated type
684+
self.type_map.insert(*result, HirType::Ptr(Box::new(ty.clone())));
674685
}
675686

676687
HirInstruction::GetElementPtr { result, ty, ptr, indices } => {
@@ -697,7 +708,7 @@ impl<'ctx> LLVMBackend<'ctx> {
697708

698709
// ========== Aggregate Operations ==========
699710
HirInstruction::ExtractValue { result, ty, aggregate, indices } => {
700-
let mut current_value = self.get_value(*aggregate)?;
711+
let agg_value = self.get_value(*aggregate)?;
701712

702713
// Extract value from struct or array using chained extraction
703714
// LLVM's build_extract_value only takes a single index, so we need to
@@ -708,43 +719,95 @@ impl<'ctx> LLVMBackend<'ctx> {
708719
));
709720
}
710721

711-
// Apply each index in sequence for nested extraction
712-
for (i, &index) in indices.iter().enumerate() {
713-
let is_last = i == indices.len() - 1;
714-
let name = if is_last {
715-
"extract"
722+
// Check if aggregate is a pointer (e.g., from Alloca)
723+
// In this case, we use GEP + Load instead of ExtractValue
724+
if agg_value.is_pointer_value() {
725+
// ty is the result type (the field type), not the aggregate type
726+
// We need to get the aggregate type from the HIR value info
727+
let result_ty = self.translate_type(ty)?;
728+
let ptr = agg_value.into_pointer_value();
729+
730+
// For pointer-based struct access, we need the aggregate type.
731+
// We can get this from the instruction that produced the pointer.
732+
// For now, get it from the type_map
733+
let agg_hir_type = self.type_map.get(aggregate).cloned();
734+
735+
let pointee_type = if let Some(HirType::Ptr(inner)) = agg_hir_type {
736+
self.translate_type(&inner)?
737+
} else if let Some(hir_ty) = agg_hir_type {
738+
// If it's not a pointer type in HIR but is a pointer value,
739+
// the type might already be the struct type
740+
self.translate_type(&hir_ty)?
716741
} else {
717-
&format!("extract_nested_{}", i)
742+
// Fallback: try to infer from the result type
743+
// This won't work correctly, but provides a fallback
744+
result_ty
718745
};
719746

720-
// Try to extract from struct
721-
if let Ok(struct_val) = TryInto::<inkwell::values::StructValue>::try_into(current_value) {
722-
let extracted = self.builder.build_extract_value(
723-
struct_val,
724-
index,
725-
name
726-
)?;
727-
current_value = extracted.as_basic_value_enum();
728-
} else if let Ok(array_val) = TryInto::<inkwell::values::ArrayValue>::try_into(current_value) {
729-
// For arrays, we can also use extract_value
730-
let extracted = self.builder.build_extract_value(
731-
array_val,
732-
index,
733-
name
734-
)?;
735-
current_value = extracted.as_basic_value_enum();
736-
} else {
737-
return Err(CompilerError::CodeGen(
738-
format!("ExtractValue can only be used on struct or array types, got: {:?}", current_value.get_type())
739-
));
747+
// Build GEP indices: first index is always 0 to dereference the pointer
748+
// then follow with the struct field indices
749+
let mut gep_indices: Vec<inkwell::values::IntValue> = vec![
750+
self.context.i32_type().const_int(0, false)
751+
];
752+
for &idx in indices {
753+
gep_indices.push(self.context.i32_type().const_int(idx as u64, false));
740754
}
741-
}
742755

743-
self.value_map.insert(*result, current_value);
756+
// GEP to get address of the field
757+
let field_ptr = unsafe {
758+
self.builder.build_gep(
759+
pointee_type,
760+
ptr,
761+
&gep_indices,
762+
"field_ptr"
763+
)?
764+
};
765+
766+
// Load the field value using the result type
767+
let loaded = self.builder.build_load(result_ty, field_ptr, "field_load")?;
768+
self.value_map.insert(*result, loaded);
769+
} else {
770+
// Original behavior: work on value types
771+
let mut current_value = agg_value;
772+
773+
// Apply each index in sequence for nested extraction
774+
for (i, &index) in indices.iter().enumerate() {
775+
let is_last = i == indices.len() - 1;
776+
let name = if is_last {
777+
"extract"
778+
} else {
779+
&format!("extract_nested_{}", i)
780+
};
781+
782+
// Try to extract from struct
783+
if let Ok(struct_val) = TryInto::<inkwell::values::StructValue>::try_into(current_value) {
784+
let extracted = self.builder.build_extract_value(
785+
struct_val,
786+
index,
787+
name
788+
)?;
789+
current_value = extracted.as_basic_value_enum();
790+
} else if let Ok(array_val) = TryInto::<inkwell::values::ArrayValue>::try_into(current_value) {
791+
// For arrays, we can also use extract_value
792+
let extracted = self.builder.build_extract_value(
793+
array_val,
794+
index,
795+
name
796+
)?;
797+
current_value = extracted.as_basic_value_enum();
798+
} else {
799+
return Err(CompilerError::CodeGen(
800+
format!("ExtractValue can only be used on struct or array types, got: {:?}", current_value.get_type())
801+
));
802+
}
803+
}
804+
805+
self.value_map.insert(*result, current_value);
806+
}
744807
}
745808

746809
HirInstruction::InsertValue { result, ty, aggregate, value, indices } => {
747-
let mut current_agg = self.get_value(*aggregate)?;
810+
let current_agg = self.get_value(*aggregate)?;
748811
let val = self.get_value(*value)?;
749812

750813
if indices.is_empty() {
@@ -753,12 +816,38 @@ impl<'ctx> LLVMBackend<'ctx> {
753816
));
754817
}
755818

756-
// For nested insertion, we need to:
757-
// 1. Extract the nested aggregate at indices[0..n-1]
758-
// 2. Insert value at the final index
759-
// 3. Insert the modified nested aggregate back
760-
if indices.len() == 1 {
761-
// Simple case: single-level insertion
819+
// Check if aggregate is a pointer (e.g., from Alloca)
820+
// In this case, we use GEP + Store instead of InsertValue
821+
if current_agg.is_pointer_value() {
822+
let llvm_ty = self.translate_type(ty)?;
823+
let ptr = current_agg.into_pointer_value();
824+
825+
// Build GEP indices: first index is always 0 to dereference the pointer
826+
// then follow with the struct field indices
827+
let mut gep_indices: Vec<inkwell::values::IntValue> = vec![
828+
self.context.i32_type().const_int(0, false)
829+
];
830+
for &idx in indices {
831+
gep_indices.push(self.context.i32_type().const_int(idx as u64, false));
832+
}
833+
834+
// GEP to get address of the field
835+
let field_ptr = unsafe {
836+
self.builder.build_gep(
837+
llvm_ty,
838+
ptr,
839+
&gep_indices,
840+
"field_ptr"
841+
)?
842+
};
843+
844+
// Store the value
845+
self.builder.build_store(field_ptr, val)?;
846+
847+
// The result is the original pointer (for chaining)
848+
self.value_map.insert(*result, current_agg);
849+
} else if indices.len() == 1 {
850+
// Simple case: single-level insertion on a value
762851
let inserted = if let Ok(struct_val) = TryInto::<inkwell::values::StructValue>::try_into(current_agg) {
763852
self.builder.build_insert_value(
764853
struct_val,

0 commit comments

Comments
 (0)