.Net8顶级技术:边界检查之IR解析(二)

Posted 六朝一洗繁华尽 四始重删雅颂分

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net8顶级技术:边界检查之IR解析(二)相关的知识,希望对你有一定的参考价值。

前言

IR技术应用在各个编程语言当中,它属于JIT的核心部分,确实有点点麻烦。但部分基本明了。本篇通过.Net8里面的边界检查的小例子了解下。前情提要,看这一篇之前建议看看前一篇:点击此处,以便于理解。


概括

1.前奏
先上C#代码:

[MethodImpl(MethodImplOptions.NoInlining)]
private static bool Test(int[] array)

   for (int i = 0; i < 0x12345; i++)
   
      if (array[i] == 42)
      
         return true;
      
   
  return false;

Test函数经过Roslyn编译成IL代码之后,会被JIT导入及操作变成IR。

BBnum BBid ref try hnd preds           weight    lp [IL range]     [jump]      [EH region]         [flags]
-----------------------------------------------------------------------------------------------------------------------------------------
BB01 [0007]  1                             1       [???..???)-> BB04 ( cond )                     internal 
BB02 [0001]  2       BB01,BB03             4     0 [004..00B)-> BB05 ( cond )                     i Loop idxlen bwd bwd-target align 
BB03 [0003]  1       BB02                  4     0 [00D..019)-> BB02 ( cond )                     i bwd 
BB04 [0005]  2       BB01,BB03             0.50    [019..01B)        (return)                     i 
BB05 [0002]  1       BB02                  0.50    [00B..00D)        (return)                     i 
-----------------------------------------------------------------------------------------------------------------------------------------

可以看到IL被分割成了五个BB(basic block).注意表格的BBnum和jump列。BB01的BBnum就是BB01,它的jump是BB04。为啥是BB04?因为BB01的IR表示的是如果(i>=0x12345),则跳转到BB04,也就是BB01的正常逻辑。下面看下这个五个BB.

------------ BB01 [???..???) -> BB04 (cond), preds= succs=BB02,BB04
***** BB01
STMT00006 ( 0x011[E-] ... ??? )
     (  7,  9) [000038] -----------                         *  JTRUE     void  
     (  5,  7) [000039] J------N---                         \\--*  GE        int   
     (  3,  2) [000040] -----------                            +--*  LCL_VAR   int    V01 loc0         
     (  1,  4) [000041] -----------                            \\--*  CNS_INT   int    0x12345

------------ BB02 [004..00B) -> BB05 (cond), preds=BB01,BB03 succs=BB03,BB05
***** BB02
STMT00002 ( 0x004[E-] ... 0x009 )
               [000013] ---XG+-----                         *  JTRUE     void  
               [000012] N--XG+-N-U-                         \\--*  EQ        int   
               [000034] ---XG+-----                            +--*  COMMA     int   
               [000026] ---X-+-----                            |  +--*  BOUNDS_CHECK_Rng void  
               [000008] -----+-----                            |  |  +--*  LCL_VAR   int    V01 loc0         
               [000025] ---X-+-----                            |  |  \\--*  ARR_LENGTH int   
               [000007] -----+-----                            |  |     \\--*  LCL_VAR   ref    V00 arg0         
               [000035] n---G+-----                            |  \\--*  IND       int   
               [000033] -----+-----                            |     \\--*  ARR_ADDR  byref int[]
               [000032] -----+-----                            |        \\--*  ADD       byref 
               [000023] -----+-----                            |           +--*  LCL_VAR   ref    V00 arg0         
               [000031] -----+-----                            |           \\--*  ADD       long  
               [000029] -----+-----                            |              +--*  LSH       long  
               [000027] -----+---U-                            |              |  +--*  CAST      long <- uint
               [000024] -----+-----                            |              |  |  \\--*  LCL_VAR   int    V01 loc0         
               [000028] -----+-N---                            |              |  \\--*  CNS_INT   long   2
               [000030] -----+-----                            |              \\--*  CNS_INT   long   16
               [000011] -----+-----                            \\--*  CNS_INT   int    42

