{"id":3182,"date":"2010-11-08T12:15:00","date_gmt":"2010-11-08T12:15:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/subterranean-il-callvirt-and-generic-types\/"},"modified":"2021-04-29T15:30:55","modified_gmt":"2021-04-29T15:30:55","slug":"subterranean-il-callvirt-and-generic-types","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/subterranean-il-callvirt-and-generic-types\/","title":{"rendered":"Subterranean IL: Callvirt and generic types"},"content":{"rendered":"<p>In this post we finally get on to how basic generic methods are implemented in IL. First of all, we should briefly cover how a generic method is declared in IL.<\/p>\n<p><b>Declaring a generic method<\/b><\/p>\n<p>The basic syntax for a generic method is fairly straightfoward. The following C# generic method definition: <\/p>\n<pre>public static void GenericMethod&lt;T1, T2&gt;(T1 arg0, T2 arg1){ \/* ... *\/ }<\/pre>\n<p> corresponds to the following IL method definition: <\/p>\n<pre>.method public static void GenericMethod&lt;T1, T2&gt;(!!T1 arg0, !!T2 arg1){ \/* ... *\/ }<\/pre>\n<p> The <code>!!<\/code> in front of the generic type references tell the IL compiler that these should be interpreted as generic method parameters, a single <code>!<\/code> in front of a type name refers to a generic parameter of a containing class. You can also refer to generic parameters by their ordinal, so <code>!!0<\/code> is the same as <code>!!T1<\/code> and <code>!!1<\/code> is the same as <code>!!T2<\/code>.<\/p>\n<p><b>Calling methods on generic types<\/b><\/p>\n<p>To start with, we&#8217;ll look at how you call a virtual method on an instance of a generic type. So, given the following generic method definition: <\/p>\n<pre>.method public static string CallToString&lt;T&gt;(!!T obj) {\n    \/\/ ????\n    callvirt instance string [mscorlib]System.Object::ToString()\n    ret\n}<\/pre>\n<p> what should go in place of the <code>????<\/code> to push the object we want to call <code>ToString<\/code> on onto the stack? Remember, <code>T<\/code> can either be a reference or value type, but <code>callvirt<\/code> only accepts a heap object reference as the <code>this<\/code> pointer. Well, as it turns out, <code>box<\/code> doesn&#8217;t have to be used on a value type; trying to <code>box<\/code> a reference type turns into a no-op. This should do what we want: <\/p>\n<pre>.method public static string CallToString&lt;T&gt;(!!T obj) {\n    ldarg.0\n    box !!T\n    callvirt instance string [mscorlib]System.Object::ToString()\n    ret\n}<\/pre>\n<p> If <code>T<\/code> is a reference type, then <code>callvirt<\/code> uses the same object reference loaded by <code>ldarg.0<\/code>. If <code>T<\/code> is a value type, then <code>ldarg.0<\/code> copies the value type value onto the stack, <code>box !!T<\/code> turns it into an object reference, and <code>callvirt<\/code> uses that as the <code>this<\/code> pointer. All well and good.<\/p>\n<p><b>Throwing in a mutable spanner<\/b><\/p>\n<p>However, once again, mutableness rears its ugly head. What if you wanted to call <code>IIncrementable.Increment<\/code> in a generic method? In other words, the equivalent of this C# method: <\/p>\n<pre>public static void CallIncrement&lt;T&gt;(T obj) where T : IIncrementable {\n    \/\/ ....\n    obj.Increment(1);\n    \/\/ ....\n}<\/pre>\n<p> (as a side point, generic type constraints are specified in IL like so: <code>.method public static void CallIncrement&lt;(IIncrementable) T&gt;(!!T obj)<\/code>)<\/p>\n<p>If <code>T<\/code> is <code>IncrementableClass<\/code>, this should increment the <code>Value<\/code> field of the object reference stored in the <code>obj<\/code> parameter. If <code>T<\/code> is <code>IncrementableStruct<\/code> the incremented value type should be copied back to the <code>obj<\/code> parameter.<\/p>\n<p>Fortunately, there is an IL command to help us do this: <code>unbox.any<\/code>, which performs the reverse of <code>box<\/code> &#8211; copies a boxed value type instance back onto the stack. Again, this operation turns into a no-op (specifically, a <code>castclass<\/code>) if the type argument is a reference type. This leads to the following IL to call the <code>IIncrementable.Increment<\/code> method on a generic type: <\/p>\n<pre>ldarg.0\nbox !!T\ndup\nldc.i4.1\ncallvirt instance void IIncrementable::Increment(int32)\nunbox.any !!T\nstarg 0<\/pre>\n<p> Phew! And, just to confirm, here&#8217;s the stack transition if <code>T<\/code> is <code>IncrementableClass<\/code>: <\/p>\n<pre>ldarg.0         \/\/ O[IncrementableClass]\nbox !!T         \/\/ O[IncrementableClass]\ndup             \/\/ O[IncrementableClass],O[IncrementableClass]\nldc.i4.1        \/\/ O[IncrementableClass],O[IncrementableClass],int32\ncallvirt...     \/\/ O[IncrementableClass]\nunbox.any !!T   \/\/ O[IncrementableClass]\nstarg 0<\/pre>\n<p> And for <code>IncrementableStruct<\/code>: <\/p>\n<pre>ldarg.0         \/\/ IncrementableStruct\nbox !!T         \/\/ O[IncrementableStruct]\ndup             \/\/ O[IncrementableStruct],O[IncrementableStruct]\nldc.i4.1        \/\/ O[IncrementableStruct],O[IncrementableStruct],int32\ncallvirt...     \/\/ O[IncrementableStruct]\nunbox.any !!T   \/\/ IncrementableStruct\nstarg 0<\/pre>\n<p> And this is only for calling a virtual method on an object in a method parameter! Versions of this will have to be constructed for calling it on a local variable, on a value that&#8217;s going to be used in another method parameter, on an array element&#8230; Furthermore, this is quite wasteful of both IL code size and run-time performance. Not only will we have to output variations of this code for every method call made on an instance of a generic type, but if <code>T<\/code> is a value type, then we are copying the entire value type instance twice, and generating a heap object that will need to be garbage collected, simply to call a method on it. There must be a better way.<\/p>\n<p><b>Finding the <code>MethodTable<\/code><\/b><\/p>\n<p>Lets take a step back. The only reason we have to box the value type instance is to generate a <code>MethodTable<\/code> pointer as part of the heap object that the virtual method call can use. But, if it&#8217;s a value type, we already know the correct table to use at compile time, as value types are all sealed. What if we could specify the <code>MethodTable<\/code> to use <em>without<\/em> having to box the instance?<\/p>\n<p>This is where the <code>constrained.<\/code> prefix instruction comes in (note the dot at the end). This specifies which <code>MethodTable<\/code> the following <code>callvirt<\/code> should use if one isn&#8217;t already provided by the <code>this<\/code> pointer. It also modifies the <code>callvirt<\/code> to take a <code>this<\/code> of type <code>&amp;<\/code> rather than <code>O<\/code>; if the constrained type is a reference type, the <code>&amp;<\/code> is a pointer to an object reference (so it&#8217;s a <code>void**<\/code> in C parlance), if it&#8217;s a value type, it&#8217;s a pointer to a value type instance (for the same reasons that <code>call<\/code> on a value type takes a <code>&amp;<\/code>).<\/p>\n<p>So, the call to <code>obj.Increment(1)<\/code> turns into the following IL: <\/p>\n<pre>ldarga 0\nldc.i4.1\nconstrained. !!T\ncallvirt instance void IIncrementable::Increment(int32)<\/pre>\n<p> More specifically, the <code>constrained callvirt<\/code> instruction has the following behaviour: <\/p>\n<ul>\n<li>If <code>T<\/code> is a reference type, the <code>this<\/code> <code>&amp;<\/code> is dereferenced to an <code>O<\/code> and passed as the <code>this<\/code> to a standard <code>callvirt<\/code><\/li>\n<li>If <code>T<\/code> is a value type and implements the method directly, the <code>this<\/code> <code>&amp;<\/code> is passed unmodified to a standard <code>call<\/code>, using the method implementation specified in the <code>MethodTable<\/code> for <code>T<\/code><\/li>\n<li>If <code>T<\/code> is a value type and does not implement the method directly, then the <code>this<\/code> <code>&amp;<\/code> is dereferenced, boxed, and the resulting <code>O<\/code> is passed as the <code>this<\/code> to a standard <code>callvirt<\/code> (this only applies to virtual methods on <code>System.Object<\/code> that are not directly overridden by the value type)<\/li>\n<\/ul>\n<p>This eliminates both the copying of value types and the IL code bloat for performing a virtual method call on an instance of a generic type.<\/p>\n<p>Also note that <code>constrained<\/code> doesn&#8217;t have to be used with a generic type; it can be used in non-generic methods as well: <\/p>\n<pre>.method public static void CallIncrement(\n        valuetype IncrementableStruct obj) {\n    ldarga 0\n    ldc.i4.1\n    constrained. IncrementableStruct\n    callvirt instance void IIncrementable::Increment(int32)\n    \/\/ ...\n}<\/pre>\n<p> although it can&#8217;t be used to directly call value type methods (as <code>call<\/code> works perfectly well for that): <\/p>\n<pre>constrained. IncrementableStruct\ncallvirt instance void IncrementableStruct::Increment(int32)\n\nIL Error: [offset 0x0000001B] Callvirt on a value type method.<\/pre>\n<\/p>\n<p><b>To wrap up<\/b><\/p>\n<p>We&#8217;ve seen how the <code>constrained callvirt<\/code> instruction performs virtual method calls on instances of a generic type that will work equally well with reference and value types. However, that isn&#8217;t the end of the story. <a href=\"https:\/\/www.simple-talk.com\/community\/blogs\/simonc\/archive\/2010\/11\/11\/95609.aspx\">Next time<\/a>, I&#8217;ll be looking at how this interacts with arrays.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post we finally get on to how basic generic methods are implemented in IL. First of all, we should briefly cover how a generic method is declared in IL. Declaring a generic method The basic syntax for a generic method is fairly straightfoward. The following C# generic method definition: public static void GenericMethod&lt;T1,&#8230;&hellip;<\/p>\n","protected":false},"author":186659,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538,2],"tags":[],"coauthors":[],"class_list":["post-3182","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","category-blogs"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/3182","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/186659"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=3182"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/3182\/revisions"}],"predecessor-version":[{"id":75612,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/3182\/revisions\/75612"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=3182"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=3182"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=3182"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=3182"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}