I noticed this behavior at default C# compiler, VS 2017 RC Enterprise
When double/float is casted to itself known type Conv.R8/Conv.R4 is emitted. However if object or non float point type is casted nothing happens.
Examples below has been compiled at release
mode. At debug
IL is similar.
Sample C# Code:
private double _Double;
private float _Float;
private int _Int;
private object _Object;
private int IntConvertToInt()
{
int x = (int)_Int; //
return x;
}
private int IntAssignToInt()
{
int x = _Int;
return x;
}
private float FloatConvertToFloat()
{
float x = (float)_Float; //Additional OpCode
return x;
}
private float FloatAssignToFloat()
{
float x = _Float;
return x;
}
private double DoubleConvertToDouble()
{
double x = (double)_Double; //Additional OpCode
return x;
}
private double DoubleAssignToDouble()
{
double x = _Double;
return x;
}
private Object ObjectConvertToObject()
{
Object x = (Object)_Object;
return x;
}
private Object ObjectAssignToObject()
{
Object x = _Object;
return x;
}
Coresponding Il:
.method private hidebysig
instance int32 IntConvertToInt () cil managed
{
// Method begins at RVA 0x2052
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ConversionTest.Program::_Int
IL_0006: ret
} // end of method Program::IntConvertToInt
.method private hidebysig
instance int32 IntAssignToInt () cil managed
{
// Method begins at RVA 0x2052
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ConversionTest.Program::_Int
IL_0006: ret
} // end of method Program::IntAssignToInt
.method private hidebysig
instance float32 FloatConvertToFloat () cil managed
{
// Method begins at RVA 0x205a
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float32 ConversionTest.Program::_Float
IL_0006: conv.r4 //here
IL_0007: ret
} // end of method Program::FloatConvertToFloat
.method private hidebysig
instance float32 FloatAssignToFloat () cil managed
{
// Method begins at RVA 0x2063
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float32 ConversionTest.Program::_Float
IL_0006: ret
} // end of method Program::FloatAssignToFloat
.method private hidebysig
instance float64 DoubleConvertToDouble () cil managed
{
// Method begins at RVA 0x206b
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float64 ConversionTest.Program::_Double
IL_0006: conv.r8 //here
IL_0007: ret
} // end of method Program::DoubleConvertToDouble
.method private hidebysig
instance float64 DoubleAssignToDouble () cil managed
{
// Method begins at RVA 0x2074
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld float64 ConversionTest.Program::_Double
IL_0006: ret
} // end of method Program::DoubleAssignToDouble
.method private hidebysig
instance object ObjectConvertToObject () cil managed
{
// Method begins at RVA 0x207c
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld object ConversionTest.Program::_Object
IL_0006: ret
} // end of method Program::ObjectConvertToObject
.method private hidebysig
instance object ObjectAssignToObject () cil managed
{
// Method begins at RVA 0x207c
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld object ConversionTest.Program::_Object
IL_0006: ret
}
Why Conv.R4/Conv.R8 is behaving this way? Is this opcode there important or can it be safety removed/trimmed out?
This link explains what is really happening. To quote the most significant part:
So, answering your specific question, no, you can not safely remove these opcodes.
Another interesting bit of information from that same link, is that this behaviour is not guaranteed by the c# compiler specification and it is, as of now, an implementation detail. Its probably not going to change as this is the behavior of all previous compilers, not only VS 2017.