Skip to content

Commit 81c0500

Browse files
committed
Add and fix some tests
1 parent 599da5a commit 81c0500

File tree

7 files changed

+428
-245
lines changed

7 files changed

+428
-245
lines changed

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
demo:
2+
cargo r -- target/demo.st tests/lit/util/printf.pli --linker=clang && ./demo.st.out
3+
4+
ir:
5+
cargo r -- target/demo.st tests/lit/util/printf.pli --linker=clang --ir --output=/dev/stdout
6+
7+
test:
8+
cargo nextest run --no-fail-fast --workspace && ./scripts/build.sh --lit
9+
10+
.PHONY: demo ir test

src/codegen/generators/data_type_generator.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
typesystem::DataType,
1414
};
1515

16-
use inkwell::types::{AnyType, AnyTypeEnum, BasicMetadataTypeEnum, FunctionType};
16+
use inkwell::types::{AnyType, AnyTypeEnum, FunctionType};
1717
use inkwell::{
1818
types::{BasicType, BasicTypeEnum},
1919
values::{BasicValue, BasicValueEnum},
@@ -104,7 +104,6 @@ pub fn generate_data_types<'ink>(
104104
log::debug!("creating type `{name}`");
105105
let gen_type = generator.create_type(name, user_type)?;
106106
generator.types_index.associate_type(name, gen_type)?
107-
//Get and associate debug type
108107
}
109108

110109
for (name, user_type) in &pou_types {
@@ -181,35 +180,36 @@ pub fn generate_data_types<'ink>(
181180
}
182181

183182
impl<'ink> DataTypeGenerator<'ink, '_> {
184-
fn create_function_type(&self, name: &str) -> Result<FunctionType<'ink>, Diagnostic> {
185-
let return_type_dt = self.index.find_return_type(name).unwrap_or(self.index.get_void_type());
186-
183+
fn create_function_type(&mut self, name: &str) -> Result<FunctionType<'ink>, Diagnostic> {
187184
let return_type = self
188185
.types_index
189-
.find_associated_type(&return_type_dt.name)
186+
.find_associated_type(
187+
self.index.find_return_type(name).unwrap_or(self.index.get_void_type()).get_name(),
188+
)
190189
.map(|opt| opt.as_any_type_enum())
191190
.unwrap_or(self.llvm.context.void_type().into());
192191

193192
let mut parameter_types = vec![];
194193

195-
// For methods, we need to add the 'this' parameter as the first parameter
194+
// We need to add the POU type as the first parameter when dealing with methods
196195
if let Some(PouIndexEntry::Method { parent_name, .. }) = self.index.find_pou(name) {
197-
// Get the owner class type and add it as the first parameter (this pointer)
198-
if let Ok(owner_type) = self.types_index.get_associated_type(parent_name) {
199-
parameter_types.push(owner_type.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into());
200-
}
196+
let ty = self.types_index.get_associated_type(parent_name).expect("must exist");
197+
parameter_types.push(ty.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into());
201198
}
202199

203200
// Add the declared parameters
204-
let declared_params = self
205-
.index
206-
.get_declared_parameters(name)
207-
.iter()
208-
.flat_map(|param| self.types_index.get_associated_type(&param.data_type_name))
209-
.map(|opt| opt.into())
210-
.collect::<Vec<BasicMetadataTypeEnum>>();
211-
212-
parameter_types.extend(declared_params);
201+
for parameter in self.index.get_declared_parameters(name) {
202+
// XXX: This is somewhat hacky, perhaps we can find a better solution? The main problem is that
203+
// we query the data types of the parameters which may not yet have been registered. For example,
204+
// at the time of writing this comment the `__auto_pointer_to_DINT` type was not registered for a
205+
// VAR_IN_OUT parameter in a POU for which we create the function type here. I suppose the creation
206+
// of function types must be done as the last step? Alternatively we could register opaque types and
207+
// expand them later?
208+
let ty = self
209+
.create_type(parameter.get_name(), self.index.get_type(&parameter.data_type_name).unwrap())
210+
.unwrap();
211+
parameter_types.push(ty.try_into().unwrap());
212+
}
213213

214214
let fn_type = match return_type {
215215
AnyTypeEnum::IntType(value) => value.fn_type(parameter_types.as_slice(), false),

src/codegen/generators/expression_generator.rs

Lines changed: 64 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use inkwell::{
99
},
1010
AddressSpace, FloatPredicate, IntPredicate,
1111
};
12+
use itertools::Either;
1213
use rustc_hash::FxHashSet;
1314

