【问题标题】:What are these opcodes for?这些操作码是做什么用的?
【发布时间】:2009-06-03 12:28:01
【问题描述】:

使用反射器我得到以下输出:

.method private hidebysig static class myModelTestarea.Foo Method() cil managed
{
  .maxstack 1
  .locals init ([0] class myModelTestarea.Foo CS$1$0000)
  L_0000: nop 
  L_0001: ldc.i4.0 
  L_0002: newarr object
  L_0007: call object myModelTestarea.Program::Resolve(object[])
  L_000c: castclass myModelTestarea.Foo
  L_0011: stloc.0 
  L_0012: br.s L_0014
  L_0014: ldloc.0 
  L_0015: ret 
}

private static Foo Method()
{
  return (Foo)Resolve();
}

private static object Resolve( params object[] args )
{
  return new Foo();
}

第 11-14 行有什么作用?我调用一个函数并得到一个结果(第 7 行)。我将结果转换为正确的返回类型(c 行) - 为什么不立即返回?

不知何故,转换后的结果被存储为一个局部变量——然后无条件跳转到下一行,再次加载局部变量。为什么?

在我看来第 11-14 行和局部变量可以省略...?

【问题讨论】:

标签: c# reflector opcode


【解决方案1】:

这看起来像一个 DEBUG 构建,它留下了额外的 IL 来帮助调试器。在 RELEASE 中再试一次,它应该看起来更干净,有优化等。

.method private hidebysig static class program/Foo Method() cil managed
{
    .maxstack 8
    L_0000: ldc.i4.0 
    L_0001: newarr object
    L_0006: call object program::Resolve(object[])
    L_000b: castclass program/Foo
    L_0010: ret 
}

【讨论】:

  • 是的,将结果存储在本地以进行调试构建非常有意义 - 谢谢
  • 需要明确的是,这是在使用 optimize- 选项而不是 debug+ 构建时发生的,尽管默认的 Debug 构建同时设置了两者。
  • 我希望 Eric 的博客早点出现;-p
【解决方案2】:

这是一个调试版本吗?它可能是为了调试器而存在的。

我在其他地方也看到过类似的东西——不过它几乎总是无害的。不要忘记大部分优化是由 JIT 完成的,它可以很容易地注意到这样的事情。唯一的缺点是更多的 IL 向 JIT 提示不应该内联该方法。

【讨论】:

  • 我想知道,@Jon,如果您碰巧知道:这些附加功能对调试有何用处?我可以也许理解将返回值存储在局部变量中 - 它允许在返回之前对其进行检查并可能更改它,尽管这并不明显:为什么调试器能够使用局部变量但堆栈上没有值?而我根本不明白的是无操作和分支到下一条指令:这些对任何东西有什么用处?
  • @Tom:我相信 no-ops 允许调试器更容易地添加断点,但这只是有点模糊的“知识”——事实上这不是 JIT 编译的代码显然让事情变得更多朦胧。恐怕我对这一切的了解都不是很深。
  • 谢谢,@乔恩。是的,我们到处都听到调试器添加断点,但我从来不明白为什么在nop 上放置断点比在其他任何东西上都更容易......(现在剩下的是找到最终问题的答案生命、宇宙和一切:当 Jon Skeet 不知道答案时该问谁......)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-24
  • 2013-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多