Skip to content

Commit 3cb94e8

Browse files
authored
feat: support named arguments for builtins (#1529)
This PR introduces the feature to use not only positional but also named arguments for the following builtins: - `SEL` - `REF` - `ADR` - `UPPER_BOUND` - `LOWER_BOUND` - `SIZEOF` - `SUB` - `DIV` > [!NOTE] > The following builtins don't support named arguments for now: > `MUX` > `GT` > `LT` > `NE` > `LE` > `EQ` > `GE` > `ADD` > `MUL`
1 parent 1d32d36 commit 3cb94e8

File tree

9 files changed

+1105
-173
lines changed

9 files changed

+1105
-173
lines changed

src/builtins.rs

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

src/codegen/tests/expression_tests.rs

Lines changed: 288 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,12 +286,31 @@ fn builtin_function_call_adr() {
286286
b : DINT;
287287
END_VAR
288288
a := ADR(b);
289+
a := ADR(IN := b);
289290
END_PROGRAM
290291
",
291292
);
292293
// WHEN compiled
293294
// We expect the same behaviour as if REF was called, due to the assignee being a pointer
294-
filtered_assert_snapshot!(result);
295+
filtered_assert_snapshot!(result, @r#"
296+
; ModuleID = '<internal>'
297+
source_filename = "<internal>"
298+
target datalayout = "[filtered]"
299+
target triple = "[filtered]"
300+
301+
%main = type { i32*, i32 }
302+
303+
@main_instance = global %main zeroinitializer
304+
305+
define void @main(%main* %0) {
306+
entry:
307+
%a = getelementptr inbounds %main, %main* %0, i32 0, i32 0
308+
%b = getelementptr inbounds %main, %main* %0, i32 0, i32 1
309+
store i32* %b, i32** %a, align 8
310+
store i32* %b, i32** %a, align 8
311+
ret void
312+
}
313+
"#);
295314
}
296315

297316
#[test]
@@ -305,12 +324,31 @@ fn builtin_function_call_ref() {
305324
b : DINT;
306325
END_VAR
307326
a := REF(b);
327+
a := REF(IN := b);
308328
END_PROGRAM
309329
",
310330
);
311331
// WHEN compiled
312332
// We expect a direct conversion and subsequent assignment to pointer(no call)
313-
filtered_assert_snapshot!(result);
333+
filtered_assert_snapshot!(result, @r#"
334+
; ModuleID = '<internal>'
335+
source_filename = "<internal>"
336+
target datalayout = "[filtered]"
337+
target triple = "[filtered]"
338+
339+
%main = type { i32*, i32 }
340+
341+
@main_instance = global %main zeroinitializer
342+
343+
define void @main(%main* %0) {
344+
entry:
345+
%a = getelementptr inbounds %main, %main* %0, i32 0, i32 0
346+
%b = getelementptr inbounds %main, %main* %0, i32 0, i32 1
347+
store i32* %b, i32** %a, align 8
348+
store i32* %b, i32** %a, align 8
349+
ret void
350+
}
351+
"#);
314352
}
315353

316354
#[test]
@@ -349,10 +387,36 @@ fn builtin_function_call_sel() {
349387
a,b,c : DINT;
350388
END_VAR
351389
a := SEL(TRUE, b,c);
390+
a := SEL(G := TRUE, IN0 := b, IN1 := c);
352391
END_PROGRAM",
353392
);
354393

355-
filtered_assert_snapshot!(result);
394+
filtered_assert_snapshot!(result, @r#"
395+
; ModuleID = '<internal>'
396+
source_filename = "<internal>"
397+
target datalayout = "[filtered]"
398+
target triple = "[filtered]"
399+
400+
%main = type { i32, i32, i32 }
401+
402+
@main_instance = global %main zeroinitializer
403+
404+
define void @main(%main* %0) {
405+
entry:
406+
%a = getelementptr inbounds %main, %main* %0, i32 0, i32 0
407+
%b = getelementptr inbounds %main, %main* %0, i32 0, i32 1
408+
%c = getelementptr inbounds %main, %main* %0, i32 0, i32 2
409+
%load_b = load i32, i32* %b, align 4
410+
%load_c = load i32, i32* %c, align 4
411+
%1 = select i1 true, i32 %load_c, i32 %load_b
412+
store i32 %1, i32* %a, align 4
413+
%load_b1 = load i32, i32* %b, align 4
414+
%load_c2 = load i32, i32* %c, align 4
415+
%2 = select i1 true, i32 %load_c2, i32 %load_b1
416+
store i32 %2, i32* %a, align 4
417+
ret void
418+
}
419+
"#);
356420
}
357421

358422
#[test]
@@ -377,10 +441,31 @@ fn builtin_function_call_move() {
377441
a,b : DINT;
378442
END_VAR
379443
a := MOVE(b);
444+
a := MOVE(IN := b);
380445
END_PROGRAM",
381446
);
382447

383-
filtered_assert_snapshot!(result);
448+
filtered_assert_snapshot!(result, @r#"
449+
; ModuleID = '<internal>'
450+
source_filename = "<internal>"
451+
target datalayout = "[filtered]"
452+
target triple = "[filtered]"
453+
454+
%main = type { i32, i32 }
455+
456+
@main_instance = global %main zeroinitializer
457+
458+
define void @main(%main* %0) {
459+
entry:
460+
%a = getelementptr inbounds %main, %main* %0, i32 0, i32 0
461+
%b = getelementptr inbounds %main, %main* %0, i32 0, i32 1
462+
%load_b = load i32, i32* %b, align 4
463+
store i32 %load_b, i32* %a, align 4
464+
%load_b1 = load i32, i32* %b, align 4
465+
store i32 %load_b1, i32* %a, align 4
466+
ret void
467+
}
468+
"#);
384469
}
385470

386471
#[test]
@@ -392,10 +477,29 @@ fn builtin_function_call_sizeof() {
392477
b: LINT;
393478
END_VAR
394479
a := SIZEOF(b);
480+
a := SIZEOF(IN := b);
395481
END_PROGRAM",
396482
);
397483

398-
filtered_assert_snapshot!(result);
484+
filtered_assert_snapshot!(result, @r#"
485+
; ModuleID = '<internal>'
486+
source_filename = "<internal>"
487+
target datalayout = "[filtered]"
488+
target triple = "[filtered]"
489+
490+
%main = type { i32, i64 }
491+
492+
@main_instance = global %main zeroinitializer
493+
494+
define void @main(%main* %0) {
495+
entry:
496+
%a = getelementptr inbounds %main, %main* %0, i32 0, i32 0
497+
%b = getelementptr inbounds %main, %main* %0, i32 0, i32 1
498+
store i32 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i32), i32* %a, align 4
499+
store i32 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i32), i32* %a, align 4
500+
ret void
501+
}
502+
"#);
399503
}
400504

