Date: Tue, 27 Jul 2004 20:09:03 -0500 (CDT) From: Chris Lattner Subject: Object model stuff: interfaces Okay, the next issue is interfaces. There are several different tradeoffs when implementing interfaces, so this is one implementation choice. Consider a modified testcase: class Base { double X; public virtual void foo(double D) { } public virtual void bar(float F) { } } interface IFOO { void foo(double D); } class Derived : Base, IFOO { float Y; public override void bar(float F) { IFOO IF = this; IF.foo(F); } } In this testcase, the vtable layout for Base looks like this: %Base.vft = constant "Base:VTy" { %llvm_msil_vtable_base { %llvm_msil_type_info* %Base.ti, sbyte* null, uint 0 }, void (%Base*, float)* %Base.bar, void (%Base*, double)* %Base.foo } I don't think there is anything unexpected here. Note that bar comes first in the vtable. The vtable for derived now looks like this: %Derived.vft = constant %Derived.VTy { ;; Normal vtable stuff %llvm_msil_vtable_base { %llvm_msil_type_info* %Derived.ti, sbyte* null, uint 0 }, ;; Derived overrides bar void (%Derived*, float)* %Derived.bar, ;; It includes base's foo void (%Base*, double)* %Base.foo, ;; It adds an interface. %IFOO:VTy* %Derived.IFOO.vft } Since 'derived' implements IFOO, it has to have a pointer to the interface vtable for IFOO in it's vtable. This interface vtable looks like this: %Derived.IFOO.vft = internal constant { { }, void (%Base*, double)* } { {} {}, void (%Base*, double)* %Base.foo } The empty struct element is just there to make the implementation simpler for me, it's obviously not needed. Basically all the IFOO vtable for "Derived" contains is a pointer to %Base.foo. Lets see how this is used. Consider the cast from Derived to IFOO: public override void bar(float F) { IFOO IF = this; I represent interface references as pairs, where there first element is a pointer to the object (void*) and the second is a pointer to the interface vtable. As such, IF is represented as: void %Derived.bar(%Derived* %this, float %F) { %IF = alloca { sbyte*, %IFOO.VTy* } %IF.obj = getelementptr { sbyte*, %IFOO.VTy* }* %IF, uint 0, uint 0 %IF.vt = getelementptr { sbyte*, %IFOO.VTy* }* %IF, uint 0, uint 1 ; Set the object pointer %tmp = cast %Derived* %this to sbyte* store sbyte* %tmp, sbyte** %IF.obj ; Load the pointer to the vtable %tmp = getelementptr %Derived* %this, uint 0, uint 0, uint 0, uint %vtbl = load %llvm_msil_vtable_base** %tmp ; Cast the vtable pointer to be the appropriate type. %tmp = cast %llvm_msil_vtable_base* %vtbl to %Derived.VTy* ; Get a pointer to the IFOO slot and load it. %vtslot = getelementptr %Derived.VTy* %tmp, uint 0, uint 3 %tmp = load void %IFOO:VTy** %vtslot ; Set the itf vtable ptr store %IFOO:VTy* %tmp, %IFOO:VTy** %IF.vt This code is messy, but SROA gets rid of all of the IF loads/stores/GEP's because it is always passed by value, leaving you with two loads. Next, consider the function call through the interface pointer: IF.foo(F); This generates: %IF.obj = getelementptr { sbyte*, %IFOO.VTy* }* %IF, uint 0, uint 0 %IF.vt = getelementptr { sbyte*, %IFOO.VTy* }* %IF, uint 0, uint 1 %obj = load sbyte** %IF.obj %vt = load %IFOO.VTy** %IF.vt ;; Will be scalarized ; Get a pointer to the foo slot and load it. %vtslot = getelementptr %IFOO.VTy* %vt, uint 0, uint 1 %tmp = load void(sbyte*, float)** %vtslot ; Call the function. call void %tmp(sbyte* %obj, float %F) Does this make sense? When casting from an interface to a derived object, you just go to the object pointer, load the OBJECT'S vtable pointer, and do a normal dynamic cast from System.Object to whatever derived class you want. -Chris -- http://llvm.cs.uiuc.edu/ http://nondot.org/sabre/