Closed AndyAyersMS closed 4 years ago
Looking at the jit side -- the problematic IL is:
newobj 0x6000009
dup
stloc.0
dup
callvirt 0x6000002
Where local 0
has a type that is a supertype of the newobj
. In impImportBlockCode
the jit deliberately substitutes the local for the result of the dup
.
// Convert a (dup, stloc) sequence into a (stloc, ldloc) sequence in the following cases:
// - If this is non-debug code - so that CSE will recognize the two as equal.
// This helps eliminate a redundant bounds check in cases such as:
// ariba[i+3] += some_value;
// - If the top of the stack is a non-leaf that may be expensive to clone.
Because we're in the importer we may be able to access a more accurate type for the value being dup
'd and spill to a temp local instead. So we'd effectively act as if the IL were
newobj 0x6000009
stloc newtemp // gets type from newobj
ldloc newtemp
ldloc newtemp
stloc.0
ldloc newtemp
callvirt 0x6000002
which would avoid losing type information and still lead to the same CSE opportunities; the only downside is re-introducing the local temp that CSC worked so hard to get rid of. In this particular case we already create a temp for the result of newobj so no additional temp is actually needed.
The jit also uses the same dup
transmogrification to turn ldloc; dup; stloc
into 'ldloc; stloc; ldloc` but here there is no type loss and it can probably stay as is.
So the logic would be something like: when importing a dup, if the TOS is not a local or a simple constant, pop the TOS and spill it to a new temp with type taken from the stack model, then reload the temp twice.
Following test case, if compiled /o+ /debug:pdbonly, and run in a debug/check CoreCLR, will trigger an assert in resolveVirtualMethod:
This it triggered by aggressive use of
dup
in the generated IL.Likely two fixes needed.
First, have the VM side validate that the method being invoked is really a virtual method of the derived class, and if not, fail devirtualization. Would be nice to get the diagnostic information back too since this case represents a modelling error in the jit, so perhaps in addition to failing,
resolveVirtualMethod
should return some kind of status code.Second, need to fix the jit not to lose the type information in this case.
This can also result in silent bad code, if it turns out the slot number presented to B is a valid one, and the method found there happens to be final.