401505
#[test]
@@ -414,12 +518,63 @@ fn builtin_function_call_lower_bound() {
414518
vla: ARRAY[*] OF DINT;
415519
END_VAR
416520
foo := LOWER_BOUND(vla, 1);
521+
foo := LOWER_BOUND(arr := vla, dim := 1);
417522
END_VAR
418523
END_FUNCTION
419524
",
420525
);
421526

422-
filtered_assert_snapshot!(result);
527+
filtered_assert_snapshot!(result, @r#"
528+
; ModuleID = '<internal>'
529+
source_filename = "<internal>"
530+
target datalayout = "[filtered]"
531+
target triple = "[filtered]"
532+
533+
%main = type { [2 x i32], i32 }
534+
%__foo_vla = type { i32*, [2 x i32] }
535+
536+
@main_instance = global %main zeroinitializer
537+
@____foo_vla__init = unnamed_addr constant %__foo_vla zeroinitializer
538+
539+
define void @main(%main* %0) {
540+
entry:
541+
%a = getelementptr inbounds %main, %main* %0, i32 0, i32 0
542+
%b = getelementptr inbounds %main, %main* %0, i32 0, i32 1
543+
%auto_deref = load [2 x i32], [2 x i32]* %a, align 4
544+
%outer_arr_gep = getelementptr inbounds [2 x i32], [2 x i32]* %a, i32 0, i32 0
545+
%vla_struct = alloca %__foo_vla, align 8
546+
%vla_array_gep = getelementptr inbounds %__foo_vla, %__foo_vla* %vla_struct, i32 0, i32 0
547+
%vla_dimensions_gep = getelementptr inbounds %__foo_vla, %__foo_vla* %vla_struct, i32 0, i32 1
548+
store [2 x i32] [i32 0, i32 1], [2 x i32]* %vla_dimensions_gep, align 4
549+
store i32* %outer_arr_gep, i32** %vla_array_gep, align 8
550+
%1 = load %__foo_vla, %__foo_vla* %vla_struct, align 8
551+
%vla_struct_ptr = alloca %__foo_vla, align 8
552+
store %__foo_vla %1, %__foo_vla* %vla_struct_ptr, align 8
553+
%call = call i32 @foo(%__foo_vla* %vla_struct_ptr)
554+
store i32 %call, i32* %b, align 4
555+
ret void
556+
}
557+
558+
define i32 @foo(%__foo_vla* %0) {
559+
entry:
560+
%foo = alloca i32, align 4
561+
%vla = alloca %__foo_vla*, align 8
562+
store %__foo_vla* %0, %__foo_vla** %vla, align 8
563+
store i32 0, i32* %foo, align 4
564+
%deref = load %__foo_vla*, %__foo_vla** %vla, align 8
565+
%dim = getelementptr inbounds %__foo_vla, %__foo_vla* %deref, i32 0, i32 1
566+
%1 = getelementptr inbounds [2 x i32], [2 x i32]* %dim, i32 0, i32 0
567+
%2 = load i32, i32* %1, align 4
568+
store i32 %2, i32* %foo, align 4
569+
%deref1 = load %__foo_vla*, %__foo_vla** %vla, align 8
570+
%dim2 = getelementptr inbounds %__foo_vla, %__foo_vla* %deref1, i32 0, i32 1
571+
%3 = getelementptr inbounds [2 x i32], [2 x i32]* %dim2, i32 0, i32 0
572+
%4 = load i32, i32* %3, align 4
573+
store i32 %4, i32* %foo, align 4
574+
%foo_ret = load i32, i32* %foo, align 4
575+
ret i32 %foo_ret
576+
}
577+
"#);
423578
}
424579

