【问题标题】:Why does C# -> CIL have a label on every instruction?为什么 C# -> CIL 在每条指令上都有一个标签?
【发布时间】:2015-12-07 10:29:47
【问题描述】:

在编译的 C# 程序上使用 ILDASM.exe 时,它​​显示方法中的每条指令都有一个标签。

例如:

IL_0001:  ldc.i4.4
IL_0002:  stloc.0
IL_0003:  ldc.r8     12.34
IL_000c:  stloc.1
IL_000d:  ldc.r8     3.1415926535897931
IL_0016:  stloc.2
IL_0017:  ldstr      "Ehsan"
IL_001c:  stloc.3
IL_001d:  ret

这是为什么?这样做是不是效率低下,还是 CIL 编译器自己优化了这些标签?

【问题讨论】:

  • 是什么让您认为这是一个标签,而不是 ildasm 如何显示 IL?
  • 所以它就像没有“IL_0001”行? @JonSkeet
  • @EhsanSajjad:有点像。我相信它们充当您可以在 IL 中使用的标签,但我不相信它们存在于实际生成的代码中。
  • 通过 ILDASM 显示的 IL 和实际在程序集中的 IL 不同?

标签: c# .net-assembly cil il


【解决方案1】:

已编译的 CIL 中不存在标签。为了方便您在反汇编代码中显示它们。

这些特定的标签对应于指令偏移量,而手工代码则没有这种限制(标签可以是任意字符串)。

【讨论】:

  • 确保每条指令都有一个标签可能比跟踪需要发出哪些标签并只发出那些标签更容易。
【解决方案2】:

在编译的 IL 中,没有标签。相反,跳转指令使用从下一条指令开始的相对偏移量。

例如,考虑这个琐碎的 C# 函数:

public static bool IsZero(int n)
{
    if (n == 0)
        return true;
    return false;
}

在 IL 中,你可以这样写:

.method public hidebysig static bool IsZero(int32 n) cil managed
{
    ldarg.0     
    brtrue.s label
    ldc.i4.1    
    ret         
label:
    ldc.i4.0    
    ret   
}

如果你使用 ilasm 编译它,然后使用 ildasm 反编译它,并启用“显示字节”,你会得到:

.method public hidebysig static bool  IsZero(int32 n) cil managed
// SIG: 00 01 02 08
{
  // Method begins at RVA 0x2052
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  /* 02   |                  */ ldarg.0
  IL_0001:  /* 2D   | 02               */ brtrue.s   IL_0005
  IL_0003:  /* 17   |                  */ ldc.i4.1
  IL_0004:  /* 2A   |                  */ ret
  IL_0005:  /* 16   |                  */ ldc.i4.0
  IL_0006:  /* 2A   |                  */ ret
} // end of method Program::IsZero

请注意标签如何以任何方式不以字节表示(以 cmets 显示)。来自原始 IL 的 brtrue.s label 在此处显示为 brtrue.s IL_0005,其中字节为 2D 022Dbrtrue.s 的编译形式,02 是相对偏移量。由于以下指令从绝对偏移量 3 开始,因此目标的绝对偏移量为 3 + 2 = 5。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-20
    • 2023-04-10
    • 1970-01-01
    • 2020-03-15
    • 2011-01-03
    • 2013-04-13
    • 2014-06-20
    • 2017-03-20
    相关资源
    最近更新 更多