2013-03-14 22 views
2

我使用ILGenerator编写的数组访问操作很简单。当使用这个确切的代码创建方法时,我打开反汇编,没关系,没有数组边界检查。DynamicAssembly中的数组边界检查仅在评估堆栈为空时有效

但是,当我第一次将其他类的实例放在评估堆栈上时,然后运行for循环,它会进行数组边界检查。我正在发布。

任何想法为什么?我已经阅读博客文章数组边界检查:http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx

 // Uncomment this to enable bound checks, type of arg0 is some my class 
     //il.Emit(OpCodes.Ldarg_0); 

     var startLbl = il.DefineLabel(); 
     var testLbl = il.DefineLabel(); 
     var index = il.DeclareLocal(typeof(Int32)); 
     var arr = il.DeclareLocal(typeof(Int32).MakeArrayType()); 

     // arr = new int[4]; 
     il.Emit(OpCodes.Ldc_I4_4); 
     il.Emit(OpCodes.Newarr, typeof(Int32)); 
     il.Emit(OpCodes.Stloc, arr); 

     // Index = 0 
     il.Emit(OpCodes.Ldc_I4_0); // Push index 
     il.Emit(OpCodes.Stloc, index); // Pop index, store 

     il.Emit(OpCodes.Br_S, testLbl); // Go to test 

     // Begin for 
     il.MarkLabel(startLbl); 

     // Load array, index 
     il.Emit(OpCodes.Ldloc, arr); 
     il.Emit(OpCodes.Ldloc, index); 

     // Now on stack: array, index 
     // Load element 
     il.Emit(OpCodes.Ldelem_I4); 
     // Nothing here now, later some function call 
     il.Emit(OpCodes.Pop); 

     // Index++ 
     il.Emit(OpCodes.Ldloc, index); 
     il.Emit(OpCodes.Ldc_I4_1); 
     il.Emit(OpCodes.Add); 
     il.Emit(OpCodes.Stloc, index); 

     il.MarkLabel(testLbl); 
     // Load index, count, test for end 
     il.Emit(OpCodes.Ldloc, index); 
     il.Emit(OpCodes.Ldloc, arr); 
     il.Emit(OpCodes.Ldlen); // Push len 
     il.Emit(OpCodes.Conv_I4); // Push len 
     il.Emit(OpCodes.Blt_S, startLbl); 
     // End for 

     // Remove instance added on top 
     //il.Emit(OpCodes.Pop); 

正如我产生IL代码,最好保持的类的实例上计算堆栈或局部变量?

例如我得到实例,通过领域,每个领域做任何事情,而不是回报。我只是将实例保存在堆栈中,并在读取下一个字段之前调用Emit(OpCodes.Dup)。但这似乎是错误的(至少在上面提到的情况下)。

任何关于生成(高效/格式良好)IL代码的文章/博客赞赏。

回答

2

在使用当地人一般通常会导致更可读的代码更容易调试,这给IL已经不是大多数开发商都用来读书是很重要的。 JIT甚至有可能会消除可能的性能损失。

从我在ILSpy中看到的,csc更喜欢本地人,尽管我不得不承认,当我查看IL而不是反编译为C#时,它主要是调试代码。由于JIT可能写的是期望它将主要运行在微软编译器的输出上,如果它不承认循环结构不符合它们的编译器所发出的结果,那就不足为奇了。这是非常有道理的,额外的堆栈条目阻碍了JIT识别它可以消除边界检查的能力。

0

你是否在释放模式下运行,调试器在你的方法是Jitted之前未连接?然后再附上? 我知道似乎不需要做这一步,但如果连接调试器,调试器将发出较少的优化代码。

包括您认为错误所在的整个方法。我建议用这个方法发出一个程序集,以便你可以运行PEVerify。有时你会成为编译代码,但无效。

抖动可以具有与代码非常硬的时间是无效的(例如,错误的堆栈等unboxed T上堆叠预期boxed T上的通用代码,只有与对象运行。),尤其是无法验证的是不正常的图案。 (例如,不会通过C#或C++/CLI发生的不安全代码)。除非您期待,否则您应始终尝试在peverify中出现0个错误。 (例如calli