@@ -2607,14 +2607,57 @@ def CIR_VTableAddrPointOp : CIR_Op<"vtable.address_point", [
2607
2607
the vtable group (as specified by Itanium ABI), and `address_point.offset`
2608
2608
(address point index) the actual address point within that vtable.
2609
2609
2610
+ The `name` argument to this operation must be the name of a C++ vtable
2611
+ object. The return value is the address of the virtual function pointer
2612
+ array within the vtable (the vptr). This value will be written to the
2613
+ vptr member of a dynamic class by the constructor of the class. Derived
2614
+ classes have their own vtable, which is used to obtain the vptr stored
2615
+ in instances of the derived class.
2616
+
2610
2617
The return type is always `!cir.vptr`.
2611
2618
2612
- Example:
2619
+ Examples:
2620
+
2621
+ ```C++
2622
+ struct Base {
2623
+ Base();
2624
+ virtual void f();
2625
+ };
2626
+ struct Derived : public Base {
2627
+ Derived();
2628
+ }
2629
+ ```
2630
+
2613
2631
```mlir
2614
- cir.global linkonce_odr @_ZTV1B = ...
2632
+ !rec_Base = !cir.record<struct "Base" {!cir.vptr}
2633
+ !rec_Derived = !cir.record<struct "Derived" {!rec_Base}
2634
+ ...
2635
+ // VTable for Base
2636
+ cir.global linkonce_odr @_ZTV1Base = ...
2615
2637
...
2616
- %3 = cir.vtable.address_point(@_ZTV1B,
2638
+ // Constructor for Base
2639
+ cir.func dso_local @_ZN4BaseC2Ev ...
2640
+ ...
2641
+ %2 = cir.vtable.address_point(@_ZTV1Base,
2617
2642
address_point = <index = 0, offset = 2>) : !cir.vptr
2643
+ // The vptr is at element zero.
2644
+ %3 = cir.cast(bitcast, %1 : !cir.ptr<!rec_Base>), cir.ptr<!cir.vptr>>
2645
+ cir.store align(8) %2, %3 : !cir.vptr, !cir.ptr<!cir.vptr>
2646
+ ...
2647
+ // VTable for Derived
2648
+ cir.global linkonce_odr @_ZTV7Derived = ...
2649
+ ...
2650
+ // Constructor for Derived
2651
+ cir.func dso_local @_ZN7DerivedC2Ev ...
2652
+ // Get the address of Base within this Derived instance
2653
+ %2 = cir.base_class_addr %1 : !cir.ptr<!rec_Derived> nonnull [0]
2654
+ cir.call @_ZN4BaseC2Ev(%2)
2655
+ %3 = cir.vtable.address_point(@_ZTV7Derived,
2656
+ address_point = <index = 0, offset = 2>) : !cir.vptr
2657
+ // The vptr is still at the start of the object in this case
2658
+ %4 = cir.cast(bitcast, %1 : !cir.ptr<!rec_Derived>), !cir.ptr<!cir.vptr>
2659
+ // This overwrites the vptr that was stored in the Base constructor call
2660
+ cir.store align(8) %3, %4 : !cir.vptr, !cir.ptr<!cir.vptr>
2618
2661
```
2619
2662
}];
2620
2663
@@ -2649,9 +2692,41 @@ def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
2649
2692
The return type is always `!cir.ptr<!cir.vptr>`.
2650
2693
2651
2694
Example:
2695
+ ```C++
2696
+ struct S {
2697
+ virtual void f1();
2698
+ virtual void f2();
2699
+ };
2700
+ void f3(S *s) {
2701
+ s->f2();
2702
+ }
2703
+ ```
2704
+
2652
2705
```mlir
2653
- %2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
2654
- %3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_C> -> !cir.ptr<!cir.vptr>
2706
+ // VTable for S
2707
+ cir.global external @_ZTV1S = #cir.vtable<{
2708
+ #cir.const_array<[
2709
+ // Offset to the base object
2710
+ #cir.ptr<null> : !cir.ptr<!u8i>,
2711
+ // Type info for S
2712
+ #cir.global_view<@_ZTI1S> : !cir.ptr<!u8i>,
2713
+ // Pointer to S::f1
2714
+ #cir.global_view<@_ZN1S2f1Ev> : !cir.ptr<!u8i>,
2715
+ // Pointer to S::f2
2716
+ #cir.global_view<@_ZN1S2f2Ev> : !cir.ptr<!u8i>
2717
+ ]> : !cir.array<!cir.ptr<!u8i> x 4>}> ...
2718
+ // f3()
2719
+ cir.func dso_local @_Z2f3P1S(%s: !cir.ptr<!rec_S>) {
2720
+ // Get the vptr -- This points to offset 2 in the vtable.
2721
+ %1 = cir.vtable.get_vptr %s : !cir.ptr<!rec_S> -> !cir.ptr<!cir.vptr>
2722
+ %2 = cir.load align(8) %2 : !cir.ptr<!cir.vptr>, !cir.vptr
2723
+ // Get the address of b->f2() -- may be Base::f2() or Derived::f2()
2724
+ %3 = cir.vtable.get_virtual_fn_addr %2[1] : !cir.vptr
2725
+ -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_Base>)>>>
2726
+ %4 = cir.load align(8) %3
2727
+ : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_Base>)>>>,
2728
+ !cir.ptr<!cir.func<(!cir.ptr<!rec_Base>)>>
2729
+ cir.call %4(%b)
2655
2730
```
2656
2731
}];
2657
2732
@@ -2688,19 +2763,98 @@ def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
2688
2763
2689
2764
The return type is a pointer-to-pointer to the function type.
2690
2765
2691
- Example:
2766
+ Example 1:
2767
+ Suppose we have two classes, Base and Derived, where Derived overrides
2768
+ virtual functions that were defined in Base. When a pointer to a Base
2769
+ object is used to call one of these function, we may not know at compile
2770
+ time whether it points to an instance of Base or an instance of Derived.
2771
+ The compiler does not need to know. It will load the vptr from the object
2772
+ and use that to get the address of the correct function to call. The
2773
+ vptr will have been initialized in the object's constructor to point to
2774
+ the correct vtable for the object being instantiated.
2775
+ ```C++
2776
+ // In this example, when f3 is called, we don't know at compile-time
2777
+ // whether
2778
+ struct Base {
2779
+ virtual void f1();
2780
+ virtual void f2();
2781
+ };
2782
+ struct Derived : public Base {
2783
+ void f1() override;
2784
+ void f2() override;
2785
+ };
2786
+ void f3(Base *b) {
2787
+ b->f2();
2788
+ }
2789
+ ```
2790
+
2692
2791
```mlir
2693
- %2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
2694
- %3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_C> -> !cir.ptr<!cir.vptr>
2695
- %4 = cir.load %3 : !cir.ptr<!cir.vptr>, !cir.vptr
2696
- %5 = cir.vtable.get_virtual_fn_addr %4[2] : !cir.vptr
2697
- -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>>
2698
- %6 = cir.load align(8) %5 : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>)
2699
- -> !s32i>>>,
2700
- !cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>
2701
- %7 = cir.call %6(%2) : (!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>,
2702
- !cir.ptr<!rec_C>) -> !s32i
2792
+ // VTable for Base
2793
+ cir.global external @_ZTV4Base = #cir.vtable<{
2794
+ #cir.const_array<[
2795
+ #cir.ptr<null> : !cir.ptr<!u8i>,
2796
+ #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>,
2797
+ #cir.global_view<@_ZN4Base2f1Ev> : !cir.ptr<!u8i>,
2798
+ #cir.global_view<@_ZN4Base2f2Ev> : !cir.ptr<!u8i>
2799
+ ]> : !cir.array<!cir.ptr<!u8i> x 4>}> ...
2800
+ // VTable for Derived
2801
+ cir.global external @_ZTV7Derived = #cir.vtable<{
2802
+ #cir.const_array<[
2803
+ #cir.ptr<null> : !cir.ptr<!u8i>,
2804
+ #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>,
2805
+ #cir.global_view<@_ZN7Derived2f1Ev> : !cir.ptr<!u8i>,
2806
+ #cir.global_view<@_ZN7Derived2f2Ev> : !cir.ptr<!u8i>
2807
+ ]> : !cir.array<!cir.ptr<!u8i> x 4>}> ...
2808
+ // f3()
2809
+ cir.func dso_local @_Z2f3P4Base(%b: !cir.ptr<!rec_Base>)
2810
+ // Get the vptr
2811
+ %1 = cir.vtable.get_vptr %b : !cir.ptr<!rec_Base> -> !cir.ptr<!cir.vptr>
2812
+ %2 = cir.load align(8) %2 : !cir.ptr<!cir.vptr>, !cir.vptr
2813
+ // Get the address of b->f2() -- may be Base::f2() or Derived::f2()
2814
+ %3 = cir.vtable.get_virtual_fn_addr %2[1] : !cir.vptr
2815
+ -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_Base>)>>>
2816
+ %4 = cir.load align(8) %3
2817
+ : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_Base>)>>>,
2818
+ !cir.ptr<!cir.func<(!cir.ptr<!rec_Base>)>>
2819
+ cir.call %4(%b)
2703
2820
```
2821
+
2822
+ Example 2:
2823
+ Consider the case of multiple inheritance, where Base1 and Base2 both
2824
+ provide virtual functions and a third class, Derived, inherits from both
2825
+ bases. When a pointer to a Derived is used to call a virtual function in
2826
+ Base2, we must retrieve a pointer to the Base2 portion of the Derived object
2827
+ and use that pointer to get the vptr for Base2 as a base class.
2828
+ ```C++
2829
+ struct Base1 {
2830
+ virtual void f1();
2831
+ };
2832
+ struct Base2 {
2833
+ virtual void f2();
2834
+ };
2835
+ struct Derived : public Base1, Base2 { };
2836
+ void f3(Derived *d) {
2837
+ d->f2();
2838
+ }
2839
+ ```
2840
+
2841
+ ```mlir
2842
+ !rec_Base1 = !cir.record<struct "Base1" {!cir.vptr}
2843
+ !rec_Base2 = !cir.record<struct "Base2" {!cir.vptr}
2844
+ !rec_Derived = !cir.record<struct "Derived" {!rec_Base1, !rec_Base2}
2845
+ cir.func dso_local @_Z2f3P7Derived(%d: !cir.ptr<!rec_Derived>)
2846
+ %2 = cir.base_class_addr %d : !cir.ptr<!rec_Derived> nonnull [8]
2847
+ -> !cir.ptr<!rec_Base2>
2848
+ %3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_Base2> -> !cir.ptr<!cir.vptr>
2849
+ %4 = cir.load align(8) %3 : !cir.ptr<!cir.vptr>, !cir.vptr
2850
+ %5 = cir.vtable.get_virtual_fn_addr %4[0] : !cir.vptr
2851
+ -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_Base2>)>>>
2852
+ %6 = cir.load align(8) %5
2853
+ : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_Base2>)>>>,
2854
+ !cir.ptr<!cir.func<(!cir.ptr<!rec_Base2>)>>
2855
+ cir.call %6(%2) : (!cir.ptr<!cir.func<(!cir.ptr<!rec_Base2>)>>,
2856
+ !cir.ptr<!rec_Base2>) -> ()
2857
+ ```
2704
2858
}];
2705
2859
2706
2860
let arguments = (ins
0 commit comments