While working on dotnet/coreclr#12676 I ran into what appears to be a bug in cast morphing. Consider the following IL code:
.assembly extern System.Console {auto}
.assembly extern mscorlib {auto}
.assembly test {}
.class Program
{
.field static int8 foo;
.method static int32 Test()
{
.entrypoint
.maxstack 1
ldc.i4 -1
stsfld int8 Program::foo
ldsfld int8 Program::foo
conv.ovf.i2.un
ret
}
}
This should throw an overflow exception. When foo is loaded the value is sign extended so on the evaluation stack we have -1. But we have conv.ovf.i2.un so the value on the stack is supposed to be treated as 4294967295 and that's too large for i2.
Morph is incorrectly removing the overflow flag from the cast and the following code is generated:
C605AB49EFFFFF mov byte ptr [reloc classVar[0xb1466240]], -1
480FBE05A349EFFF movsx rax, byte ptr [reloc classVar[0xb1466240]]
480FBFC0 movsx rax, ax
The problem is likely in this fgMorphCast code:
if (srcSize < dstSize) // widening cast
{
// Keep any long casts
if (dstSize == sizeof(int))
{
// Only keep signed to unsigned widening cast with overflow check
if (!tree->gtOverflow() || !unsignedDst || unsignedSrc)
{
goto REMOVE_CAST;
}
}
// Casts from signed->unsigned can never overflow while widening
if (unsignedSrc || !unsignedDst)
{
tree->gtFlags &= ~GTF_OVERFLOW;
}
}
In this example srcSize is 1 and dstSize is 2 so it concludes that we have a widening cast to signed and drops the overflow flag. srcSize should really be 4, it's 1 only if the cast doesn't "reinterpret" the sign of the source field.
Also note that the "Casts from signed->unsigned can never overflow while widening" comment is wrong. It should be something like "Casts from unsigned or to signed can never overflow while widening".
While working on dotnet/coreclr#12676 I ran into what appears to be a bug in cast morphing. Consider the following IL code:
This should throw an overflow exception. When
foois loaded the value is sign extended so on the evaluation stack we have -1. But we haveconv.ovf.i2.unso the value on the stack is supposed to be treated as 4294967295 and that's too large fori2.Morph is incorrectly removing the overflow flag from the cast and the following code is generated:
The problem is likely in this fgMorphCast code:
In this example
srcSizeis 1 anddstSizeis 2 so it concludes that we have a widening cast to signed and drops the overflow flag.srcSizeshould really be 4, it's 1 only if the cast doesn't "reinterpret" the sign of the source field.Also note that the "Casts from signed->unsigned can never overflow while widening" comment is wrong. It should be something like "Casts from unsigned or to signed can never overflow while widening".