1415
use plc_ast::{
@@ -30,13 +31,12 @@ use crate::{
3031
llvm_typesystem::{cast_if_needed, get_llvm_int_type},
3132
},
3233
index::{
33-
const_expressions::ConstId, ArgumentType, ImplementationIndexEntry, Index, PouIndexEntry,
34-
VariableIndexEntry, VariableType,
34+
const_expressions::ConstId, ArgumentType, ImplementationIndexEntry, ImplementationType, Index,
35+
PouIndexEntry, VariableIndexEntry, VariableType,
3536
},
3637
resolver::{AnnotationMap, AstAnnotations, StatementAnnotation},
37-
typesystem,
3838
typesystem::{
39-
is_same_type_class, DataType, DataTypeInformation, DataTypeInformationProvider, Dimension,
39+
self, is_same_type_class, DataType, DataTypeInformation, DataTypeInformationProvider, Dimension,
4040
StringEncoding, VarArgs, DINT_TYPE, INT_SIZE, INT_TYPE, LINT_TYPE,
4141
},
4242
};
@@ -485,60 +485,45 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
485485
operator: &AstNode,
486486
parameters: Option<&AstNode>,
487487
) -> Result<ExpressionValue<'ink>, Diagnostic> {
488-
// Extract the base expression from the dereference operator
489-
let base = if let AstStatement::ReferenceExpr(ReferenceExpr {
490-
access: ReferenceAccess::Deref,
491-
base: Some(base),
492-
}) = operator.get_stmt()
493-
{
494-
base
495-
} else {
496-
return Err(Diagnostic::codegen_error(
497-
"Expected dereference expression with base for function pointer call",
498-
operator,
499-
));
488+
// Assumptions:
489+
// - Used internally only (during lowering for polymorphism) hence unwraps should be fine
490+
// - Stateful function call (i.e. the instance is always part of the argument list at position 0)
491+
// - Pointer does not point to another pointer, rather directly to the function implementation
492+
// - The function pointer is dereferenced, e.g. `myFnPtr^(...)` rather than `myFnPtr(...)`
493+
494+
// fnPtr^(instanceFbA);
495+
// ^^^^^
496+
let AstStatement::ReferenceExpr(ReferenceExpr { access: ReferenceAccess::Deref, base: Some(base) }) =
497+
&operator.stmt
498+
else {
499+
unreachable!("internal error, invalid function invocation")
500500
};
501501

502-
// Get the function pointer type information
503-
let function_pointer_type = self.annotations.get_type_or_void(base, self.index);
502+
// fnPtr -> PointerType { inner_type_name: "FbA.doNothing" }
503+
let pointer_type = self.annotations.get_type(base, self.index).unwrap(); // -> PointerType, FbA.doNothing
504504

505-
// Extract the target function type from the REF_TO pointer type
506-
let target_function_type = if let DataTypeInformation::Pointer { inner_type_name, .. } =
507-
function_pointer_type.get_type_information()
508-
{
509-
self.index.find_effective_type_by_name(inner_type_name).ok_or_else(|| {
510-
Diagnostic::codegen_error(
511-
format!("Could not find function type: {}", inner_type_name).as_str(),
512-
operator,
513-
)
514-
})?
515-
} else {
516-
return Err(Diagnostic::codegen_error("Expected pointer type for function pointer", operator));
505+
// PointerType { inner_type_name: "FbA.doNothing" } -> DataType { name: "FbA.doNothing", information: Struct { source: Pou }
506+
let target_function_type = match pointer_type.get_type_information() {
507+
DataTypeInformation::Pointer { inner_type_name, .. } => {
508+
self.index.find_effective_type_by_name(inner_type_name).unwrap()
509+
}
510+
511+
_ => todo!(),
517512
};
518513

519-
// Get the function from the index to determine its signature
520-
let function_pou = self.index.find_pou(target_function_type.get_name()).ok_or_else(|| {
521-
Diagnostic::codegen_error(
522-
format!("Could not find function POU: {}", target_function_type.get_name()).as_str(),
523-
operator,
524-
)
525-
})?;
514+
// "fba.doNothing" -> "Method { name: "FbA.doNothing", parent_name: "FbA", property: None, declaration_kind: Concrete, return_type: "VOID", instance_struct_name: "FbA.doNothing", linkage: Internal, location: SourceLocation { span: Range(2:19 - 2:28), file: Some("<internal>") } }"
515+
let function_pou = self.index.find_pou(target_function_type.get_name()).unwrap();
526516

527-
// Get the function implementation
528-
let implementation = function_pou.find_implementation(self.index).ok_or_else(|| {
529-
Diagnostic::codegen_error(
530-
format!("Could not find implementation for function: {}", target_function_type.get_name())
531-
.as_str(),
532-
operator,
533-
)
534-
})?;
517+
// "Method { name: FbA.doNothing, ..." -> "ImplementationIndexEntry { call_name: "FbA.doNothing"
518+
let implementation = function_pou.find_implementation(self.index).unwrap();
535519

536520
// Generate the arguments list
537521
let parameters_list = parameters.map(flatten_expression_list).unwrap_or_default();
538522

539-
// For method function pointers, we need to handle the instance parameter specially
540523
let (method_instance, actual_parameters) =
541-
if matches!(function_pou, PouIndexEntry::Method { .. }) && !parameters_list.is_empty() {
524+
if matches!(implementation.implementation_type, ImplementationType::Method { .. })
525+
&& !parameters_list.is_empty()
526+
{
542527
// For methods, the first parameter is the instance, the rest are method parameters
543528
// But if there's only one parameter, it's just the instance and there are no method parameters
544529
if parameters_list.len() == 1 {
@@ -547,84 +532,26 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
547532
(Some(&parameters_list[0]), &parameters_list[1..])
548533
}
549534
} else {
550-
(None, parameters_list.as_slice())
535+
panic!("{implementation:#?}");
551536
};
552537

553-
// For function blocks, we need special handling for parameters
554-
if matches!(function_pou, PouIndexEntry::FunctionBlock { .. }) {
555-
// Get the function block instance pointer
556-
if let Ok(fb_ptr) = self
557-
.generate_expression_value(base)
558-
.map(|v| v.as_r_value(self.llvm, Some("deref".to_string())))
559-
{
560-
// For function blocks, parameters need to be assigned to the instance variables
561-
// before calling the function block body
562-
563-
// Generate parameter assignments to the function block instance
564-
let fb_instance_ptr = fb_ptr.into_pointer_value();
565-
for argument in parameters_list.iter() {
566-
self.generate_call_struct_argument_assignment(&CallParameterAssignment {
567-
assignment: argument,
568-
function_name: implementation.get_type_name(),
569-
index: self
570-
.annotations
571-
.get_hint(argument)
572-
.and_then(StatementAnnotation::get_location_in_parent)
573-
.expect("arguments must have a type hint"),
574-
parameter_struct: fb_instance_ptr,
575-
})?;
576-
}
577-
578-
// Get the function block body implementation
579-
let function_value = self
580-
.llvm_index
581-
.find_associated_implementation(implementation.get_type_name())
582-
.ok_or_else(|| {
583-
Diagnostic::codegen_error(
584-
format!("Could not find LLVM function for: {}", implementation.get_type_name())
585-
.as_str(),
586-
operator,
587-
)
588-
})?;
589-
590-
// Generate the debug statement for the call
591-
self.register_debug_location(operator);
592-
593-
// Call the function block body with just the instance pointer
594-
let call = self.llvm.builder.build_call(function_value, &[fb_ptr.into()], "call");
595-
596-
// Handle output assignments after the call
597-
// Copy output values back to the assigned output variables
598-
self.assign_output_values(fb_instance_ptr, implementation.get_type_name(), parameters_list)?;
599-
600-
// Handle the return value
601-
let value = call
602-
.try_as_basic_value()
603-
.either(Ok, |_| {
604-
// Return null pointer for void functions
605-
Ok(get_llvm_int_type(self.llvm.context, INT_SIZE, INT_TYPE)
606-
.ptr_type(AddressSpace::from(ADDRESS_SPACE_CONST))
607-
.const_null()
608-
.as_basic_value_enum())
609-
})
610-
.map(ExpressionValue::RValue);
611-
612-
return value;
613-
}
614-
}
615-
616-
// Generate the function pointer value (only for non-FB cases)
538+
// TODO: Why alloca?
539+
// %fnPtr = alloca void (%FbA*)*, align 8
617540
let function_pointer_value = self.generate_expression_value(base)?;
541+
542+
// %1 = load void (%FbA*)*, void (%FbA*)** %fnPtr, align 8
618543
let function_pointer = match function_pointer_value {
619544
ExpressionValue::LValue(ptr) => {
620545
// Load the function pointer from memory
621546
self.llvm.load_pointer(&ptr, "").into_pointer_value()
622547
}
623-
ExpressionValue::RValue(val) => val.into_pointer_value(),
548+
_ => unreachable!("?"),
624549
};
625550

626-
// Generate function arguments for non-function block cases
551+
// TODO: Debug & Understand
627552
let declared_parameters = self.index.get_declared_parameters(implementation.get_type_name());
553+
554+
// TODO: Debug & Understand
628555
let mut arguments_list =
629556
self.generate_function_arguments(function_pou, actual_parameters, declared_parameters)?;
630557

@@ -649,57 +576,35 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
649576
}
650577
}
651578

652-
// Get the LLVM function type from the implementation
653-
let function_value = self
654-
.llvm_index
655-
.find_associated_implementation(implementation.get_type_name())
656-
.ok_or_else(|| {
657-
Diagnostic::codegen_error(
658-
format!("Could not find LLVM function for: {}", implementation.get_type_name()).as_str(),
659-
operator,
660-
)
661-
})?;
662-
let function_type = function_value.get_type();
663-
664579
// Cast the function pointer to the correct function type if needed
665580
let callable_ptr = if function_pointer.get_type().get_element_type().is_function_type() {
581+
// -> %1 = load void (%FbA*)*, void (%FbA*)** %fnPtr, align 8
666582
function_pointer
667583
} else {
668-
// Cast to the correct function pointer type
669-
self.llvm
670-
.builder
671-
.build_bitcast(
672-
function_pointer,
673-
function_type.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)),
674-
"cast_fn_ptr",
675-
)
676-
.into_pointer_value()
584+
unimplemented!()
677585
};
678586

