Skip to content

Commit

Permalink
feat: allow VAR_EXTERNAL declarations (#1324)
Browse files Browse the repository at this point in the history
* feat: allow `VAR_EXTERNAL` declarations
Adds a new variable block type for VAR_EXTERNAL to the compiler. These blocks do not have any functionality for now and the compiler will emit a warning when such blocks are declared.
  • Loading branch information
mhasel authored Oct 8, 2024
1 parent 223a9e0 commit 28f7349
Show file tree
Hide file tree
Showing 33 changed files with 811 additions and 17 deletions.
2 changes: 2 additions & 0 deletions compiler/plc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ pub enum VariableBlockType {
Output,
Global,
InOut,
External,
}

impl Display for VariableBlockType {
Expand All @@ -371,6 +372,7 @@ impl Display for VariableBlockType {
VariableBlockType::Output => write!(f, "Output"),
VariableBlockType::Global => write!(f, "Global"),
VariableBlockType::InOut => write!(f, "InOut"),
VariableBlockType::External => write!(f, "External"),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ lazy_static! {
E103, Error, include_str!("./error_codes/E103.md"), // Immutable Hardware Binding
E104, Error, include_str!("./error_codes/E104.md"), // Config Variable With Incomplete Address
E105, Error, include_str!("./error_codes/E105.md"), // CONSTANT keyword in POU
E106, Warning, include_str!("./error_codes/E106.md"), // VAR_EXTERNAL have no effect
E107, Error, include_str!("./error_codes/E107.md"), // Missing configuration for template variable
E108, Error, include_str!("./error_codes/E108.md"), // Template variable is configured multiple times
);
Expand Down
23 changes: 23 additions & 0 deletions compiler/plc_diagnostics/src/diagnostics/error_codes/E106.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# VAR_EXTERNAL blocks have no effect

Variables declared in a `VAR_EXTERNAL` block are currently ignored and the referenced globals will be used instead.

Example:
```
VAR_GLOBAL
myArray : ARRAY [0..10] OF INT;
myString: STRING;
END_VAR
FUNCTION main
VAR_EXTERNAL CONSTANT
myArray : ARRAY [0..10] OF INT;
END_VAR
myArray[5] := 42;
myString := 'Hello, world!';
END_FUNCTION
```

In this example, even though `arr` is declared as `VAR_EXTERNAL CONSTANT`, the `CONSTANT` constraint will be ignored and
the global `myArray` will be mutated. The global `myString` can be read from and written to from within `main` even though it
is not declared in a `VAR_EXTERNAL` block.
2 changes: 1 addition & 1 deletion src/codegen/generators/data_type_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ impl<'ink, 'b> DataTypeGenerator<'ink, 'b> {
if let DataTypeInformation::Struct { source, members, .. } = information {
let members = members
.iter()
.filter(|it| !it.is_temp() && !it.is_return())
.filter(|it| !(it.is_temp() || it.is_return() || it.is_var_external()))
.map(|m| self.types_index.get_associated_type(m.get_type_name()))
.collect::<Result<Vec<BasicTypeEnum>, Diagnostic>>()?;

Expand Down
4 changes: 2 additions & 2 deletions src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
}

// handle all parameters (without return!)
for m in members.iter().filter(|it| !it.is_return()) {
for m in members.iter().filter(|it| !(it.is_return() || it.is_var_external())) {
let parameter_name = m.get_name();

let (name, variable) = if m.is_parameter() {
Expand Down Expand Up @@ -713,7 +713,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
//Generate reference to parameter
// cannot use index from members because return and temp variables may not be considered for index in build_struct_gep
let mut var_count = 0;
for m in members.iter() {
for m in members.iter().filter(|it| !it.is_var_external()) {
let parameter_name = m.get_name();

let (name, variable) = if m.is_temp() || m.is_return() {
Expand Down
124 changes: 124 additions & 0 deletions src/codegen/tests/code_gen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3516,3 +3516,127 @@ fn array_of_struct_as_member_of_another_struct_and_variable_declaration_is_initi

insta::assert_snapshot!(res);
}

#[test]
// XXX: this behaviour might change in future, for now `VAR_EXTERNAL` variables are ignored
fn variables_in_var_external_block_are_not_generated() {
let res = codegen(
"
VAR_GLOBAL
arr: ARRAY [0..100] OF INT;
END_VAR
FUNCTION foo
VAR_EXTERNAL
arr : ARRAY [0..100] OF INT;
END_VAR
END_FUNCTION
FUNCTION_BLOCK bar
VAR_EXTERNAL CONSTANT
arr : ARRAY [0..100] OF INT;
END_VAR
END_FUNCTION_BLOCK
PROGRAM baz
VAR_EXTERNAL CONSTANT
arr : ARRAY [0..100] OF INT;
END_VAR
END_PROGRAM
CLASS qux
VAR_EXTERNAL
arr : ARRAY [0..100] OF INT;
END_VAR
END_CLASS
",
);

insta::assert_snapshot!(res, @r###"
; ModuleID = '<internal>'
source_filename = "<internal>"
%bar = type {}
%baz = type {}
%qux = type {}
@arr = global [101 x i16] zeroinitializer
@__bar__init = unnamed_addr constant %bar zeroinitializer
@baz_instance = global %baz zeroinitializer
@__qux__init = unnamed_addr constant %qux zeroinitializer
define void @foo() {
entry:
ret void
}
define void @bar(%bar* %0) {
entry:
ret void
}
define void @baz(%baz* %0) {
entry:
ret void
}
define void @qux(%qux* %0) {
entry:
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"
%baz = type {}
%bar = type {}
%qux = type {}
@baz_instance = external global %baz
@__bar__init = external global %bar
@__qux__init = external global %qux
define void @__init_baz(%baz* %0) {
entry:
%self = alloca %baz*, align 8
store %baz* %0, %baz** %self, align 8
ret void
}
declare void @baz(%baz*)
define void @__init_bar(%bar* %0) {
entry:
%self = alloca %bar*, align 8
store %bar* %0, %bar** %self, align 8
ret void
}
declare void @bar(%bar*)
define void @__init_qux(%qux* %0) {
entry:
%self = alloca %qux*, align 8
store %qux* %0, %qux** %self, align 8
ret void
}
declare void @qux(%qux*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"
%baz = type {}
@baz_instance = external global %baz
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
define void @__init___testproject() {
entry:
call void @__init_baz(%baz* @baz_instance)
ret void
}
declare void @__init_baz(%baz*)
declare void @baz(%baz*)
"###);
}
72 changes: 72 additions & 0 deletions src/codegen/tests/initialization_test/complex_initializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,3 +1296,75 @@ fn var_config_aliased_variables_initialized() {
declare void @FB(%FB*)
"###);
}

#[test]
fn var_external_blocks_are_ignored_in_init_functions() {
let res = codegen(
r"
VAR_GLOBAL
s: STRING;
refString AT s : STRING;
END_VAR
FUNCTION_BLOCK foo
VAR_EXTERNAL
refString : STRING;
END_VAR
END_FUNCTION
FUNCTION bar
VAR_EXTERNAL
refString : STRING;
END_VAR
END_FUNCTION
",
);

insta::assert_snapshot!(res, @r###"
; ModuleID = '<internal>'
source_filename = "<internal>"
%foo = type {}
@s = global [81 x i8] zeroinitializer
@refString = global [81 x i8]* null
@__foo__init = unnamed_addr constant %foo zeroinitializer
define void @foo(%foo* %0) {
entry:
ret void
}
define void @bar() {
entry:
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"
%foo = type {}
@__foo__init = external global %foo
define void @__init_foo(%foo* %0) {
entry:
%self = alloca %foo*, align 8
store %foo* %0, %foo** %self, align 8
ret void
}
declare void @foo(%foo*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"
@s = external global [81 x i8]
@refString = external global [81 x i8]*
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
define void @__init___testproject() {
entry:
store [81 x i8]* @s, [81 x i8]** @refString, align 8
ret void
}
"###)
}
36 changes: 29 additions & 7 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub struct VariableIndexEntry {
pub argument_type: ArgumentType,
/// true if this variable is a compile-time-constant
is_constant: bool,
// true if this variable is in a 'VAR_EXTERNAL' block
is_var_external: bool,
/// the variable's datatype
pub data_type_name: String,
/// the index of the member-variable in it's container (e.g. struct). defautls to 0 (Single variables)
Expand Down Expand Up @@ -130,6 +132,7 @@ pub struct MemberInfo<'b> {
variable_type_name: &'b str,
binding: Option<HardwareBinding>,
is_constant: bool,
is_var_external: bool,
varargs: Option<VarArgs>,
}

Expand All @@ -148,6 +151,7 @@ impl VariableIndexEntry {
initial_value: None,
argument_type,
is_constant: false,
is_var_external: false,
data_type_name: data_type_name.to_string(),
location_in_parent,
linkage: LinkageType::Internal,
Expand All @@ -169,6 +173,7 @@ impl VariableIndexEntry {
initial_value: None,
argument_type: ArgumentType::ByVal(VariableType::Global),
is_constant: false,
is_var_external: false,
data_type_name: data_type_name.to_string(),
location_in_parent: 0,
linkage: LinkageType::Internal,
Expand Down Expand Up @@ -203,6 +208,11 @@ impl VariableIndexEntry {
self
}

pub fn set_var_external(mut self, var_external: bool) -> Self {
self.is_var_external = var_external;
self
}

/// Creates a new VariableIndexEntry from the current entry with a new container and type
/// This is used to create new entries from previously generic entries
pub fn into_typed(&self, container: &str, new_type: &str) -> Self {
Expand Down Expand Up @@ -253,6 +263,10 @@ impl VariableIndexEntry {
self.linkage == LinkageType::External
}

pub fn is_var_external(&self) -> bool {
self.is_var_external
}

pub fn get_declaration_type(&self) -> ArgumentType {
self.argument_type
}
Expand Down Expand Up @@ -350,6 +364,7 @@ pub enum VariableType {
InOut,
Global,
Return,
External,
}

impl VariableType {
Expand All @@ -368,6 +383,7 @@ impl std::fmt::Display for VariableType {
VariableType::InOut => write!(f, "InOut"),
VariableType::Global => write!(f, "Global"),
VariableType::Return => write!(f, "Return"),
VariableType::External => write!(f, "External"),
}
}
}
Expand Down Expand Up @@ -1138,13 +1154,18 @@ impl Index {
/// Searches for variable name in the given container, if not found, attempts to search for it in super classes
pub fn find_member(&self, container_name: &str, variable_name: &str) -> Option<&VariableIndexEntry> {
// Find pou in index
self.find_local_member(container_name, variable_name).or_else(|| {
if let Some(class) = self.find_pou(container_name).and_then(|it| it.get_super_class()) {
self.find_member(class, variable_name)
} else {
None
}
})
self.find_local_member(container_name, variable_name)
.or_else(|| {
if let Some(class) = self.find_pou(container_name).and_then(|it| it.get_super_class()) {
self.find_member(class, variable_name)
} else {
None
}
})
.filter(|it| {
// VAR_EXTERNAL variables are not local members
!it.is_var_external()
})
}

/// Searches for method names in the given container, if not found, attempts to search for it in super class
Expand Down Expand Up @@ -1518,6 +1539,7 @@ impl Index {
.set_initial_value(initial_value)
.set_hardware_binding(member_info.binding)
.set_varargs(member_info.varargs)
.set_var_external(member_info.is_var_external)
}

pub fn register_enum_variant(
Expand Down
Loading

0 comments on commit 28f7349

Please sign in to comment.