Skip to content

Commit 7300420

Browse files
committed
tests: Add user-defined dynamic dispatch test
1 parent 81c0500 commit 7300420

File tree

2 files changed

+305
-0
lines changed

2 files changed

+305
-0
lines changed

src/codegen/tests/function_pointer_tests.rs

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,3 +375,218 @@ fn void_pointer_casting() {
375375
attributes #0 = { argmemonly nofree nounwind willreturn }
376376
"#);
377377
}
378+
379+
#[test]
380+
fn user_defined_virtual_table_calls() {
381+
let result = codegen(
382+
r"
383+
// Virtual Table Definitions
384+
TYPE UserVT_FbA:
385+
STRUCT
386+
printNumber: POINTER TO FbA.printNumber := ADR(FbA.printNumber);
387+
END_STRUCT
388+
END_TYPE
389+
390+
TYPE UserVT_FbB:
391+
STRUCT
392+
// No override, hence the ADR(FbA.<...>)
393+
printNumber: POINTER TO FbA.printNumber := ADR(FbA.printNumber);
394+
END_STRUCT
395+
END_TYPE
396+
397+
TYPE UserVT_FbC:
398+
STRUCT
399+
// override, hence the ADR(FbC.<...>)
400+
printNumber: POINTER TO FbA.printNumber := ADR(FbC.printNumber);
401+
END_STRUCT
402+
END_TYPE
403+
404+
// Virtual Table Instances
405+
VAR_GLOBAL
406+
UserVT_FbA_instance: UserVT_FbA;
407+
UserVT_FbB_instance: UserVT_FbB;
408+
UserVT_FbC_instance: UserVT_FbC;
409+
END_VAR
410+
411+
FUNCTION_BLOCK FbA
412+
VAR
413+
vt: POINTER TO __VOID;
414+
localStateA: DINT := 5;
415+
END_VAR
416+
417+
METHOD printNumber
418+
VAR_INPUT
419+
in: DINT;
420+
END_VAR
421+
END_METHOD
422+
END_FUNCTION_BLOCK
423+
424+
FUNCTION_BLOCK FbB EXTENDS FbA
425+
VAR
426+
localStateB: DINT := 10;
427+
END_VAR
428+
END_FUNCTION_BLOCK
429+
430+
FUNCTION_BLOCK FbC EXTENDS FbA
431+
VAR
432+
localStateC: DINT := 15;
433+
END_VAR
434+
435+
METHOD printNumber
436+
VAR_INPUT
437+
in: DINT;
438+
END_VAR
439+
END_METHOD
440+
END_FUNCTION_BLOCK
441+
442+
FUNCTION main
443+
VAR
444+
instanceFbA: FbA;
445+
instanceFbB: FbB;
446+
instanceFbC: FbC;
447+
refInstanceFbA: POINTER TO FbA;
448+
END_VAR
449+
450+
// Virtual Table Initialization
451+
instanceFbA.vt := ADR(UserVT_FbA_instance);
452+
instanceFbB.vt := ADR(UserVT_FbB_instance);
453+
instanceFbC.vt := ADR(UserVT_FbC_instance);
454+
455+
refInstanceFbA := ADR(instanceFbA);
456+
UserVT_FbA#(refInstanceFbA^.vt^).printNumber^(refInstanceFbA^, 5);
457+
458+
refInstanceFbA := ADR(instanceFbB);
459+
UserVT_FbA#(refInstanceFbA^.vt^).printNumber^(refInstanceFbA^, 10);
460+
461+
refInstanceFbA := ADR(instanceFbC);
462+
UserVT_FbA#(refInstanceFbA^.vt^).printNumber^(refInstanceFbA^, 15);
463+
END_FUNCTION
464+
");
465+
466+
// Lots of yada yada, the interesting part happens in the `main` function
467+
filtered_assert_snapshot!(result, @r#"
468+
; ModuleID = '<internal>'
469+
source_filename = "<internal>"
470+
target datalayout = "[filtered]"
471+
target triple = "[filtered]"
472+
473+
%UserVT_FbA = type { void (%FbA*, i32)* }
474+
%FbA = type { i32*, i32 }
475+
%UserVT_FbB = type { void (%FbA*, i32)* }
476+
%UserVT_FbC = type { void (%FbA*, i32)* }
477+
%FbB = type { i32 }
478+
%FbC = type { i32 }
479+
480+
@UserVT_FbA_instance = global %UserVT_FbA zeroinitializer
481+
@__UserVT_FbA__init = unnamed_addr constant %UserVT_FbA zeroinitializer
482+
@__FbA__init = unnamed_addr constant %FbA { i32* null, i32 5 }
483+
@UserVT_FbB_instance = global %UserVT_FbB zeroinitializer
484+
@__UserVT_FbB__init = unnamed_addr constant %UserVT_FbB zeroinitializer
485+
@UserVT_FbC_instance = global %UserVT_FbC zeroinitializer
486+
@__UserVT_FbC__init = unnamed_addr constant %UserVT_FbC zeroinitializer
487+
@__FbB__init = unnamed_addr constant %FbB { i32 10 }
488+
@__FbC__init = unnamed_addr constant %FbC { i32 15 }
489+
490+
define void @FbA__printNumber(%FbA* %0, i32 %1) {
491+
entry:
492+
%this = alloca %FbA*, align 8
493+
store %FbA* %0, %FbA** %this, align 8
494+
%vt = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
495+
%localStateA = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 1
496+
%in = alloca i32, align 4
497+
store i32 %1, i32* %in, align 4
498+
ret void
499+
}
500+
501+
define void @FbA(%FbA* %0) {
502+
entry:
503+
%this = alloca %FbA*, align 8
504+
store %FbA* %0, %FbA** %this, align 8
505+
%vt = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 0
506+
%localStateA = getelementptr inbounds %FbA, %FbA* %0, i32 0, i32 1
507+
ret void
508+
}
509+
510+
define void @FbB(%FbB* %0) {
511+
entry:
512+
%this = alloca %FbB*, align 8
513+
store %FbB* %0, %FbB** %this, align 8
514+
%localStateB = getelementptr inbounds %FbB, %FbB* %0, i32 0, i32 0
515+
ret void
516+
}
517+
518+
define void @FbC(%FbC* %0) {
519+
entry:
520+
%this = alloca %FbC*, align 8
521+
store %FbC* %0, %FbC** %this, align 8
522+
%localStateC = getelementptr inbounds %FbC, %FbC* %0, i32 0, i32 0
523+
ret void
524+
}
525+
526+
define void @FbC__printNumber(%FbC* %0, i32 %1) {
527+
entry:
528+
%this = alloca %FbC*, align 8
529+
store %FbC* %0, %FbC** %this, align 8
530+
%localStateC = getelementptr inbounds %FbC, %FbC* %0, i32 0, i32 0
531+
%in = alloca i32, align 4
532+
store i32 %1, i32* %in, align 4
533+
ret void
534+
}
535+
536+
define void @main() {
537+
entry:
538+
%instanceFbA = alloca %FbA, align 8
539+
%instanceFbB = alloca %FbB, align 8
540+
%instanceFbC = alloca %FbC, align 8
541+
%refInstanceFbA = alloca %FbA*, align 8
542+
%0 = bitcast %FbA* %instanceFbA to i8*
543+
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 bitcast (%FbA* @__FbA__init to i8*), i64 ptrtoint (%FbA* getelementptr (%FbA, %FbA* null, i32 1) to i64), i1 false)
544+
%1 = bitcast %FbB* %instanceFbB to i8*
545+
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %1, i8* align 1 bitcast (%FbB* @__FbB__init to i8*), i64 ptrtoint (%FbB* getelementptr (%FbB, %FbB* null, i32 1) to i64), i1 false)
546+
%2 = bitcast %FbC* %instanceFbC to i8*
547+
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %2, i8* align 1 bitcast (%FbC* @__FbC__init to i8*), i64 ptrtoint (%FbC* getelementptr (%FbC, %FbC* null, i32 1) to i64), i1 false)
548+
store %FbA* null, %FbA** %refInstanceFbA, align 8
549+
%vt = getelementptr inbounds %FbA, %FbA* %instanceFbA, i32 0, i32 0
550+
store i32* bitcast (%UserVT_FbA* @UserVT_FbA_instance to i32*), i32** %vt, align 8
551+
%vt1 = getelementptr inbounds %FbB, %FbB* %instanceFbB, i32 0, i32 0
552+
store i32* bitcast (%UserVT_FbB* @UserVT_FbB_instance to i32*), i32* %vt1, align 8
553+
%vt2 = getelementptr inbounds %FbC, %FbC* %instanceFbC, i32 0, i32 0
554+
store i32* bitcast (%UserVT_FbC* @UserVT_FbC_instance to i32*), i32* %vt2, align 8
555+
store %FbA* %instanceFbA, %FbA** %refInstanceFbA, align 8
556+
%deref = load %FbA*, %FbA** %refInstanceFbA, align 8
557+
%vt3 = getelementptr inbounds %FbA, %FbA* %deref, i32 0, i32 0
558+
%deref4 = load i32*, i32** %vt3, align 8
559+
%cast = bitcast i32* %deref4 to %UserVT_FbA*
560+
%printNumber = getelementptr inbounds %UserVT_FbA, %UserVT_FbA* %cast, i32 0, i32 0
561+
%3 = load void (%FbA*, i32)*, void (%FbA*, i32)** %printNumber, align 8
562+
%deref5 = load %FbA*, %FbA** %refInstanceFbA, align 8
563+
call void %3(%FbA* %deref5, i32 5)
564+
%4 = bitcast %FbB* %instanceFbB to %FbA*
565+
store %FbA* %4, %FbA** %refInstanceFbA, align 8
566+
%deref6 = load %FbA*, %FbA** %refInstanceFbA, align 8
567+
%vt7 = getelementptr inbounds %FbA, %FbA* %deref6, i32 0, i32 0
568+
%deref8 = load i32*, i32** %vt7, align 8
569+
%cast9 = bitcast i32* %deref8 to %UserVT_FbA*
570+
%printNumber10 = getelementptr inbounds %UserVT_FbA, %UserVT_FbA* %cast9, i32 0, i32 0
571+
%5 = load void (%FbA*, i32)*, void (%FbA*, i32)** %printNumber10, align 8
572+
%deref11 = load %FbA*, %FbA** %refInstanceFbA, align 8
573+
call void %5(%FbA* %deref11, i32 10)
574+
%6 = bitcast %FbC* %instanceFbC to %FbA*
575+
store %FbA* %6, %FbA** %refInstanceFbA, align 8
576+
%deref12 = load %FbA*, %FbA** %refInstanceFbA, align 8
577+
%vt13 = getelementptr inbounds %FbA, %FbA* %deref12, i32 0, i32 0
578+
%deref14 = load i32*, i32** %vt13, align 8
579+
%cast15 = bitcast i32* %deref14 to %UserVT_FbA*
580+
%printNumber16 = getelementptr inbounds %UserVT_FbA, %UserVT_FbA* %cast15, i32 0, i32 0
581+
%7 = load void (%FbA*, i32)*, void (%FbA*, i32)** %printNumber16, align 8
582+
%deref17 = load %FbA*, %FbA** %refInstanceFbA, align 8
583+
call void %7(%FbA* %deref17, i32 15)
584+
ret void
585+
}
586+
587+
; Function Attrs: argmemonly nofree nounwind willreturn
588+
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #0
589+
590+
attributes #0 = { argmemonly nofree nounwind willreturn }
591+
"#);
592+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: (%COMPILE %s && %RUN) | %CHECK %s
2+
3+
// Virtual Table Definitions
4+
TYPE UserVT_FbA:
5+
STRUCT
6+
printNumber: POINTER TO FbA.printNumber := ADR(FbA.printNumber);
7+
END_STRUCT
8+
END_TYPE
9+
10+
TYPE UserVT_FbB:
11+
STRUCT
12+
// No override, hence the ADR(FbA.<...>)
13+
printNumber: POINTER TO FbA.printNumber := ADR(FbA.printNumber);
14+
END_STRUCT
15+
END_TYPE
16+
17+
TYPE UserVT_FbC:
18+
STRUCT
19+
// override, hence the ADR(FbC.<...>)
20+
printNumber: POINTER TO FbA.printNumber := ADR(FbC.printNumber);
21+
END_STRUCT
22+
END_TYPE
23+
24+
// Virtual Table Instances
25+
VAR_GLOBAL
26+
UserVT_FbA_instance: UserVT_FbA;
27+
UserVT_FbB_instance: UserVT_FbB;
28+
UserVT_FbC_instance: UserVT_FbC;
29+
END_VAR
30+
31+
FUNCTION_BLOCK FbA
32+
VAR
33+
vt: POINTER TO __VOID;
34+
localStateA: DINT := 5;
35+
END_VAR
36+
37+
METHOD printNumber
38+
VAR_INPUT
39+
in: DINT;
40+
END_VAR
41+
42+
printf('FbA::printNumber: localStateA = %d, in = %d$N', localStateA, in);
43+
END_METHOD
44+
END_FUNCTION_BLOCK
45+
46+
FUNCTION_BLOCK FbB EXTENDS FbA
47+
VAR
48+
localStateB: DINT := 10;
49+
END_VAR
50+
END_FUNCTION_BLOCK
51+
52+
FUNCTION_BLOCK FbC EXTENDS FbA
53+
VAR
54+
localStateC: DINT := 15;
55+
END_VAR
56+
57+
METHOD printNumber
58+
VAR_INPUT
59+
in: DINT;
60+
END_VAR
61+
62+
printf('FbC::printNumber: localStateA = %d, localStateC = %d, in = %d$N', localStateA, localStateC, in);
63+
END_METHOD
64+
END_FUNCTION_BLOCK
65+
66+
FUNCTION main
67+
VAR
68+
instanceFbA: FbA;
69+
instanceFbB: FbB;
70+
instanceFbC: FbC;
71+
refInstanceFbA: POINTER TO FbA;
72+
END_VAR
73+
74+
// Virtual Table Initialization
75+
instanceFbA.vt := ADR(UserVT_FbA_instance);
76+
instanceFbB.vt := ADR(UserVT_FbB_instance);
77+
instanceFbC.vt := ADR(UserVT_FbC_instance);
78+
79+
// CHECK: FbA::printNumber: localStateA = 5, in = 5
80+
refInstanceFbA := ADR(instanceFbA);
81+
UserVT_FbA#(refInstanceFbA^.vt^).printNumber^(refInstanceFbA^, 5);
82+
83+
// CHECK: FbA::printNumber: localStateA = 5, in = 10
84+
refInstanceFbA := ADR(instanceFbB);
85+
UserVT_FbA#(refInstanceFbA^.vt^).printNumber^(refInstanceFbA^, 10);
86+
87+
// CHECK: FbC::printNumber: localStateA = 5, localStateC = 15, in = 15
88+
refInstanceFbA := ADR(instanceFbC);
89+
UserVT_FbA#(refInstanceFbA^.vt^).printNumber^(refInstanceFbA^, 15);
90+
END_FUNCTION

0 commit comments

Comments
 (0)