Skip to content

Commit 77827dc

Browse files
committed
codegen: Generate function pointer calls
1 parent 892ce11 commit 77827dc

File tree

9 files changed

+644
-65
lines changed

9 files changed

+644
-65
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,16 @@ impl AstNode {
13971397
pub fn with_metadata(self, metadata: MetaData) -> AstNode {
13981398
AstNode { metadata: Some(metadata), ..self }
13991399
}
1400+
1401+
pub fn get_deref_expr(&self) -> Option<&ReferenceExpr> {
1402+
match &self.stmt {
1403+
AstStatement::ReferenceExpr(expr) => match expr {
1404+
ReferenceExpr { access: ReferenceAccess::Deref, .. } => Some(expr),
1405+
_ => None,
1406+
},
1407+
_ => None,
1408+
}
1409+
}
14001410
}
14011411

14021412
#[derive(Clone, Copy, Debug, PartialEq, Eq)]

src/builtins.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ lazy_static! {
5050
generic_name_resolver: no_generic_name_resolver,
5151
code: |generator, params, location| {
5252
if let [reference] = params {
53+
// Return the pointer value of a function when dealing with them, e.g. `ADR(MyFb.myMethod)`
54+
if let Some(StatementAnnotation::Function { qualified_name, .. }) = generator.annotations.get(reference) {
55+
if let Some(fn_value) = generator.llvm_index.find_associated_implementation(qualified_name) {
56+
return Ok(ExpressionValue::RValue(fn_value.as_global_value().as_pointer_value().as_basic_value_enum()));
57+
}
58+
}
59+
5360
generator
5461
.generate_lvalue(reference)
5562
.map(|it| ExpressionValue::RValue(it.as_basic_value_enum()))
@@ -103,6 +110,13 @@ lazy_static! {
103110
generic_name_resolver: no_generic_name_resolver,
104111
code: |generator, params, location| {
105112
if let [reference] = params {
113+
// Return the pointer value of a function when dealing with them, e.g. `ADR(MyFb.myMethod)`
114+
if let Some(StatementAnnotation::Function { qualified_name, .. }) = generator.annotations.get(reference) {
115+
if let Some(fn_value) = generator.llvm_index.find_associated_implementation(qualified_name) {
116+
return Ok(ExpressionValue::RValue(fn_value.as_global_value().as_pointer_value().as_basic_value_enum()));
117+
}
118+
}
119+
106120
generator
107121
.generate_lvalue(reference)
108122
.map(|it| ExpressionValue::RValue(it.as_basic_value_enum()))

src/codegen/generators/data_type_generator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
330330
AnyTypeEnum::FunctionType(_) => unreachable!(),
331331
};
332332

333-
eprintln!("created function type: {}", fn_type.print_to_string());
334333
Ok(fn_type)
335334
}
336335

src/codegen/generators/expression_generator.rs

Lines changed: 83 additions & 3 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::{
@@ -490,8 +491,9 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
490491
operator: &AstNode,
491492
parameters: Option<&AstNode>,
492493
) -> Result<ExpressionValue<'ink>, Diagnostic> {
494+
// Check if we are dealing with something alike `foo^(...)`
493495
if self.annotations.get(operator).is_some_and(StatementAnnotation::is_fnptr) {
494-
unimplemented!();
496+
return self.generate_fnptr_call(operator, parameters);
495497
}
496498

497499
// find the pou we're calling
@@ -588,6 +590,65 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
588590
value
589591
}
590592

593+
fn generate_fnptr_call(
594+
&self,
595+
operator: &AstNode,
596+
arguments: Option<&AstNode>,
597+
) -> Result<ExpressionValue<'ink>, Diagnostic> {
598+
let Some(ReferenceExpr { base: Some(ref base), .. }) = operator.get_deref_expr() else {
599+
unreachable!("internal error, invalid function invocation")
600+
};
601+
602+
let qualified_pou_name = self.annotations.get(operator).unwrap().qualified_name().unwrap();
603+
let impl_entry = self.index.find_implementation_by_name(qualified_pou_name).unwrap();
604+
debug_assert!(impl_entry.is_method(), "internal error, invalid function invocation");
605+
606+
// Get the associated variable then load it, e.g. `%localFnPtrVariable = alloca void (%Fb*)*, align 8`
607+
// followed by `%1 = load void (%Fb*)*, void (%Fb*)** %localFnPtrVariable, align 8``
608+
let function_pointer_value = match self.generate_expression_value(base)? {
609+
ExpressionValue::LValue(value) => self.llvm.load_pointer(&value, "").into_pointer_value(),
610+
ExpressionValue::RValue(_) => unreachable!(),
611+
};
612+
613+
// Generate the argument list; our assumption is function pointers are only supported for methods
614+
// right now, hence we explicitly fetch the instance arguments from the list. In desugared code it we
615+
// would have something alike `fnPtr^(instanceFb, arg1, arg2, ..., argN)`
616+
let arguments = {
617+
let arguments = arguments.map(flatten_expression_list).unwrap_or_default();
618+
let (instance, arguments) = match arguments.len() {
619+
0 => panic!("invalid desugared code, no instance argument found"),
620+
1 => (self.generate_lvalue(&arguments[0])?, [].as_slice()),
621+
_ => (self.generate_lvalue(&arguments[0])?, &arguments[1..]),
622+
};
623+
624+
let mut generated_arguments = self.generate_function_arguments(
625+
self.index.find_pou(qualified_pou_name).unwrap(),
626+
arguments,
627+
self.index.get_declared_parameters(qualified_pou_name),
628+
)?;
629+
630+
generated_arguments.insert(0, instance.as_basic_value_enum().into());
631+
generated_arguments
632+
};
633+
634+
// Finally generate the function pointer call
635+
let callable = CallableValue::try_from(function_pointer_value).unwrap();
636+
let call = self.llvm.builder.build_call(callable, &arguments, "fnptr_call");
637+
638+
let value = match call.try_as_basic_value() {
639+
Either::Left(value) => value,
640+
Either::Right(_) => {
641+
// TODO: When is this neccessary?
642+
get_llvm_int_type(self.llvm.context, INT_SIZE, INT_TYPE)
643+
.ptr_type(AddressSpace::from(ADDRESS_SPACE_CONST))
644+
.const_null()
645+
.as_basic_value_enum()
646+
}
647+
};
648+
649+
Ok(ExpressionValue::RValue(value))
650+
}
651+
591652
/// copies the output values to the assigned output variables
592653
/// - `parameter_struct` a pointer to a struct-instance that holds all function-parameters
593654
/// - `function_name` the name of the callable
@@ -2614,13 +2675,32 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
26142675
}
26152676

26162677
// `INT#target` (INT = base)
2617-
(ReferenceAccess::Cast(target), Some(_base)) => {
2678+
(ReferenceAccess::Cast(target), Some(base)) => {
26182679
if target.as_ref().is_identifier() {
26192680
let mr =
26202681
AstFactory::create_member_reference(target.as_ref().clone(), None, target.get_id());
26212682
self.generate_expression_value(&mr)
2622-
} else {
2683+
} else if target.as_ref().is_literal(){
26232684
self.generate_expression_value(target.as_ref())
2685+
} else {
2686+
// Otherwise just bitcast the target to the given type
2687+
let base_type = self.annotations.get_type_or_void(base, self.index);
2688+
let base_type_name = base_type.get_name();
2689+
2690+
// Generate the value we're casting
2691+
let target_value = self.generate_expression_value(target.as_ref())?;
2692+
2693+
// Get the LLVM type for the cast target
2694+
let target_llvm_type = self.llvm_index.get_associated_type(base_type_name)
2695+
.map(|t| t.ptr_type(AddressSpace::from(0)))
2696+
.unwrap_or_else(|_| self.llvm.context.i8_type().ptr_type(AddressSpace::from(0)));
2697+
2698+
// Perform the bitcast
2699+
let basic_value = target_value.get_basic_value_enum();
2700+
let cast_ptr = self.llvm.builder.build_bitcast(basic_value, target_llvm_type, "cast");
2701+
let cast_value = ExpressionValue::RValue(cast_ptr);
2702+
2703+
Ok(cast_value)
26242704
}
26252705
}
26262706

src/codegen/llvm_index.rs

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
22
use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum, PointerType};
3-
use inkwell::values::{BasicValueEnum, FunctionValue, GlobalValue, PointerValue};
3+
use inkwell::values::{AnyValue, BasicValueEnum, FunctionValue, GlobalValue, PointerValue};
44
use inkwell::AddressSpace;
55
use plc_diagnostics::diagnostics::Diagnostic;
66
use plc_source::source_location::SourceLocation;
@@ -111,7 +111,10 @@ impl<'ink> LlvmTypedIndex<'ink> {
111111
type_name: &str,
112112
target_type: AnyTypeEnum<'ink>,
113113
) -> Result<(), Diagnostic> {
114-
self.type_associations.insert(type_name.to_lowercase(), target_type);
114+
let name = type_name.to_lowercase();
115+
116+
log::debug!("registered `{name}` as type `{}`", target_type.print_to_string());
117+
self.type_associations.insert(name, target_type);
115118
Ok(())
116119
}
117120

@@ -120,7 +123,10 @@ impl<'ink> LlvmTypedIndex<'ink> {
120123
type_name: &str,
121124
target_type: AnyTypeEnum<'ink>,
122125
) -> Result<(), Diagnostic> {
123-
self.pou_type_associations.insert(type_name.to_lowercase(), target_type);
126+
let name = type_name.to_lowercase();
127+
128+
log::debug!("registered `{name}` as POU type `{}`", target_type.print_to_string());
129+
self.pou_type_associations.insert(name, target_type);
124130
Ok(())
125131
}
126132

@@ -129,7 +135,10 @@ impl<'ink> LlvmTypedIndex<'ink> {
129135
type_name: &str,
130136
initial_value: BasicValueEnum<'ink>,
131137
) -> Result<(), Diagnostic> {
132-
self.initial_value_associations.insert(type_name.to_lowercase(), initial_value);
138+
let name = type_name.to_lowercase();
139+
140+
log::debug!("registered `{name}` as initial value type `{}`", initial_value.print_to_string());
141+
self.initial_value_associations.insert(name, initial_value);
133142
Ok(())
134143
}
135144

@@ -139,8 +148,62 @@ impl<'ink> LlvmTypedIndex<'ink> {
139148
variable_name: &str,
140149
target_value: PointerValue<'ink>,
141150
) -> Result<(), Diagnostic> {
142-
let qualified_name = qualified_name(container_name, variable_name);
143-
self.loaded_variable_associations.insert(qualified_name.to_lowercase(), target_value);
151+
let name = qualified_name(container_name, variable_name).to_lowercase();
152+
153+
log::debug!("registered `{name}` as loaded local type `{}`", target_value.print_to_string());
154+
self.loaded_variable_associations.insert(name, target_value);
155+
Ok(())
156+
}
157+
158+
pub fn associate_global(
159+
&mut self,
160+
variable_name: &str,
161+
global_variable: GlobalValue<'ink>,
162+
) -> Result<(), Diagnostic> {
163+
let name = variable_name.to_lowercase();
164+
165+
log::debug!("registered `{name}` as global variable type `{}`", global_variable.print_to_string());
166+
self.global_values.insert(name.clone(), global_variable);
167+
self.initial_value_associations.insert(name, global_variable.as_pointer_value().into());
168+
169+
// FIXME: Do we want to call .insert_new_got_index() here?
170+
Ok(())
171+
}
172+
173+
pub fn associate_implementation(
174+
&mut self,
175+
callable_name: &str,
176+
function_value: FunctionValue<'ink>,
177+
) -> Result<(), Diagnostic> {
178+
let name = callable_name.to_lowercase();
179+
180+
log::debug!("registered `{name}` as implementation type `{}`", function_value.print_to_string());
181+
self.implementations.insert(name, function_value);
182+
183+
Ok(())
184+
}
185+
186+
pub fn associate_utf08_literal(&mut self, literal: &str, literal_variable: GlobalValue<'ink>) {
187+
log::debug!("registered literal {literal}");
188+
self.utf08_literals.insert(literal.to_string(), literal_variable);
189+
}
190+
191+
pub fn associate_utf16_literal(&mut self, literal: &str, literal_variable: GlobalValue<'ink>) {
192+
log::debug!("registered literal {literal}");
193+
self.utf16_literals.insert(literal.to_string(), literal_variable);
194+
}
195+
196+
pub fn associate_got_index(&mut self, variable_name: &str, index: u64) -> Result<(), Diagnostic> {
197+
let name = variable_name.to_lowercase();
198+
self.got_indices.insert(name, index);
199+
200+
Ok(())
201+
}
202+
203+
pub fn insert_new_got_index(&mut self, variable_name: &str) -> Result<(), Diagnostic> {
204+
let idx = self.got_indices.values().max().copied().unwrap_or(0);
205+
self.got_indices.insert(variable_name.to_lowercase(), idx);
206+
144207
Ok(())
145208
}
146209

@@ -192,42 +255,6 @@ impl<'ink> LlvmTypedIndex<'ink> {
192255
.or_else(|| self.parent_index.and_then(|it| it.find_associated_initial_value(type_name)))
193256
}
194257

195-
pub fn associate_global(
196-
&mut self,
197-
variable_name: &str,
198-
global_variable: GlobalValue<'ink>,
199-
) -> Result<(), Diagnostic> {
200-
self.global_values.insert(variable_name.to_lowercase(), global_variable);
201-
self.initial_value_associations
202-
.insert(variable_name.to_lowercase(), global_variable.as_pointer_value().into());
203-
204-
// FIXME: Do we want to call .insert_new_got_index() here?
205-
206-
Ok(())
207-
}
208-
209-
pub fn associate_got_index(&mut self, variable_name: &str, index: u64) -> Result<(), Diagnostic> {
210-
self.got_indices.insert(variable_name.to_lowercase(), index);
211-
Ok(())
212-
}
213-
214-
pub fn insert_new_got_index(&mut self, variable_name: &str) -> Result<(), Diagnostic> {
215-
let idx = self.got_indices.values().max().copied().unwrap_or(0);
216-
217-
self.got_indices.insert(variable_name.to_lowercase(), idx);
218-
219-
Ok(())
220-
}
221-
222-
pub fn associate_implementation(
223-
&mut self,
224-
callable_name: &str,
225-
function_value: FunctionValue<'ink>,
226-
) -> Result<(), Diagnostic> {
227-
self.implementations.insert(callable_name.to_lowercase(), function_value);
228-
Ok(())
229-
}
230-
231258
pub fn find_associated_implementation(&self, callable_name: &str) -> Option<FunctionValue<'ink>> {
232259
self.implementations
233260
.get(&callable_name.to_lowercase())
@@ -260,20 +287,12 @@ impl<'ink> LlvmTypedIndex<'ink> {
260287
self.constants.get(qualified_name).copied()
261288
}
262289

263-
pub fn associate_utf08_literal(&mut self, literal: &str, literal_variable: GlobalValue<'ink>) {
264-
self.utf08_literals.insert(literal.to_string(), literal_variable);
265-
}
266-
267290
pub fn find_utf08_literal_string(&self, literal: &str) -> Option<&GlobalValue<'ink>> {
268291
self.utf08_literals
269292
.get(literal)
270293
.or_else(|| self.parent_index.and_then(|it| it.find_utf08_literal_string(literal)))
271294
}
272295

273-
pub fn associate_utf16_literal(&mut self, literal: &str, literal_variable: GlobalValue<'ink>) {
274-
self.utf16_literals.insert(literal.to_string(), literal_variable);
275-
}
276-
277296
pub fn find_utf16_literal_string(&self, literal: &str) -> Option<&GlobalValue<'ink>> {
278297
self.utf16_literals
279298
.get(literal)

src/codegen/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod constants_tests;
77
mod debug_tests;
88
mod directaccess_test;
99
mod expression_tests;
10+
mod fnptr;
1011
mod function_tests;
1112
mod generics_test;
1213
mod initialization_test;

0 commit comments

Comments
 (0)