mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-27 14:16:39 +00:00
282 lines
6.6 KiB
Go
282 lines
6.6 KiB
Go
|
/*
|
||
|
* Copyright 2022 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 abi
|
||
|
|
||
|
import (
|
||
|
`fmt`
|
||
|
`reflect`
|
||
|
`unsafe`
|
||
|
|
||
|
. `github.com/chenzhuoyu/iasm/x86_64`
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
PtrSize = 8 // pointer size
|
||
|
PtrAlign = 8 // pointer alignment
|
||
|
)
|
||
|
|
||
|
var iregOrderC = []Register{
|
||
|
RDI,
|
||
|
RSI,
|
||
|
RDX,
|
||
|
RCX,
|
||
|
R8,
|
||
|
R9,
|
||
|
}
|
||
|
|
||
|
var xregOrderC = []Register{
|
||
|
XMM0,
|
||
|
XMM1,
|
||
|
XMM2,
|
||
|
XMM3,
|
||
|
XMM4,
|
||
|
XMM5,
|
||
|
XMM6,
|
||
|
XMM7,
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
intType = reflect.TypeOf(0)
|
||
|
ptrType = reflect.TypeOf(unsafe.Pointer(nil))
|
||
|
)
|
||
|
|
||
|
func (self *Frame) argv(i int) *MemoryOperand {
|
||
|
return Ptr(RSP, int32(self.Prev() + self.desc.Args[i].Mem))
|
||
|
}
|
||
|
|
||
|
// spillv is used for growstack spill registers
|
||
|
func (self *Frame) spillv(i int) *MemoryOperand {
|
||
|
// remain one slot for caller return pc
|
||
|
return Ptr(RSP, PtrSize + int32(self.desc.Args[i].Mem))
|
||
|
}
|
||
|
|
||
|
func (self *Frame) retv(i int) *MemoryOperand {
|
||
|
return Ptr(RSP, int32(self.Prev() + self.desc.Rets[i].Mem))
|
||
|
}
|
||
|
|
||
|
func (self *Frame) resv(i int) *MemoryOperand {
|
||
|
return Ptr(RSP, int32(self.Offs() - uint32((i+1) * PtrSize)))
|
||
|
}
|
||
|
|
||
|
func (self *Frame) emitGrowStack(p *Program, entry *Label) {
|
||
|
// spill all register arguments
|
||
|
for i, v := range self.desc.Args {
|
||
|
if v.InRegister {
|
||
|
if v.IsFloat == floatKind64 {
|
||
|
p.MOVSD(v.Reg, self.spillv(i))
|
||
|
} else if v.IsFloat == floatKind32 {
|
||
|
p.MOVSS(v.Reg, self.spillv(i))
|
||
|
}else {
|
||
|
p.MOVQ(v.Reg, self.spillv(i))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// call runtime.morestack_noctxt
|
||
|
p.MOVQ(F_morestack_noctxt, R12)
|
||
|
p.CALLQ(R12)
|
||
|
// load all register arguments
|
||
|
for i, v := range self.desc.Args {
|
||
|
if v.InRegister {
|
||
|
if v.IsFloat == floatKind64 {
|
||
|
p.MOVSD(self.spillv(i), v.Reg)
|
||
|
} else if v.IsFloat == floatKind32 {
|
||
|
p.MOVSS(self.spillv(i), v.Reg)
|
||
|
}else {
|
||
|
p.MOVQ(self.spillv(i), v.Reg)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// jump back to the function entry
|
||
|
p.JMP(entry)
|
||
|
}
|
||
|
|
||
|
func (self *Frame) GrowStackTextSize() uint32 {
|
||
|
p := DefaultArch.CreateProgram()
|
||
|
// spill all register arguments
|
||
|
for i, v := range self.desc.Args {
|
||
|
if v.InRegister {
|
||
|
if v.IsFloat == floatKind64 {
|
||
|
p.MOVSD(v.Reg, self.spillv(i))
|
||
|
} else if v.IsFloat == floatKind32 {
|
||
|
p.MOVSS(v.Reg, self.spillv(i))
|
||
|
}else {
|
||
|
p.MOVQ(v.Reg, self.spillv(i))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// call runtime.morestack_noctxt
|
||
|
p.MOVQ(F_morestack_noctxt, R12)
|
||
|
p.CALLQ(R12)
|
||
|
// load all register arguments
|
||
|
for i, v := range self.desc.Args {
|
||
|
if v.InRegister {
|
||
|
if v.IsFloat == floatKind64 {
|
||
|
p.MOVSD(self.spillv(i), v.Reg)
|
||
|
} else if v.IsFloat == floatKind32 {
|
||
|
p.MOVSS(self.spillv(i), v.Reg)
|
||
|
} else {
|
||
|
p.MOVQ(self.spillv(i), v.Reg)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// jump back to the function entry
|
||
|
l := CreateLabel("")
|
||
|
p.Link(l)
|
||
|
p.JMP(l)
|
||
|
|
||
|
return uint32(len(p.Assemble(0)))
|
||
|
}
|
||
|
|
||
|
func (self *Frame) emitPrologue(p *Program) {
|
||
|
p.SUBQ(self.Size(), RSP)
|
||
|
p.MOVQ(RBP, Ptr(RSP, int32(self.Offs())))
|
||
|
p.LEAQ(Ptr(RSP, int32(self.Offs())), RBP)
|
||
|
}
|
||
|
|
||
|
func (self *Frame) emitEpilogue(p *Program) {
|
||
|
p.MOVQ(Ptr(RSP, int32(self.Offs())), RBP)
|
||
|
p.ADDQ(self.Size(), RSP)
|
||
|
p.RET()
|
||
|
}
|
||
|
|
||
|
func (self *Frame) emitReserveRegs(p *Program) {
|
||
|
// spill reserved registers
|
||
|
for i, r := range ReservedRegs(self.ccall) {
|
||
|
switch r.(type) {
|
||
|
case Register64:
|
||
|
p.MOVQ(r, self.resv(i))
|
||
|
case XMMRegister:
|
||
|
p.MOVSD(r, self.resv(i))
|
||
|
default:
|
||
|
panic(fmt.Sprintf("unsupported register type %t to reserve", r))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (self *Frame) emitSpillPtrs(p *Program) {
|
||
|
// spill pointer argument registers
|
||
|
for i, r := range self.desc.Args {
|
||
|
if r.InRegister && r.IsPointer {
|
||
|
p.MOVQ(r.Reg, self.argv(i))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (self *Frame) emitClearPtrs(p *Program) {
|
||
|
// spill pointer argument registers
|
||
|
for i, r := range self.desc.Args {
|
||
|
if r.InRegister && r.IsPointer {
|
||
|
p.MOVQ(int64(0), self.argv(i))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (self *Frame) emitCallC(p *Program, addr uintptr) {
|
||
|
p.MOVQ(addr, RAX)
|
||
|
p.CALLQ(RAX)
|
||
|
}
|
||
|
|
||
|
type floatKind uint8
|
||
|
|
||
|
const (
|
||
|
notFloatKind floatKind = iota
|
||
|
floatKind32
|
||
|
floatKind64
|
||
|
)
|
||
|
|
||
|
type Parameter struct {
|
||
|
InRegister bool
|
||
|
IsPointer bool
|
||
|
IsFloat floatKind
|
||
|
Reg Register
|
||
|
Mem uint32
|
||
|
Type reflect.Type
|
||
|
}
|
||
|
|
||
|
func mkIReg(vt reflect.Type, reg Register64) (p Parameter) {
|
||
|
p.Reg = reg
|
||
|
p.Type = vt
|
||
|
p.InRegister = true
|
||
|
p.IsPointer = isPointer(vt)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func isFloat(vt reflect.Type) floatKind {
|
||
|
switch vt.Kind() {
|
||
|
case reflect.Float32:
|
||
|
return floatKind32
|
||
|
case reflect.Float64:
|
||
|
return floatKind64
|
||
|
default:
|
||
|
return notFloatKind
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func mkXReg(vt reflect.Type, reg XMMRegister) (p Parameter) {
|
||
|
p.Reg = reg
|
||
|
p.Type = vt
|
||
|
p.InRegister = true
|
||
|
p.IsFloat = isFloat(vt)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func mkStack(vt reflect.Type, mem uint32) (p Parameter) {
|
||
|
p.Mem = mem
|
||
|
p.Type = vt
|
||
|
p.InRegister = false
|
||
|
p.IsPointer = isPointer(vt)
|
||
|
p.IsFloat = isFloat(vt)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (self Parameter) String() string {
|
||
|
if self.InRegister {
|
||
|
return fmt.Sprintf("[%%%s, Pointer(%v), Float(%v)]", self.Reg, self.IsPointer, self.IsFloat)
|
||
|
} else {
|
||
|
return fmt.Sprintf("[%d(FP), Pointer(%v), Float(%v)]", self.Mem, self.IsPointer, self.IsFloat)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func CallC(addr uintptr, fr Frame, maxStack uintptr) []byte {
|
||
|
p := DefaultArch.CreateProgram()
|
||
|
|
||
|
stack := CreateLabel("_stack_grow")
|
||
|
entry := CreateLabel("_entry")
|
||
|
p.Link(entry)
|
||
|
fr.emitStackCheck(p, stack, maxStack)
|
||
|
fr.emitPrologue(p)
|
||
|
fr.emitReserveRegs(p)
|
||
|
fr.emitSpillPtrs(p)
|
||
|
fr.emitExchangeArgs(p)
|
||
|
fr.emitCallC(p, addr)
|
||
|
fr.emitExchangeRets(p)
|
||
|
fr.emitRestoreRegs(p)
|
||
|
fr.emitEpilogue(p)
|
||
|
p.Link(stack)
|
||
|
fr.emitGrowStack(p, entry)
|
||
|
|
||
|
return p.Assemble(0)
|
||
|
}
|
||
|
|
||
|
|
||
|
func (self *Frame) emitDebug(p *Program) {
|
||
|
p.INT(3)
|
||
|
}
|