Open NinoFloris opened 4 years ago
It's not readily apparent from the disassembly above, but both calls are in fact devirtualized. But this happens "late" as the object being dispatched is the return value from a call, and because of this, we currently can't do inlining or box removal optimizations.
;; early attempt during importation
impDevirtualizeCall: Trying to devirtualize interface call:
class for 'this' is IMe (attrib 00200400)
base method is IMe::Hello
No unique implementor of interface 00000000D1FFAB1E (IMe), sorry
;; late attempt after inlining
impDevirtualizeCall: Trying to devirtualize interface call:
class for 'this' is Combined [exact] (attrib 20000010)
base method is IMe::Hello
--- base class is interface
devirt to Combined::Hello -- exact
[000009] --C-G------- * CALLV stub int IMe.Hello
[000006] ------------ this in rcx \--* LCL_VAR ref V02 tmp1
exact; can devirtualize
... after devirt...
[000009] --C-G------- * CALL nullcheck int Combined.Hello
[000006] ------------ this in rcx \--* LCL_VAR ref V02 tmp1
The calls end up getting invoked via an indirection cell which makes them look like they're possibly still virtual.
Linked this into the devirtualization "todo" issue #7541. Marking as future.
Ran from a VM due to https://github.com/dotnet/BenchmarkDotNet/issues/1499
The idea is to do a two step virtual call, once dispatching from
this
(Combined) and once to a separate sealed class (Separate), JustMe is the control as it devirtualizes correctly.And the resulting asm:
Expectation would be that all benchmarks compile down to
mov eax, 1
category:cq theme:devirtualization skill-level:expert cost:large impact:medium