425580
#[test]
@@ -438,12 +593,63 @@ fn builtin_function_call_upper_bound() {
438593
vla: ARRAY[*] OF DINT;
439594
END_VAR
440595
foo := UPPER_BOUND(vla, 1);
596+
foo := UPPER_BOUND(arr := vla, dim := 1);
441597
END_VAR
442598
END_FUNCTION
443599
",
444600
);
445601

446-
filtered_assert_snapshot!(result);
602+
filtered_assert_snapshot!(result, @r#"
603+
; ModuleID = '<internal>'
604+
source_filename = "<internal>"
605+
target datalayout = "[filtered]"
606+
target triple = "[filtered]"
607+
608+
%main = type { [2 x i32], i32 }
609+
%__foo_vla = type { i32*, [2 x i32] }
610+
611+
@main_instance = global %main zeroinitializer
612+
@____foo_vla__init = unnamed_addr constant %__foo_vla zeroinitializer
613+
614+
define void @main(%main* %0) {
615+
entry:
616+
%a = getelementptr inbounds %main, %main* %0, i32 0, i32 0
617+
%b = getelementptr inbounds %main, %main* %0, i32 0, i32 1
618+
%auto_deref = load [2 x i32], [2 x i32]* %a, align 4
619+
%outer_arr_gep = getelementptr inbounds [2 x i32], [2 x i32]* %a, i32 0, i32 0
620+
%vla_struct = alloca %__foo_vla, align 8
621+
%vla_array_gep = getelementptr inbounds %__foo_vla, %__foo_vla* %vla_struct, i32 0, i32 0
622+
%vla_dimensions_gep = getelementptr inbounds %__foo_vla, %__foo_vla* %vla_struct, i32 0, i32 1
623+
store [2 x i32] [i32 0, i32 1], [2 x i32]* %vla_dimensions_gep, align 4
624+
store i32* %outer_arr_gep, i32** %vla_array_gep, align 8
625+
%1 = load %__foo_vla, %__foo_vla* %vla_struct, align 8
626+
%vla_struct_ptr = alloca %__foo_vla, align 8
627+
store %__foo_vla %1, %__foo_vla* %vla_struct_ptr, align 8
628+
%call = call i32 @foo(%__foo_vla* %vla_struct_ptr)
629+
store i32 %call, i32* %b, align 4
630+
ret void
631+
}
632+
633+
define i32 @foo(%__foo_vla* %0) {
634+
entry:
635+
%foo = alloca i32, align 4
636+
%vla = alloca %__foo_vla*, align 8
637+
store %__foo_vla* %0, %__foo_vla** %vla, align 8
638+
store i32 0, i32* %foo, align 4
639+
%deref = load %__foo_vla*, %__foo_vla** %vla, align 8
640+
%dim = getelementptr inbounds %__foo_vla, %__foo_vla* %deref, i32 0, i32 1
641+
%1 = getelementptr inbounds [2 x i32], [2 x i32]* %dim, i32 0, i32 1
642+
%2 = load i32, i32* %1, align 4
643+
store i32 %2, i32* %foo, align 4
644+
%deref1 = load %__foo_vla*, %__foo_vla** %vla, align 8
645+
%dim2 = getelementptr inbounds %__foo_vla, %__foo_vla* %deref1, i32 0, i32 1
646+
%3 = getelementptr inbounds [2 x i32], [2 x i32]* %dim2, i32 0, i32 1
647+
%4 = load i32, i32* %3, align 4
648+
store i32 %4, i32* %foo, align 4
649+
%foo_ret = load i32, i32* %foo, align 4
650+
ret i32 %foo_ret
651+
}
652+
"#);
447653
}
448654

