2010-04-09 131 views
8

我想了解IL在while循环中的外观。我写了这个C#函数:IL中的循环 - 为什么stloc.0和ldloc.0?

static void Brackets() 
    { 
     while (memory[pointer] > 0) 
     { 
      // Snipped body of the while loop, as it's not important 
     } 
    } 

的IL看起来是这样的:

.method private hidebysig static void Brackets() cil managed 
{ 
    // Code size  37 (0x25) 
    .maxstack 2 
    .locals init ([0] bool CS$4$0000) 
    IL_0000: nop 
    IL_0001: br.s  IL_0012 
    IL_0003: nop 
    // Snipped body of the while loop, as it's not important 
    IL_0011: nop 
    IL_0012: ldsfld  uint8[] BFHelloWorldCSharp.Program::memory 
    IL_0017: ldsfld  int16 BFHelloWorldCSharp.Program::pointer 
    IL_001c: ldelem.u1 
    IL_001d: ldc.i4.0 
    IL_001e: cgt 
    IL_0020: stloc.0 
    IL_0021: ldloc.0 
    IL_0022: brtrue.s IL_0003 
    IL_0024: ret 
} // end of method Program::Brackets 

在大多数情况下,这是非常简单的,除了CGT之后的部分。

我不明白的是本地[0]和stloc.0/ldloc.0。就我所见,cgt将结果推送到堆栈,stloc.0从堆栈中获取结果到本地变量中,ldloc.0再次将结果推送到堆栈,并且brtrue.s从堆栈中读取。

这样做的目的是什么?难道这不能简化为cgt跟brtrue.s?

+1

基于“因为它不重要”和你的twitter饲料,你会得到额外的金块......注意'br * _s'和'br *'之间的区别。 '_s'变体使用一个小的*相对*偏移量,如果位置不靠近,**将不起作用(非's'变体使用*绝对*偏移量而没有“小”限制)。因此,如果你的编译器不能预测(预先)body的大小,那么使用'br *'优先于'br * _s'。 – 2010-04-09 05:05:31

+0

@Marc谢谢,碰到这个问题:D我的Demo应用程序有一条指令,所以编译器生成_s变体,但是我真正的编译器有更长的循环体。积极的副作用:我从那个“非法的单字节分支”例外中学到了一些东西:) – 2010-04-09 05:15:23

+0

IIRC,ILGenerator足够聪明,可以为手边的情况发出正确的操作码,如果您使用正确的方法。 – 2010-04-09 07:41:44

回答

6

尝试使用优化进行编译。

+0

这实际上“修复”它 - 它只是为了调试/断点的目的吗?只是好奇它为什么首先产生。 – 2010-04-09 04:22:51

+0

我猜想两个互相取消的指令来自编译器的不同部分。我完全可以想象,所有while循环最初都会转换为nop(作为跳转目标).... ldloc。 brtrue.s(nop的地址)。如果你知道优化器会在下一个阶段删除不需要的nop等等,似乎足够理性了。 – 2010-04-09 20:34:59

4

这是一个调试版本(来自nop)。所有的赌注都关闭,但它看起来像它只是引入一个布尔变量简单:

goto testforexit; 
body: 
    .. 
testforexit: 
    bool tmp = memory[pointer] > 0; 
    if(tmp) goto body; 

建立与释放的优化启用,这应该删除这些变量。