github.com/cloudwego/frugal@v0.1.15/internal/atm/pgen/pgen_gcwb_go121_121_amd64.go (about) 1 //go:build go1.21 && !go1.23 2 // +build go1.21,!go1.23 3 4 /* 5 * Copyright 2022 ByteDance Inc. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 package pgen 21 22 import ( 23 `github.com/cloudwego/iasm/x86_64` 24 25 `github.com/cloudwego/frugal/internal/atm/abi` 26 `github.com/cloudwego/frugal/internal/atm/hir` 27 `github.com/cloudwego/frugal/internal/atm/rtx` 28 ) 29 30 /* 31 * wbStorePointer is the underlying logic for hir.OP_sp (sp: Store Pointer) 32 * It checks rtx.V_pWriteBarrier first. 33 * IF the flag equals to zero, we only need to update the pointer; go GC is not involved at all. 34 * ELSE we jump to the label `_wb_store`, which (following the go1.21 way): 35 * 1. Calls runtime.gcWriteBarrier2 36 * 2. Stores new/old pointers to the allocated slots, with R11 as the beginning address 37 * 3. Updates the pointer 38 * 4. Jumps back to label `_wb_return` 39 * Since it's unlikely to go the `ELSE` path, we postpone the translation, to make generated binary 40 * more compact, which can make most use of the program cache of x86_64 architecture. 41 */ 42 func (self *CodeGen) wbStorePointer(p *x86_64.Program, s hir.PointerRegister, d *x86_64.MemoryOperand) { 43 wb := x86_64.CreateLabel("_wb_store") 44 rt := x86_64.CreateLabel("_wb_return") 45 46 /* check for write barrier */ 47 p.MOVQ(uintptr(rtx.V_pWriteBarrier), RAX) 48 p.CMPB(0, Ptr(RAX, 0)) 49 p.JNE(wb) /* jump to wbStoreFn (write barrier store pointer) */ 50 51 /* replace with the new pointer */ 52 wbUpdatePointer := func() { 53 if s == hir.Pn { 54 p.MOVQ(0, d) 55 } else { 56 p.MOVQ(self.r(s), d) 57 } 58 } 59 60 /* Save new pointer to 0[R11] as required by gcWriteBarrier2 */ 61 wbStoreNewPointerForGC := func(r11 x86_64.Register64) { 62 if s == hir.Pn { /* Pointer to Nil */ 63 p.MOVQ(0, Ptr(r11, 0)) 64 } else { 65 p.MOVQ(self.r(s), Ptr(r11, 0)) 66 } 67 } 68 69 /* Save old pointer to 8[R11] as required by gcWriteBarrier2 */ 70 wbStoreOldPointerForGC := func(r11 x86_64.Register64) { 71 p.MOVQ(d, RDI) 72 p.MOVQ(RDI, Ptr(r11, abi.PtrSize)) 73 } 74 75 /* write barrier wrapper */ 76 wbStoreFn := func(p *x86_64.Program) { 77 self.abiSpillReserved(p) 78 self.abiLoadReserved(p) 79 p.MOVQ(R11, RAX) /* Save R11 -> RAX since R11 will be clobbered by gcWriteBarrier2 */ 80 p.MOVQ(uintptr(rtx.F_gcWriteBarrier2), RSI) 81 p.CALLQ(RSI) /* apply 2 slots and save the beginning address in R11 */ 82 p.XCHGQ(RAX, R11) /* Restore R11 <- RAX, and save R11(slotAddr) -> RAX */ 83 self.abiSaveReserved(p) 84 self.abiRestoreReserved(p) 85 wbStoreNewPointerForGC(RAX) /* MOV r(s), 0[R11] */ 86 wbStoreOldPointerForGC(RAX) /* MOV RDI, 8[R11] */ 87 p.JMP(rt) 88 } 89 90 /* defer the call to the end of generated code */ 91 p.Link(rt) 92 wbUpdatePointer() /* Need to do it in go1.21+ both for direct_store or wb_store */ 93 self.later(wb, wbStoreFn) 94 }