------------ BB03 [00D..019) -> BB02 (cond), preds=BB02 succs=BB04,BB02
***** BB03
STMT00003 ( 0x00D[E-] ... 0x010 )
               [000018] -A---+-----                         *  ASG       int   
               [000017] D----+-N---                         +--*  LCL_VAR   int    V01 loc0         
               [000016] -----+-----                         \\--*  ADD       int   
               [000014] -----+-----                            +--*  LCL_VAR   int    V01 loc0         
               [000015] -----+-----                            \\--*  CNS_INT   int    1

***** BB03
STMT00001 ( 0x011[E-] ... 0x017 )
     (  7,  9) [000006] -----------                         *  JTRUE     void  
     (  5,  7) [000005] J------N---                         \\--*  LT        int   
     (  3,  2) [000003] -----------                            +--*  LCL_VAR   int    V01 loc0         
     (  1,  4) [000004] -----------                            \\--*  CNS_INT   int    0x12345

------------ BB04 [019..01B) (return), preds=BB01,BB03 succs=
***** BB04
STMT00005 ( 0x019[E-] ... 0x01A )
               [000022] -----+-----                         *  RETURN    int   
               [000037] -----+-----                         \\--*  CNS_INT   int    0

------------ BB05 [00B..00D) (return), preds=BB02 succs=
***** BB05
STMT00004 ( 0x00B[E-] ... 0x00C )
               [000020] -----+-----                         *  RETURN    int   
               [000036] -----+-----                         \\--*  CNS_INT   int    1

preds表示能在逻辑上执行到当前块的所有快,succs表示当前语句逻辑能达到的BB块。举个例子:比如BB01,首先看下这条IR表示的如果(i>=0x12345),则跳转到BB04,也就是直接返回0。因为逻辑是索引大于了循环的最大次数,是不合理的。如果(i<0x12345),则跳转到BB02,也就是判断(array[i]是否等于42)。上面BB01的predes为空,则表示没有逻辑能达到这条语句。它的succs为BB02和BB04,跟上面的推测吻合。其它依次类推。

2.BB的IR表示
通过上面的BB01到BB05的观察,得知它们分别表示如下:
一:BB01

if(i>=0x12345)

二:BB02

if(array[i]==42)

三:BB03

i=i+1;
if(i<0x12345)

四:BB04

return 0

五:BB05

return 1

以上循环被分割成了五个BB。它的实际逻辑如下:

if(i>=0x12345)

  return flase;

else

  for(i<0x12345;i++)
  
    if(array[i]==42)
    
      return true;
    
  
  return flase;

所以呢,实际是示例的for循环,被分解成了上面的代码。但是还没完,为了确保这个array[i]不会出现内存访问的错误,BB02里面有个BOUNDS_CHECK_Rng的边界检查技术,它会判断array[i]里的i索引是否查过array.length的长度,因为在for循环里面,所以每次都会判断,会增加相应的开销。为了达到最优的效果,.Net8会去掉这开销。那么应该怎么做呢?继续看。
JIT先增加BB06,BB07,BB08,BB09四个块,然后把BOUNDS_CHECK_Rng给去掉。
去掉前后对比如下。
去掉前:

          [000013] ---XG+-----                         *  JTRUE     void  
               [000012] N--XG+-N-U-                         \\--*  EQ        int   
               [000034] ---XG+-----                            +--*  COMMA     int   
               [000026] ---X-+-----                            |  +--*  BOUNDS_CHECK_Rng void  
               [000008] -----+-----                            |  |  +--*  LCL_VAR   int    V01 loc0         
               [000025] ---X-+-----                            |  |  \\--*  ARR_LENGTH int   
               [000007] -----+-----                            |  |     \\--*  LCL_VAR   ref    V00 arg0         
               [000035] n---G+-----                            |  \\--*  IND       int   
               [000033] -----+-----                            |     \\--*  ARR_ADDR  byref int[]
               [000032] -----+-----                            |        \\--*  ADD       byref 
               [000023] -----+-----                            |           +--*  LCL_VAR   ref    V00 arg0         
               [000031] -----+-----                            |           \\--*  ADD       long  
               [000029] -----+-----                            |              +--*  LSH       long  
               [000027] -----+---U-                            |              |  +--*  CAST      long <- uint
               [000024] -----+-----                            |              |  |  \\--*  LCL_VAR   int    V01 loc0         
               [000028] -----+-N---                            |              |  \\--*  CNS_INT   long   2
               [000030] -----+-----                            |              \\--*  CNS_INT   long   16
               [000011] -----+-----                            \\--*  CNS_INT   int    42

