// 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 += 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: 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 }