449655
#[test]
@@ -467,6 +673,7 @@ fn builtin_function_call_upper_bound_expr() {
467673
END_VAR
468674
// upper bound of 4th dimension => 8th element in dimension array
469675
foo := UPPER_BOUND(vla, MY_CONST - (2 * 3));
676+
foo := UPPER_BOUND(arr := vla, dim := MY_CONST - (2 * 3));
470677
END_VAR
471678
END_FUNCTION
472679
",
@@ -748,6 +955,80 @@ fn builtin_div_mixed() {
748955
filtered_assert_snapshot!(res);
749956
}
750957

958+
#[test]
959+
fn builtin_div_with_named_arguments() {
960+
let src = r#"
961+
FUNCTION main : DINT
962+
VAR
963+
x : DINT := 20;
964+
y : DINT := 4;
965+
END_VAR
966+
DIV(IN1 := x, IN2 := y);
967+
END_FUNCTION
968+
"#;
969+
970+
let res = codegen(src);
971+
972+
filtered_assert_snapshot!(res, @r#"
973+
; ModuleID = '<internal>'
974+
source_filename = "<internal>"
975+
target datalayout = "[filtered]"
976+
target triple = "[filtered]"
977+
978+
define i32 @main() {
979+
entry:
980+
%main = alloca i32, align 4
981+
%x = alloca i32, align 4
982+
%y = alloca i32, align 4
983+
store i32 20, i32* %x, align 4
984+
store i32 4, i32* %y, align 4
985+
store i32 0, i32* %main, align 4
986+
%load_x = load i32, i32* %x, align 4
987+
%load_y = load i32, i32* %y, align 4
988+
%tmpVar = sdiv i32 %load_x, %load_y
989+
%main_ret = load i32, i32* %main, align 4
990+
ret i32 %main_ret
991+
}
992+
"#);
993+
}
994+
995+
#[test]
996+
fn builtin_sub_with_named_arguments() {
997+
let src = r#"
998+
FUNCTION main : DINT
999+
VAR
1000+
x : DINT := 20;
1001+
y : DINT := 4;
1002+
END_VAR
1003+
SUB(IN1 := x, IN2 := y);
1004+
END_FUNCTION
1005+
"#;
1006+
1007+
let res = codegen(src);
1008+
1009+
filtered_assert_snapshot!(res, @r#"
1010+
; ModuleID = '<internal>'
1011+
source_filename = "<internal>"
1012+
target datalayout = "[filtered]"
1013+
target triple = "[filtered]"
1014+
1015+
define i32 @main() {
1016+
entry:
1017+
%main = alloca i32, align 4
1018+
%x = alloca i32, align 4
1019+
%y = alloca i32, align 4
1020+
store i32 20, i32* %x, align 4
1021+
store i32 4, i32* %y, align 4
1022+
store i32 0, i32* %main, align 4
1023+
%load_x = load i32, i32* %x, align 4
1024+
%load_y = load i32, i32* %y, align 4
1025+
%tmpVar = sub i32 %load_x, %load_y
1026+
%main_ret = load i32, i32* %main, align 4
1027+
ret i32 %main_ret
1028+
}
1029+
"#);
1030+
}
1031+
7511032
#[test]
7521033
fn global_namespace_operator() {
7531034
let src = r#"

0 commit comments

Comments
 (0)