Skip to content

feat: Function Pointers #1492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.PHONY: test run

test:
cargo nextest run --workspace --no-fail-fast && ./scripts/build.sh --lit

run:
cargo r -- target/demo.st tests/lit/util/printf.pli --linker=clang && ./demo.st.out
10 changes: 10 additions & 0 deletions compiler/plc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,16 @@ impl AstNode {
pub fn with_metadata(self, metadata: MetaData) -> AstNode {
AstNode { metadata: Some(metadata), ..self }
}

pub fn get_deref_expr(&self) -> Option<&ReferenceExpr> {
match &self.stmt {
AstStatement::ReferenceExpr(expr) => match expr {
ReferenceExpr { access: ReferenceAccess::Deref, .. } => Some(expr),
_ => None,
},
_ => None,
}
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down
86 changes: 59 additions & 27 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -246,20 +246,37 @@ function run_package_std() {
cp "$rel_dir/"*.a "$lib_dir" 2>/dev/null || log "$rel_dir does not contain *.a files"
# Create an SO file from the copied a file
log "Creating a shared library from the compiled static library"
log "Running : $cc --shared -L$lib_dir \
-Wl,--whole-archive -liec61131std \
-o $lib_dir/out.so -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld \
--target=$val"
$cc --shared -L"$lib_dir" \
-Wl,--whole-archive -liec61131std \
-o "$lib_dir/out.so" -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld \
--target="$val"

mv "$lib_dir/out.so" "$lib_dir/libiec61131std.so"
# Check if we're on macOS and adjust linker flags accordingly
unameOut="$(uname -s)"
case "${unameOut}" in
Darwin*)
log "Running : $cc --shared -L$lib_dir \
-Wl,-force_load,$lib_dir/libiec61131std.a \
-o $lib_dir/libiec61131std.so \
-lm -framework CoreFoundation \
--target=$val"
$cc --shared -L"$lib_dir" \
-Wl,-force_load,"$lib_dir/libiec61131std.a" \
-o "$lib_dir/libiec61131std.so" \
-lm -framework CoreFoundation \
--target="$val"
;;
*)
log "Running : $cc --shared -L$lib_dir \
-Wl,--whole-archive -liec61131std \
-o $lib_dir/libiec61131std.so -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld \
--target=$val"
$cc --shared -L"$lib_dir" \
-Wl,--whole-archive -liec61131std \
-o "$lib_dir/libiec61131std.so" -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld \
--target="$val"
;;
esac

done
else
lib_dir=$OUTPUT_DIR/lib
Expand All @@ -272,21 +289,36 @@ function run_package_std() {
cp "$rel_dir/"*.a "$lib_dir" 2>/dev/null || log "$rel_dir does not contain *.a files"
# Create an SO file from the copied a file
log "Creating a shared library from the compiled static library"
log "Running : $cc --shared -L"$lib_dir" \
-Wl,--whole-archive -liec61131std \
-o "$lib_dir/out.so" -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld "
$cc --shared -L"$lib_dir" \
-Wl,--whole-archive -liec61131std \
-o "$lib_dir/out.so" -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld
mv "$lib_dir/out.so" "$lib_dir/libiec61131std.so"
# Check if we're on macOS and adjust linker flags accordingly
unameOut="$(uname -s)"
case "${unameOut}" in
Darwin*)
log "Running : $cc --shared -L"$lib_dir" \
-Wl,-force_load,$lib_dir/libiec61131std.a \
-o "$lib_dir/libiec61131std.so" \
-lm -framework CoreFoundation"
$cc --shared -L"$lib_dir" \
-Wl,-force_load,"$lib_dir/libiec61131std.a" \
-o "$lib_dir/libiec61131std.so" \
-lm -framework CoreFoundation
;;
*)
log "Running : $cc --shared -L"$lib_dir" \
-Wl,--whole-archive -liec61131std \
-o "$lib_dir/libiec61131std.so" -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld "
$cc --shared -L"$lib_dir" \
-Wl,--whole-archive -liec61131std \
-o "$lib_dir/libiec61131std.so" -Wl,--no-whole-archive \
-lm \
-fuse-ld=lld
;;
esac
fi

log "Enabling read/write on the output folder"
chmod a+rw $OUTPUT_DIR -R
chmod -R a+rw $OUTPUT_DIR

}

Expand Down Expand Up @@ -506,7 +538,7 @@ fi

if [[ -d $project_location/target/ ]]; then
log "Allow access to target folders"
chmod a+rw -R $project_location/target/
chmod -R a+rw $project_location/target/
fi

