zoukankan      html  css  js  c++  java
  • 《Inside Microsoft IL Assembler》学习笔记2:让IL代码简短些

    还是学习笔记1中的那个例子,可以改成如下的样子,功能不变,但运行时占用的资源会小一些。我在所有和笔记1中代码有了变化的位置加上了注释,如下:
     1//----------- Program header
     2.assembly extern mscorlib { }
     3.assembly OddOrEven  { }
     4.module OddOrEven.exe
     5//----------- Class declaration
     6.namespace Odd.or {
     7    .class public auto ansi Even extends [mscorlib]System.Object {
     8//----------- Field declaration
     9        .field public static int32 val
    10//----------- Method declaration
    11        .method public static void check( ) cil managed {
    12            .entrypoint
    13            .locals init (int32 Retval)
    14        AskForNumber:
    15            ldstr "Enter a number"
    16            call void [mscorlib]System.Console::WriteLine(string)
    17            call string [mscorlib]System.Console::ReadLine()
    18            ldstr "%d" // 所有的字符串常量都不需要特殊的声明,il汇编器看到这样的字符串之后会自动将其加入到元数据中
    19            ldsflda int32 Odd.or.Even::val
    20            call vararg int32 sscanf(string,string,,int32*
    21            stloc.0 // java的虚机和.net clr都使用0,1,2,3这样的编号来表示本地变量。这里的"0"就指向唯一的一个本地变量Retval
    22            ldloc.0 // 将第一个本地变量的值压栈
    23            brfalse.s Error  // brfalse.s指令是brfalse的简化指令。当指令被编译成操作码之后,brfalse接受的是一个4字节的参数,
    24        //而brfalse.s仅接受一个单字节的参数,这意味着只能在代码段的{-128字节~~+128字节}的范围内(相对于当前位置而言)跳转。
    25            ldsfld int32 Odd.or.Even::val
    26            ldc.i4.1  // 向栈顶压入一个单字节常量,值为1(ldc.i4 1也是向栈顶压入1,但要用4个字节)
    27            and
    28            brfalse.s ItsEven  
    29            ldstr "odd!"
    30            br.s PrintAndReturn 
    31        ItsEven:
    32            ldstr "even!"
    33            br.s PrintAndReturn  
    34        Error:
    35            ldstr "How rude!"
    36        PrintAndReturn:
    37            call void [mscorlib]System.Console::WriteLine(string)
    38            ldloc.0  
    39            brtrue.s AskForNumber  
    40            ret
    41        }
     // End of method
    42    }
     // End of class
    43}
     // End of namespace
    44//----------- Calling unmanaged code
    45.method public static pinvokeimpl("msvcrt.dll" cdecl) 
    46    vararg int32 sscanf(string,string) cil managed { }
    47

      另外,提起上文中的ldloc.0这样的指令,笔者一开始也是未明其意。其实,在clr执行程序的过程中,涉及到的内存使用主要有三类,除了我们常说的托管堆,每个线程都有自己专属的两个栈:计算栈(Evaluation Stack)和调用栈(Call Stack)。这两个栈都经常被提到,又都专属于clr线程,所以有的时候会混,其实它们的功能是完全不同的。调用栈就是最传统的用来记录方法调用次序的栈,每个线程会得到1MB的调用栈空间,每调用一个新的方法都会压栈,传入参数、临时变量等都会压入栈中,待方法执行完毕就完全释放。ldloc.0中的"0",所指的就是当前调用栈上的第一个临时变量。而计算栈在某种程度上可以视为clr虚拟机的寄存器(这么说只是因为它是和虚机上的计算功能直接交互的存储单位),我们常说的clr虚机是完全基于“栈”的,指的就是计算栈。虚机上的所有操作,都要从计算栈的参数,结果也都会压入到计算栈顶。计算栈的单位既不是字,也不是字节,而是slot(怎么翻译合适呢,咳咳),根据slot内所装数据类型的不同,slot的大小也不同。当clr栈顶的slot里取到数据之后,在运算之前会检验数据的类型,如果无法转换成运算期望的类型,会抛出异常。蔡学庸在msdn上有篇文章,里面的一个贴图看着很明白,就在这里借用一下吧:

     参考资料:MSIL instruction table

  • 相关阅读:
    SEO优化---学会建立高转化率的网站关键词库
    从一个程序员的角度看——微信小应用
    当AngularJS POST方法碰上PHP
    angularJS(6)
    彻底解决显示Opencv中Mat图像到Mfc窗口问题
    数据结构与算法基础总结
    java类别问题
    java基础知识
    逻辑地址、线性地址、物理地址和虚拟地址的区别
    TCP协议中的三次握手和四次挥手(图解)
  • 原文地址:https://www.cnblogs.com/xingyukun/p/813102.html
Copyright © 2011-2022 走看看