C# 显式基类型强制转换时可能会冗余操作码
我在默认C#编译器VS 2017 RC Enterprise中注意到了这种行为 将double/float转换为自身时,将发出已知类型Conv.R8/Conv.R4。 但是,如果对象或非浮点类型被强制转换,则不会发生任何情况 以下示例已在C# 显式基类型强制转换时可能会冗余操作码,c#,cil,C#,Cil,我在默认C#编译器VS 2017 RC Enterprise中注意到了这种行为 将double/float转换为自身时,将发出已知类型Conv.R8/Conv.R4。 但是,如果对象或非浮点类型被强制转换,则不会发生任何情况 以下示例已在发行版模式下编译。在调试时,IL与之类似。 示例C#代码: 共同响应Il: .method private hidebysig instance int32 IntConvertToInt () cil managed { /
发行版
模式下编译。在调试时,IL与之类似。
示例C#代码:
共同响应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的行为是这样的?
该操作码是否重要,或者是否可以安全删除/删除?这解释了实际发生的情况。引用最重要的部分:
第12.1.3节中的CLI规范规定了在存储位置中使用浮点数(float和double)时的精确精度。但是,当在其他位置(如执行堆栈、参数返回值等)使用浮点数时,它允许超过精度。使用的精度将留给运行时和底层硬件。这种额外的精度可能导致不同机器或运行时之间浮点计算的细微差异
这就是额外的conv.r4
和conv.r8
指令的作用。通常,它们用于将非浮点值强制转换为浮点值。但是,它们的一个副作用是结果值将具有类型指定的精确精度。这意味着当应用于计算堆栈上的浮点值时,它会将其截断到指定的精度
所以,回答你的具体问题,不,你不能安全地删除这些操作码
来自同一链接的另一个有趣的信息是,c#编译器规范并不能保证这种行为,到目前为止,这是一个实现细节。它可能不会改变,因为这是所有以前的编译器的行为,不仅仅是VS 2017。请发布所有这些字段的声明。@LasseV.Karlsen
ldfld
参数是否已经显示声明的字段类型<代码>ldfld float32转换测试。程序::_Float指示其加载float32
,然后调用conv.r4
。讨论了这些指令是否冗余以及何时冗余。此外,C#spec可能会有更新。
.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
}