Skip to content

Commit f7b7c11

Browse files
committed
wip: first working version, lowering / desugaring
1 parent 6c66147 commit f7b7c11

23 files changed

+1641
-322
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,20 @@ impl AstNode {
12421242
)
12431243
}
12441244

1245+
pub fn get_base(&self) -> Option<&AstNode> {
1246+
match &self.stmt {
1247+
AstStatement::ReferenceExpr(ReferenceExpr { base: Some(base), .. }) => Some(base.as_ref()),
1248+
_ => None,
1249+
}
1250+
}
1251+
1252+
pub fn get_base_mut(&mut self) -> Option<&mut AstNode> {
1253+
match &mut self.stmt {
1254+
AstStatement::ReferenceExpr(ReferenceExpr { base: Some(base), .. }) => Some(base.as_mut()),
1255+
_ => None,
1256+
}
1257+
}
1258+
12451259
pub fn get_initial_base(&self) -> Option<&AstNode> {
12461260
match &self.stmt {
12471261
AstStatement::ReferenceExpr(ReferenceExpr { base, .. }, ..) => {

compiler/plc_driver/src/pipelines.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ use plc::{
2525
index::{indexer, FxIndexSet, Index},
2626
linker::LinkerType,
2727
lowering::{
28-
calls::AggregateTypeLowerer, property::PropertyLowerer, vtable::VTableGenerator, InitVisitor,
28+
calls::AggregateTypeLowerer, polymorphism::PolymorphicCallDesugarer, property::PropertyLowerer,
29+
vtable::VTableGenerator, InitVisitor,
2930
},
3031
output::FormatOption,
3132
parser::parse_file,
@@ -260,9 +261,13 @@ impl<T: SourceContainer> BuildPipeline<T> {
260261
let mut_participants: Vec<Box<dyn PipelineParticipantMut>> = vec![
261262
Box::new(PropertyLowerer::new(self.context.provider())),
262263
Box::new(InitParticipant::new(self.project.get_init_symbol_name(), self.context.provider())),
264+
// XXX: The PolymorphicCallDesugarer must run before InheritanceLowerer because child classes
265+
// might access the parents __vtable, in which case the inheritance lowerer will patch the
266+
// access to something like __vtable_{parent name}. Ugh :/
267+
Box::new(VTableGenerator::new(self.context.provider())),
268+
Box::new(PolymorphicCallDesugarer::new(self.context.provider())),
263269
Box::new(AggregateTypeLowerer::new(self.context.provider())),
264270
Box::new(InheritanceLowerer::new(self.context.provider())),
265-
Box::new(VTableGenerator::new(self.context.provider())),
266271
];
267272

268273
for participant in mut_participants {

compiler/plc_driver/src/pipelines/participant.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use std::{
1414
use ast::provider::IdProvider;
1515
use plc::{
1616
codegen::GeneratedModule,
17-
lowering::{calls::AggregateTypeLowerer, vtable::VTableGenerator},
17+
lowering::{
18+
calls::AggregateTypeLowerer, polymorphism::PolymorphicCallDesugarer, vtable::VTableGenerator,
19+
},
1820
output::FormatOption,
1921
ConfigFormat, OnlineChange, Target,
2022
};
@@ -294,3 +296,27 @@ impl PipelineParticipantMut for VTableGenerator {
294296
project.index(self.id_provider.clone())
295297
}
296298
}
299+
300+
impl PipelineParticipantMut for PolymorphicCallDesugarer {
301+
fn post_annotate(&mut self, annotated_project: AnnotatedProject) -> AnnotatedProject {
302+
let AnnotatedProject { units, index, annotations } = annotated_project;
303+
self.index = Some(index);
304+
self.annotations = Some(annotations.annotation_map);
305+
306+
let units = units
307+
.into_iter()
308+
.map(|AnnotatedUnit { mut unit, .. }| {
309+
self.desugar_unit(&mut unit);
310+
unit
311+
})
312+
.collect();
313+
314+
let indexed_project = IndexedProject {
315+
project: ParsedProject { units },
316+
index: self.index.take().expect("Index"),
317+
unresolvables: vec![],
318+
};
319+
320+
indexed_project.annotate(self.ids.clone())
321+
}
322+
}

src/codegen/generators/expression_generator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
232232
Ok(ExpressionValue::LValue(this_value))
233233
}
234234
AstStatement::ReferenceExpr(data) => {
235-
dbg!(&data, &expression);
236235
let res =
237236
self.generate_reference_expression(&data.access, data.base.as_deref(), expression)?;
238237
let val = match res {

src/codegen/tests/function_pointer_tests.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,23 @@ fn function_pointer_method_no_parameters() {
2727
target datalayout = "[filtered]"
2828
target triple = "[filtered]"
2929
30-
%FbA = type {}
30+
%FbA = type { i32* }
3131
3232
@__FbA__init = unnamed_addr constant %FbA zeroinitializer
3333
3434
define void @FbA(%FbA* %0) {
3535
entry:
3636
%this = alloca %FbA*, align 8
3737
store %FbA* %0, %FbA** %this, align 8
38+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
3839
ret void
3940
}
4041
4142
define void @FbA__foo(%FbA* %0) {
4243
entry:
4344
%this = alloca %FbA*, align 8
4445
store %FbA* %0, %FbA** %this, align 8
46+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
4547
ret void
4648
}
4749
@@ -107,21 +109,23 @@ fn function_pointer_method_with_input_output_inout() {
107109
target datalayout = "[filtered]"
108110
target triple = "[filtered]"
109111
110-
%FbA = type {}
112+
%FbA = type { i32* }
111113
112114
@__FbA__init = unnamed_addr constant %FbA zeroinitializer
113115
114116
define void @FbA(%FbA* %0) {
115117
entry:
116118
%this = alloca %FbA*, align 8
117119
store %FbA* %0, %FbA** %this, align 8
120+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
118121
ret void
119122
}
120123
121124
define void @FbA__foo(%FbA* %0, i16 %1, i16* %2, i16* %3) {
122125
entry:
123126
%this = alloca %FbA*, align 8
124127
store %FbA* %0, %FbA** %this, align 8
128+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
125129
%in = alloca i16, align 2
126130
store i16 %1, i16* %in, align 2
127131
%out = alloca i16*, align 8
@@ -205,21 +209,23 @@ fn function_pointer_method_with_input_output_inout_shifted_position_to_right_by_
205209
target datalayout = "[filtered]"
206210
target triple = "[filtered]"
207211
208-
%FbA = type {}
212+
%FbA = type { i32* }
209213
210214
@__FbA__init = unnamed_addr constant %FbA zeroinitializer
211215
212216
define void @FbA(%FbA* %0) {
213217
entry:
214218
%this = alloca %FbA*, align 8
215219
store %FbA* %0, %FbA** %this, align 8
220+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
216221
ret void
217222
}
218223
219224
define void @FbA__foo(%FbA* %0, i16 %1, i16* %2, i16* %3) {
220225
entry:
221226
%this = alloca %FbA*, align 8
222227
store %FbA* %0, %FbA** %this, align 8
228+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
223229
%in = alloca i16, align 2
224230
store i16 %1, i16* %in, align 2
225231
%out = alloca i16*, align 8
@@ -312,7 +318,7 @@ fn void_pointer_casting() {
312318
target triple = "[filtered]"
313319
314320
%UserDefinedVirtualTable = type { i32 (%FbA*, i16, i16*, i16*)* }
315-
%FbA = type { i32* }
321+
%FbA = type { i32*, i32* }
316322
317323
@userDefinedVirtualTableInstance = global %UserDefinedVirtualTable zeroinitializer
318324
@__UserDefinedVirtualTable__init = unnamed_addr constant %UserDefinedVirtualTable zeroinitializer
@@ -322,7 +328,8 @@ fn void_pointer_casting() {
322328
entry:
323329
%this = alloca %FbA*, align 8
324330
store %FbA* %0, %FbA** %this, align 8
325-
%vt = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
331+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
332+
%vt = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 1
326333
%FbA.foo = alloca i32, align 4
327334
%in = alloca i16, align 2
328335
store i16 %1, i16* %in, align 2
@@ -339,7 +346,8 @@ fn void_pointer_casting() {
339346
entry:
340347
%this = alloca %FbA*, align 8
341348
store %FbA* %0, %FbA** %this, align 8
342-
%vt = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
349+
%__vtable = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
350+
%vt = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 1
343351
ret void
344352
}
345353
@@ -358,7 +366,7 @@ fn void_pointer_casting() {
358366
store i16 789, i16* %localInOut, align 2
359367
store %FbA* %instanceFbA, %FbA** %refInstanceFbA, align 8
360368
%deref = load %FbA*, %FbA** %refInstanceFbA, align 8
361-
%vt = getelementptr inbounds %FbA, %FbA* %deref, i32 0, i32 0
369+
%vt = getelementptr inbounds %FbA, %FbA* %deref, i32 0, i32 1
362370
%deref1 = load i32*, i32** %vt, align 8
363371
%cast = bitcast i32* %deref1 to %UserDefinedVirtualTable*
364372
%methodPtr = getelementptr inbounds %UserDefinedVirtualTable, %UserDefinedVirtualTable* %cast, i32 0, i32 0

src/index.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
22

3-
use std::{collections::VecDeque, hash::BuildHasherDefault};
3+
use std::{
4+
collections::VecDeque,
5+
hash::{BuildHasherDefault, Hash},
6+
};
47

58
use itertools::Itertools;
69
use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
@@ -646,7 +649,7 @@ impl From<&Interface> for InterfaceIndexEntry {
646649
}
647650
}
648651

649-
#[derive(Debug, PartialEq, Clone)]
652+
#[derive(Debug, PartialEq, Clone, Eq)]
650653
pub enum PouIndexEntry {
651654
Program {
652655
name: String,
@@ -2196,6 +2199,47 @@ impl Index {
21962199
current_methods
21972200
}
21982201
}
2202+
2203+
/// Returns all methods defined in a container, including methods from super "classes". Thereby the result
2204+
/// is in fixed traversal order, meaning that the methods of the super class are always positioned before
2205+
/// the methods of any child class. This ordering is neccessary for virtual tables, where bitcasting them
2206+
/// from one type to another requires such an order to ensure that the correct method is called.
2207+
///
2208+
/// For example, if class `A` has a method `foo` and class `B` inherits from `A` but adds another method
2209+
/// `bar` then the virtual table must have the form [`A.foo`] and [`B.foo`, `B.bar`] such that upcasting
2210+
/// `B` to `A` will still call method `foo` rather than `bar`. If not, e.g. [`B.bar`, `B.foo`] is used,
2211+
/// the upcasting to `A` would result calling `B.bar` when we have a call such as `reInstance^.foo()`
2212+
pub fn get_methods_in_fixed_order(&self, container: &str) -> Vec<&PouIndexEntry> {
2213+
let res = self.get_methods_recursive_in_fixed_order(container, FxIndexMap::default());
2214+
res.into_values().collect()
2215+
}
2216+
2217+
/// See [`Index::get_methods_in_fixed_order`]
2218+
fn get_methods_recursive_in_fixed_order<'b>(
2219+
&'b self,
2220+
container: &str,
2221+
mut collected: FxIndexMap<&'b str, &'b PouIndexEntry>,
2222+
) -> FxIndexMap<&'b str, &'b PouIndexEntry> {
2223+
if let Some(pou) = self.find_pou(container) {
2224+
if let Some(super_class) = pou.get_super_class() {
2225+
// We want to recursively climb up the inheritance chain before collecting methods
2226+
collected = self.get_methods_recursive_in_fixed_order(super_class, collected);
2227+
}
2228+
2229+
let methods = self
2230+
.get_pous()
2231+
.values()
2232+
.filter(|pou| pou.is_method())
2233+
.filter(|pou| pou.get_parent_pou_name().is_some_and(|opt| opt == container));
2234+
2235+
for method in methods {
2236+
let name = method.get_name().split_once('.').unwrap().1;
2237+
collected.insert(name, method);
2238+
}
2239+
}
2240+
2241+
collected
2242+
}
21992243
}
22002244

22012245
/// Returns a default initialization name for a variable or type

src/index/tests/index_tests.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2207,3 +2207,58 @@ fn pou_with_recursive_type_fails() {
22072207
let pou_type = index.find_pou_type("fb").unwrap();
22082208
assert!(pou_type.get_type_information().get_size(&index).is_err());
22092209
}
2210+
2211+
#[test]
2212+
fn fixed_order() {
2213+
let (_, index) = index(
2214+
r#"
2215+
FUNCTION_BLOCK A
2216+
METHOD foo
2217+
END_METHOD
2218+
2219+
METHOD bar
2220+
END_METHOD
2221+
2222+
METHOD baz
2223+
END_METHOD
2224+
END_FUNCTION_BLOCK
2225+
2226+
FUNCTION_BLOCK B EXTENDS A
2227+
METHOD foo
2228+
END_METHOD
2229+
END_FUNCTION_BLOCK
2230+
2231+
FUNCTION_BLOCK C EXTENDS A
2232+
METHOD foo
2233+
END_METHOD
2234+
2235+
METHOD baz
2236+
END_METHOD
2237+
END_FUNCTION_BLOCK
2238+
2239+
FUNCTION_BLOCK D EXTENDS B
2240+
METHOD baz
2241+
END_METHOD
2242+
2243+
METHOD qux
2244+
END_METHOD
2245+
END_FUNCTION_BLOCK
2246+
"#,
2247+
);
2248+
2249+
let methods_a =
2250+
index.get_methods_in_fixed_order("A").iter().map(|pou| pou.get_name()).collect::<Vec<_>>();
2251+
assert_eq!(methods_a, vec!["A.foo", "A.bar", "A.baz"]);
2252+
2253+
let methods_b =
2254+
index.get_methods_in_fixed_order("B").iter().map(|pou| pou.get_name()).collect::<Vec<_>>();
2255+
assert_eq!(methods_b, vec!["B.foo", "A.bar", "A.baz"]);
2256+
2257+
let methods_c =
2258+
index.get_methods_in_fixed_order("C").iter().map(|pou| pou.get_name()).collect::<Vec<_>>();
2259+
assert_eq!(methods_c, vec!["C.foo", "A.bar", "C.baz"]);
2260+
2261+
let methods_d =
2262+
index.get_methods_in_fixed_order("D").iter().map(|pou| pou.get_name()).collect::<Vec<_>>();
2263+
assert_eq!(methods_d, vec!["B.foo", "A.bar", "D.baz", "D.qux"]);
2264+
}

src/lowering.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_hash::FxHashMap;
1616

1717
pub mod calls;
1818
mod initializers;
19+
pub mod polymorphism;
1920
pub mod property;
2021
pub mod vtable;
2122

0 commit comments

Comments
 (0)