去掉后:

  [000013] ----G+-----                         *  JTRUE     void  
               [000012] N---G+-N-U-                         \\--*  EQ        int   
               [000034] ----G+-N---                            +--*  COMMA     int   
               [000026] -----+-----                            |  +--*  NOP       void  
               [000035] n---G+-----                            |  \\--*  IND       int   
               [000033] -----+-----                            |     \\--*  ARR_ADDR  byref int[]
               [000032] -----+-----                            |        \\--*  ADD       byref 
               [000023] -----+-----                            |           +--*  LCL_VAR   ref    V00 arg0         
               [000031] -----+-----                            |           \\--*  ADD       long  
               [000029] -----+-----                            |              +--*  LSH       long  
               [000027] -----+---U-                            |              |  +--*  CAST      long <- uint
               [000024] -----+-----                            |              |  |  \\--*  LCL_VAR   int    V01 loc0         
               [000028] -----+-N---                            |              |  \\--*  CNS_INT   long   2
               [000030] -----+-----                            |              \\--*  CNS_INT   long   16
               [000011] -----+-----                            \\--*  CNS_INT   int    42

然后再新增BB10,BB11,BB12,BB13四个BB块。这些BB块如下所示:

------------ BB01 [???..???) -> BB12 (cond), preds= succs=BB02,BB12

***** BB01
STMT00006 ( 0x011[E-] ... ??? )
     (  7,  9) [000038] -----------                         *  JTRUE     void  
     (  5,  7) [000039] J------N---                         \\--*  GE        int   
     (  3,  2) [000040] -----------                            +--*  LCL_VAR   int    V01 loc0         
     (  1,  4) [000041] -----------                            \\--*  CNS_INT   int    0x12345

------------ BB02 [???..???), preds=BB01 succs=BB03

------------ BB03 [???..???) -> BB09 (cond), preds=BB02 succs=BB04,BB09

***** BB03
STMT00010 ( ??? ... ??? )
     (  7,  6) [000072] -----------                         *  JTRUE     void  
     (  5,  4) [000071] J------N---                         \\--*  EQ        int   
     (  3,  2) [000069] -----------                            +--*  LCL_VAR   ref    V00 arg0         
     (  1,  1) [000070] -----------                            \\--*  CNS_INT   ref    null

------------ BB04 [???..???) -> BB09 (cond), preds=BB03 succs=BB05,BB09

***** BB04
STMT00011 ( ??? ... ??? )
     (  7,  6) [000076] -----------                         *  JTRUE     void  
     (  5,  4) [000075] J------N---                         \\--*  LT        int   
     (  3,  2) [000073] -----------                            +--*  LCL_VAR   int    V01 loc0         
     (  1,  1) [000074] -----------                            \\--*  CNS_INT   int    0

------------ BB05 [???..???) -> BB09 (cond), preds=BB04 succs=BB06,BB09

***** BB05
STMT00012 ( ??? ... ??? )
     (  9, 11) [000081] ---X-------                         *  JTRUE     void  
     (  7,  9) [000080] J--X---N---                         \\--*  LT        int   
     (  5,  4) [000079] ---X-------                            +--*  ARR_LENGTH int   
     (  3,  2) [000078] -----------                            |  \\--*  LCL_VAR   ref    V00 arg0         
     (  1,  4) [000077] -----------                            \\--*  CNS_INT   int    0x12345

------------ BB06 [004..00B) -> BB13 (cond), preds=BB05,BB07 succs=BB07,BB13

