⋯ 9 hidden lines@@ -10,11 +10,16 @@ import ( 10 "math" 10 "math" 11 "reflect" 11 "reflect" 12 "runtime" 12 "runtime" 13 + "sync" 13 "unsafe" 14 "unsafe" 14 15 15 "github.com/ebitengine/purego/internal/strings" 16 "github.com/ebitengine/purego/internal/strings" 16 ) 17 ) 17 18 19 + var thePool = sync.Pool{New: func () any {20 + return new(syscall15Args) 21 + }} 22 + 18 // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). 23 // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). 19 // It panics if it can't find the name symbol. 24 // It panics if it can't find the name symbol. 20 func RegisterLibFunc(fptr interface {}, handle uintptr, name string) {25 func RegisterLibFunc(fptr interface {}, handle uintptr, name string) { ⋯ 179 hidden lines@@ -200,18 +205,6 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { 200 } 205 } 201 } 206 } 202 v := reflect.MakeFunc(ty, func (args []reflect.Value) (results []reflect.Value) { 207 v := reflect.MakeFunc(ty, func (args []reflect.Value) (results []reflect.Value) { 203 - if len(args) > 0 { 204 - if variadic, ok := args[len(args)-1].Interface().([]interface {}); ok { 205 - // subtract one from args bc the last argument in args is []interface{} 206 - // which we are currently expanding 207 - tmp := make([]reflect.Value, len(args)-1+len(variadic)) 208 - n := copy(tmp, args[:len(args)-1]) 209 - for i, v := range variadic { 210 - tmp[n+i] = reflect.ValueOf(v) 211 - } 212 - args = tmp 213 - } 214 - } 215 var sysargs [maxArgs]uintptr 208 var sysargs [maxArgs]uintptr 216 stack := sysargs[numOfIntegerRegisters():] 209 stack := sysargs[numOfIntegerRegisters():] 217 var floats [numOfFloats]uintptr 210 var floats [numOfFloats]uintptr ⋯ 42 hidden lines@@ -260,7 +253,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { 260 runtime.KeepAlive(keepAlive) 253 runtime.KeepAlive(keepAlive) 261 runtime.KeepAlive(args) 254 runtime.KeepAlive(args) 262 }() 255 }() 263 - var syscall syscall15Args 256 + syscall := thePool.Get().(*syscall15Args) 257 + defer thePool.Put(syscall) 264 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { 258 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { 265 outType := ty.Out(0) 259 outType := ty.Out(0) 266 if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { 260 if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { ⋯ 9 hidden lines@@ -276,40 +270,21 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { 276 } 270 } 277 } 271 } 278 } 272 } 279 - for _, v := range args { 273 + for i, v := range args { 280 - switch v.Kind() { 274 + if variadic, ok := args[i].Interface().([]interface {}); ok { 281 - case reflect.String: 275 + if i != len(args)-1 { 282 - ptr := strings.CString(v.String()) 276 + panic("purego: can only expand last parameter" ) 283 - keepAlive = append(keepAlive, ptr) 284 - addInt(uintptr(unsafe.Pointer(ptr))) 285 - case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 286 - addInt(uintptr(v.Uint())) 287 - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 288 - addInt(uintptr(v.Int())) 289 - case reflect.Ptr, reflect.UnsafePointer, reflect.Slice: 290 - // There is no need to keepAlive this pointer separately because it is kept alive in the args variable 291 - addInt(v.Pointer()) 292 - case reflect.Func: 293 - addInt(NewCallback(v.Interface())) 294 - case reflect.Bool: 295 - if v.Bool() { 296 - addInt(1) 297 - } else { 298 - addInt(0) 299 } 277 } 300 - case reflect.Float32: 278 + for _, x := range variadic { 301 - addFloat(uintptr(math.Float32bits(float32(v.Float())))) 279 + keepAlive = addValue(reflect.ValueOf(x), keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) 302 - case reflect.Float64: 280 + } 303 - addFloat(uintptr(math.Float64bits(v.Float()))) 281 + continue 304 - case reflect.Struct: 305 - keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive) 306 - default : 307 - panic("purego: unsupported kind: " + v.Kind().String()) 308 } 282 } 283 + keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) 309 } 284 } 310 if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { 285 if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { 311 // Use the normal arm64 calling convention even on Windows 286 // Use the normal arm64 calling convention even on Windows 312 - syscall = syscall15Args{ 287 + *syscall = syscall15Args{ 313 cfn, 288 cfn, 314 sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], 289 sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], 315 sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], 290 sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], ⋯ 1 hidden line@@ -317,7 +292,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { 317 floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], 292 floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], 318 syscall.arm64_r8, 293 syscall.arm64_r8, 319 } 294 } 320 - runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall)) 295 + runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall)) 321 } else { 296 } else { 322 // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats 297 // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats 323 syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], 298 syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], ⋯ 33 hidden lines@@ -357,15 +332,54 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { 357 // On 32bit platforms syscall.r2 is the upper part of a 64bit return. 332 // On 32bit platforms syscall.r2 is the upper part of a 64bit return. 358 v.SetFloat(math.Float64frombits(uint64(syscall.f1))) 333 v.SetFloat(math.Float64frombits(uint64(syscall.f1))) 359 case reflect.Struct: 334 case reflect.Struct: 360 - v = getStruct(outType, syscall) 335 + v = getStruct(outType, *syscall) 361 default : 336 default : 362 panic("purego: unsupported return kind: " + outType.Kind().String()) 337 panic("purego: unsupported return kind: " + outType.Kind().String()) 363 } 338 } 364 - return []reflect.Value{v} 339 + if len(args) > 0 { 340 + // reuse args slice instead of allocating one when possible 341 + args[0] = v 342 + return args[:1] 343 + } else { 344 + return []reflect.Value{v} 345 + } 365 }) 346 }) 366 fn.Set(v) 347 fn.Set(v) 367 } 348 } 368 349 350 + func addValue(v reflect.Value, keepAlive []interface {}, addInt func (x uintptr), addFloat func (x uintptr), addStack func (x uintptr), numInts *int, numFloats *int, numStack *int) []interface {} {351 + switch v.Kind() { 352 + case reflect.String: 353 + ptr := strings.CString(v.String()) 354 + keepAlive = append(keepAlive, ptr) 355 + addInt(uintptr(unsafe.Pointer(ptr))) 356 + case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 357 + addInt(uintptr(v.Uint())) 358 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 359 + addInt(uintptr(v.Int())) 360 + case reflect.Ptr, reflect.UnsafePointer, reflect.Slice: 361 + // There is no need to keepAlive this pointer separately because it is kept alive in the args variable 362 + addInt(v.Pointer()) 363 + case reflect.Func: 364 + addInt(NewCallback(v.Interface())) 365 + case reflect.Bool: 366 + if v.Bool() { 367 + addInt(1) 368 + } else { 369 + addInt(0) 370 + } 371 + case reflect.Float32: 372 + addFloat(uintptr(math.Float32bits(float32(v.Float())))) 373 + case reflect.Float64: 374 + addFloat(uintptr(math.Float64bits(v.Float()))) 375 + case reflect.Struct: 376 + keepAlive = addStruct(v, numInts, numFloats, numStack, addInt, addFloat, addStack, keepAlive) 377 + default : 378 + panic("purego: unsupported kind: " + v.Kind().String()) 379 + } 380 + return keepAlive 381 + } 382 + 369 // maxRegAllocStructSize is the biggest a struct can be while still fitting in registers. 383 // maxRegAllocStructSize is the biggest a struct can be while still fitting in registers. 370 // if it is bigger than this than enough space must be allocated on the heap and then passed into 384 // if it is bigger than this than enough space must be allocated on the heap and then passed into 371 // the function as the first parameter on amd64 or in R8 on arm64. 385 // the function as the first parameter on amd64 or in R8 on arm64.