@@ -2608,14 +2608,56 @@ def CIR_VTableAddrPointOp : CIR_Op<"vtable.address_point", [
26082608 the vtable group (as specified by Itanium ABI), and `address_point.offset`
26092609 (address point index) the actual address point within that vtable.
26102610
2611+ The `name` argument to this operation must be the name of a C++ vtable
2612+ object. The return value is the address of the virtual function pointer
2613+ array within the vtable (the vptr). This value will be written to the
2614+ vptr member of a dynamic class by the constructor of the class. Derived
2615+ classes have their own vtable, which is used to obtain the vptr stored
2616+ in instances of the derived class.
2617+
26112618 The return type is always `!cir.vptr`.
26122619
2613- Example:
2620+ Examples:
2621+
2622+ ```C++
2623+ struct Base {
2624+ Base();
2625+ virtual void f();
2626+ };
2627+ struct Derived : public Base {
2628+ Derived();
2629+ };
2630+ ```
2631+
26142632 ```mlir
2615- cir.global linkonce_odr @_ZTV1B = ...
2633+ !rec_Base = !cir.record<struct "Base" {!cir.vptr}
2634+ !rec_Derived = !cir.record<struct "Derived" {!rec_Base}
2635+ ...
2636+ // VTable for Base
2637+ cir.global linkonce_odr @_ZTV1Base = ...
26162638 ...
2617- %3 = cir.vtable.address_point(@_ZTV1B,
2639+ // Constructor for Base
2640+ cir.func dso_local @_ZN4BaseC2Ev ...
2641+ ...
2642+ %2 = cir.vtable.address_point(@_ZTV1Base,
26182643 address_point = <index = 0, offset = 2>) : !cir.vptr
2644+ %3 = cir.vtable.get_vptr %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.vtable.get_vptr %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>
26192661 ```
26202662 }];
26212663
@@ -2650,9 +2692,41 @@ def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
26502692 The return type is always `!cir.ptr<!cir.vptr>`.
26512693
26522694 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+
26532705 ```mlir
2654- %2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
2655- %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) %1 : !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)
26562730 ```
26572731 }];
26582732
@@ -2689,19 +2763,98 @@ def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
26892763
26902764 The return type is a pointer-to-pointer to the function type.
26912765
2692- 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+
26932791 ```mlir
2694- %2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
2695- %3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_C> -> !cir.ptr<!cir.vptr>
2696- %4 = cir.load %3 : !cir.ptr<!cir.vptr>, !cir.vptr
2697- %5 = cir.vtable.get_virtual_fn_addr %4[2] : !cir.vptr
2698- -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>>
2699- %6 = cir.load align(8) %5 : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>)
2700- -> !s32i>>>,
2701- !cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>
2702- %7 = cir.call %6(%2) : (!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>,
2703- !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) %1 : !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)
27042820 ```
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+ ```
27052858 }];
27062859
27072860 let arguments = (ins
0 commit comments