***** BB06
STMT00002 ( 0x004[E-] ... 0x009 )
               [000013] ----G+-----                         *  JTRUE     void  
               [000012] N---G+-N-U-                         \\--*  EQ        int   
               [000034] ----G+-N---                            +--*  COMMA     int   
               [000026] -----+-----                            |  +--*  NOP       void  
               [000035] n---G+-----                            |  \\--*  IND       int   
               [000033] -----+-----                            |     \\--*  ARR_ADDR  byref int[]
               [000032] -----+-----                            |        \\--*  ADD       byref 
               [000023] -----+-----                            |           +--*  LCL_VAR   ref    V00 arg0         
               [000031] -----+-----                            |           \\--*  ADD       long  
               [000029] -----+-----                            |              +--*  LSH       long  
               [000027] -----+---U-                            |              |  +--*  CAST      long <- uint
               [000024] -----+-----                            |              |  |  \\--*  LCL_VAR   int    V01 loc0         
               [000028] -----+-N---                            |              |  \\--*  CNS_INT   long   2
               [000030] -----+-----                            |              \\--*  CNS_INT   long   16
               [000011] -----+-----                            \\--*  CNS_INT   int    42

------------ BB07 [00D..019) -> BB06 (cond), preds=BB06 succs=BB08,BB06

***** BB07
STMT00003 ( 0x00D[E-] ... 0x010 )
               [000018] -A---+-----                         *  ASG       int   
               [000017] D----+-N---                         +--*  LCL_VAR   int    V01 loc0         
               [000016] -----+-----                         \\--*  ADD       int   
               [000014] -----+-----                            +--*  LCL_VAR   int    V01 loc0         
               [000015] -----+-----                            \\--*  CNS_INT   int    1

***** BB07
STMT00001 ( 0x011[E-] ... 0x017 )
     (  7,  9) [000006] -----------                         *  JTRUE     void  
     (  5,  7) [000005] J------N---                         \\--*  LT        int   
     (  3,  2) [000003] -----------                            +--*  LCL_VAR   int    V01 loc0         
     (  1,  4) [000004] -----------                            \\--*  CNS_INT   int    0x12345

------------ BB08 [???..???) -> BB12 (always), preds=BB07 succs=BB12

------------ BB09 [???..???), preds=BB03,BB04,BB05 succs=BB10

------------ BB10 [004..00B) -> BB13 (cond), preds=BB09,BB11 succs=BB11,BB13

***** BB10
STMT00007 ( 0x004[E-] ... ??? )
               [000042] ---XGO-----                         *  JTRUE     void  
               [000043] N--XGO-N-U-                         \\--*  EQ        int   
               [000044] ---XGO-----                            +--*  COMMA     int   
               [000045] ---X-O-----                            |  +--*  BOUNDS_CHECK_Rng void  
               [000046] -----------                            |  |  +--*  LCL_VAR   int    V01 loc0         
               [000047] ---X-------                            |  |  \\--*  ARR_LENGTH int   
               [000048] -----------                            |  |     \\--*  LCL_VAR   ref    V00 arg0         
               [000049] n---GO-----                            |  \\--*  IND       int   
               [000050] -----O-----                            |     \\--*  ARR_ADDR  byref int[]
               [000051] -----------                            |        \\--*  ADD       byref 
               [000052] -----------                            |           +--*  LCL_VAR   ref    V00 arg0         
               [000053] -----------                            |           \\--*  ADD       long  
               [000054] -----------                            |              +--*  LSH       long  
               [000055] ---------U-                            |              |  +--*  CAST      long <- uint
               [000056] -----------                            |              |  |  \\--*  LCL_VAR   int    V01 loc0         
               [000057] -------N---                            |              |  \\--*  CNS_INT   long   2
               [000058] -----------                            |              \\--*  CNS_INT   long   16
               [000059] -----------                            \\--*  CNS_INT   int    42

------------ BB11 [00D..019) -> BB10 (cond), preds=BB10 succs=BB12,BB10

***** BB11
STMT00008 ( 0x00D[E-] ... ??? )
               [000060] -A---------                         *  ASG       int   
               [000061] D------N---                         +--*  LCL_VAR   int    V01 loc0         
               [000062] -----------                         \\--*  ADD       int   
               [000063] -----------                            +--*  LCL_VAR   int    V01 loc0         
               [000064] -----------                            \\--*  CNS_INT   int    1

