go-compute/assembler/func.go

107 lines
2.5 KiB
Go

//go:build !purego
package assembler
import (
"errors"
"fmt"
"git.gammaspectra.live/WeebDataHoarder/compute-go/assembler/obj"
memory "git.gammaspectra.live/WeebDataHoarder/compute-go/malloc"
"git.gammaspectra.live/WeebDataHoarder/compute-go/types"
"reflect"
"unsafe"
)
type CreateFunctionArgNone struct{}
type CreateFunctionCallback func(b *Builder, params []AddressableList, returns []AddressableList, regs *RegisterAllocator, data *DataAllocator) (frameSize, textFlag int, err error)
type CreateFunctionData struct {
Code []byte
Listing []ListingEntry
}
type AddressableList []types.Addressable
func (a AddressableList) One() types.Addressable {
if len(a) != 1 {
panic("invalid")
}
return a[0]
}
func CreateFunction[FuncType any](b *Builder, abi obj.ABI, name string, callback CreateFunctionCallback, data *CreateFunctionData) (f FuncType, free func() error, err error) {
var zeroVal FuncType
fType := reflect.TypeFor[FuncType]()
if fType.Kind() != reflect.Func {
return zeroVal, nil, errors.New("not a function")
}
info := b.FuncArgs(fType, abi)
var params, returns []AddressableList
for i := range len(info.InParams()) {
params = append(params, b.funcParam(info, info.InParam(i), fmt.Sprintf("param%d", i)))
}
for i := range len(info.OutParams()) {
returns = append(returns, b.funcParam(info, info.OutParam(i), fmt.Sprintf("return%d", i)))
}
regAllocator := NewRegisterAllocator(b.abiDef)
//reserve parameter registers
for _, regs := range params {
for _, reg := range regs {
addr := reg.Addr()
if addr.Type == obj.TYPE_REG {
regAllocator.Reserve(types.Register(addr.Reg))
}
}
}
dataAllocator := NewDataAllocator(b.abiDef)
frameSize, textFlag, err := callback(b, params, returns, regAllocator, dataAllocator)
if err != nil {
dataAllocator.FreeAll()
return zeroVal, nil, err
}
var listing *[]ListingEntry
if data != nil {
listing = &data.Listing
}
code, errs := b.Function(name, info, frameSize, textFlag, listing)
if errs != nil {
dataAllocator.FreeAll()
return zeroVal, nil, errs[0]
}
if data != nil {
data.Code = code
}
mem, err := b.pageAllocator.AllocMemory(uint64(len(code)))
if err != nil {
return zeroVal, nil, err
}
copy(mem, code)
err = memory.PageReadExecute(mem)
if err != nil {
dataAllocator.FreeAll()
b.pageAllocator.FreeMemory(mem)
return zeroVal, nil, err
}
var fVal FuncType
fMem := &mem
fVal = *(*FuncType)(unsafe.Pointer(&fMem))
return fVal, func() error {
dataAllocator.FreeAll()
return b.pageAllocator.FreeMemory(mem)
}, nil
}