Where and how are IL opcodes implemented in the .NET runtime source code

67 Views Asked by At

I was researching call vs callvirt opcodes with all of the gotchas along the way.

You can read the description online, but when you encounter this

The callvirt instruction calls a late-bound method on an object. That is, the method is chosen based on the runtime type of obj rather than the compile-time class visible in the method pointer.

you know it's not the full story since callvirt is emitted for virtually everything by the C# compiler and that the implementation details make up for all the difference.

I think what the callvirt internally does is check the metadata token from the method table for whether it's virtual or not and if not virtual, doesn't really bother with the object's vtables. But how do I know?

Can somebody explain how the runtime code is structured with regard to handling of the opcodes (I guess the JITter) so I can more easily browse the source code and understand the implementation details.

2

There are 2 best solutions below

2
Deepak On

We can handle this in 2-3 different ways... 1)OpCodes Class: The OpCodes class in the System.Reflection.Emit namespace provides field representations of the Microsoft Intermediate Language (MSIL) instructions for emission by the ILGenerator class members. This class is used when you want to emit IL instructions dynamically in your .NET code.

  1. Use CodeDOM to build and compile a code tree. Build an Expression tree and compile it. Generate code one IL instruction after another. Adding a New Bytecode Instruction to the CLR: If you want to add a new bytecode instruction to the CLR, you would need to make changes in opcode.def
1
Jeremy Lakeman On
public class Base {
    public virtual void A(){}
    public void B(){}
    
    public void Test(){
        this.A();
        this.B();
    }
}
public class Derived : Base{
    public override void A(){}
}

If the compiler can't know which method will be called at compile time, the runtime uses callvirt to find the method implementation based on the type of the variable. This is true for any virtual method, or any method called via an interface.

The runtime may optimise these calls and de-virtualise them where appropriate.

it's not the full story since callvirt is emitted for virtually everything by the C# compiler

Only because so many methods are virtual or accessed via an interface.