Skip to content

Commit 1a1ab0e

Browse files
authored
feat: init functions for address-initialization (#1259)
Adds groundwork for initializer-function support
1 parent 36401b2 commit 1a1ab0e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2513
-268
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,32 @@ pub struct VariableBlock {
361361
pub location: SourceLocation,
362362
}
363363

364+
impl VariableBlock {
365+
pub fn with_block_type(mut self, block_type: VariableBlockType) -> Self {
366+
self.variable_block_type = block_type;
367+
self
368+
}
369+
370+
pub fn with_variables(mut self, variables: Vec<Variable>) -> Self {
371+
self.variables = variables;
372+
self
373+
}
374+
}
375+
376+
impl Default for VariableBlock {
377+
fn default() -> Self {
378+
VariableBlock {
379+
access: AccessModifier::Internal,
380+
constant: false,
381+
retain: false,
382+
variables: vec![],
383+
variable_block_type: VariableBlockType::Local,
384+
linkage: LinkageType::Internal,
385+
location: SourceLocation::internal(),
386+
}
387+
}
388+
}
389+
364390
impl Debug for VariableBlock {
365391
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
366392
f.debug_struct("VariableBlock")
@@ -401,6 +427,10 @@ impl Variable {
401427
};
402428
std::mem::replace(&mut self.data_type_declaration, new_data_type)
403429
}
430+
431+
pub fn get_name(&self) -> &str {
432+
&self.name
433+
}
404434
}
405435

406436
#[derive(Clone, PartialEq)]

compiler/plc_ast/src/pre_processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ fn replace_generic_type_name(dt: &mut DataTypeDeclaration, generics: &FxHashMap<
281281
},
282282
DataTypeDeclaration::DataTypeReference { referenced_type, .. } => {
283283
if let Some(type_name) = generics.get(referenced_type) {
284-
*referenced_type = type_name.clone();
284+
referenced_type.clone_from(type_name);
285285
}
286286
}
287287
}

compiler/plc_driver/src/pipelines.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ use ast::{
1111
provider::IdProvider,
1212
};
1313

