Skip to content

Commit ce68921

Browse files
authored
feat: Function Pointers (#1492)
Introduce function pointers, a prerequisite for polymorphism. Function pointers currently support methods and function blocks only, though supporting standalone functions in the future should be straightforward. Furthermore `__FPOINTER` has been introduced as a reserved keyword, which can be used to declare function pointers. For example `fooPtr: __FPOINTER MyFb.foo := ADR(MyFb.foo)`.
1 parent f6e9c77 commit ce68921

File tree

58 files changed

+3336
-200
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3336
-200
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,9 @@ pub enum DataType {
709709
/// e.g., `foo : POINTER TO DINT := ADR(stringValue)`. When true, the type of the pointer must match
710710
/// the referenced type exactly.
711711
type_safe: bool,
712+
713+
/// Indicates whether the pointer is a function pointer.
714+
is_function: bool,
712715
},
713716
StringType {
714717
name: Option<String>,
@@ -1262,6 +1265,17 @@ impl AstNode {
12621265
matches!(self.stmt, AstStatement::This)
12631266
}
12641267

1268+
pub fn is_this_deref(&self) -> bool {
1269+
match &self.stmt {
1270+
AstStatement::ReferenceExpr(
1271+
ReferenceExpr { access: ReferenceAccess::Deref, base: Some(base) },
1272+
..,
1273+
) => base.is_this(),
1274+
1275+
_ => false,
1276+
}
1277+
}
1278+
12651279
pub fn is_paren(&self) -> bool {
12661280
matches!(self.stmt, AstStatement::ParenExpression { .. })
12671281
}
@@ -1397,6 +1411,16 @@ impl AstNode {
13971411
pub fn with_metadata(self, metadata: MetaData) -> AstNode {
13981412
AstNode { metadata: Some(metadata), ..self }
13991413
}
1414+
1415+
pub fn get_deref_expr(&self) -> Option<&ReferenceExpr> {
1416+
match &self.stmt {
1417+
AstStatement::ReferenceExpr(expr) => match expr {
1418+
ReferenceExpr { access: ReferenceAccess::Deref, .. } => Some(expr),
1419+
_ => None,
1420+
},
1421+
_ => None,
1422+
}
1423+
}
14001424
}
14011425

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

src/builtins.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,25 @@ 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+
match generator.annotations.get(reference) {
55+
Some(StatementAnnotation::Function { qualified_name, .. }) => {
56+
if let Some(fn_value) = generator.llvm_index.find_associated_implementation(qualified_name) {
57+
return Ok(ExpressionValue::RValue(fn_value.as_global_value().as_pointer_value().as_basic_value_enum()));
58+
}
59+
}
60+
61+
Some(StatementAnnotation::Type { type_name }) => {
62+
if generator.index.find_type(type_name).is_some_and(|opt| opt.information.is_function_block()) {
63+
if let Some(fn_value) = generator.llvm_index.find_associated_implementation(type_name) {
64+
return Ok(ExpressionValue::RValue(fn_value.as_global_value().as_pointer_value().as_basic_value_enum()));
65+
}
66+
}
67+
}
68+
69+
_ => (),
70+
};
71+
5372
generator
5473
.generate_lvalue(reference)
5574
.map(|it| ExpressionValue::RValue(it.as_basic_value_enum()))
@@ -88,7 +107,7 @@ lazy_static! {
88107
let ptr_type = resolver::add_pointer_type(
89108
&mut annotator.annotation_map.new_index,
90109
input_type,
91-
true
110+
true,
92111
);
93112

94113
annotator.annotate(
@@ -103,6 +122,13 @@ lazy_static! {
103122
generic_name_resolver: no_generic_name_resolver,
104123
code: |generator, params, location| {
105124
if let [reference] = params {
125+
// Return the pointer value of a function when dealing with them, e.g. `ADR(MyFb.myMethod)`
126+
if let Some(StatementAnnotation::Function { qualified_name, .. }) = generator.annotations.get(reference) {
127+
if let Some(fn_value) = generator.llvm_index.find_associated_implementation(qualified_name) {
128+
return Ok(ExpressionValue::RValue(fn_value.as_global_value().as_pointer_value().as_basic_value_enum()));
129+
}
130+
}
131+
106132
generator
107133
.generate_lvalue(reference)
108134
.map(|it| ExpressionValue::RValue(it.as_basic_value_enum()))

src/codegen.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ impl<'ink> CodeGen<'ink> {
343343
let location = (&unit.file).into();
344344

345345
self.debug.finalize();
346-
log::debug!("{}", self.module.to_string());
346+
log::trace!("{}", self.module.to_string());
347347

348348
#[cfg(feature = "verify")]
349349
{
@@ -382,7 +382,7 @@ impl<'ink> GeneratedModule<'ink> {
382382
.create_module_from_ir(buffer)
383383
.map_err(|it| Diagnostic::new(it.to_string_lossy()).with_error_code("E071"))?;
384384

385-
log::debug!("{}", module.to_string());
385+
log::trace!("{}", module.to_string());
386386

387387
Ok(GeneratedModule { module, location: path.into(), engine: RefCell::new(None) })
388388
}
@@ -391,7 +391,7 @@ impl<'ink> GeneratedModule<'ink> {
391391
self.module
392392
.link_in_module(other.module)
393393
.map_err(|it| Diagnostic::new(it.to_string_lossy()).with_error_code("E071"))?;
394-
log::debug!("Merged: {}", self.module.to_string());
394+
log::trace!("Merged: {}", self.module.to_string());
395395

396396
Ok(self)
397397
}
@@ -569,8 +569,8 @@ impl<'ink> GeneratedModule<'ink> {
569569
/// * `codegen` - The generated LLVM module to be persisted
570570
/// * `output` - The location to save the generated ir file
571571
pub fn persist_to_ir(&self, output: PathBuf) -> Result<PathBuf, Diagnostic> {
572-
log::debug!("Output location: {}", output.to_string_lossy());
573-
log::debug!("{}", self.persist_to_string());
572+
log::trace!("Output location: {}", output.to_string_lossy());
573+
log::trace!("{}", self.persist_to_string());
574574

575575
self.module
576576
.print_to_file(&output)

src/codegen/generators/data_type_generator.rs

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
22
use crate::codegen::debug::Debug;
3-
use crate::index::{FxIndexSet, Index, VariableIndexEntry, VariableType};
3+
use crate::codegen::llvm_index::TypeHelper;
4+
use crate::index::{FxIndexSet, Index, PouIndexEntry, VariableIndexEntry, VariableType};
45
use crate::resolver::{AstAnnotations, Dependency};
56
use crate::typesystem::{self, DataTypeInformation, Dimension, StringEncoding, StructSource};
67
use crate::{
@@ -12,6 +13,7 @@ use crate::{
1213
typesystem::DataType,
1314
};
1415

16+
use inkwell::types::{AnyType, AnyTypeEnum, FunctionType};
1517
use inkwell::{
1618
types::{BasicType, BasicTypeEnum},
1719
values::{BasicValue, BasicValueEnum},
@@ -200,15 +202,22 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
200202
/// Creates an llvm type to be associated with the given data type.
201203
/// Generates only an opaque type for structs.
202204
/// Eagerly generates but does not associate nested array and referenced aliased types
203-
fn create_type(&mut self, name: &str, data_type: &DataType) -> Result<BasicTypeEnum<'ink>, Diagnostic> {
205+
fn create_type(&mut self, name: &str, data_type: &DataType) -> Result<AnyTypeEnum<'ink>, Diagnostic> {
204206
let information = data_type.get_type_information();
205207
match information {
206208
DataTypeInformation::Struct { source, .. } => match source {
207-
StructSource::Pou(..) => self.types_index.get_associated_pou_type(data_type.get_name()),
208-
StructSource::OriginalDeclaration => {
209-
self.types_index.get_associated_type(data_type.get_name())
210-
}
211-
StructSource::Internal(_) => self.types_index.get_associated_type(data_type.get_name()),
209+
StructSource::Pou(..) => self
210+
.types_index
211+
.get_associated_pou_type(data_type.get_name())
212+
.map(|res| res.as_any_type_enum()),
213+
StructSource::OriginalDeclaration => self
214+
.types_index
215+
.get_associated_type(data_type.get_name())
216+
.map(|res| res.as_any_type_enum()),
217+
StructSource::Internal(_) => self
218+
.types_index
219+
.get_associated_type(data_type.get_name())
220+
.map(|res| res.as_any_type_enum()),
212221
},
213222

214223
// We distinguish between two types of arrays, normal and variable length ones.
@@ -222,7 +231,7 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
222231
.get_effective_type_by_name(inner_type_name)
223232
.and_then(|inner_type| self.create_type(inner_type_name, inner_type))
224233
.and_then(|inner_type| self.create_nested_array_type(inner_type, dimensions))
225-
.map(|it| it.as_basic_type_enum())
234+
.map(|it| it.as_any_type_enum())
226235
}
227236
}
228237
DataTypeInformation::Integer { size, .. } => {
@@ -263,15 +272,80 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
263272
.and_then(|data_type| self.create_type(name, data_type)),
264273
DataTypeInformation::Void => Ok(get_llvm_int_type(self.llvm.context, 32, "Void").into()),
265274
DataTypeInformation::Pointer { inner_type_name, .. } => {
266-
let inner_type = self.create_type(inner_type_name, self.index.get_type(inner_type_name)?)?;
267-
Ok(inner_type.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into())
275+
let inner_type = if information.is_function_pointer() {
276+
self.create_function_type(inner_type_name)?.as_any_type_enum()
277+
} else {
278+
self.create_type(inner_type_name, self.index.get_type(inner_type_name)?)?
279+
};
280+
281+
Ok(inner_type.create_ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into())
268282
}
269283
DataTypeInformation::Generic { .. } => {
270284
unreachable!("Generic types should not be generated")
271285
}
272286
}
273287
}
274288

289+
fn create_function_type(&mut self, method_name: &str) -> Result<FunctionType<'ink>, Diagnostic> {
290+
let return_type = self
291+
.types_index
292+
.find_associated_type(self.index.get_return_type_or_void(method_name).get_name())
293+
.map(|opt| opt.as_any_type_enum())
294+
.unwrap_or(self.llvm.context.void_type().as_any_type_enum());
295+
296+
let mut parameter_types = Vec::new();
297+
298+
match self.index.find_pou(method_name) {
299+
Some(PouIndexEntry::Method { parent_name, .. }) => {
300+
let ty = self.types_index.get_associated_type(parent_name).expect("must exist");
301+
let ty_ptr = ty.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into();
302+
303+
// Methods are defined as functions in the LLVM IR, but carry the underlying POU type as their
304+
// first parameter to operate on them, hence push the POU type to the very first position.
305+
parameter_types.push(ty_ptr);
306+
307+
for parameter in self.index.get_declared_parameters(method_name) {
308+
// Instead of relying on the LLVM index, we create data-types on the fly here because some
309+
// types have not yet been visited and as a result may not be in the index. For example at
310+
// the time of writing this the index was not able to find a input parameter of type
311+
// `__auto_pointer_to_DINT`, consequently panicking
312+
let ty = self.create_type(
313+
parameter.get_name(),
314+
self.index.get_type(&parameter.data_type_name).expect("must exist"),
315+
)?;
316+
317+
parameter_types.push(ty.try_into().unwrap());
318+
}
319+
}
320+
321+
Some(PouIndexEntry::FunctionBlock { name, .. }) => {
322+
let ty = self.types_index.get_associated_type(name).expect("must exist");
323+
let ty_ptr = ty.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into();
324+
325+
// Function blocks are a bit "weird" in that they only expect an instance argument even if
326+
// they define input, output and/or inout parameters. Effectively, while being methods per-se,
327+
// their calling convention differs from regular methods.
328+
parameter_types.push(ty_ptr);
329+
}
330+
331+
_ => unreachable!("internal error, invalid method call"),
332+
}
333+
334+
let fn_type = match return_type {
335+
AnyTypeEnum::ArrayType(value) => value.fn_type(parameter_types.as_slice(), false),
336+
AnyTypeEnum::FloatType(value) => value.fn_type(parameter_types.as_slice(), false),
337+
AnyTypeEnum::IntType(value) => value.fn_type(parameter_types.as_slice(), false),
338+
AnyTypeEnum::PointerType(value) => value.fn_type(parameter_types.as_slice(), false),
339+
AnyTypeEnum::StructType(value) => value.fn_type(parameter_types.as_slice(), false),
340+
AnyTypeEnum::VectorType(value) => value.fn_type(parameter_types.as_slice(), false),
341+
AnyTypeEnum::VoidType(value) => value.fn_type(parameter_types.as_slice(), false),
342+
343+
AnyTypeEnum::FunctionType(_) => unreachable!(),
344+
};
345+
346+
Ok(fn_type)
347+
}
348+
275349
fn generate_initial_value(
276350
&mut self,
277351
data_type: &DataType,
@@ -435,9 +509,9 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
435509
/// `arr: ARRAY[0..3] OF INT`.
436510
fn create_nested_array_type(
437511
&self,
438-
inner_type: BasicTypeEnum<'ink>,
512+
inner_type: AnyTypeEnum<'ink>,
439513
dimensions: &[Dimension],
440-
) -> Result<BasicTypeEnum<'ink>, Diagnostic> {
514+
) -> Result<AnyTypeEnum<'ink>, Diagnostic> {
441515
let len = dimensions
442516
.iter()
443517
.map(|dimension| {
@@ -453,14 +527,17 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
453527
})?;
454528

455529
let result = match inner_type {
456-
BasicTypeEnum::IntType(ty) => ty.array_type(len),
457-
BasicTypeEnum::FloatType(ty) => ty.array_type(len),
458-
BasicTypeEnum::StructType(ty) => ty.array_type(len),
459-
BasicTypeEnum::ArrayType(ty) => ty.array_type(len),
460-
BasicTypeEnum::PointerType(ty) => ty.array_type(len),
461-
BasicTypeEnum::VectorType(ty) => ty.array_type(len),
530+
AnyTypeEnum::IntType(ty) => ty.array_type(len),
531+
AnyTypeEnum::FloatType(ty) => ty.array_type(len),
532+
AnyTypeEnum::StructType(ty) => ty.array_type(len),
533+
AnyTypeEnum::ArrayType(ty) => ty.array_type(len),
534+
AnyTypeEnum::PointerType(ty) => ty.array_type(len),
535+
AnyTypeEnum::VectorType(ty) => ty.array_type(len),
536+
537+
AnyTypeEnum::FunctionType(_) => unimplemented!("function types are not supported in arrays"),
538+
AnyTypeEnum::VoidType(_) => unimplemented!("void types not supported in arrays"),
462539
}
463-
.as_basic_type_enum();
540+
.as_any_type_enum();
464541

465542
Ok(result)
466543
}

0 commit comments

Comments
 (0)