| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-FileCopyrightText: 2025 The Ebitengine Authors
- package purego
- import (
- "math"
- "reflect"
- "unsafe"
- )
- func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
- outSize := outType.Size()
- switch {
- case outSize == 0:
- return reflect.New(outType).Elem()
- case outSize <= 8:
- r1 := syscall.a1
- if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
- r1 = syscall.f1
- if numFields == 2 {
- r1 = syscall.f2<<32 | syscall.f1
- }
- }
- return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
- case outSize <= 16:
- r1, r2 := syscall.a1, syscall.a2
- if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
- switch numFields {
- case 4:
- r1 = syscall.f2<<32 | syscall.f1
- r2 = syscall.f4<<32 | syscall.f3
- case 3:
- r1 = syscall.f2<<32 | syscall.f1
- r2 = syscall.f3
- case 2:
- r1 = syscall.f1
- r2 = syscall.f2
- default:
- panic("unreachable")
- }
- }
- return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
- default:
- // create struct from the Go pointer created above
- // weird pointer dereference to circumvent go vet
- return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
- }
- }
- const (
- _NO_CLASS = 0b00
- _FLOAT = 0b01
- _INT = 0b11
- )
- func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
- if v.Type().Size() == 0 {
- return keepAlive
- }
- if size := v.Type().Size(); size <= 16 {
- placeRegisters(v, addFloat, addInt)
- } else {
- keepAlive = placeStack(v, keepAlive, addInt)
- }
- return keepAlive // the struct was allocated so don't panic
- }
- func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
- var val uint64
- var shift byte
- var flushed bool
- class := _NO_CLASS
- var place func(v reflect.Value)
- place = func(v reflect.Value) {
- var numFields int
- if v.Kind() == reflect.Struct {
- numFields = v.Type().NumField()
- } else {
- numFields = v.Type().Len()
- }
- for k := 0; k < numFields; k++ {
- flushed = false
- var f reflect.Value
- if v.Kind() == reflect.Struct {
- f = v.Field(k)
- } else {
- f = v.Index(k)
- }
- align := byte(f.Type().Align()*8 - 1)
- shift = (shift + align) &^ align
- if shift >= 64 {
- shift = 0
- flushed = true
- if class == _FLOAT {
- addFloat(uintptr(val))
- } else {
- addInt(uintptr(val))
- }
- }
- switch f.Type().Kind() {
- case reflect.Struct:
- place(f)
- case reflect.Bool:
- if f.Bool() {
- val |= 1 << shift
- }
- shift += 8
- class |= _INT
- case reflect.Uint8:
- val |= f.Uint() << shift
- shift += 8
- class |= _INT
- case reflect.Uint16:
- val |= f.Uint() << shift
- shift += 16
- class |= _INT
- case reflect.Uint32:
- val |= f.Uint() << shift
- shift += 32
- class |= _INT
- case reflect.Uint64, reflect.Uint, reflect.Uintptr:
- addInt(uintptr(f.Uint()))
- shift = 0
- flushed = true
- class = _NO_CLASS
- case reflect.Int8:
- val |= uint64(f.Int()&0xFF) << shift
- shift += 8
- class |= _INT
- case reflect.Int16:
- val |= uint64(f.Int()&0xFFFF) << shift
- shift += 16
- class |= _INT
- case reflect.Int32:
- val |= uint64(f.Int()&0xFFFF_FFFF) << shift
- shift += 32
- class |= _INT
- case reflect.Int64, reflect.Int:
- addInt(uintptr(f.Int()))
- shift = 0
- flushed = true
- class = _NO_CLASS
- case reflect.Float32:
- if class == _FLOAT {
- addFloat(uintptr(val))
- val = 0
- shift = 0
- }
- val |= uint64(math.Float32bits(float32(f.Float()))) << shift
- shift += 32
- class |= _FLOAT
- case reflect.Float64:
- addFloat(uintptr(math.Float64bits(float64(f.Float()))))
- shift = 0
- flushed = true
- class = _NO_CLASS
- case reflect.Ptr, reflect.UnsafePointer:
- addInt(f.Pointer())
- shift = 0
- flushed = true
- class = _NO_CLASS
- case reflect.Array:
- place(f)
- default:
- panic("purego: unsupported kind " + f.Kind().String())
- }
- }
- }
- place(v)
- if !flushed {
- if class == _FLOAT {
- addFloat(uintptr(val))
- } else {
- addInt(uintptr(val))
- }
- }
- }
- func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
- // Struct is too big to be placed in registers.
- // Copy to heap and place the pointer in register
- ptrStruct := reflect.New(v.Type())
- ptrStruct.Elem().Set(v)
- ptr := ptrStruct.Elem().Addr().UnsafePointer()
- keepAlive = append(keepAlive, ptr)
- addInt(uintptr(ptr))
- return keepAlive
- }
- // shouldBundleStackArgs always returns false on loong64
- // since C-style stack argument bundling is only needed on Darwin ARM64.
- func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
- return false
- }
- // structFitsInRegisters is not used on loong64.
- func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
- panic("purego: structFitsInRegisters should not be called on loong64")
- }
- // collectStackArgs is not used on loong64.
- func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
- keepAlive []any, addInt, addFloat, addStack func(uintptr),
- pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
- panic("purego: collectStackArgs should not be called on loong64")
- }
- // bundleStackArgs is not used on loong64.
- func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
- panic("purego: bundleStackArgs should not be called on loong64")
- }
|