Page 16 - 《软件学报》2024年第6期
P. 16

2592                                                       软件学报  2024  年第  35  卷第  6  期


                 令, 因此指令列表中只包含涉及存储指针的              store 指令和可能引起控制流变化的          cmp  和  br 指令. 其中指令  store
                 addr dst , addr 代表将  addr 存入 addr ds 所指向的内存区域.
                                            t

                                         (World)        W     ::= (GS,GH,GG)
                                         (Goroutine set)  GS  ::= (G 1 ,G 2 ,...,G n )
                                         (Go heap)      GH    ::=  {addr}
                                         (Go global)    GG    ::=  {addr}
                                         (Goroutine)    G     ::= (C,S)                               (1)
                                         (Code)         C     ::=  [instr]
                                         (Goroutine stack)  S  ::= [addr lo ,addr hi )
                                         (Memory address)  addr  ::=  n (unsigned nums)
                                         (Instruction)  instr  ::=  store addr dst , addr
                                                                  | br addr | cmp

                                                        def
                                              code( instr )  = C  instr ∈ C
                                                        def
                                              codeG(C 0 )  = G  G·C == C 0                            (2)
                                                        def
                                               codeS (C)  = S 0  S 0 == codeG(C).S

                                           S = codeS (code(s l ))
                 2.2   违反 Go 逃逸不变式的判定规则
                    为便于描述, 首先引入一些辅助函数, 定义见公式 (2). 其中              code(instr) 用于获取指令  instr 所在的指令序列   C ,
                                           C 的                                   C 的
                 codeG(   C ) 用于获取执行指令序列       Goroutine, codeS(   C ) 用于获取执行指令序列    Goroutine 栈.
                    为了后面讨论违反逃逸不变式的判定规则以及栈对象的生命期, 接下来定义两个概念.
                    定义  1 (不合法的 store 指令). 若一条    store 指令 s l : store addr dst , addr 的内存访问违反了 Go 逃逸不变式, 则将

                 其记为  illegal(s l ) .
                    定义  2 (栈对象所在的栈帧深度). 若       addr 为某栈对象地址, 则     fd(addr) 代表  addr 指向的栈对象所在的栈帧深
                 度. 处于栈顶的函数栈帧深度为 1, 其余函数的栈帧深度沿着栈上的调用链依次加 1. 越靠近栈顶的栈帧, 其栈帧深
                 度越小.
                    根据第 1.1 节所述的两条      Go  逃逸不变式, 可得违反     Go  逃逸不变式的情况为:
                    • 违反逃逸不变式     1: 栈对象指针被堆对象获取.
                    • 违反逃逸不变式     2: 栈对象指针被生命期更长的对象获取.
                    针对违反逃逸不变式 1 的情况, 其表现为堆对象指向了栈对象, 栈对象地址在某处被存入堆对象中. 因此, 针
                 对指令 s l : store addr dst , addr , 可用公式  (3) 来判断是否违反了逃逸不变式  1, 即 addr dst  是堆地址且  addr 是当前执行
                 指令  s l 所在的  Goroutine 栈中的地址时, 指令  s l 会因引起逃逸不变式的违例而视为不合法.

                                           S = codeS (code(s l ))  addr ∈ S∧addr dst ∈ GH
                                                                                                      (3)
                                                         illegal(s l )
                    针对违反逃逸不变式 2 的情况, 比当前栈对象生命期更长的对象可能有多种情况, 包括堆对象、全局对象、
                 其他 Goroutine 的栈对象, 以及当前 Goroutine 的栈对象. 前     3  种情况都认为是当前 Goroutine 栈之外的对象. 下面
                 分别进行讨论.
                    • 将栈对象地址写入当前 Goroutine 栈之外的违例情况. 除了公式               (3) 讨论过的堆对象外, 所有全局对象都可
                 认为比当前栈对象生命期更长. 此时表现为全局对象指向了栈对象, 栈对象地址在某处被存入全局变量中. 因此,
                 针对指令 s l : store addr dst , addr, 可用公式  (4) 来判断是否将栈地址存入全局变量中.

                                                              addr ∈ S∧addr dst ∈ GG
                                                                                                      (4)
                                                         illegal(s l )
                    由于不同    Goroutine 的执行受  Go  运行调度的影响可能相互交叠, 分处在不同            Goroutine 栈中的栈对象生命期
                 可能存在交叠, 也可能存在不确定的一先一后, 因此将当前                 Goroutine 中的一个栈对象的地址存到另一个          Goroutine
                 栈中也是不安全的. 为此, 针对指令 s l : store addr dst , addr, 可用公式  (5) 来判断是否将当前 Goroutine 中的栈地址存
                 入了另一个    Goroutine 栈中.
   11   12   13   14   15   16   17   18   19   20   21