679-
// Convert the function pointer to a CallableValue
680-
let callable = CallableValue::try_from(callable_ptr).map_err(|_| {
681-
Diagnostic::codegen_error("Could not convert function pointer to callable", operator)
682-
})?;
683-
684-
// Generate the debug statement for the call
685-
self.register_debug_location(operator);
587+
// -> CallableValue(Right(PointerValue { ptr_value: Value { name: "", address: 0x14d00acc0, is_const: false, is_null: false, is_undef: false, llvm_value: " %1 = load void (%FbA*)*, void (%FbA*)** %fnPtr, align 8", llvm_type: "void (%FbA*)*" } }))
588+
let callable = CallableValue::try_from(callable_ptr).unwrap();
686589

687590
// Generate the indirect call
591+
// -> call void %1(%FbA* %instanceFbA)
688592
let call = self.llvm.builder.build_call(callable, &arguments_list, "call");
593+
// panic!("{call:#?}");
689594

690595
// Handle the return value similar to regular function calls
691-
let value = call
692-
.try_as_basic_value()
693-
.either(Ok, |_| {
694-
// Return null pointer for void functions
695-
Ok(get_llvm_int_type(self.llvm.context, INT_SIZE, INT_TYPE)
696-
.ptr_type(AddressSpace::from(ADDRESS_SPACE_CONST))
697-
.const_null()
698-
.as_basic_value_enum())
699-
})
700-
.map(ExpressionValue::RValue);
596+
let value = match call.try_as_basic_value() {
597+
Either::Left(value) => {
598+
// If the call returns a value, we return it as an r-value
599+
value
600+
}
601+
Either::Right(_) => get_llvm_int_type(self.llvm.context, INT_SIZE, INT_TYPE)
602+
.ptr_type(AddressSpace::from(ADDRESS_SPACE_CONST))
603+
.const_null()
604+
.as_basic_value_enum(),
605+
};
701606

702-
value
607+
Ok(ExpressionValue::RValue(value))
703608
}
704609

705610
/// generates the given call-statement <operator>(<parameters>)
@@ -712,8 +617,12 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
712617
operator: &AstNode,
713618
parameters: Option<&AstNode>,
714619
) -> Result<ExpressionValue<'ink>, Diagnostic> {
715-
// TODO(vosa): Perhaps to be 100% sure also check if the operator has a pointer annotation; for now this works though
716-
if operator.is_deref() {
620+
if operator.is_deref()
621+
&& self
622+
.annotations
623+
.get_type(operator, self.index)
624+
.is_some_and(|opt| opt.is_method_pointer(self.index))
625+
{
717626
return self.generate_function_pointer_call(operator, parameters);
718627
}
719628

0 commit comments

Comments
 (0)