Skip to content

Commit c7f3d82

Browse files
authored
fix: Bitaccess for VAR_OUTPUT (#1214)
Resolves #1176
1 parent 45f487f commit c7f3d82

File tree

8 files changed

+834
-244
lines changed

8 files changed

+834
-244
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,14 @@ impl AstNode {
851851
matches!(self.stmt, AstStatement::EmptyStatement(..))
852852
}
853853

854+
pub fn is_assignment(&self) -> bool {
855+
matches!(self.stmt, AstStatement::Assignment(..))
856+
}
857+
858+
pub fn is_output_assignment(&self) -> bool {
859+
matches!(self.stmt, AstStatement::OutputAssignment(..))
860+
}
861+
854862
pub fn is_reference(&self) -> bool {
855863
matches!(self.stmt, AstStatement::ReferenceExpr(..))
856864
}

libs/stdlib/tests/counters_tests.rs

Lines changed: 72 additions & 72 deletions
Large diffs are not rendered by default.

src/codegen/generators/expression_generator.rs

Lines changed: 272 additions & 85 deletions
Large diffs are not rendered by default.

src/codegen/generators/statement_generator.rs

Lines changed: 21 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use super::{
44
llvm::Llvm,
55
};
66
use crate::{
7-
codegen::{debug::Debug, llvm_typesystem::cast_if_needed},
7+
codegen::debug::Debug,
88
codegen::{debug::DebugBuilderEnum, LlvmTypedIndex},
99
index::{ImplementationIndexEntry, Index},
1010
resolver::{AnnotationMap, AstAnnotations, StatementAnnotation},
11-
typesystem::{self, DataTypeInformation},
11+
typesystem::DataTypeInformation,
1212
};
1313
use inkwell::{
1414
basic_block::BasicBlock,
@@ -244,7 +244,7 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
244244
self.register_debug_location(left_statement);
245245
//TODO: Looks hacky, the strings will be similar so we should look into making the assignment a bit nicer.
246246
if left_statement.has_direct_access() {
247-
return self.generate_direct_access_assignment(left_statement, right_statement);
247+
return self.generate_assignment_statement_direct_access(left_statement, right_statement);
248248
}
249249
//TODO: Also hacky but for now we cannot generate assignments for hardware access
250250
if matches!(left_statement.get_stmt(), AstStatement::HardwareAccess { .. }) {
@@ -279,96 +279,32 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
279279
self.debug.set_debug_location(self.llvm, &self.function_context.function, line, column);
280280
}
281281

282-
fn generate_direct_access_assignment(
282+
fn generate_assignment_statement_direct_access(
283283
&self,
284284
left_statement: &AstNode,
285285
right_statement: &AstNode,
286286
) -> Result<(), Diagnostic> {
287-
//TODO : Validation
288287
let exp_gen = self.create_expr_generator();
289288

290-
// given a complex direct-access assignemnt: a.b.c.%W3,%X1
291-
// we want to deconstruct the targe-part (a.b.c) and the direct-access sequence (%W3.%X1)
292-
let Some((target, access_sequence)) = collect_base_and_direct_access_for_assignment(left_statement)
293-
else {
289+
// Left pointer
290+
let Some((base, _)) = collect_base_and_direct_access_for_assignment(left_statement) else {
294291
unreachable!("Invalid direct-access expression: {left_statement:#?}")
295292
};
293+
let left_expr_value = exp_gen.generate_expression_value(base)?;
294+
let left_value = left_expr_value.as_r_value(self.llvm, None).into_int_value();
295+
let left_pointer = left_expr_value.get_basic_value_enum().into_pointer_value();
296296

297-
let left_type = exp_gen.get_type_hint_for(target)?;
297+
// Generate an expression for the right size
298298
let right_type = exp_gen.get_type_hint_for(right_statement)?;
299-
300-
//special case if we deal with a single bit, then we need to switch to a faked u1 type
301-
let right_type =
302-
if let DataTypeInformation::Integer { semantic_size: Some(typesystem::U1_SIZE), .. } =
303-
*right_type.get_type_information()
304-
{
305-
self.index.get_type_or_panic(typesystem::U1_TYPE)
306-
} else {
307-
right_type
308-
};
309-
310-
//Left pointer
311-
let left_expression_value = exp_gen.generate_expression_value(target)?;
312-
let left_value = left_expression_value.as_r_value(self.llvm, None).into_int_value();
313-
let left = left_expression_value.get_basic_value_enum().into_pointer_value();
314-
//Build index
315-
if let Some((element, direct_access)) = access_sequence.split_first() {
316-
let mut rhs = if let AstStatement::DirectAccess(data, ..) = element.get_stmt() {
317-
exp_gen.generate_direct_access_index(
318-
&data.access,
319-
&data.index,
320-
right_type.get_type_information(),
321-
left_type,
322-
)
323-
} else {
324-
//TODO: using the global context we could get a slice here
325-
Err(Diagnostic::new(format!("{element:?} not a direct access"))
326-
.with_error_code("E055")
327-
.with_location(element.get_location()))
328-
}?;
329-
for element in direct_access {
330-
let rhs_next = if let AstStatement::DirectAccess(data, ..) = element.get_stmt() {
331-
exp_gen.generate_direct_access_index(
332-
&data.access,
333-
&data.index,
334-
right_type.get_type_information(),
335-
left_type,
336-
)
337-
} else {
338-
//TODO: using the global context we could get a slice here
339-
Err(Diagnostic::new(&format!("{element:?} not a direct access"))
340-
.with_error_code("E055")
341-
.with_location(element.get_location()))
342-
}?;
343-
rhs = self.llvm.builder.build_int_add(rhs, rhs_next, "");
344-
}
345-
//Build mask for the index
346-
//Get the target bit type as all ones
347-
let rhs_type = self.llvm_index.get_associated_type(right_type.get_name())?.into_int_type();
348-
let ones = rhs_type.const_all_ones();
349-
//Extend the mask to the target type
350-
let extended_mask = self.llvm.builder.build_int_z_extend(ones, left_value.get_type(), "ext");
351-
//Position the ones in their correct locations
352-
let shifted_mask = self.llvm.builder.build_left_shift(extended_mask, rhs, "shift");
353-
//Invert the mask
354-
let mask = self.llvm.builder.build_not(shifted_mask, "invert");
355-
//And the result with the mask to erase the set bits at the target location
356-
let and_value = self.llvm.builder.build_and(left_value, mask, "erase");
357-
358-
//Generate an expression for the right size
359-
let right = exp_gen.generate_expression(right_statement)?;
360-
//Cast the right side to the left side type
361-
let lhs = cast_if_needed!(self, left_type, right_type, right, None).into_int_value();
362-
//Shift left by the direct access
363-
let value = self.llvm.builder.build_left_shift(lhs, rhs, "value");
364-
365-
//OR the result and store it in the left side
366-
let or_value = self.llvm.builder.build_or(and_value, value, "or");
367-
self.llvm.builder.build_store(left, or_value);
368-
} else {
369-
unreachable!();
370-
}
371-
Ok(())
299+
let right_expr = exp_gen.generate_expression(right_statement)?;
300+
301+
exp_gen.generate_assignment_with_direct_access(
302+
left_statement,
303+
left_value,
304+
left_pointer,
305+
right_type,
306+
right_expr,
307+
)
372308
}
373309

374310
/// generates a for-loop statement
@@ -842,9 +778,8 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
842778
}
843779
}
844780

845-
/// when generating an assignment to a direct-access (e.g. a.b.c.%W3.%X2 := 2;)
846-
/// we want to deconstruct the sequence into the base-statement (a.b.c) and the sequence
847-
/// of direct-access commands (vec![%W3, %X2])
781+
/// Deconstructs assignments such as `a.b.c.%W3.%X2 := 2` into a base statement and its direct-access sequences.
782+
/// For the given example this function would return `(Node(a.b.c), vec![Node(%W3), Node(%X2)])`
848783
fn collect_base_and_direct_access_for_assignment(
849784
left_statement: &AstNode,
850785
) -> Option<(&AstNode, Vec<&AstNode>)> {

0 commit comments

Comments
 (0)