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 9 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
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 is_deref(&self) -> bool {
matches!(
self,
AstNode {
stmt: AstStatement::ReferenceExpr(ReferenceExpr { access: ReferenceAccess::Deref, .. }),
..
}
)
}
}

#[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
18 changes: 18 additions & 0 deletions src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ lazy_static! {
generic_name_resolver: no_generic_name_resolver,
code: |generator, params, location| {
if let [reference] = params {
// TODO(vosa): See if there is a better way
// Check if this is a qualified method reference like fb.fbSpecificMethod
if let Some(resolver::StatementAnnotation::Function { qualified_name, .. }) = generator.annotations.get(reference) {
// This is a qualified method reference - return the function pointer directly
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 +112,15 @@ lazy_static! {
generic_name_resolver: no_generic_name_resolver,
code: |generator, params, location| {
if let [reference] = params {
// TODO(vosa): See if there is a better way
// Check if this is a qualified method reference like fb.fbSpecificMethod
if let Some(resolver::StatementAnnotation::Function { qualified_name, .. }) = generator.annotations.get(reference) {
// This is a qualified method reference - return the function pointer directly
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
8 changes: 4 additions & 4 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 @@ -570,7 +570,7 @@ impl<'ink> GeneratedModule<'ink> {
/// * `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!("{}", self.persist_to_string());

self.module
.print_to_file(&output)
Expand Down
97 changes: 79 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, BasicMetadataTypeEnum, 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 @@ -85,24 +87,28 @@ pub fn generate_data_types<'ink>(
// and associate them in the llvm index
for (name, user_type) in &types {
if let DataTypeInformation::Struct { name: struct_name, .. } = user_type.get_type_information() {
log::debug!("creating struct stub `{name}`");
generator.types_index.associate_type(name, llvm.create_struct_stub(struct_name).into())?;
}
}
// pou_types will always be struct
for (name, user_type) in &pou_types {
if let DataTypeInformation::Struct { name: struct_name, .. } = user_type.get_type_information() {
log::debug!("creating POU stub `{name}`");
generator.types_index.associate_pou_type(name, llvm.create_struct_stub(struct_name).into())?;
}
}

// now create all other types (enum's, arrays, etc.)
for (name, user_type) in &types {
log::debug!("creating type `{name}`");
let gen_type = generator.create_type(name, user_type)?;
generator.types_index.associate_type(name, gen_type)?
//Get and associate debug type
}

for (name, user_type) in &pou_types {
log::debug!("creating type `{name}`");
let gen_type = generator.create_type(name, user_type)?;
generator.types_index.associate_pou_type(name, gen_type)?
}
Expand All @@ -112,8 +118,9 @@ pub fn generate_data_types<'ink>(
types_to_init.extend(types);
types_to_init.extend(pou_types);
// now since all types should be available in the llvm index, we can think about constructing and associating
for (_, user_type) in &types_to_init {
for (name, user_type) in &types_to_init {
//Expand all types
log::debug!("expanding type `{name}`");
generator.expand_opaque_types(user_type)?;
}

Expand Down Expand Up @@ -174,6 +181,45 @@ pub fn generate_data_types<'ink>(
}

impl<'ink> DataTypeGenerator<'ink, '_> {
fn create_function_type(&self, name: &str) -> Result<FunctionType<'ink>, Diagnostic> {
let return_type_dt = self.index.find_return_type(name).unwrap_or(self.index.get_void_type());

let return_type = self
.types_index
.find_associated_type(&return_type_dt.name)
.map(|opt| opt.as_any_type_enum())
.unwrap_or(self.llvm.context.void_type().into());

let mut parameter_types = vec![];

// For methods, we need to add the 'this' parameter as the first parameter
if let Some(PouIndexEntry::Method { parent_name, .. }) = self.index.find_pou(name) {
// Get the owner class type and add it as the first parameter (this pointer)
if let Ok(owner_type) = self.types_index.get_associated_type(parent_name) {
parameter_types.push(owner_type.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into());
}
}

// Add the declared parameters
let declared_params = self
.index
.get_declared_parameters(name)
.iter()
.flat_map(|param| self.types_index.get_associated_type(&param.data_type_name))
.map(|opt| opt.into())
.collect::<Vec<BasicMetadataTypeEnum>>();

parameter_types.extend(declared_params);

let fn_type = match return_type {
AnyTypeEnum::IntType(value) => value.fn_type(parameter_types.as_slice(), false),
AnyTypeEnum::VoidType(value) => value.fn_type(parameter_types.as_slice(), false),
_ => unimplemented!(),
};

Ok(fn_type)
}

/// generates the members of an opaque struct and associates its initial values
fn expand_opaque_types(&mut self, data_type: &DataType) -> Result<(), Diagnostic> {
let information = data_type.get_type_information();
Expand All @@ -200,15 +246,27 @@ 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::Pou(PouType::Function | PouType::Method { .. }, ..) => {
let gen_type = self.create_function_type(name)?;

// TODO(vosa): Strictly speaking we don't need to register in LLVM index, i.e. `return Ok(gen_type.as_any_type_enum())` would suffice without breaking any tests; re-think approach with AnyType changes in API
self.types_index.associate_pou_type(name, gen_type.as_any_type_enum())?;
self.types_index.get_associated_function_type(name).map(|it| it.as_any_type_enum())
}
StructSource::Pou(..) => self
.types_index
.get_associated_pou_type(data_type.get_name())
.map(|it| it.as_any_type_enum()),
StructSource::OriginalDeclaration => {
self.types_index.get_associated_type(data_type.get_name())
self.types_index.get_associated_type(data_type.get_name()).map(|it| it.as_any_type_enum())
}
StructSource::Internal(_) => {
self.types_index.get_associated_type(data_type.get_name()).map(|it| it.as_any_type_enum())
}
StructSource::Internal(_) => self.types_index.get_associated_type(data_type.get_name()),
},

// We distinguish between two types of arrays, normal and variable length ones.
Expand All @@ -222,7 +280,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,7 +322,7 @@ 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")
Expand Down Expand Up @@ -435,9 +493,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 +511,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(_) => unreachable!("Function types are not supported in arrays"),
AnyTypeEnum::VoidType(_) => unreachable!("Void types are not supported in arrays"),
}
.as_basic_type_enum();
.as_any_type_enum();

Ok(result)
}
Expand Down
Loading