14-
use plc::index::FxIndexSet;
1514
use plc::{
1615
codegen::{CodegenContext, GeneratedModule},
17-
index::Index,
16+
index::{FxIndexSet, Index},
1817
output::FormatOption,
1918
parser::parse_file,
2019
resolver::{AnnotationMapImpl, AstAnnotations, Dependency, StringLiterals, TypeAnnotator},
@@ -127,6 +126,7 @@ impl<T: SourceContainer + Sync> ParsedProject<T> {
127126
// import builtin functions
128127
let builtins = plc::builtins::parse_built_ins(id_provider);
129128
global_index.import(plc::index::visitor::visit(&builtins));
129+
global_index.register_global_init_function();
130130

131131
IndexedProject { project: ParsedProject { project: self.project, units }, index: global_index }
132132
}
@@ -148,7 +148,8 @@ impl<T: SourceContainer + Sync> IndexedProject<T> {
148148
pub fn annotate(self, mut id_provider: IdProvider) -> AnnotatedProject<T> {
149149
//Resolve constants
150150
//TODO: Not sure what we are currently doing with unresolvables
151-
let (mut full_index, _unresolvables) = plc::resolver::const_evaluator::evaluate_constants(self.index);
151+
let (mut full_index, unresolvables) = plc::resolver::const_evaluator::evaluate_constants(self.index);
152+
152153
//Create and call the annotator
153154
let mut annotated_units = Vec::new();
154155
let mut all_annotations = AnnotationMapImpl::default();
@@ -171,8 +172,15 @@ impl<T: SourceContainer + Sync> IndexedProject<T> {
171172

172173
full_index.import(std::mem::take(&mut all_annotations.new_index));
173174

174-
let annotations = AstAnnotations::new(all_annotations, id_provider.next_id());
175+
TypeAnnotator::lower_init_functions(
176+
unresolvables,
177+
&mut all_annotations,
178+
&mut full_index,
179+
&id_provider,
180+
&mut annotated_units,
181+
);
175182

183+
let annotations = AstAnnotations::new(all_annotations, id_provider.next_id());
176184
AnnotatedProject {
177185
project: self.project.project,
178186
units: annotated_units,
@@ -341,7 +349,11 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
341349
let current_dir = env::current_dir()?;
342350
let current_dir = compile_options.root.as_deref().unwrap_or(&current_dir);
343351
let unit_location = PathBuf::from(&unit.file_name);
344-
let unit_location = fs::canonicalize(unit_location)?;
352+
let unit_location = if unit_location.exists() {
353+
fs::canonicalize(unit_location)?
354+
} else {
355+
unit_location
356+
};
345357
let output_name = if unit_location.starts_with(current_dir) {
346358
unit_location.strip_prefix(current_dir).map_err(|it| {
347359
Diagnostic::new(format!(

compiler/plc_xml/src/model/pou.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl<'xml> Parseable for Pou<'xml> {
6060
let actions: Vec<Action<'_>> = Parseable::visit(reader, Some(tag))?;
6161
for mut action in actions.into_iter() {
6262
// Copy the action type names
63-
action.type_name = pou.name.clone();
63+
action.type_name.clone_from(&pou.name);
6464
pou.actions.push(action);
6565
}
6666
}

src/builtins.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ lazy_static! {
4242
END_FUNCTION
4343
",
4444
annotation: None,
45-
validation: None,
45+
validation: Some(|validator, operator, parameters, _, _| {
46+
validate_argument_count(validator, operator, &parameters, 1);
47+
}),
4648
generic_name_resolver: no_generic_name_resolver,
4749
code: |generator, params, location| {
4850
if let [reference] = params {
4951
generator
5052
.generate_lvalue(reference)
51-
.map(|it| ExpressionValue::RValue(generator.ptr_as_value(it)))
53+
.map(|it| ExpressionValue::RValue(it.as_basic_value_enum()))
5254
} else {
5355
Err(Diagnostic::codegen_error(
5456
"Expected exactly one parameter for REF",
@@ -91,16 +93,7 @@ lazy_static! {
9193
);
9294
}),
9395
validation: Some(|validator, operator, parameters, _, _| {
94-
let Some(params) = parameters else {
95-
validator.push_diagnostic(Diagnostic::invalid_argument_count(1, 0, operator.get_location()));
96-
return;
97-
};
98-
99-
let params = flatten_expression_list(params);
100-
101-
if params.len() > 1 {
102-
validator.push_diagnostic(Diagnostic::invalid_argument_count(1, params.len(), operator.get_location()));
103-
}
96+
validate_argument_count(validator, operator, &parameters, 1);
10497
}),
10598
generic_name_resolver: no_generic_name_resolver,
10699
code: |generator, params, location| {
@@ -804,6 +797,28 @@ fn validate_variable_length_array_bound_function(
804797
}
805798
}
806799

800+
fn validate_argument_count(
801+
validator: &mut Validator,
802+
operator: &AstNode,
803+
parameters: &Option<&AstNode>,
804+
expected: usize,
805+
) {
806+
let Some(params) = parameters else {
807+
validator.push_diagnostic(Diagnostic::invalid_argument_count(expected, 0, operator.get_location()));
808+
return;
809+
};
810+
811+
let params = flatten_expression_list(params);
812+
813+
if params.len() != expected {
814+
validator.push_diagnostic(Diagnostic::invalid_argument_count(
815+
expected,
816+
params.len(),
817+
operator.get_location(),
818+
));
819+
}
820+
}
821+
807822
/// Generates the code for the LOWER- AND UPPER_BOUND built-in functions, returning an error if the function
808823
/// arguments are incorrect.
809824
fn generate_variable_length_array_bound_function<'ink>(

src/codegen/generators/data_type_generator.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub fn generate_data_types<'ink>(
119119

120120
let mut tries = 0;
121121
let mut errors = FxHashMap::default();
122+
122123
// If the tries are equal to the number of types remaining, it means we failed to resolve
123124
// anything
124125
while tries < types_to_init.len() {
@@ -149,20 +150,26 @@ pub fn generate_data_types<'ink>(
149150
}
150151
//If we didn't resolve anything this cycle, report the remaining issues and exit
151152
if !types_to_init.is_empty() {
153+
let mut init_later = 0;
152154
//Report each error as a new diagnostic, add the type's location as related to the error
153155
let diags = types_to_init
154156
.into_iter()
155157
.map(|(name, ty)| {
158+
if index.type_has_init_function(name) {
159+
init_later += 1;
160+
}
156161
errors
157162
.remove(name)
158163
.map(|diag| diag.with_secondary_location(&ty.location))
159164
.unwrap_or_else(|| Diagnostic::cannot_generate_initializer(name, ty.location.clone()))
160165
})
161166
.collect::<Vec<_>>();
162-
//Report the operation failure
163-
return Err(Diagnostic::new("Some initial values were not generated")
164-
.with_error_code("E075")
165-
.with_sub_diagnostics(diags));
167+
if diags.len() > init_later {
168+
//Report the operation failure
169+
return Err(Diagnostic::new("Some initial values were not generated")
170+
.with_error_code("E075")
171+
.with_sub_diagnostics(diags)); // FIXME: these sub-diagnostics aren't printed to the console
172+
}
166173
}
167174
Ok(generator.types_index)
168175
}
@@ -372,6 +379,7 @@ impl<'ink, 'b> DataTypeGenerator<'ink, 'b> {
372379
self.annotations,
373380
&self.types_index,
374381
);
382+
375383
generator.generate_expression(initializer).map(Some).map_err(|_| {
376384
Diagnostic::cannot_generate_initializer(qualified_name, initializer.get_location())
377385
})

src/codegen/generators/expression_generator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,6 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
717717
let assigned_output = self.generate_lvalue(expr)?;
718718
let assigned_output_type =
719719
self.annotations.get_type_or_void(expr, self.index).get_type_information();
720-
721720
let output = builder.build_struct_gep(parameter_struct, index, "").map_err(|_| {
722721
Diagnostic::codegen_error(
723722
format!("Cannot build generate parameter: {parameter:#?}"),

src/codegen/generators/statement_generator.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
255255
let expr = exp.generate_reference_expression(&data.access, data.base.as_deref(), left)?;
256256
expr.get_basic_value_enum().into_pointer_value()
257257
};
258-
let right_expr_val = ref_builtin.codegen(&exp, &[&right], right.get_location())?;
258+
let right_expr_val = ref_builtin.codegen(&exp, &[right], right.get_location())?;
259259

260260
self.llvm.builder.build_store(left_ptr_val, right_expr_val.get_basic_value_enum());
261261
Ok(())
@@ -276,6 +276,16 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
276276
if left_statement.has_direct_access() {
277277
return self.generate_assignment_statement_direct_access(left_statement, right_statement);
278278
}
279+
280+
if self.annotations.get(left_statement).is_some_and(|it| {
281+
// TODO(mhasel): ideally the resolver decides which assignment statement to call when lowering the init functions,
282+
// but that requires refactoring of how `aliases` and `reference to` LHS/RHS nodes are annotated. this is a workaround.
283+
self.index.is_init_function(self.function_context.linking_context.get_call_name())
284+
&& (it.is_alias() || it.is_reference_to())
285+
}) {
286+
return self.generate_ref_assignment(left_statement, right_statement);
287+
};
288+
279289
//TODO: Also hacky but for now we cannot generate assignments for hardware access
280290
if matches!(left_statement.get_stmt(), AstStatement::HardwareAccess { .. }) {
281291
return Ok(());

src/codegen/generators/variable_generator.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
/// offers operations to generate global variables
44
use crate::{
5-
codegen::{debug::Debug, llvm_index::LlvmTypedIndex, llvm_typesystem::cast_if_needed},
5+
codegen::{
6+
const_expressions::{ConstExpression, UnresolvableKind},
7+
debug::Debug,
8+
llvm_index::LlvmTypedIndex,
9+
llvm_typesystem::cast_if_needed,
10+
},
611
index::{get_initializer_name, Index, PouIndexEntry, VariableIndexEntry},
712
resolver::{AnnotationMap, AstAnnotations, Dependency},
813
};
@@ -127,7 +132,15 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
127132
if linkage == LinkageType::External {
128133
global_ir_variable = global_ir_variable.make_external();
129134
} else {
130-
let initial_value = if let Some(initializer) = self
135+
let initial_value = if let Some(ConstExpression::Unresolvable {
136+
reason: UnresolvableKind::Address { .. },
137+
..
138+
}) = global_variable
139+
.initial_value
140+
.and_then(|it| self.global_index.get_const_expressions().find_const_expression(&it))
141+
{
142+
None
143+
} else if let Some(initializer) = self
131144
.global_index
132145
.get_const_expressions()
133146
.maybe_get_constant_statement(&global_variable.initial_value)
@@ -153,7 +166,6 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
153166
} else {
154167
None
155168
};
156-
157169
let initial_value = initial_value
158170
// 2nd try: find an associated default value for the declared type
159171
.or_else(|| self.types_index.find_associated_initial_value(type_name))

src/codegen/tests/expression_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ fn builtin_function_call_adr() {
290290
",
291291
);
292292
// WHEN compiled
293-
// We expect a direct conversion to lword and subsequent assignment (no call)
293+
// We expect the same behaviour as if REF was called, due to the assignee being a pointer
294294
insta::assert_snapshot!(result);
295295
}
296296

0 commit comments

Comments
 (0)