3

I was digging around in ILDASM and Reflector as found that:

  1. == is compiled to the "ceq" MSIL command
  2. object.Equals is left as is
  3. object.Equals calls object.InternalEquals

This question showed me how to find out how InternalEquals might be implemented i.e. in .cpp class (or whatever, somewhere in the CLR).

My question is:

What does ceq become? Another method in a different .cpp class? I.e. they are completely different peices of code? So although the default behaviour of == and Equals appears to be the same, it is different code?

Community
  • 1
  • 1
J M
  • 1,877
  • 2
  • 20
  • 32
  • Some exceptions that `==` isn't compiled to `ceq`: `string`, `System.Type`, see http://stackoverflow.com/questions/17634395/what-is-a-better-way-to-check-that-a-given-object-is-a-particular-value-type – colinfang Jul 14 '13 at 16:54

3 Answers3

20

The == operator doesn't always get translated to ceq. A type can overload it with operator==(). System.Decimal does this for example, it overloads all of the operators since their implementation is untrivial and the jitter doesn't have special knowledge of the type (the compiler does).

You'll find it back with Reflector as the Decimal.op_Equality() method. Which leads you to FCallCompare, a method that's attributed with MethodImplOptions.InternalCall. These kind of methods are special, the jitter has secret knowledge of them. You can find their implementation through the clr/src/vm/ecall.cpp source code file in Rotor. It contains a table of all internal call functions, the jitter looks up the table entry by the method name. Then compiles the address of the corresponding C++ function as provided in the table into the call instruction. Beware that the function name was changed since the Rotor release, search for FCallAdd, it it the next entry in the table. Which takes you to COMDecimal::Compare. Which takes you to the comdecimal.cpp source code file.

The x86 and x64 jitters know how to convert the ceq opcode to machine code directly without needing a helper function, it generates the native machine instructions inline. Actual generated code depends on the type of the values being compared. And the target, the x64 jitter uses SSE instructions, the x86 uses FPU instructions to compare floating point values. Other jitters will implement them differently yet of course.

A helper function like Object.InternalEquals() is also an internal method, just like FCallCompare. You'd use the same strategy to find the implementation.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    Now this is what I want. Full on specifics. Thank you, once again. This will keep me quiet for a few days :). – J M Mar 08 '11 at 00:27
  • I wish I could upvote/accept twice. Seriously, it's so hard to get a starting point and not being able to findthe answer drives me mad. – J M Mar 08 '11 at 00:29
  • Odd, you don't seem to have too much trouble getting answers at SO :) – Hans Passant Mar 08 '11 at 00:40
  • 1
    Yes, sorry, I didn't mean I can't get answers here and I'm very appreciative of all responses. What I meant was that I usually only post here after I've gone through several hours of pain not being able to work it out myself. Therefore, when someone answers, with detail, insight and true knowledge, it means a lot. – J M Mar 08 '11 at 09:51
4

You are seeing ceq because there is no overloaded == - it is doing a direct reference compare. To do that, all it has to do is directly compare two numbers on the stack; this is just about the fastest thing it can do.

object.Equals is ambiguous; there are two;

x.Equals(y) is a virtual method, so may well be overridden. Depending on the type, a virtual call, a static call or a constrained call will be issued, which may have a custom implementation.

object.Equals(x,y) is a static method, which checks first for nulls; 2 nulls = true, 1 null = false, 0 nulls - call x.Equals(y).

But to focus on the question, it is logically a == on native ints; in most JITs I would hope that this remains a == against two integer types (possibly pointers), but JITs vary (or may in fact not even exist - MF is an interpreter).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Sorry for the delay is thanks. I appreciate the time taken to explain. Re the ambiguity; sorry, I meant the instance Equals method. My question is trying to get an understanding of why there are two different ways of doing an equality check, implemented in different places (within the CLR?), one that compiles to an msil instruction by default and the other that stays as a call to an external method by default, both of which can be overridden.... – J M Mar 08 '11 at 00:08
  • @JM `==` can be *overloaded*, but not overridden; this is a big difference - between static analysis and polymorphism. – Marc Gravell Mar 08 '11 at 06:11
  • Yes, mistake there, possibly because it was quarter past midnight but that's not realy a good excuse :). Embarassing. Thank you for pointing it out. I'm editing my comment :) – J M Mar 08 '11 at 09:55
  • Except it appears I can't...oh well. – J M Mar 08 '11 at 09:55
2

Yes, they run different code.

  • Equals is an instance method.
  • == is a static operator.

Both can be redefined for custom types.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • Thanks for taking the time to answer. I do understand that Equals is an instance method and == is an operator. What I don't understand is the difference between how the default implementations i.e. the "ceq" call and the call to "object.InternalEquals" differ and why there are two ways of doing it in the first place.... – J M Mar 08 '11 at 00:14