Skip to content

Commit 28f7349

Browse files
authored
feat: allow VAR_EXTERNAL declarations (#1324)
* 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.
1 parent 223a9e0 commit 28f7349

File tree

33 files changed

+811
-17
lines changed

33 files changed

+811
-17
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ pub enum VariableBlockType {
360360
Output,
361361
Global,
362362
InOut,
363+
External,
363364
}
364365

365366
impl Display for VariableBlockType {
@@ -371,6 +372,7 @@ impl Display for VariableBlockType {
371372
VariableBlockType::Output => write!(f, "Output"),
372373
VariableBlockType::Global => write!(f, "Global"),
373374
VariableBlockType::InOut => write!(f, "InOut"),
375+
VariableBlockType::External => write!(f, "External"),
374376
}
375377
}
376378
}

compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ lazy_static! {
207207
E103, Error, include_str!("./error_codes/E103.md"), // Immutable Hardware Binding
208208
E104, Error, include_str!("./error_codes/E104.md"), // Config Variable With Incomplete Address
209209
E105, Error, include_str!("./error_codes/E105.md"), // CONSTANT keyword in POU
210+
E106, Warning, include_str!("./error_codes/E106.md"), // VAR_EXTERNAL have no effect
210211
E107, Error, include_str!("./error_codes/E107.md"), // Missing configuration for template variable
211212
E108, Error, include_str!("./error_codes/E108.md"), // Template variable is configured multiple times
212213
);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# VAR_EXTERNAL blocks have no effect
2+
3+
Variables declared in a `VAR_EXTERNAL` block are currently ignored and the referenced globals will be used instead.
4+
5+
Example:
6+
```
7+
VAR_GLOBAL
8+
myArray : ARRAY [0..10] OF INT;
9+
myString: STRING;
10+
END_VAR
11+
12+
FUNCTION main
13+
VAR_EXTERNAL CONSTANT
14+
myArray : ARRAY [0..10] OF INT;
15+
END_VAR
16+
myArray[5] := 42;
17+
myString := 'Hello, world!';
18+
END_FUNCTION
19+
```
20+
21+
In this example, even though `arr` is declared as `VAR_EXTERNAL CONSTANT`, the `CONSTANT` constraint will be ignored and
22+
the global `myArray` will be mutated. The global `myString` can be read from and written to from within `main` even though it
23+
is not declared in a `VAR_EXTERNAL` block.

