mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-16 15:09:11 +08:00
220 lines
5.8 KiB
Go
220 lines
5.8 KiB
Go
/*
|
|
* Copyright 2021 ByteDance Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package jit
|
|
|
|
import (
|
|
`fmt`
|
|
`sync`
|
|
_ `unsafe`
|
|
|
|
`github.com/bytedance/sonic/internal/rt`
|
|
`github.com/bytedance/sonic/loader`
|
|
`github.com/twitchyliquid64/golang-asm/asm/arch`
|
|
`github.com/twitchyliquid64/golang-asm/obj`
|
|
`github.com/twitchyliquid64/golang-asm/obj/x86`
|
|
`github.com/twitchyliquid64/golang-asm/objabi`
|
|
)
|
|
|
|
type Backend struct {
|
|
Ctxt *obj.Link
|
|
Arch *arch.Arch
|
|
Head *obj.Prog
|
|
Tail *obj.Prog
|
|
Prog []*obj.Prog
|
|
}
|
|
|
|
var (
|
|
_progPool sync.Pool
|
|
)
|
|
|
|
func newProg() *obj.Prog {
|
|
if val := _progPool.Get(); val == nil {
|
|
return new(obj.Prog)
|
|
} else {
|
|
return remProg(val.(*obj.Prog))
|
|
}
|
|
}
|
|
|
|
func remProg(p *obj.Prog) *obj.Prog {
|
|
*p = obj.Prog{}
|
|
return p
|
|
}
|
|
|
|
func newBackend(name string) (ret *Backend) {
|
|
ret = new(Backend)
|
|
ret.Arch = arch.Set(name)
|
|
ret.Ctxt = newLinkContext(ret.Arch.LinkArch)
|
|
ret.Arch.Init(ret.Ctxt)
|
|
return
|
|
}
|
|
|
|
func newLinkContext(arch *obj.LinkArch) (ret *obj.Link) {
|
|
ret = obj.Linknew(arch)
|
|
ret.Headtype = objabi.Hlinux
|
|
ret.DiagFunc = diagLinkContext
|
|
return
|
|
}
|
|
|
|
func diagLinkContext(str string, args ...interface{}) {
|
|
rt.Throw(fmt.Sprintf(str, args...))
|
|
}
|
|
|
|
func (self *Backend) New() (ret *obj.Prog) {
|
|
ret = newProg()
|
|
ret.Ctxt = self.Ctxt
|
|
self.Prog = append(self.Prog, ret)
|
|
return
|
|
}
|
|
|
|
func (self *Backend) Append(p *obj.Prog) {
|
|
if self.Head == nil {
|
|
self.Head = p
|
|
self.Tail = p
|
|
} else {
|
|
self.Tail.Link = p
|
|
self.Tail = p
|
|
}
|
|
}
|
|
|
|
func (self *Backend) Release() {
|
|
self.Arch = nil
|
|
self.Ctxt = nil
|
|
|
|
/* return all the progs into pool */
|
|
for _, p := range self.Prog {
|
|
_progPool.Put(p)
|
|
}
|
|
|
|
/* clear all the references */
|
|
self.Head = nil
|
|
self.Tail = nil
|
|
self.Prog = nil
|
|
}
|
|
|
|
func (self *Backend) Assemble() ([]byte, loader.Pcdata) {
|
|
var sym obj.LSym
|
|
var fnv obj.FuncInfo
|
|
|
|
/* construct the function */
|
|
sym.Func = &fnv
|
|
fnv.Text = self.Head
|
|
|
|
/* call the assembler */
|
|
self.Arch.Assemble(self.Ctxt, &sym, self.New)
|
|
pcdata := self.GetPcspTable(self.Ctxt, &sym, self.New)
|
|
return sym.P, pcdata
|
|
}
|
|
|
|
func max(a, b int32) int32 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func nextPc(p *obj.Prog) uint32 {
|
|
if p.Link != nil && p.Pc + int64(p.Isize) != p.Link.Pc {
|
|
panic("p.PC + p.Isize != p.Link.PC")
|
|
}
|
|
return uint32(p.Pc + int64(p.Isize))
|
|
}
|
|
|
|
// NOTE: copied from https://github.com/twitchyliquid64/golang-asm/blob/8d7f1f783b11f9a00f5bcdfcae17f5ac8f22512e/obj/x86/obj6.go#L811.
|
|
// we add two instructions such as subq/addq %rsp, $imm to the table.
|
|
func (self *Backend) GetPcspTable(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) loader.Pcdata {
|
|
pcdata := loader.Pcdata{}
|
|
var deltasp int32
|
|
var maxdepth int32
|
|
foundRet := false
|
|
p := cursym.Func.Text
|
|
for ; p != nil; p = p.Link {
|
|
if foundRet {
|
|
break
|
|
}
|
|
switch p.As {
|
|
default: continue
|
|
case x86.APUSHL, x86.APUSHFL:
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp += 4
|
|
maxdepth = max(maxdepth, deltasp)
|
|
continue
|
|
|
|
case x86.APUSHQ, x86.APUSHFQ:
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp += 8
|
|
maxdepth = max(maxdepth, deltasp)
|
|
continue
|
|
|
|
case x86.APUSHW, x86.APUSHFW:
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp += 2
|
|
maxdepth = max(maxdepth, deltasp)
|
|
continue
|
|
|
|
case x86.APOPL, x86.APOPFL:
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp -= 4
|
|
continue
|
|
|
|
case x86.APOPQ, x86.APOPFQ:
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp -= 8
|
|
continue
|
|
|
|
case x86.APOPW, x86.APOPFW:
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp -= 2
|
|
continue
|
|
|
|
case x86.AADJSP:
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp += int32(p.From.Offset)
|
|
maxdepth = max(maxdepth, deltasp)
|
|
continue
|
|
|
|
case x86.ASUBQ:
|
|
// subq %rsp, $imm
|
|
if p.To.Reg == x86.REG_SP && p.To.Type == obj.TYPE_REG {
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp += int32(p.From.Offset)
|
|
maxdepth = max(maxdepth, deltasp)
|
|
}
|
|
continue
|
|
case x86.AADDQ:
|
|
// addq %rsp, $imm
|
|
if p.To.Reg == x86.REG_SP && p.To.Type == obj.TYPE_REG {
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
deltasp -= int32(p.From.Offset)
|
|
}
|
|
continue
|
|
case obj.ARET:
|
|
if deltasp != 0 {
|
|
panic("unbalanced PUSH/POP")
|
|
}
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: nextPc(p), Val: int32(deltasp)})
|
|
foundRet = true
|
|
}
|
|
}
|
|
|
|
// the instructions after the RET instruction
|
|
if p != nil {
|
|
pcdata = append(pcdata, loader.Pcvalue{PC: uint32(cursym.Size), Val: int32(maxdepth)})
|
|
}
|
|
|
|
return pcdata
|
|
}
|