if [[ $offline -ne 0 ]]; then
Expand Down
14 changes: 14 additions & 0 deletions src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ lazy_static! {
generic_name_resolver: no_generic_name_resolver,
code: |generator, params, location| {
if let [reference] = params {
// Return the pointer value of a function when dealing with them, e.g. `ADR(MyFb.myMethod)`
if let Some(StatementAnnotation::Function { qualified_name, .. }) = generator.annotations.get(reference) {
if let Some(fn_value) = generator.llvm_index.find_associated_implementation(qualified_name) {
return Ok(ExpressionValue::RValue(fn_value.as_global_value().as_pointer_value().as_basic_value_enum()));
}
}

generator
.generate_lvalue(reference)
.map(|it| ExpressionValue::RValue(it.as_basic_value_enum()))
Expand Down Expand Up @@ -103,6 +110,13 @@ lazy_static! {
generic_name_resolver: no_generic_name_resolver,
code: |generator, params, location| {
if let [reference] = params {
// Return the pointer value of a function when dealing with them, e.g. `ADR(MyFb.myMethod)`
if let Some(StatementAnnotation::Function { qualified_name, .. }) = generator.annotations.get(reference) {
if let Some(fn_value) = generator.llvm_index.find_associated_implementation(qualified_name) {
return Ok(ExpressionValue::RValue(fn_value.as_global_value().as_pointer_value().as_basic_value_enum()));
}
}

generator
.generate_lvalue(reference)
.map(|it| ExpressionValue::RValue(it.as_basic_value_enum()))
Expand Down
10 changes: 5 additions & 5 deletions src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ impl<'ink> CodeGen<'ink> {
let location = (&unit.file).into();

self.debug.finalize();
log::debug!("{}", self.module.to_string());
log::trace!("{}", self.module.to_string());

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

log::debug!("{}", module.to_string());
log::trace!("{}", module.to_string());

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

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

self.module
.print_to_file(&output)
Expand Down
100 changes: 82 additions & 18 deletions src/codegen/generators/data_type_generator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
use crate::codegen::debug::Debug;
use crate::index::{FxIndexSet, Index, VariableIndexEntry, VariableType};
use crate::codegen::llvm_index::TypeHelper;
use crate::index::{FxIndexSet, Index, PouIndexEntry, VariableIndexEntry, VariableType};
use crate::resolver::{AstAnnotations, Dependency};
use crate::typesystem::{self, DataTypeInformation, Dimension, StringEncoding, StructSource};
use crate::{
Expand All @@ -12,12 +13,13 @@ use crate::{
typesystem::DataType,
};

use inkwell::types::{AnyType, AnyTypeEnum, FunctionType};
use inkwell::{
types::{BasicType, BasicTypeEnum},
values::{BasicValue, BasicValueEnum},
AddressSpace,
};
use plc_ast::ast::{AstNode, AstStatement};
use plc_ast::ast::{AstNode, AstStatement, PouType};
use plc_ast::literals::AstLiteral;
use plc_diagnostics::diagnostics::Diagnostic;
use plc_source::source_location::SourceLocation;
Expand Down Expand Up @@ -200,15 +202,26 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
/// Creates an llvm type to be associated with the given data type.
/// Generates only an opaque type for structs.
/// Eagerly generates but does not associate nested array and referenced aliased types
fn create_type(&mut self, name: &str, data_type: &DataType) -> Result<BasicTypeEnum<'ink>, Diagnostic> {
fn create_type(&mut self, name: &str, data_type: &DataType) -> Result<AnyTypeEnum<'ink>, Diagnostic> {
let information = data_type.get_type_information();
match information {
DataTypeInformation::Struct { source, .. } => match source {
StructSource::Pou(..) => self.types_index.get_associated_pou_type(data_type.get_name()),
StructSource::OriginalDeclaration => {
self.types_index.get_associated_type(data_type.get_name())
StructSource::Pou(PouType::Method { .. }) => {
Ok(self.create_function_type(name)?.as_any_type_enum())
}
StructSource::Internal(_) => self.types_index.get_associated_type(data_type.get_name()),

StructSource::Pou(..) => self
.types_index
.get_associated_pou_type(data_type.get_name())
.map(|res| res.as_any_type_enum()),
StructSource::OriginalDeclaration => self
.types_index
.get_associated_type(data_type.get_name())
.map(|res| res.as_any_type_enum()),
StructSource::Internal(_) => self
.types_index
.get_associated_type(data_type.get_name())
.map(|res| res.as_any_type_enum()),
},

// We distinguish between two types of arrays, normal and variable length ones.
Expand All @@ -222,7 +235,7 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
.get_effective_type_by_name(inner_type_name)
.and_then(|inner_type| self.create_type(inner_type_name, inner_type))
.and_then(|inner_type| self.create_nested_array_type(inner_type, dimensions))
.map(|it| it.as_basic_type_enum())
.map(|it| it.as_any_type_enum())
}
}
DataTypeInformation::Integer { size, .. } => {
Expand Down Expand Up @@ -264,14 +277,62 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
DataTypeInformation::Void => Ok(get_llvm_int_type(self.llvm.context, 32, "Void").into()),
DataTypeInformation::Pointer { inner_type_name, .. } => {
let inner_type = self.create_type(inner_type_name, self.index.get_type(inner_type_name)?)?;
Ok(inner_type.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into())
Ok(inner_type.create_ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into())
}
DataTypeInformation::Generic { .. } => {
unreachable!("Generic types should not be generated")
}
}
}

// TODO(vosa): Is this neccessary? Are the function types indexed and later on used in the expression
// generator? If not this whole commit could be reverted, double-check before merging
fn create_function_type(&mut self, pou_name: &str) -> Result<FunctionType<'ink>, Diagnostic> {
let return_type = self
.types_index
.find_associated_type(self.index.get_return_type_or_void(pou_name).get_name())
.map(|opt| opt.as_any_type_enum())
.unwrap_or(self.llvm.context.void_type().as_any_type_enum());

let mut parameter_types = Vec::new();

// Methods are defined as functions in the LLVM IR, but carry the underlying POU type as their first
// parameter to operate on them, hence push the POU type to the very first position.
if let Some(PouIndexEntry::Method { parent_name, .. }) = self.index.find_pou(pou_name) {
let ty = self.types_index.get_associated_type(parent_name).expect("must exist");
let ty_ptr = ty.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into();

parameter_types.push(ty_ptr);
}

for parameter in self.index.get_declared_parameters(pou_name) {
// Instead of relying on the LLVM index, we create data-types directly in here because some of
// them may not have been registered yet. For example, at the time of writing this comment the
// `__auto_pointer_to_DINT` type was not present in the index for a VAR_IN_OUT parameter which
// resulted in an error
let ty = self.create_type(
parameter.get_name(),
self.index.get_type(&parameter.data_type_name).expect("must exist"),
)?;

parameter_types.push(ty.try_into().unwrap());
}

let fn_type = match return_type {
AnyTypeEnum::ArrayType(value) => value.fn_type(parameter_types.as_slice(), false),
AnyTypeEnum::FloatType(value) => value.fn_type(parameter_types.as_slice(), false),
AnyTypeEnum::IntType(value) => value.fn_type(parameter_types.as_slice(), false),
AnyTypeEnum::PointerType(value) => value.fn_type(parameter_types.as_slice(), false),
AnyTypeEnum::StructType(value) => value.fn_type(parameter_types.as_slice(), false),
AnyTypeEnum::VectorType(value) => value.fn_type(parameter_types.as_slice(), false),
AnyTypeEnum::VoidType(value) => value.fn_type(parameter_types.as_slice(), false),

AnyTypeEnum::FunctionType(_) => unreachable!(),
};

Ok(fn_type)
}

fn generate_initial_value(
&mut self,
data_type: &DataType,
Expand Down Expand Up @@ -435,9 +496,9 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
/// `arr: ARRAY[0..3] OF INT`.
fn create_nested_array_type(
&self,
inner_type: BasicTypeEnum<'ink>,
inner_type: AnyTypeEnum<'ink>,
dimensions: &[Dimension],
) -> Result<BasicTypeEnum<'ink>, Diagnostic> {
) -> Result<AnyTypeEnum<'ink>, Diagnostic> {
let len = dimensions
.iter()
.map(|dimension| {
Expand All @@ -453,14 +514,17 @@ impl<'ink> DataTypeGenerator<'ink, '_> {
})?;

let result = match inner_type {
BasicTypeEnum::IntType(ty) => ty.array_type(len),
BasicTypeEnum::FloatType(ty) => ty.array_type(len),
BasicTypeEnum::StructType(ty) => ty.array_type(len),
BasicTypeEnum::ArrayType(ty) => ty.array_type(len),
BasicTypeEnum::PointerType(ty) => ty.array_type(len),
BasicTypeEnum::VectorType(ty) => ty.array_type(len),
AnyTypeEnum::IntType(ty) => ty.array_type(len),
AnyTypeEnum::FloatType(ty) => ty.array_type(len),
AnyTypeEnum::StructType(ty) => ty.array_type(len),
AnyTypeEnum::ArrayType(ty) => ty.array_type(len),
AnyTypeEnum::PointerType(ty) => ty.array_type(len),
AnyTypeEnum::VectorType(ty) => ty.array_type(len),

AnyTypeEnum::FunctionType(_) => unimplemented!("function types are not supported in arrays"),
AnyTypeEnum::VoidType(_) => unimplemented!("void types not supported in arrays"),
}
.as_basic_type_enum();
.as_any_type_enum();

Ok(result)
}
Expand Down
Loading