IL Call Vs. Callvirt Instruction

.NET languages compiles to IL code (aka msil or cil) The IL code contains instructions that in run time the jit compiler translate it to machine code.

In this post I want to talk about method call instructions.

When we writing the following in C#


 MyClass().MyMethod();

The code will translate to one of the following: call or callvirt. Theoretically there is one more option named calli but c# compiler won’t generate calli instruction so I’ll skip this.

What is the difference and why should I care?

The difference is if the code will translating to direct call to supplied address (call) or to a late-bound call to specific address that we have to find via the object vtable (callvirt).

From MSDN:

Callvirt: The callvirt instruction calls a late-bound method on an object.

Call: The call instruction calls the method indicated by the method descriptor passed with the instruction.

And why should I care? The first reason came up is performance, but actually you don’t need to care about this. First because you can’t do anything about this (except of writing your own CLR) and second because when you write your code you don’t really need to think “how I make it to emit call instead of callvirt” this is not so serious performance problem in .NET section. If you want to tweak these things you need to change language.

So as I see it, the reason is just a curiosity of what going on under the hood and why this happen and not this etc. And for me this is the most important reason. Because certainly knowing what happen under the hood give a lot in way of understanding what you do in your code and give a good debugging abilities,.

Back to topic, I think it’s reasonably to say that “direct call” mean static methods, sealed class methods, because these methods can be knowing at compile time and late-bound call mean virtual methods because these type of methods can’t be known at compile time and need to be determined in run time.

But this is not the true…

Again from MSDN:

Note that a delegate’s Invoke method can be called with either the call or callvirt instruction.

It is valid to call a virtual method using call (rather than callvirt).

So we saw that even virtual methods can be called via call instruction. But more surprising it that a non virtual methods will be called via callvirt instruction, It was understate if regular non virtual method called via callvirt because there is a change to them to being overridden, but for sealed classes it’s completely don’t make sense. Sealed class methods never been overridden. So why is this behaviour?

The answer is that callvirt do one more thing that we need even if of non virtual methods. The thing is a null check.

In disassembly its look like this:
cmp         dword ptr [ecx], ecx

This will raise a NullReferenceException if the pointer in ecx register is invalid.

Note: If you want to read more about this, read this post at The Old New Thing.


Why we need the null check?

When we call to method on object the this pointer pass as the first argument, this can be a null.

What will be happen without the null check?

Case one: We try to use the this and we will get a NullReferenceException . This is an expected behaviour.

Case two: We don’t use this in the method and everything work just great. Some people will say that this is a little wired but after all this is a legal.

Case three: We don’t use this in the method but we call other method and there we try to use this. Then we get a NullReferenceException  from the second method and this is more problematic from case two because its a little confusing that we doesn’t get the exception in the first when we use the object instance to call the first method..

So the design decision was to check for null every time we call an instance method. (as Eric Gunnerson explained here)

So to summarize it, methods calls generate a callvirt instruction because we need to make sure that the this pointer isn’t null.


Do I miss something here?

Virtual calls is not just about null check, its a different way to call methods as we see from MSDN. So just because the null check we use vtable for non virtual methods? Why to search for direct method in vtable

Fortunately Iv’e founded this mail in mono mailing list that said that indeed we not really do virtual call for non-virtual method but we just relay on the null check of the callvirt instruction and then we call the method directly.

There is also the constrained opcode that can change the behavior from callvirt to call.


Part two

In the next post I’ll show that not all methods actually use the callvirt instruction and also put some IL examples to demonstrate it.

Update

In C# 6 there is a new null check operator ‘?’
When you use ‘?’ on an non virtual method, C# will generate call instruction because the ‘?’ ensure that is not null by check for null and copy the reference to a local value.
See this question in SO

Advertisements
This entry was posted in .NET and tagged , , , , , , . Bookmark the permalink.

4 Responses to IL Call Vs. Callvirt Instruction

  1. Pingback: IL Call VS. Callvirt Instruction Part two | My coding place

  2. Pingback: Value type methods – call, callvirt, constrained and hidden boxing | My Coding Place

  3. Thanks for posting this. I have been checking out
    your content for ages and it always brings me
    back! I’m a long time reader, however I’ve never been compelled to leave a comment until I started
    my own gaming blog.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s