***** BB11
STMT00009 ( 0x011[E-] ... ??? )
     (  7,  9) [000065] -----------                         *  JTRUE     void  
     (  5,  7) [000066] J------N---                         \\--*  LT        int   
     (  3,  2) [000067] -----------                            +--*  LCL_VAR   int    V01 loc0         
     (  1,  4) [000068] -----------                            \\--*  CNS_INT   int    0x12345

------------ BB12 [019..01B) (return), preds=BB01,BB08,BB11 succs=

***** BB12
STMT00005 ( 0x019[E-] ... 0x01A )
               [000022] -----+-----                         *  RETURN    int   
               [000037] -----+-----                         \\--*  CNS_INT   int    0

------------ BB13 [00B..00D) (return), preds=BB06,BB10 succs=

***** BB13
STMT00004 ( 0x00B[E-] ... 0x00C )
               [000020] -----+-----                         *  RETURN    int   
               [000036] -----+-----                         \\--*  CNS_INT   int    1

-------------------------------------------------------------------------------------------------------------------

3.BB块分析
通过去掉的边界检查,进行的优化之后。新增了7个BB块,总共有13个BB块。那么这些BB干嘛的呢?实际上就是为了去掉边界检查(因为在for循环里,每次都要判断),而确保内存array[i]在正确内存范围内。逐一来看下:
BB01:

if(i>=0x12345)判断索引是否大于循环最大值

BB02

BB03

if(array==null) //这里是判断数组的地址是否等于0

BB04

if(i<0)判断索引是否小于0

BB05

if(array.length<0x12345)判断数组长度是否小于循环最大数0x12345

BB06

if(array[i]==42)

BB07

i=i+1索引自增

BB08

BB09

BB10

if(i<array.length) //这里跟上面的BB06一样,但是多了边界检查。BB06去掉,这里没去掉。是因为这里需要边界检查,而BB06不需要。一个快速路径,一个慢速路径。
if(array[i]==42)

BB11

i=i+1

BB12

return 0

BB02

return 1

它实际逻辑是:

if(i<0x12345 && array!= null && i>0 && array.Length >= 0x12345 )//再去掉边界检查之后的优化里,这进行大量的检查,确保array[i],在正确内存范围内。

    for (int i = 0; i < 0x12345; i++)
    
        if (array[i] == 42) 不检查边界,因为上面的if检查过了
        
            return true;
        
    

else  //如果上面的if有一个条件不符合,则进行边界检查。优化不成功

   for (int i = 0; i < 0x12345; i++)
    
        if (array[i] == 42) 这里需要边界检查也就是BOUNDS_CHECK_Rng
        
            return true;
        
    


结尾

作者:江湖评谈
欢迎关注公众号,第一时间首发分享技术文章。

计算机网络之应用层

前言

DNS:
    解析域名成ip地址

域名:
    全球唯一
    顶级域名:com,edu,net,cn,org,gov
    二级域名:需要申请
    三级域名 www mail ftp,跟不同服务相关的。

域名解析:
    分布式查询技术

DNS服务器:
    节省域名解析的流量

DHCP:
    动态主机配置

FTP:
    文件传输协议

HTTP:
    超文本传输协议,用来访问网站的协议,使用TCP的80端口。

web代理服务器:
    节省内网访问Internet带宽
    通过web代理绕过防火墙

发送数据与接收数据

技术图片

技术图片

将一个网段划分为多个网段

技术图片

技术图片

合并网段

技术图片

DHCP服务

动态主机配置协议 DHCP:
    负责给互联网上的计算机提供动态的IP地址。 

连接到因特网的计算机都需要配置以下几个项目: 
    (1) IP 地址 
    (2) 子网掩码 
    (3) 默认路由器的 IP 地址即网关 
    (4) 域名服务器的 IP 地址


