Skip to content

Commit fcd4c02

Browse files
authored
feat: initialize configured template variables (#1317)
* feat: initialize var_config variables
1 parent c74de93 commit fcd4c02

File tree

11 files changed

+332
-85
lines changed

11 files changed

+332
-85
lines changed

compiler/plc_ast/src/pre_processor.rs

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,34 @@ fn process_global_variables(unit: &mut CompilationUnit, id_provider: &mut IdProv
187187
update_generated_globals(unit, mangled_globals);
188188
}
189189

190+
fn process_var_config_variables(unit: &mut CompilationUnit) {
191+
let block = get_internal_global_block(unit);
192+
let variables = unit.var_config.iter().filter_map(|ConfigVariable { data_type, address, .. }| {
193+
let AstStatement::HardwareAccess(hardware) = &address.stmt else {
194+
unreachable!("Must be parsed as hardware access")
195+
};
196+
197+
if hardware.is_template() {
198+
return None;
199+
}
200+
201+
let name = hardware.get_mangled_variable_name();
202+
if block.is_some_and(|it| it.variables.iter().any(|v| v.name == name)) {
203+
return None;
204+
};
205+
206+
Some(Variable {
207+
name,
208+
data_type_declaration: data_type.get_inner_pointer_ty().unwrap_or(data_type.clone()),
209+
initializer: None,
210+
address: None,
211+
location: address.get_location(),
212+
})
213+
});
214+
215+
update_generated_globals(unit, variables.collect())
216+
}
217+
190218
fn update_generated_globals(unit: &mut CompilationUnit, mangled_globals: Vec<Variable>) {
191219
let mut block = if let Some(index) = unit.global_vars.iter().position(|block| {
192220
block.variable_block_type == VariableBlockType::Global && block.location.is_internal()
@@ -200,32 +228,17 @@ fn update_generated_globals(unit: &mut CompilationUnit, mangled_globals: Vec<Var
200228
block.variables.push(var);
201229
}
202230
}
231+
203232
unit.global_vars.push(block);
204233
}
205234

206-
fn process_var_config_variables(unit: &mut CompilationUnit) {
207-
let variables =
208-
unit.var_config.iter().filter_map(|ConfigVariable { data_type, address, location, .. }| {
209-
let AstStatement::HardwareAccess(hardware) = &address.stmt else {
210-
unreachable!("Must be parsed as hardware access")
211-
};
212-
213-
if hardware.is_template() {
214-
return None;
215-
}
216-
217-
let name = hardware.get_mangled_variable_name();
218-
219-
Some(Variable {
220-
name,
221-
data_type_declaration: data_type.clone(),
222-
initializer: None,
223-
address: None,
224-
location: location.clone(),
225-
})
226-
});
227-
228-
update_generated_globals(unit, variables.collect())
235+
fn get_internal_global_block(unit: &CompilationUnit) -> Option<&VariableBlock> {
236+
unit.global_vars
237+
.iter()
238+
.position(|block| {
239+
block.variable_block_type == VariableBlockType::Global && block.location.is_internal()
240+
})
241+
.and_then(|index| unit.global_vars.get(index))
229242
}
230243

231244
fn build_enum_initializer(

src/codegen/tests/initialization_test/complex_initializers.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,3 +1170,116 @@ fn override_default_initializer() {
11701170

11711171
insta::assert_snapshot!(res, @r###""###);
11721172
}
1173+
1174+
#[test]
1175+
fn var_config_aliased_variables_initialized() {
1176+
let res = codegen(
1177+
r"
1178+
FUNCTION_BLOCK FB
1179+
VAR
1180+
foo AT %I* : DINT;
1181+
END_VAR
1182+
END_FUNCTION_BLOCK
1183+
1184+
VAR_CONFIG
1185+
prog.instance1.foo AT %IX1.2.1 : DINT;
1186+
prog.instance2.foo AT %QX1.2.2 : DINT;
1187+
END_VAR
1188+
1189+
PROGRAM prog
1190+
VAR
1191+
instance1: FB;
1192+
instance2: FB;
1193+
END_VAR
1194+
END_PROGRAM
1195+
",
1196+
);
1197+
1198+
insta::assert_snapshot!(res, @r###"
1199+
; ModuleID = '<internal>'
1200+
source_filename = "<internal>"
1201+
1202+
%FB = type { i32* }
1203+
%prog = type { %FB, %FB }
1204+
1205+
@__PI_1_2_1 = global i32 0, section "var-$RUSTY$__PI_1_2_1:i32"
1206+
@__PI_1_2_2 = global i32 0, section "var-$RUSTY$__PI_1_2_2:i32"
1207+
@__FB__init = unnamed_addr constant %FB zeroinitializer, section "var-$RUSTY$__FB__init:r1pi32"
1208+
@prog_instance = global %prog zeroinitializer, section "var-$RUSTY$prog_instance:r2r1pi32r1pi32"
1209+
1210+
define void @FB(%FB* %0) section "fn-$RUSTY$FB:v" {
1211+
entry:
1212+
%foo = getelementptr inbounds %FB, %FB* %0, i32 0, i32 0
1213+
ret void
1214+
}
1215+
1216+
define void @prog(%prog* %0) section "fn-$RUSTY$prog:v" {
1217+
entry:
1218+
%instance1 = getelementptr inbounds %prog, %prog* %0, i32 0, i32 0
1219+
%instance2 = getelementptr inbounds %prog, %prog* %0, i32 0, i32 1
1220+
ret void
1221+
}
1222+
; ModuleID = '__initializers'
1223+
source_filename = "__initializers"
1224+
1225+
%FB = type { i32* }
1226+
%prog = type { %FB, %FB }
1227+
1228+
@__FB__init = external global %FB, section "var-$RUSTY$__FB__init:r1pi32"
1229+
@prog_instance = external global %prog, section "var-$RUSTY$prog_instance:r2r1pi32r1pi32"
1230+
1231+
define void @__init_fb(%FB* %0) section "fn-$RUSTY$__init_fb:v[pr1pi32]" {
1232+
entry:
1233+
%self = alloca %FB*, align 8
1234+
store %FB* %0, %FB** %self, align 8
1235+
ret void
1236+
}
1237+
1238+
declare void @FB(%FB*) section "fn-$RUSTY$FB:v"
1239+
1240+
define void @__init_prog(%prog* %0) section "fn-$RUSTY$__init_prog:v[pr2r1pi32r1pi32]" {
1241+
entry:
1242+
%self = alloca %prog*, align 8
1243+
store %prog* %0, %prog** %self, align 8
1244+
%deref = load %prog*, %prog** %self, align 8
1245+
%instance1 = getelementptr inbounds %prog, %prog* %deref, i32 0, i32 0
1246+
call void @__init_fb(%FB* %instance1)
1247+
%deref1 = load %prog*, %prog** %self, align 8
1248+
%instance2 = getelementptr inbounds %prog, %prog* %deref1, i32 0, i32 1
1249+
call void @__init_fb(%FB* %instance2)
1250+
ret void
1251+
}
1252+
1253+
declare void @prog(%prog*) section "fn-$RUSTY$prog:v"
1254+
; ModuleID = '__init___testproject'
1255+
source_filename = "__init___testproject"
1256+
1257+
%prog = type { %FB, %FB }
1258+
%FB = type { i32* }
1259+
1260+
@prog_instance = external global %prog, section "var-$RUSTY$prog_instance:r2r1pi32r1pi32"
1261+
@__FB__init = external global %FB, section "var-$RUSTY$__FB__init:r1pi32"
1262+
@__PI_1_2_1 = external global i32, section "var-$RUSTY$__PI_1_2_1:i32"
1263+
@__PI_1_2_2 = external global i32, section "var-$RUSTY$__PI_1_2_2:i32"
1264+
1265+
define void @__init___testproject() section "fn-$RUSTY$__init___testproject:v" {
1266+
entry:
1267+
call void @__init_prog(%prog* @prog_instance)
1268+
call void @__init___var_config()
1269+
ret void
1270+
}
1271+
1272+
define void @__init___var_config() section "fn-$RUSTY$__init___var_config:v" {
1273+
entry:
1274+
store i32* @__PI_1_2_1, i32** getelementptr inbounds (%prog, %prog* @prog_instance, i32 0, i32 0, i32 0), align 8
1275+
store i32* @__PI_1_2_2, i32** getelementptr inbounds (%prog, %prog* @prog_instance, i32 0, i32 1, i32 0), align 8
1276+
ret void
1277+
}
1278+
1279+
declare void @__init_prog(%prog*) section "fn-$RUSTY$__init_prog:v[pr2r1pi32r1pi32]"
1280+
1281+
declare void @prog(%prog*) section "fn-$RUSTY$prog:v"
1282+
1283+
declare void @FB(%FB*) section "fn-$RUSTY$FB:v"
1284+
"###);
1285+
}

src/index/tests/index_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,12 +2120,12 @@ fn var_config_hardware_address_creates_global_variable() {
21202120
span: Range(
21212121
TextLocation {
21222122
line: 2,
2123-
column: 16,
2124-
offset: 40,
2123+
column: 24,
2124+
offset: 48,
21252125
}..TextLocation {
21262126
line: 2,
2127-
column: 23,
2128-
offset: 47,
2127+
column: 37,
2128+
offset: 61,
21292129
},
21302130
),
21312131
},

src/lowering.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
};
55
use initializers::{Init, InitAssignments, Initializers, GLOBAL_SCOPE};
66
use plc_ast::{
7-
ast::{AstFactory, AstNode, CompilationUnit, DataType, LinkageType, PouType},
7+
ast::{AstFactory, AstNode, CompilationUnit, ConfigVariable, DataType, LinkageType, PouType},
88
mut_visitor::{AstVisitorMut, WalkerMut},
99
provider::IdProvider,
1010
visit_all_nodes_mut,
@@ -17,6 +17,7 @@ pub struct AstLowerer {
1717
index: Index,
1818
annotations: AstAnnotations,
1919
unresolved_initializers: Initializers,
20+
var_config_initializers: Vec<AstNode>,
2021
units: Vec<CompilationUnit>,
2122
ctxt: LoweringContext,
2223
}
@@ -51,6 +52,7 @@ impl AstLowerer {
5152
index,
5253
annotations,
5354
unresolved_initializers: Initializers::new(&unresolved_initializers),
55+
var_config_initializers: vec![],
5456
units: vec![],
5557
ctxt: LoweringContext::new(id_provider),
5658
}
@@ -61,6 +63,7 @@ impl AstLowerer {
6163
index: self.index,
6264
annotations: self.annotations,
6365
unresolved_initializers: self.unresolved_initializers,
66+
var_config_initializers: self.var_config_initializers,
6467
units,
6568
ctxt: self.ctxt,
6669
}
@@ -173,7 +176,6 @@ impl AstLowerer {
173176
/// Updates the scope and initialized variable for struct types. Adds entries for each encountered struct
174177
/// (this includes POU-structs, i.e. programs, ...) to the initializer map if no entry is present
175178
fn update_struct_initializers(&mut self, user_type: &mut plc_ast::ast::UserTypeDeclaration) {
176-
// alias == subrangetype?
177179
let effective_type =
178180
user_type.data_type.get_name().and_then(|it| self.index.find_effective_type_by_name(it));
179181
if let DataType::StructType { .. } = &user_type.data_type {
@@ -224,6 +226,13 @@ impl AstLowerer {
224226

225227
self.unresolved_initializers.maybe_insert_initializer(GLOBAL_SCOPE, Some(variable.get_name()), &None);
226228
}
229+
230+
fn collect_var_config_assignments(&mut self, var_config: &[ConfigVariable]) {
231+
let assignments = var_config.iter().map(|var| {
232+
AstFactory::create_assignment(var.reference.clone(), var.address.clone(), self.ctxt.next_id())
233+
});
234+
self.var_config_initializers.extend(assignments);
235+
}
227236
}
228237

229238
impl AstVisitorMut for AstLowerer {
@@ -232,6 +241,7 @@ impl AstVisitorMut for AstLowerer {
232241
}
233242

234243
fn visit_compilation_unit(&mut self, unit: &mut CompilationUnit) {
244+
self.collect_var_config_assignments(&unit.var_config);
235245
unit.walk(self)
236246
}
237247

@@ -468,7 +478,7 @@ impl LoweringContext {
468478
self.id_provider.clone()
469479
}
470480

471-
fn _next_id(&mut self) -> usize {
481+
fn next_id(&mut self) -> usize {
472482
self.id_provider.next_id()
473483
}
474484
}

src/lowering/initializers.rs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use plc_source::source_location::SourceLocation;
1111

1212
use super::AstLowerer;
1313
pub(crate) const GLOBAL_SCOPE: &str = "__global";
14+
const INIT_COMPILATION_UNIT: &str = "__initializers";
15+
const VAR_CONFIG_INIT: &str = "__init___var_config";
1416

1517
/// POUs and datatypes which require initialization via generated function call.
1618
/// The key corresponds to the scope in which the initializers were encountered.
@@ -99,23 +101,30 @@ impl<'lwr> Init<'lwr> for Initializers {
99101

100102
impl AstLowerer {
101103
pub fn lower_init_functions(mut self, init_symbol_name: &str) -> Self {
102-
let res = create_init_units(&self);
104+
let units = create_init_units(&self);
103105

104-
if let Some(init_unit) = res.into_iter().reduce(|mut acc_unit, unit| {
106+
if let Some(init_unit) = units.into_iter().reduce(|mut acc_unit, unit| {
105107
acc_unit.import(unit);
106108
acc_unit
107109
}) {
108110
self.units.push(init_unit);
109111
}
110112

111-
if let Some(init_unit) = create_init_wrapper_function(&self, init_symbol_name) {
112-
self.units.push(init_unit);
113+
if let Some(global_init) = create_init_wrapper_function(&mut self, init_symbol_name) {
114+
self.units.push(global_init);
113115
}
114116

115117
self
116118
}
117119
}
118120

121+
fn create_var_config_init(statements: Vec<AstNode>) -> CompilationUnit {
122+
let loc = SourceLocation::internal_in_unit(Some(INIT_COMPILATION_UNIT));
123+
let pou = new_pou(VAR_CONFIG_INIT, vec![], &loc); // this can probably just be internal
124+
let implementation = new_implementation(VAR_CONFIG_INIT, statements, &loc);
125+
new_unit(pou, implementation, INIT_COMPILATION_UNIT)
126+
}
127+
119128
fn create_init_units(lowerer: &AstLowerer) -> Vec<CompilationUnit> {
120129
let lookup = lowerer.unresolved_initializers.keys().map(|it| it.as_str()).collect::<FxIndexSet<_>>();
121130
lowerer
@@ -207,13 +216,14 @@ fn create_init_unit(
207216
statements.extend(member_init_calls);
208217
let implementation = new_implementation(&init_fn_name, statements, location);
209218

210-
Some(new_unit(init_pou, implementation, "__initializers"))
219+
Some(new_unit(init_pou, implementation, INIT_COMPILATION_UNIT))
211220
}
212221

213-
fn create_init_wrapper_function(lowerer: &AstLowerer, init_symbol_name: &str) -> Option<CompilationUnit> {
214-
if lowerer.unresolved_initializers.is_empty() {
222+
fn create_init_wrapper_function(lowerer: &mut AstLowerer, init_symbol_name: &str) -> Option<CompilationUnit> {
223+
let skip_var_config = lowerer.var_config_initializers.is_empty();
224+
if skip_var_config && lowerer.unresolved_initializers.is_empty() {
215225
return None;
216-
}
226+
};
217227

218228
let mut id_provider = lowerer.ctxt.id_provider.clone();
219229
let init_pou = new_pou(init_symbol_name, vec![], &SourceLocation::internal());
@@ -272,8 +282,25 @@ fn create_init_wrapper_function(lowerer: &AstLowerer, init_symbol_name: &str) ->
272282

273283
assignments.extend(calls);
274284

285+
if !skip_var_config {
286+
assignments.push(AstFactory::create_call_statement(
287+
create_member_reference(VAR_CONFIG_INIT, id_provider.clone(), None),
288+
None,
289+
id_provider.next_id(),
290+
SourceLocation::internal(),
291+
));
292+
};
293+
275294
let implementation = new_implementation(init_symbol_name, assignments, &SourceLocation::internal());
276-
Some(new_unit(init_pou, implementation, init_symbol_name))
295+
let mut global_init = new_unit(init_pou, implementation, init_symbol_name);
296+
297+
if skip_var_config {
298+
return Some(global_init);
299+
};
300+
301+
let var_config_init = create_var_config_init(std::mem::take(&mut lowerer.var_config_initializers));
302+
global_init.import(var_config_init);
303+
Some(global_init)
277304
}
278305

279306
fn new_pou(name: &str, variable_blocks: Vec<VariableBlock>, location: &SourceLocation) -> Pou {

0 commit comments

Comments
 (0)