src/codegen/generators/data_type_generator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ impl<'ink, 'b> DataTypeGenerator<'ink, 'b> {
177177
if let DataTypeInformation::Struct { source, members, .. } = information {
178178
let members = members
179179
.iter()
180-
.filter(|it| !it.is_temp() && !it.is_return())
180+
.filter(|it| !(it.is_temp() || it.is_return() || it.is_var_external()))
181181
.map(|m| self.types_index.get_associated_type(m.get_type_name()))
182182
.collect::<Result<Vec<BasicTypeEnum>, Diagnostic>>()?;
183183

src/codegen/generators/pou_generator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
596596
}
597597

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

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

719719
let (name, variable) = if m.is_temp() || m.is_return() {

src/codegen/tests/code_gen_tests.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3516,3 +3516,127 @@ fn array_of_struct_as_member_of_another_struct_and_variable_declaration_is_initi
35163516

35173517
insta::assert_snapshot!(res);
35183518
}
3519+
3520+
#[test]
3521+
// XXX: this behaviour might change in future, for now `VAR_EXTERNAL` variables are ignored
3522+
fn variables_in_var_external_block_are_not_generated() {
3523+
let res = codegen(
3524+
"
3525+
VAR_GLOBAL
3526+
arr: ARRAY [0..100] OF INT;
3527+
END_VAR
3528+
3529+
FUNCTION foo
3530+
VAR_EXTERNAL
3531+
arr : ARRAY [0..100] OF INT;
3532+
END_VAR
3533+
END_FUNCTION
3534+
3535+
FUNCTION_BLOCK bar
3536+
VAR_EXTERNAL CONSTANT
3537+
arr : ARRAY [0..100] OF INT;
3538+
END_VAR
3539+
END_FUNCTION_BLOCK
3540+
3541+
PROGRAM baz
3542+
VAR_EXTERNAL CONSTANT
3543+
arr : ARRAY [0..100] OF INT;
3544+
END_VAR
3545+
END_PROGRAM
3546+
3547+
CLASS qux
3548+
VAR_EXTERNAL
3549+
arr : ARRAY [0..100] OF INT;
3550+
END_VAR
3551+
END_CLASS
3552+
",
3553+
);
3554+
3555+
insta::assert_snapshot!(res, @r###"
3556+
; ModuleID = '<internal>'
3557+
source_filename = "<internal>"
3558+
3559+
%bar = type {}
3560+
%baz = type {}
3561+
%qux = type {}
3562+
3563+
@arr = global [101 x i16] zeroinitializer
3564+
@__bar__init = unnamed_addr constant %bar zeroinitializer
3565+
@baz_instance = global %baz zeroinitializer
3566+
@__qux__init = unnamed_addr constant %qux zeroinitializer
3567+
3568+
define void @foo() {
3569+
entry:
3570+
ret void
3571+
}
3572+
3573+
define void @bar(%bar* %0) {
3574+
entry:
3575+
ret void
3576+
}
3577+
3578+
define void @baz(%baz* %0) {
3579+
entry:
3580+
ret void
3581+
}
3582+
3583+
define void @qux(%qux* %0) {
3584+
entry:
3585+
ret void
3586+
}
3587+
; ModuleID = '__initializers'
3588+
source_filename = "__initializers"
3589+
3590+
%baz = type {}
3591+
%bar = type {}
3592+
%qux = type {}
3593+
3594+
@baz_instance = external global %baz
3595+
@__bar__init = external global %bar
3596+
@__qux__init = external global %qux
3597+
3598+
define void @__init_baz(%baz* %0) {
3599+
entry:
3600+
%self = alloca %baz*, align 8
3601+
store %baz* %0, %baz** %self, align 8
3602+
ret void
3603+
}
3604+
3605+
declare void @baz(%baz*)
3606+
3607+
define void @__init_bar(%bar* %0) {
3608+
entry:
3609+
%self = alloca %bar*, align 8
3610+
store %bar* %0, %bar** %self, align 8
3611+
ret void
3612+
}
3613+
3614+
declare void @bar(%bar*)
3615+
3616+
define void @__init_qux(%qux* %0) {
3617+
entry:
3618+
%self = alloca %qux*, align 8
3619+
store %qux* %0, %qux** %self, align 8
3620+
ret void
3621+
}
3622+
3623+
declare void @qux(%qux*)
3624+
; ModuleID = '__init___testproject'
3625+
source_filename = "__init___testproject"
3626+
3627+
%baz = type {}
3628+
3629+
@baz_instance = external global %baz
3630+
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
3631+
3632+
define void @__init___testproject() {
3633+
entry:
3634+
call void @__init_baz(%baz* @baz_instance)
3635+
ret void
3636+
}
3637+
3638+
declare void @__init_baz(%baz*)
3639+
3640+
declare void @baz(%baz*)
3641+
"###);
3642+
}

src/codegen/tests/initialization_test/complex_initializers.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,3 +1296,75 @@ fn var_config_aliased_variables_initialized() {
12961296
declare void @FB(%FB*)
12971297
"###);
12981298
}
1299+
1300+
#[test]
1301+
fn var_external_blocks_are_ignored_in_init_functions() {
1302+
let res = codegen(
1303+
r"
1304+
VAR_GLOBAL
1305+
s: STRING;
1306+
refString AT s : STRING;
1307+
END_VAR
1308+
1309+
FUNCTION_BLOCK foo
1310+
VAR_EXTERNAL
1311+
refString : STRING;
1312+
END_VAR
1313+
END_FUNCTION
1314+
1315+
FUNCTION bar
1316+
VAR_EXTERNAL
1317+
refString : STRING;
1318+
END_VAR
1319+
END_FUNCTION
1320+
",
1321+
);
1322+
1323+
insta::assert_snapshot!(res, @r###"
1324+
; ModuleID = '<internal>'
1325+
source_filename = "<internal>"
1326+
1327+
%foo = type {}
1328+
1329+
@s = global [81 x i8] zeroinitializer
1330+
@refString = global [81 x i8]* null
1331+
@__foo__init = unnamed_addr constant %foo zeroinitializer
1332+
1333+
define void @foo(%foo* %0) {
1334+
entry:
1335+
ret void
1336+
}
1337+
1338+
define void @bar() {
1339+
entry:
1340+
ret void
1341+
}
1342+
; ModuleID = '__initializers'
1343+
source_filename = "__initializers"
1344+
1345+
%foo = type {}
1346+
1347+
@__foo__init = external global %foo
1348+
1349+
define void @__init_foo(%foo* %0) {
1350+
entry:
1351+
%self = alloca %foo*, align 8
1352+
store %foo* %0, %foo** %self, align 8
1353+
ret void
1354+
}
1355+
1356+
declare void @foo(%foo*)
1357+
; ModuleID = '__init___testproject'
1358+
source_filename = "__init___testproject"
1359+
1360+
@s = external global [81 x i8]
1361+
@refString = external global [81 x i8]*
1362+
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
1363+
1364+
define void @__init___testproject() {
1365+
entry:
1366+
store [81 x i8]* @s, [81 x i8]** @refString, align 8
1367+
ret void
1368+
}
1369+
"###)
1370+
}

src/index.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ pub struct VariableIndexEntry {
7070
pub argument_type: ArgumentType,
7171
/// true if this variable is a compile-time-constant
7272
is_constant: bool,
73+
// true if this variable is in a 'VAR_EXTERNAL' block
74+
is_var_external: bool,
7375
/// the variable's datatype
7476
pub data_type_name: String,
7577
/// the index of the member-variable in it's container (e.g. struct). defautls to 0 (Single variables)
@@ -130,6 +132,7 @@ pub struct MemberInfo<'b> {
130132
variable_type_name: &'b str,
131133
binding: Option<HardwareBinding>,
132134
is_constant: bool,
135+
is_var_external: bool,
133136
varargs: Option<VarArgs>,
134137
}
135138

@@ -148,6 +151,7 @@ impl VariableIndexEntry {
148151
initial_value: None,
149152
argument_type,
150153
is_constant: false,
154+
is_var_external: false,
151155
data_type_name: data_type_name.to_string(),
152156
location_in_parent,
153157
linkage: LinkageType::Internal,
@@ -169,6 +173,7 @@ impl VariableIndexEntry {
169173
initial_value: None,
170174
argument_type: ArgumentType::ByVal(VariableType::Global),
171175
is_constant: false,
176+
is_var_external: false,
172177
data_type_name: data_type_name.to_string(),
173178
location_in_parent: 0,
174179
linkage: LinkageType::Internal,
@@ -203,6 +208,11 @@ impl VariableIndexEntry {
203208
self
204209
}
205210

211+
pub fn set_var_external(mut self, var_external: bool) -> Self {
212+
self.is_var_external = var_external;
213+
self
214+
}
215+
206216
/// Creates a new VariableIndexEntry from the current entry with a new container and type
207217
/// This is used to create new entries from previously generic entries
208218
pub fn into_typed(&self, container: &str, new_type: &str) -> Self {
@@ -253,6 +263,10 @@ impl VariableIndexEntry {
253263
self.linkage == LinkageType::External
254264
}
255265

266+
pub fn is_var_external(&self) -> bool {
267+
self.is_var_external
268+
}
269+
256270
pub fn get_declaration_type(&self) -> ArgumentType {
257271
self.argument_type
258272
}
@@ -350,6 +364,7 @@ pub enum VariableType {
350364
InOut,
351365
Global,
352366
Return,
367+
External,
353368
}
354369

355370
impl VariableType {
@@ -368,6 +383,7 @@ impl std::fmt::Display for VariableType {
368383
VariableType::InOut => write!(f, "InOut"),
369384
VariableType::Global => write!(f, "Global"),
370385
VariableType::Return => write!(f, "Return"),
386+
VariableType::External => write!(f, "External"),
371387
}
372388
}
373389
}
@@ -1138,13 +1154,18 @@ impl Index {
11381154
/// Searches for variable name in the given container, if not found, attempts to search for it in super classes
11391155
pub fn find_member(&self, container_name: &str, variable_name: &str) -> Option<&VariableIndexEntry> {
11401156
// Find pou in index
1141-
self.find_local_member(container_name, variable_name).or_else(|| {
1142-
if let Some(class) = self.find_pou(container_name).and_then(|it| it.get_super_class()) {
1143-
self.find_member(class, variable_name)
1144-
} else {
1145-
None
1146-
}
1147-
})
1157+
self.find_local_member(container_name, variable_name)
1158+
.or_else(|| {
1159+
if let Some(class) = self.find_pou(container_name).and_then(|it| it.get_super_class()) {
1160+
self.find_member(class, variable_name)
1161+
} else {
1162+
None
1163+
}
1164+
})
1165+
.filter(|it| {
1166+
// VAR_EXTERNAL variables are not local members
1167+
!it.is_var_external()
1168+
})
11481169
}
11491170

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

15231545
pub fn register_enum_variant(

0 commit comments

Comments
 (0)