IP地址获取方式有两种:
    一种静态IP,一种动态IP。 

    静态IP是人工自己指定的,一般公司自己组建的局域网、学校机房的固定的计算机、机房服务器、互联网上的大型服务器,
    凡是位置固定不动的,都用静态IP地址。 

    动态IP地址是用DHCP服务器来分配的地址,适用于计算机位置不固定、家庭拨号上网等情况。可以避免产生IP地址冲突。


DHCP客户端请求IP地址的过程(逆arp协议):
    需要地址的客户机先在网上发广播包请求地址,DHCP服务器收到广播包后在自己的地址池里选一个地址(包括配套的子网掩码和网关),
    租给该客户机,该客户机再给DHCP服务器一个确认。 
    注意:DHCP服务器本身必须是静态地址!!!


DHCP服务器既可以给本网段计算机分配地址,也可以跨网段分配地址。
    DHCP服务器想给几个网段的计算机分配地址,就得先创建几个作用域。

    区别是,本网段的计算机请求地址,发广播,DHCP服务器能直接收到请求;
    而外网段的计算机请求地址,先发广播让外网段的网关收到,
    该网关把请求通过互联网发送给DHCP服务器(这时候就是点对点通信而不是广播了)。

FTP文件传送服务

FTP连接方式:
    控制连接:标准端口为21,用于发送FTP命令信息。 
    数据连接:标准端口为20,用于上传、下载数据。 

客户端选择数据连接的建立类型:
    主动模式:FTP客户端告诉FTP服务器使用什么端口,FTP服务器就主动用自己的20端口和FTP客户端的这个端口建立连接。 
    被动模式:服务端在指定范围内打开一个新的端口,被动等待客户端发起连接。 

FTP传输模式 
    文本模式:ASCII模式,以文本序列传输数据 
    二进制模式:Binary模式,以二进制序列传输数据 


主动模式防火墙打开21端口,用于客户端和服务器建立连接;打开20端口用于互相传输数据。

被动模式,FTP服务器打的是一个新端口,指不定是哪个端口呢,如1234端口,
如果想成功地进行数据通信,防火墙必须打开1234端口;
如果还有其他的客户端请求连接服务器,服务器又打开其他新的端口,那么防火墙又得打开另一个新的端口了,十分不方便。
因此,FTP服务一般都使用主动模式。

FTP使用两个TCP链接

技术图片

远程终端协议TELNET

只要网络通,就能利用telnet输入命令来远程控制另一台计算机。一般多用来远程配置路由器。使用TCP的23端口。

远程桌面协议RDP

它和远程终端协议的功能一样,都是为了远程管理另一台计算机,
只不过上面那个telnet通过命令行管理,而这个通过图形界面管理。
一般用来远程管理服务器。使用TCP的3389端口。

电子邮件服务

发送邮件的协议:SMTP使用TCP的25端口
读取邮件的协议:POP3 使用TCP的110端口,IMAP 使用TCP的143端口


收发邮件的过程如下: 

    a.发件人调用PC机中的用户代理撰写和编辑要发送的邮件。

    b.发件人的用户代理把邮件用SMTP协议发给发送方邮件服务器。 

    c.SMTP服务器把邮件临时存放在邮件缓存队列中,等待发送。 

    d.发送方邮件服务器的SMTP客户与接收方邮件服务器的SMTP服务器建立TCP连接,然后就把邮件缓存队列中的邮件依次发送出去。

    e.运行在接收方邮件服务器中的SMTP服务器进程收到邮件后,把邮件放入收件人的用户邮箱中,等待收件人进行读取。 

    f.收件人在打算收信时,就运行PC机中的用户代理,使用POP3(或IMAP)协议读取发送给自己的邮件。

以上是关于.Net8顶级技术:边界检查之IR解析(二)的主要内容,如果未能解决你的问题,请参考以下文章

借用可以委派的解析器的检查器问题

20.三层技术之OSPF区域划分与路由重分发

LLVM 之 IR 篇:零基础快速入门 LLVM IR

LLVM 之 IR 篇:如何使用 LLVM IR 优化器

测试用例设计之边界值测试痛点解析(下)

LLVM 之 IR 篇:如何编写生成 LLVM IR 的工具