显式基类型转换时可能是冗余操作码

时间:2016-12-20 17:34:36

标签: c# cil

我在默认的C#编译器VS 2017 RC Enterprise

中注意到了这种行为

当double / float被转换为自身已知类型时,会发出Conv.R8 / Conv.R4。 但是,如果铸造对象或非浮点类型,则不会发生任何事情。

以下示例已在release模式下编译。在debug IL类似。 样本C#代码:

    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
}

为什么Conv.R4 / Conv.R8的表现如此? 这个操作码是重要的还是可以安全删除/修剪掉?

1 个答案:

答案 0 :(得分:3)

link解释了实际发生的事情。引用最重要的部分:

  

第12.1.3节中的CLI规范规定了浮点数(float和double)在存储位置使用时的精确精度。但是,当浮点数用于其他位置(如执行堆栈,参数返回值等)时,它允许超出精度。使用的精度是运行时和底层硬件的精度。这种额外的精度可能会导致不同机器或运行时之间浮点评估的细微差别。

     

这是额外的for file in $(find . -print) do if [ -d $file -o -x $file ]; then chmod 777 $file else chmod 666 $file fi done conv.r4指令的来源。通常它们用于将非浮点值强制转换为浮点值。但是它们的一个副作用是结果值将具有由类型指定的精确精度。这意味着当应用于评估堆栈上的浮点值时,它会将其截断为指定的精度。

所以,回答你的具体问题,不,你不能安全地删除这些操作码。

来自同一链接的另一个有趣的信息是,c#编译器规范无法保证这种行为,而且截至目前,它是一个实现细节。它可能不会改变,因为这是所有以前的编译器的行为,而不仅仅是 VS 2017