“This is the 21st day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

preface

This article focuses on how defer is stored on the stack.

Stack allocation

Stack allocation processes include deferprocStack, deferReturn

DeferprocStack:

func deferprocStack(d *_defer) {
   gp := getg()
   ifgp.m.curg ! = gp { throw("defer on system stack")}ifgoexperiment.RegabiDefer && d.siz ! =0 {
      throw("defer with non-empty frame")
   }
   d.started = false
   d.heap = false
   d.openDefer = false
   d.sp = getcallersp()
   d.pc = getcallerpc()
   d.framepc = 0
   d.varp = 0* (*uintptr)(unsafe.Pointer(&d._panic)) = 0* (*uintptr)(unsafe.Pointer(&d.fd)) = 0* (*uintptr)(unsafe.Pointer(&d.link)) = uintptr(unsafe.Pointer(gp._defer))
   *(*uintptr)(unsafe.Pointer(&gp._defer)) = uintptr(unsafe.Pointer(d))

   return0()
}
Copy the code

The bottom part of the long, pointer operation is really just an assignment to the defer structure and hangs defer on the current coroutine. The benefit of this is that there is no write barrier, but I’ll leave it to the garbage collection algorithm to explain what that is.

Stack memory is a bit faster than heap allocation, which requires creating and copying a defer structure in the heap at this step.

Inline optimization

Can it be further optimized than stack allocation?

The easiest way to do this is to call it directly like a function

For example

func f(a){
    deferproc a()
    deferproc b()
    c()
    
    deferreturn()
}
Copy the code

Can be approximated as

func f(a){
    c()
    b()
    a()
}
Copy the code

We considered deferProc a() to keep track of the required parameters, but there was still a problem with deferReturn when we weren’t sure if the defer would need to be executed. It is possible that defer B () will be wrapped in if. It’s obviously not healthy to put defer in a linked list without being able to determine at compile time whether to execute it or not.

In go, the Go editor takes an ingenious approach to bitmap judgment.

func f(a){
    defer a()
    if isTrue {
        defer b()
    }
    c()
}
Copy the code

into

func f(a){
    deferBits |= 1<<0
    tmpF1 = a
    if isTrue{
        deferBits |= 1<<1
        tmpF2 = b
    }
    c()
    
    / / deferreturn area
    if deferBIts & 1<<1! =0 {
        tmpF2()
    }
     if deferBIts & 1<<0! =0 {
        tmpF1()
    }
}
Copy the code

Bitmap decoupling of the if judgment criteria from whether to defer meets the requirements in most cases with minimal cost.