detached 8b02958

chore: switch purego dependency to bnema/purego fork

Replace local path replace directive with GitHub URL pointing to
bnema/purego@91aa177 to avoid accidental pushes to upstream.
commit
8b029581aa8aebd90aa84a2b638058062afa9bdd
author
brice · · 2026-03-23 19:51 UTC
parent
f14f9b6
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
51 files · +1860 -488
M go.mod +2 -0
@@ -3,3 +3,5 @@ module github.com/bnema/puregotk
33 go 1.26.1
44
55 require github.com/ebitengine/purego v0.10.0
6+
7+replace github.com/ebitengine/purego => github.com/bnema/purego v0.0.0-20260323095903-91aa177a2704
@@ -3,3 +3,5 @@ module github.com/bnema/puregotk
3 go 1.26.13 go 1.26.1
4 4
5 require github.com/ebitengine/purego v0.10.05 require github.com/ebitengine/purego v0.10.0
6+
7+replace github.com/ebitengine/purego => github.com/bnema/purego v0.0.0-20260323095903-91aa177a2704
M go.sum +2 -2 generated file: collapsed
M vendor/github.com/ebitengine/purego/README.md +3 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/dlfcn_nocgo_netbsd.go +2 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/func.go +220 -107 generated file: collapsed
A vendor/github.com/ebitengine/purego/hostlayout_go123.go +12 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/hostlayout_nogo123.go +11 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/abi_riscv64.h +72 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/asm_riscv64.s +14 -55 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/go_freebsd.go +4 -25 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux.go +4 -25 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/go_netbsd.go +4 -25 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_netbsd.go +2 -2 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/linux.go +184 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/netbsd.go +1 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_386.s +18 -4 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_amd64.s +1 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm.s +44 -3 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_arm64.s +1 -4 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_linux_386.s +78 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_linux_amd64.s +69 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_linux_arm.s +69 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_linux_arm64.s +60 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_linux_loong64.s +60 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_linux_ppc64le.s +69 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_linux_riscv64.s +60 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_loong64.s +1 -11 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_ppc64le.s +30 -129 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_riscv64.s +11 -7 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/zsymbols_linux.go +110 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/internal/fakecgo/ztrampolines_linux.s +30 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/struct_386.go +12 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/struct_amd64.go +162 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/struct_arm.go +8 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/struct_arm64.go +203 -7
@@ -24,7 +24,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
2424 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
2525 r1 = syscall.f1
2626 if numFields == 2 {
27- r1 = syscall.f2<<32 | syscall.f1
27+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
2828 }
2929 }
3030 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
@@ -33,16 +33,16 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
3333 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
3434 switch numFields {
3535 case 4:
36- r1 = syscall.f2<<32 | syscall.f1
37- r2 = syscall.f4<<32 | syscall.f3
36+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
37+ r2 = syscall.f4<<32 | syscall.f3&math.MaxUint32
3838 case 3:
39- r1 = syscall.f2<<32 | syscall.f1
39+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
4040 r2 = syscall.f3
4141 case 2:
4242 r1 = syscall.f1
4343 r2 = syscall.f2
4444 default:
45- panic("unreachable")
45+ panic("not reached")
4646 }
4747 }
4848 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
@@ -54,7 +54,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
5454 case 3:
5555 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()
5656 default:
57- panic("unreachable")
57+ panic("not reached")
5858 }
5959 }
6060 // create struct from the Go pointer created in arm64_r8
@@ -190,7 +190,7 @@ func placeRegistersArm64(v reflect.Value, addFloat func(uintptr), addInt func(ui
190190 shift = 0
191191 flushed = true
192192 class = _NO_CLASS
193- case reflect.Ptr, reflect.UnsafePointer:
193+ case reflect.Pointer, reflect.UnsafePointer:
194194 addInt(f.Pointer())
195195 shift = 0
196196 flushed = true
@@ -547,3 +547,199 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
547547 size := structType.Size()
548548 copyStruct8ByteChunks(ptr, size, addStack)
549549 }
550+
551+// getCallbackStruct reads a struct argument from the callback frame on arm64.
552+// It mirrors the AAPCS64 rules used by addStruct for the Go→C path.
553+//
554+// getCallbackStruct is only used on Unix. On Windows, callbacks are handled by
555+// the runtime's own callback mechanism, so this function is compiled but unused.
556+//
557+// AAPCS64 struct argument passing rules:
558+// - HFA (Homogeneous Float Aggregate): each float field in a separate float register
559+// - Non-HFA ≤ 16 bytes: packed into 1–2 integer registers
560+// - > 16 bytes (not HFA): passed by pointer in integer register
561+// - Register overflow: struct goes on the stack
562+// - Darwin ARM64: byte-level packing on the stack
563+func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value {
564+ switch runtime.GOOS {
565+ case "darwin", "freebsd", "linux", "netbsd":
566+ default:
567+ panic("purego: getCallbackStruct is not supported on " + runtime.GOOS)
568+ }
569+
570+ f := (*[callbackMaxFrame]uintptr)(frame)
571+
572+ if isHFA(inType) {
573+ _, numFloatFields := isAllSameFloat(inType)
574+ if *floatsN+numFloatFields <= numOfFloatRegisters() {
575+ return readHFAFromRegisters(inType, f, floatsN, numFloatFields)
576+ }
577+ // Not enough float registers: entire struct goes on stack.
578+ // Exhaust float registers per AAPCS64.
579+ *floatsN = numOfFloatRegisters()
580+ return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset)
581+ }
582+
583+ if size := inType.Size(); size <= 16 {
584+ // Non-HFA composite ≤ 16 bytes: packed into 1–2 integer registers.
585+ numSlots := int((size + 7) / 8)
586+ if *intsN+numSlots <= numOfIntegerRegisters() {
587+ pos := numOfFloatRegisters() + *intsN
588+ var r1, r2 uintptr
589+ r1 = f[pos]
590+ *intsN++
591+ if numSlots == 2 {
592+ r2 = f[pos+1]
593+ *intsN++
594+ }
595+ if numSlots == 1 {
596+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
597+ }
598+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
599+ }
600+ // Not enough int registers: goes on stack.
601+ return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset)
602+ }
603+
604+ // > 16 bytes, not HFA: passed by pointer in integer register.
605+ if *intsN < numOfIntegerRegisters() {
606+ ptr := f[numOfFloatRegisters()+*intsN]
607+ *intsN++
608+ return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
609+ }
610+
611+ // Pointer on stack (rare: all integer registers exhausted).
612+ if runtime.GOOS == "darwin" {
613+ ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeOf(uintptr(0)))
614+ ptr := uintptr(ptrVal.Uint())
615+ return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
616+ }
617+ ptr := f[*stackSlot]
618+ *stackSlot++
619+ return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
620+}
621+
622+// readHFAFromRegisters reads an HFA (Homogeneous Float Aggregate) struct from
623+// float registers. Each float field occupies one float register slot.
624+// For float32, the value is in the low 32 bits of the register.
625+func readHFAFromRegisters(inType reflect.Type, f *[callbackMaxFrame]uintptr, floatsN *int, numFields int) reflect.Value {
626+ size := inType.Size()
627+
628+ // Determine the element type
629+ root := inType.Field(0).Type
630+ for root.Kind() == reflect.Struct {
631+ root = root.Field(0).Type
632+ }
633+ isFloat32 := root.Kind() == reflect.Float32
634+
635+ if isFloat32 {
636+ // Each float32 is in a separate register (low 32 bits).
637+ // Pack pairs into uintptrs to match the struct memory layout.
638+ switch {
639+ case size <= 8:
640+ var r1 uintptr
641+ switch numFields {
642+ case 1:
643+ r1 = f[*floatsN]
644+ case 2:
645+ r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32
646+ }
647+ *floatsN += numFields
648+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
649+ case size <= 16:
650+ var r1, r2 uintptr
651+ switch numFields {
652+ case 3:
653+ r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32
654+ r2 = f[*floatsN+2]
655+ case 4:
656+ r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32
657+ r2 = f[*floatsN+3]<<32 | f[*floatsN+2]&math.MaxUint32
658+ }
659+ *floatsN += numFields
660+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
661+ default:
662+ panic("not reached")
663+ }
664+ }
665+
666+ // float64: each field occupies one full register.
667+ switch numFields {
668+ case 1:
669+ r1 := f[*floatsN]
670+ *floatsN++
671+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
672+ case 2:
673+ r1, r2 := f[*floatsN], f[*floatsN+1]
674+ *floatsN += 2
675+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
676+ case 3:
677+ r1, r2, r3 := f[*floatsN], f[*floatsN+1], f[*floatsN+2]
678+ *floatsN += 3
679+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c uintptr }{r1, r2, r3})).Elem()
680+ case 4:
681+ r1, r2, r3, r4 := f[*floatsN], f[*floatsN+1], f[*floatsN+2], f[*floatsN+3]
682+ *floatsN += 4
683+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{r1, r2, r3, r4})).Elem()
684+ default:
685+ panic("purego: HFA with more than 4 fields is not supported")
686+ }
687+}
688+
689+// readStructFromStackArm64 reads a struct argument from the stack area of the
690+// callback frame. On Darwin ARM64, arguments are byte-packed on the stack.
691+// On Linux ARM64, arguments are 8-byte aligned.
692+func readStructFromStackArm64(inType reflect.Type, f *[callbackMaxFrame]uintptr, frame unsafe.Pointer, stackSlot *int, stackByteOffset *uintptr) reflect.Value {
693+ if runtime.GOOS == "darwin" {
694+ return callbackArgFromStack(frame, *stackSlot, stackByteOffset, inType)
695+ }
696+ // Linux ARM64: 8-byte aligned slots.
697+ numSlots := int((inType.Size() + 7) / 8)
698+ v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem()
699+ *stackSlot += numSlots
700+ return v
701+}
702+
703+func setStruct(a *callbackArgs, ret reflect.Value) {
704+ outSize := ret.Type().Size()
705+ switch {
706+ case outSize == 0:
707+ return
708+ case outSize <= 8:
709+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
710+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields == 2 {
711+ a.result[1] = a.result[0] >> 32 // expanding two float32s into a.result[0] and a.result[1]
712+ a.result[0] &= math.MaxUint32 // clear the top bits since they contain the second argument
713+ }
714+ return
715+ case outSize <= 16:
716+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
717+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats {
718+ switch numFields {
719+ case 4:
720+ a.result[3] = a.result[1] >> 32
721+ a.result[2] = a.result[1] & math.MaxUint32
722+ a.result[1] = a.result[0] >> 32
723+ a.result[0] &= math.MaxUint32
724+ case 3:
725+ a.result[2] = a.result[1] & math.MaxUint32
726+ a.result[1] = a.result[0] >> 32
727+ a.result[0] &= math.MaxUint32
728+ case 2:
729+ // two float64s are already in a.result[0] and a.result[1]
730+ default:
731+ panic("not reached")
732+ }
733+ }
734+ return
735+ default:
736+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields <= 4 {
737+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
738+ return
739+ }
740+ // The caller passed the address to place the return struct,
741+ // so copy the Go struct into the provided memory.
742+ reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret)
743+ return
744+ }
745+}
@@ -24,7 +24,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
24 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {24 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
25 r1 = syscall.f125 r1 = syscall.f1
26 if numFields == 2 {26 if numFields == 2 {
27- r1 = syscall.f2<<32 | syscall.f127+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
28 }28 }
29 }29 }
30 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()30 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
@@ -33,16 +33,16 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
33 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {33 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
34 switch numFields {34 switch numFields {
35 case 4:35 case 4:
36- r1 = syscall.f2<<32 | syscall.f136+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
37- r2 = syscall.f4<<32 | syscall.f337+ r2 = syscall.f4<<32 | syscall.f3&math.MaxUint32
38 case 3:38 case 3:
39- r1 = syscall.f2<<32 | syscall.f139+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
40 r2 = syscall.f340 r2 = syscall.f3
41 case 2:41 case 2:
42 r1 = syscall.f142 r1 = syscall.f1
43 r2 = syscall.f243 r2 = syscall.f2
44 default:44 default:
45- panic("unreachable")45+ panic("not reached")
46 }46 }
47 }47 }
48 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()48 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
@@ -54,7 +54,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
54 case 3:54 case 3:
55 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()55 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()
56 default:56 default:
57- panic("unreachable")57+ panic("not reached")
58 }58 }
59 }59 }
60 // create struct from the Go pointer created in arm64_r860 // create struct from the Go pointer created in arm64_r8
@@ -190,7 +190,7 @@ func placeRegistersArm64(v reflect.Value, addFloat func(uintptr), addInt func(ui
190 shift = 0190 shift = 0
191 flushed = true191 flushed = true
192 class = _NO_CLASS192 class = _NO_CLASS
193- case reflect.Ptr, reflect.UnsafePointer:193+ case reflect.Pointer, reflect.UnsafePointer:
194 addInt(f.Pointer())194 addInt(f.Pointer())
195 shift = 0195 shift = 0
196 flushed = true196 flushed = true
@@ -547,3 +547,199 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
547 size := structType.Size()547 size := structType.Size()
548 copyStruct8ByteChunks(ptr, size, addStack)548 copyStruct8ByteChunks(ptr, size, addStack)
549 }549 }
550+
551+// getCallbackStruct reads a struct argument from the callback frame on arm64.
552+// It mirrors the AAPCS64 rules used by addStruct for the Go→C path.
553+//
554+// getCallbackStruct is only used on Unix. On Windows, callbacks are handled by
555+// the runtime's own callback mechanism, so this function is compiled but unused.
556+//
557+// AAPCS64 struct argument passing rules:
558+// - HFA (Homogeneous Float Aggregate): each float field in a separate float register
559+// - Non-HFA ≤ 16 bytes: packed into 1–2 integer registers
560+// - > 16 bytes (not HFA): passed by pointer in integer register
561+// - Register overflow: struct goes on the stack
562+// - Darwin ARM64: byte-level packing on the stack
563+func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value {
564+ switch runtime.GOOS {
565+ case "darwin", "freebsd", "linux", "netbsd":
566+ default:
567+ panic("purego: getCallbackStruct is not supported on " + runtime.GOOS)
568+ }
569+
570+ f := (*[callbackMaxFrame]uintptr)(frame)
571+
572+ if isHFA(inType) {
573+ _, numFloatFields := isAllSameFloat(inType)
574+ if *floatsN+numFloatFields <= numOfFloatRegisters() {
575+ return readHFAFromRegisters(inType, f, floatsN, numFloatFields)
576+ }
577+ // Not enough float registers: entire struct goes on stack.
578+ // Exhaust float registers per AAPCS64.
579+ *floatsN = numOfFloatRegisters()
580+ return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset)
581+ }
582+
583+ if size := inType.Size(); size <= 16 {
584+ // Non-HFA composite ≤ 16 bytes: packed into 1–2 integer registers.
585+ numSlots := int((size + 7) / 8)
586+ if *intsN+numSlots <= numOfIntegerRegisters() {
587+ pos := numOfFloatRegisters() + *intsN
588+ var r1, r2 uintptr
589+ r1 = f[pos]
590+ *intsN++
591+ if numSlots == 2 {
592+ r2 = f[pos+1]
593+ *intsN++
594+ }
595+ if numSlots == 1 {
596+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
597+ }
598+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
599+ }
600+ // Not enough int registers: goes on stack.
601+ return readStructFromStackArm64(inType, f, frame, stackSlot, stackByteOffset)
602+ }
603+
604+ // > 16 bytes, not HFA: passed by pointer in integer register.
605+ if *intsN < numOfIntegerRegisters() {
606+ ptr := f[numOfFloatRegisters()+*intsN]
607+ *intsN++
608+ return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
609+ }
610+
611+ // Pointer on stack (rare: all integer registers exhausted).
612+ if runtime.GOOS == "darwin" {
613+ ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeOf(uintptr(0)))
614+ ptr := uintptr(ptrVal.Uint())
615+ return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
616+ }
617+ ptr := f[*stackSlot]
618+ *stackSlot++
619+ return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
620+}
621+
622+// readHFAFromRegisters reads an HFA (Homogeneous Float Aggregate) struct from
623+// float registers. Each float field occupies one float register slot.
624+// For float32, the value is in the low 32 bits of the register.
625+func readHFAFromRegisters(inType reflect.Type, f *[callbackMaxFrame]uintptr, floatsN *int, numFields int) reflect.Value {
626+ size := inType.Size()
627+
628+ // Determine the element type
629+ root := inType.Field(0).Type
630+ for root.Kind() == reflect.Struct {
631+ root = root.Field(0).Type
632+ }
633+ isFloat32 := root.Kind() == reflect.Float32
634+
635+ if isFloat32 {
636+ // Each float32 is in a separate register (low 32 bits).
637+ // Pack pairs into uintptrs to match the struct memory layout.
638+ switch {
639+ case size <= 8:
640+ var r1 uintptr
641+ switch numFields {
642+ case 1:
643+ r1 = f[*floatsN]
644+ case 2:
645+ r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32
646+ }
647+ *floatsN += numFields
648+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
649+ case size <= 16:
650+ var r1, r2 uintptr
651+ switch numFields {
652+ case 3:
653+ r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32
654+ r2 = f[*floatsN+2]
655+ case 4:
656+ r1 = f[*floatsN+1]<<32 | f[*floatsN]&math.MaxUint32
657+ r2 = f[*floatsN+3]<<32 | f[*floatsN+2]&math.MaxUint32
658+ }
659+ *floatsN += numFields
660+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
661+ default:
662+ panic("not reached")
663+ }
664+ }
665+
666+ // float64: each field occupies one full register.
667+ switch numFields {
668+ case 1:
669+ r1 := f[*floatsN]
670+ *floatsN++
671+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
672+ case 2:
673+ r1, r2 := f[*floatsN], f[*floatsN+1]
674+ *floatsN += 2
675+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
676+ case 3:
677+ r1, r2, r3 := f[*floatsN], f[*floatsN+1], f[*floatsN+2]
678+ *floatsN += 3
679+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c uintptr }{r1, r2, r3})).Elem()
680+ case 4:
681+ r1, r2, r3, r4 := f[*floatsN], f[*floatsN+1], f[*floatsN+2], f[*floatsN+3]
682+ *floatsN += 4
683+ return reflect.NewAt(inType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{r1, r2, r3, r4})).Elem()
684+ default:
685+ panic("purego: HFA with more than 4 fields is not supported")
686+ }
687+}
688+
689+// readStructFromStackArm64 reads a struct argument from the stack area of the
690+// callback frame. On Darwin ARM64, arguments are byte-packed on the stack.
691+// On Linux ARM64, arguments are 8-byte aligned.
692+func readStructFromStackArm64(inType reflect.Type, f *[callbackMaxFrame]uintptr, frame unsafe.Pointer, stackSlot *int, stackByteOffset *uintptr) reflect.Value {
693+ if runtime.GOOS == "darwin" {
694+ return callbackArgFromStack(frame, *stackSlot, stackByteOffset, inType)
695+ }
696+ // Linux ARM64: 8-byte aligned slots.
697+ numSlots := int((inType.Size() + 7) / 8)
698+ v := reflect.NewAt(inType, unsafe.Pointer(&f[*stackSlot])).Elem()
699+ *stackSlot += numSlots
700+ return v
701+}
702+
703+func setStruct(a *callbackArgs, ret reflect.Value) {
704+ outSize := ret.Type().Size()
705+ switch {
706+ case outSize == 0:
707+ return
708+ case outSize <= 8:
709+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
710+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields == 2 {
711+ a.result[1] = a.result[0] >> 32 // expanding two float32s into a.result[0] and a.result[1]
712+ a.result[0] &= math.MaxUint32 // clear the top bits since they contain the second argument
713+ }
714+ return
715+ case outSize <= 16:
716+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
717+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats {
718+ switch numFields {
719+ case 4:
720+ a.result[3] = a.result[1] >> 32
721+ a.result[2] = a.result[1] & math.MaxUint32
722+ a.result[1] = a.result[0] >> 32
723+ a.result[0] &= math.MaxUint32
724+ case 3:
725+ a.result[2] = a.result[1] & math.MaxUint32
726+ a.result[1] = a.result[0] >> 32
727+ a.result[0] &= math.MaxUint32
728+ case 2:
729+ // two float64s are already in a.result[0] and a.result[1]
730+ default:
731+ panic("not reached")
732+ }
733+ }
734+ return
735+ default:
736+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields <= 4 {
737+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
738+ return
739+ }
740+ // The caller passed the address to place the return struct,
741+ // so copy the Go struct into the provided memory.
742+ reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret)
743+ return
744+ }
745+}
M vendor/github.com/ebitengine/purego/struct_loong64.go +9 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/struct_ppc64le.go +9 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/struct_riscv64.go +9 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/struct_s390x.go +9 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/sys_amd64.s +16 -16 generated file: collapsed
M vendor/github.com/ebitengine/purego/sys_arm64.s +2 -1 generated file: collapsed
M vendor/github.com/ebitengine/purego/sys_unix_arm64.s +8 -3 generated file: collapsed
M vendor/github.com/ebitengine/purego/sys_unix_loong64.s +9 -5 generated file: collapsed
M vendor/github.com/ebitengine/purego/syscall.go +21 -0 generated file: collapsed
M vendor/github.com/ebitengine/purego/syscall_32bit.go +21 -0 generated file: collapsed
A vendor/github.com/ebitengine/purego/syscall_notstackargs.go +38 -0 generated file: collapsed
R vendor/github.com/ebitengine/purego/syscall_sysv_stackargs.go → vendor/github.com/ebitengine/purego/syscall_stackargs.go +13 -3 generated file: collapsed · renamed, 54% similar
D vendor/github.com/ebitengine/purego/syscall_sysv_others.go +0 -28 generated file: collapsed
R vendor/github.com/ebitengine/purego/syscall_sysv.go → vendor/github.com/ebitengine/purego/syscall_unix.go +53 -18 generated file: collapsed · renamed, 85% similar
M vendor/github.com/ebitengine/purego/syscall_windows.go +8 -0 generated file: collapsed
M vendor/modules.txt +2 -1 generated file: collapsed

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close