detached 7e97482

purego: support structs on Windows amd64/arm64 (#465)

Enable passing and returning C structs by value on Windows for
forward calls (Go calling C).

On amd64 the Win64 ABI is used: aggregates of exactly 1, 2, 4, or 8
bytes are passed by value in a single integer slot and returned in
RAX, while all other sizes are passed and returned through a
caller-allocated pointer. This maps cleanly onto the positional
syscall.SyscallN path and needs no float-register handling, because
Win64 never passes struct fields in XMM registers. Unlike the System
V ABI, an empty struct argument still consumes a slot on Win64.

On arm64 the existing AAPCS64 path already runs on Windows via the
shared assembly, so only the support gate is opened.

Passing or returning structs in callbacks created with NewCallback
is not supported on Windows, because they go through the standard
library syscall.NewCallback, which cannot carry struct values.

The struct test C library used `long` for values that must be 64-bit,
which is only 32-bit on Windows (LLP64) and overflowed the 64-bit
shift expressions and shrank struct sizes. Replace it with
width-correct stdint types: int64_t/uint64_t for Go's fixed-width
int64/uint64, and intptr_t/uintptr_t for Go's word-sized int/uint, so
the library is correct under both LP64 and LLP64.

Closes #237

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit
7e9748223902fac7b8c3a7d22afb24ed8d26a137
author
Hajime Hoshi · · 2026-06-20 04:29 UTC
committer
GitHub · · 2026-06-20 04:29 UTC
parent
5faf54b
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
5 files · +158 -68
M README.md +13 -11
@@ -30,28 +30,30 @@ except for float arguments and return values.
3030
3131 Tier 1 platforms are the primary targets officially supported by PureGo. When a new version of PureGo is released, any critical bugs found on Tier 1 platforms are treated as release blockers. The release will be postponed until such issues are resolved.
3232
33-- **Android**: amd64<sup>1</sup>, arm64<sup>1</sup>
34-- **iOS**: amd64<sup>1</sup>, arm64<sup>1</sup>
33+- **Android**: amd64<sup>1,2</sup>, arm64<sup>1,2</sup>
34+- **iOS**: amd64<sup>1,2</sup>, arm64<sup>1,2</sup>
3535 - **Linux**: amd64, arm64
3636 - **macOS**: amd64, arm64
37-- **Windows**: amd64, arm64
37+- **Windows**: amd64<sup>3</sup>, arm64<sup>3</sup>
3838
3939 ### Tier 2
4040
4141 Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs on Tier 2 platforms do not block new PureGo releases. However, fixes contributed by external contributors are very welcome and encouraged.
4242
43-- **Android**: 386<sup>1</sup>, arm<sup>1</sup>
44-- **FreeBSD**: amd64<sup>2</sup>, arm64<sup>2</sup>
45-- **Linux**: 386, arm, loong64, ppc64le, riscv64, s390x<sup>1</sup>
46-- **NetBSD**: amd64<sup>2</sup>, arm64<sup>2</sup>
47-- **Windows**: 386<sup>3</sup>, arm<sup>3,4</sup>
43+- **Android**: 386<sup>1,2</sup>, arm<sup>1,2</sup>
44+- **FreeBSD**: amd64<sup>2,4</sup>, arm64<sup>2,4</sup>
45+- **Linux**: 386<sup>2</sup>, arm<sup>2</sup>, loong64<sup>2</sup>, ppc64le<sup>2</sup>, riscv64<sup>2</sup>, s390x<sup>1,2</sup>
46+- **NetBSD**: amd64<sup>2,4</sup>, arm64<sup>2,4</sup>
47+- **Windows**: 386<sup>2,5</sup>, arm<sup>2,5,6</sup>
4848
4949 #### Support Notes
5050
5151 1. These architectures require CGO_ENABLED=1 to compile
52-2. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=0
53-3. These architectures only support `SyscallN` and `NewCallback`
54-4. These architectures are no longer supported as of Go 1.26
52+2. These architectures do not support passing structs by value as arguments or return values
53+3. These architectures support passing structs by value as arguments and return values when calling C functions, but not in callbacks created with `NewCallback`
54+4. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=0
55+5. These architectures only support `SyscallN` and `NewCallback`
56+6. These architectures are no longer supported as of Go 1.26
5557
5658 ## Example
5759
@@ -30,28 +30,30 @@ except for float arguments and return values.
30 30
31 Tier 1 platforms are the primary targets officially supported by PureGo. When a new version of PureGo is released, any critical bugs found on Tier 1 platforms are treated as release blockers. The release will be postponed until such issues are resolved.31 Tier 1 platforms are the primary targets officially supported by PureGo. When a new version of PureGo is released, any critical bugs found on Tier 1 platforms are treated as release blockers. The release will be postponed until such issues are resolved.
32 32
33-- **Android**: amd64<sup>1</sup>, arm64<sup>1</sup>33+- **Android**: amd64<sup>1,2</sup>, arm64<sup>1,2</sup>
34-- **iOS**: amd64<sup>1</sup>, arm64<sup>1</sup>34+- **iOS**: amd64<sup>1,2</sup>, arm64<sup>1,2</sup>
35 - **Linux**: amd64, arm6435 - **Linux**: amd64, arm64
36 - **macOS**: amd64, arm6436 - **macOS**: amd64, arm64
37-- **Windows**: amd64, arm6437+- **Windows**: amd64<sup>3</sup>, arm64<sup>3</sup>
38 38
39 ### Tier 239 ### Tier 2
40 40
41 Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs on Tier 2 platforms do not block new PureGo releases. However, fixes contributed by external contributors are very welcome and encouraged.41 Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs on Tier 2 platforms do not block new PureGo releases. However, fixes contributed by external contributors are very welcome and encouraged.
42 42
43-- **Android**: 386<sup>1</sup>, arm<sup>1</sup>43+- **Android**: 386<sup>1,2</sup>, arm<sup>1,2</sup>
44-- **FreeBSD**: amd64<sup>2</sup>, arm64<sup>2</sup>44+- **FreeBSD**: amd64<sup>2,4</sup>, arm64<sup>2,4</sup>
45-- **Linux**: 386, arm, loong64, ppc64le, riscv64, s390x<sup>1</sup>45+- **Linux**: 386<sup>2</sup>, arm<sup>2</sup>, loong64<sup>2</sup>, ppc64le<sup>2</sup>, riscv64<sup>2</sup>, s390x<sup>1,2</sup>
46-- **NetBSD**: amd64<sup>2</sup>, arm64<sup>2</sup>46+- **NetBSD**: amd64<sup>2,4</sup>, arm64<sup>2,4</sup>
47-- **Windows**: 386<sup>3</sup>, arm<sup>3,4</sup>47+- **Windows**: 386<sup>2,5</sup>, arm<sup>2,5,6</sup>
48 48
49 #### Support Notes49 #### Support Notes
50 50
51 1. These architectures require CGO_ENABLED=1 to compile51 1. These architectures require CGO_ENABLED=1 to compile
52-2. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=052+2. These architectures do not support passing structs by value as arguments or return values
53-3. These architectures only support `SyscallN` and `NewCallback`53+3. These architectures support passing structs by value as arguments and return values when calling C functions, but not in callbacks created with `NewCallback`
54-4. These architectures are no longer supported as of Go 1.2654+4. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=0
55+5. These architectures only support `SyscallN` and `NewCallback`
56+6. These architectures are no longer supported as of Go 1.26
55 57
56 ## Example58 ## Example
57 59
M func.go +36 -8
@@ -64,7 +64,7 @@ func RegisterLibFunc(fptr any, handle uintptr, name string) {
6464 // int64 <=> int64_t
6565 // float32 <=> float
6666 // float64 <=> double
67-// struct <=> struct (darwin amd64/arm64, linux amd64/arm64)
67+// struct <=> struct (darwin amd64/arm64, linux amd64/arm64, windows amd64/arm64)
6868 // func <=> C function
6969 // unsafe.Pointer, *T <=> void*
7070 // []T => void*
@@ -102,6 +102,9 @@ func RegisterLibFunc(fptr any, handle uintptr, name string) {
102102 // On Darwin ARM64, purego handles proper alignment of struct arguments when passing them on the stack,
103103 // following the C ABI's byte-level packing rules.
104104 //
105+// On Windows, struct arguments and returns are supported on amd64 and arm64 when calling C functions.
106+// Passing or returning structs in callbacks created with [NewCallback] is not supported on Windows.
107+//
105108 // # Example
106109 //
107110 // All functions below call this C function:
@@ -175,7 +178,8 @@ func RegisterFunc(fptr any, cfn uintptr) {
175178 }
176179 case reflect.Struct:
177180 ensureStructSupported()
178- if arg.Size() == 0 {
181+ if arg.Size() == 0 && runtime.GOOS != "windows" {
182+ // On Windows an empty struct still consumes one argument slot.
179183 continue
180184 }
181185 addInt := func(u uintptr) {
@@ -196,9 +200,9 @@ func RegisterFunc(fptr any, cfn uintptr) {
196200 ensureStructSupported()
197201 outType := ty.Out(0)
198202 checkStructFieldsSupported(outType)
199- if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
200- // on amd64 if struct is bigger than 16 bytes allocate the return struct
201- // and pass it in as a hidden first argument.
203+ if runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size()) {
204+ // on amd64 a struct returned in memory is allocated by the caller
205+ // and its pointer is passed as a hidden first argument.
202206 ints++
203207 }
204208 }
@@ -283,7 +287,9 @@ func RegisterFunc(fptr any, cfn uintptr) {
283287 var arm64_r8 uintptr
284288 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
285289 outType := ty.Out(0)
286- if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && outType.Size() > maxRegAllocStructSize {
290+ amd64InMemory := runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size())
291+ otherInMemory := (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && outType.Size() > maxRegAllocStructSize
292+ if amd64InMemory || otherInMemory {
287293 val := reflect.New(outType)
288294 keepAlive = append(keepAlive, val)
289295 addInt(val.Pointer())
@@ -499,9 +505,31 @@ func ensureStructSupported() {
499505 if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
500506 panic("purego: struct arguments/returns are only supported on amd64 and arm64")
501507 }
502- if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
503- panic("purego: struct arguments/returns are only supported on darwin and linux")
508+ if runtime.GOOS != "darwin" && runtime.GOOS != "linux" && runtime.GOOS != "windows" {
509+ panic("purego: struct arguments/returns are only supported on darwin, linux, and windows")
510+ }
511+}
512+
513+// amd64StructReturnInMemory reports whether a struct return value of the given
514+// size is returned through a caller-allocated hidden pointer (true) rather than
515+// in registers (false). It must only be consulted on amd64.
516+func amd64StructReturnInMemory(size uintptr) bool {
517+ if size == 0 {
518+ return false
519+ }
520+ if runtime.GOOS == "windows" {
521+ // The Win64 ABI returns aggregates of exactly 1, 2, 4, or 8 bytes in
522+ // RAX. Every other size is returned through a caller-allocated hidden
523+ // pointer that the callee also returns in RAX.
524+ switch size {
525+ case 1, 2, 4, 8:
526+ return false
527+ default:
528+ return true
529+ }
504530 }
531+ // The System V ABI returns aggregates of up to two eightbytes in registers.
532+ return size > maxRegAllocStructSize
505533 }
506534
507535 func roundUpTo8(val uintptr) uintptr {
@@ -64,7 +64,7 @@ func RegisterLibFunc(fptr any, handle uintptr, name string) {
64 // int64 <=> int64_t64 // int64 <=> int64_t
65 // float32 <=> float65 // float32 <=> float
66 // float64 <=> double66 // float64 <=> double
67-// struct <=> struct (darwin amd64/arm64, linux amd64/arm64)67+// struct <=> struct (darwin amd64/arm64, linux amd64/arm64, windows amd64/arm64)
68 // func <=> C function68 // func <=> C function
69 // unsafe.Pointer, *T <=> void*69 // unsafe.Pointer, *T <=> void*
70 // []T => void*70 // []T => void*
@@ -102,6 +102,9 @@ func RegisterLibFunc(fptr any, handle uintptr, name string) {
102 // On Darwin ARM64, purego handles proper alignment of struct arguments when passing them on the stack,102 // On Darwin ARM64, purego handles proper alignment of struct arguments when passing them on the stack,
103 // following the C ABI's byte-level packing rules.103 // following the C ABI's byte-level packing rules.
104 //104 //
105+// On Windows, struct arguments and returns are supported on amd64 and arm64 when calling C functions.
106+// Passing or returning structs in callbacks created with [NewCallback] is not supported on Windows.
107+//
105 // # Example108 // # Example
106 //109 //
107 // All functions below call this C function:110 // All functions below call this C function:
@@ -175,7 +178,8 @@ func RegisterFunc(fptr any, cfn uintptr) {
175 }178 }
176 case reflect.Struct:179 case reflect.Struct:
177 ensureStructSupported()180 ensureStructSupported()
178- if arg.Size() == 0 {181+ if arg.Size() == 0 && runtime.GOOS != "windows" {
182+ // On Windows an empty struct still consumes one argument slot.
179 continue183 continue
180 }184 }
181 addInt := func(u uintptr) {185 addInt := func(u uintptr) {
@@ -196,9 +200,9 @@ func RegisterFunc(fptr any, cfn uintptr) {
196 ensureStructSupported()200 ensureStructSupported()
197 outType := ty.Out(0)201 outType := ty.Out(0)
198 checkStructFieldsSupported(outType)202 checkStructFieldsSupported(outType)
199- if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {203+ if runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size()) {
200- // on amd64 if struct is bigger than 16 bytes allocate the return struct204+ // on amd64 a struct returned in memory is allocated by the caller
201- // and pass it in as a hidden first argument.205+ // and its pointer is passed as a hidden first argument.
202 ints++206 ints++
203 }207 }
204 }208 }
@@ -283,7 +287,9 @@ func RegisterFunc(fptr any, cfn uintptr) {
283 var arm64_r8 uintptr287 var arm64_r8 uintptr
284 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {288 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
285 outType := ty.Out(0)289 outType := ty.Out(0)
286- if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && outType.Size() > maxRegAllocStructSize {290+ amd64InMemory := runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size())
291+ otherInMemory := (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && outType.Size() > maxRegAllocStructSize
292+ if amd64InMemory || otherInMemory {
287 val := reflect.New(outType)293 val := reflect.New(outType)
288 keepAlive = append(keepAlive, val)294 keepAlive = append(keepAlive, val)
289 addInt(val.Pointer())295 addInt(val.Pointer())
@@ -499,9 +505,31 @@ func ensureStructSupported() {
499 if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {505 if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
500 panic("purego: struct arguments/returns are only supported on amd64 and arm64")506 panic("purego: struct arguments/returns are only supported on amd64 and arm64")
501 }507 }
502- if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {508+ if runtime.GOOS != "darwin" && runtime.GOOS != "linux" && runtime.GOOS != "windows" {
503- panic("purego: struct arguments/returns are only supported on darwin and linux")509+ panic("purego: struct arguments/returns are only supported on darwin, linux, and windows")
510+ }
511+}
512+
513+// amd64StructReturnInMemory reports whether a struct return value of the given
514+// size is returned through a caller-allocated hidden pointer (true) rather than
515+// in registers (false). It must only be consulted on amd64.
516+func amd64StructReturnInMemory(size uintptr) bool {
517+ if size == 0 {
518+ return false
519+ }
520+ if runtime.GOOS == "windows" {
521+ // The Win64 ABI returns aggregates of exactly 1, 2, 4, or 8 bytes in
522+ // RAX. Every other size is returned through a caller-allocated hidden
523+ // pointer that the callee also returns in RAX.
524+ switch size {
525+ case 1, 2, 4, 8:
526+ return false
527+ default:
528+ return true
529+ }
504 }530 }
531+ // The System V ABI returns aggregates of up to two eightbytes in registers.
532+ return size > maxRegAllocStructSize
505 }533 }
506 534
507 func roundUpTo8(val uintptr) uintptr {535 func roundUpTo8(val uintptr) uintptr {
M struct_amd64.go +40 -0
@@ -12,6 +12,19 @@ import (
1212
1313 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
1414 outSize := outType.Size()
15+ if runtime.GOOS == "windows" {
16+ switch {
17+ case outSize == 0:
18+ return reflect.New(outType).Elem()
19+ case amd64StructReturnInMemory(outSize):
20+ // Returned through the caller-allocated hidden pointer, which the
21+ // callee also returns in RAX.
22+ return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
23+ default:
24+ // 1, 2, 4, or 8 byte aggregates are returned in RAX.
25+ return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.a1})).Elem()
26+ }
27+ }
1528 switch {
1629 case outSize == 0:
1730 return reflect.New(outType).Elem()
@@ -87,6 +100,12 @@ const (
87100 )
88101
89102 func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
103+ if runtime.GOOS == "windows" {
104+ // Win64 still passes an empty struct as an argument slot, so this must
105+ // run before the zero-size early return used by the System V path.
106+ return addStructWindows(v, addInt, keepAlive)
107+ }
108+
90109 if v.Type().Size() == 0 {
91110 return keepAlive
92111 }
@@ -112,6 +131,27 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
112131 return keepAlive
113132 }
114133
134+// addStructWindows passes a struct argument under the Win64 ABI. Aggregates of
135+// exactly 1, 2, 4, or 8 bytes are passed by value in a single integer slot; all
136+// other sizes are passed as a pointer to a caller-allocated copy. Empty structs
137+// fall in the latter group: unlike the System V ABI, Win64 still consumes an
138+// argument slot for them.
139+func addStructWindows(v reflect.Value, addInt func(uintptr), keepAlive []any) []any {
140+ switch v.Type().Size() {
141+ case 1, 2, 4, 8:
142+ var val uintptr
143+ reflect.NewAt(v.Type(), unsafe.Pointer(&val)).Elem().Set(v)
144+ addInt(val)
145+ default:
146+ ptrStruct := reflect.New(v.Type())
147+ ptrStruct.Elem().Set(v)
148+ ptr := ptrStruct.Elem().Addr().UnsafePointer()
149+ keepAlive = append(keepAlive, ptr)
150+ addInt(uintptr(ptr))
151+ }
152+ return keepAlive
153+}
154+
115155 func postMerger(t reflect.Type) (passInMemory bool) {
116156 // (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other
117157 // eightbyte isn’t SSEUP, the whole argument is passed in memory.
@@ -12,6 +12,19 @@ import (
12 12
13 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {13 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
14 outSize := outType.Size()14 outSize := outType.Size()
15+ if runtime.GOOS == "windows" {
16+ switch {
17+ case outSize == 0:
18+ return reflect.New(outType).Elem()
19+ case amd64StructReturnInMemory(outSize):
20+ // Returned through the caller-allocated hidden pointer, which the
21+ // callee also returns in RAX.
22+ return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
23+ default:
24+ // 1, 2, 4, or 8 byte aggregates are returned in RAX.
25+ return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.a1})).Elem()
26+ }
27+ }
15 switch {28 switch {
16 case outSize == 0:29 case outSize == 0:
17 return reflect.New(outType).Elem()30 return reflect.New(outType).Elem()
@@ -87,6 +100,12 @@ const (
87 )100 )
88 101
89 func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {102 func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
103+ if runtime.GOOS == "windows" {
104+ // Win64 still passes an empty struct as an argument slot, so this must
105+ // run before the zero-size early return used by the System V path.
106+ return addStructWindows(v, addInt, keepAlive)
107+ }
108+
90 if v.Type().Size() == 0 {109 if v.Type().Size() == 0 {
91 return keepAlive110 return keepAlive
92 }111 }
@@ -112,6 +131,27 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
112 return keepAlive131 return keepAlive
113 }132 }
114 133
134+// addStructWindows passes a struct argument under the Win64 ABI. Aggregates of
135+// exactly 1, 2, 4, or 8 bytes are passed by value in a single integer slot; all
136+// other sizes are passed as a pointer to a caller-allocated copy. Empty structs
137+// fall in the latter group: unlike the System V ABI, Win64 still consumes an
138+// argument slot for them.
139+func addStructWindows(v reflect.Value, addInt func(uintptr), keepAlive []any) []any {
140+ switch v.Type().Size() {
141+ case 1, 2, 4, 8:
142+ var val uintptr
143+ reflect.NewAt(v.Type(), unsafe.Pointer(&val)).Elem().Set(v)
144+ addInt(val)
145+ default:
146+ ptrStruct := reflect.New(v.Type())
147+ ptrStruct.Elem().Set(v)
148+ ptr := ptrStruct.Elem().Addr().UnsafePointer()
149+ keepAlive = append(keepAlive, ptr)
150+ addInt(uintptr(ptr))
151+ }
152+ return keepAlive
153+}
154+
115 func postMerger(t reflect.Type) (passInMemory bool) {155 func postMerger(t reflect.Type) (passInMemory bool) {
116 // (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other156 // (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other
117 // eightbyte isn’t SSEUP, the whole argument is passed in memory.157 // eightbyte isn’t SSEUP, the whole argument is passed in memory.
M struct_test.go +36 -11
@@ -1,7 +1,7 @@
11 // SPDX-License-Identifier: Apache-2.0
22 // SPDX-FileCopyrightText: 2024 The Ebitengine Authors
33
4-//go:build (darwin || linux) && (amd64 || arm64)
4+//go:build (darwin || linux || windows) && (amd64 || arm64)
55
66 package purego_test
77
@@ -15,6 +15,7 @@ import (
1515 "unsafe"
1616
1717 "github.com/ebitengine/purego"
18+ "github.com/ebitengine/purego/internal/load"
1819 )
1920
2021 func TestRegisterFunc_structArgs(t *testing.T) {
@@ -26,10 +27,15 @@ func TestRegisterFunc_structArgs(t *testing.T) {
2627 }
2728 defer os.Remove(libFileName)
2829
29- lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
30+ lib, err := load.OpenLibrary(libFileName)
3031 if err != nil {
31- t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
32+ t.Fatalf("OpenLibrary(%q) failed: %v", libFileName, err)
3233 }
34+ defer func() {
35+ if err := load.CloseLibrary(lib); err != nil {
36+ t.Fatalf("failed to close library: %v", err)
37+ }
38+ }()
3339
3440 const (
3541 expectedUnsigned = 0xdeadbeef
@@ -41,8 +47,9 @@ func TestRegisterFunc_structArgs(t *testing.T) {
4147 )
4248
4349 implementations := []struct {
44- name string
45- register func(fptr any, handle uintptr, name string, goFn any)
50+ name string
51+ usesCallbacks bool
52+ register func(fptr any, handle uintptr, name string, goFn any)
4653 }{
4754 {
4855 name: "RegisterLibFunc",
@@ -51,7 +58,8 @@ func TestRegisterFunc_structArgs(t *testing.T) {
5158 },
5259 },
5360 {
54- name: "GoCallbackFunc",
61+ name: "GoCallbackFunc",
62+ usesCallbacks: true,
5563 register: func(fptr any, handle uintptr, name string, goFn any) {
5664 fnType := reflect.TypeOf(fptr).Elem()
5765 if fnType.NumOut() > 0 {
@@ -69,6 +77,11 @@ func TestRegisterFunc_structArgs(t *testing.T) {
6977 }
7078 for _, imp := range implementations {
7179 imp := imp
80+ if imp.usesCallbacks && runtime.GOOS == "windows" {
81+ // Callbacks on Windows use the stdlib syscall.NewCallback, which does
82+ // not support struct arguments or returns.
83+ continue
84+ }
7285 t.Run(imp.name, func(t *testing.T) {
7386 register := imp.register
7487 {
@@ -865,20 +878,27 @@ func TestRegisterFunc_structReturns(t *testing.T) {
865878 }
866879 defer os.Remove(libFileName)
867880
868- lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
881+ lib, err := load.OpenLibrary(libFileName)
869882 if err != nil {
870- t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
883+ t.Fatalf("OpenLibrary(%q) failed: %v", libFileName, err)
871884 }
885+ defer func() {
886+ if err := load.CloseLibrary(lib); err != nil {
887+ t.Fatalf("failed to close library: %v", err)
888+ }
889+ }()
872890 implementations := []struct {
873- name string
874- register func(fptr any, handle uintptr, name string)
891+ name string
892+ usesCallbacks bool
893+ register func(fptr any, handle uintptr, name string)
875894 }{
876895 {
877896 name: "RegisterLibFunc",
878897 register: purego.RegisterLibFunc,
879898 },
880899 {
881- name: "GoCallbackFunc",
900+ name: "GoCallbackFunc",
901+ usesCallbacks: true,
882902 register: func(fptr any, _ uintptr, _ string) {
883903 fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {
884904 retType := reflect.TypeOf(fptr).Elem().Out(0)
@@ -899,6 +919,11 @@ func TestRegisterFunc_structReturns(t *testing.T) {
899919 }
900920 for _, imp := range implementations {
901921 imp := imp
922+ if imp.usesCallbacks && runtime.GOOS == "windows" {
923+ // Callbacks on Windows use the stdlib syscall.NewCallback, which does
924+ // not support struct arguments or returns.
925+ continue
926+ }
902927 t.Run(imp.name, func(t *testing.T) {
903928 register := imp.register
904929 {
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: Apache-2.01 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: 2024 The Ebitengine Authors2 // SPDX-FileCopyrightText: 2024 The Ebitengine Authors
3 3
4-//go:build (darwin || linux) && (amd64 || arm64)4+//go:build (darwin || linux || windows) && (amd64 || arm64)
5 5
6 package purego_test6 package purego_test
7 7
@@ -15,6 +15,7 @@ import (
15 "unsafe"15 "unsafe"
16 16
17 "github.com/ebitengine/purego"17 "github.com/ebitengine/purego"
18+ "github.com/ebitengine/purego/internal/load"
18 )19 )
19 20
20 func TestRegisterFunc_structArgs(t *testing.T) {21 func TestRegisterFunc_structArgs(t *testing.T) {
@@ -26,10 +27,15 @@ func TestRegisterFunc_structArgs(t *testing.T) {
26 }27 }
27 defer os.Remove(libFileName)28 defer os.Remove(libFileName)
28 29
29- lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)30+ lib, err := load.OpenLibrary(libFileName)
30 if err != nil {31 if err != nil {
31- t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)32+ t.Fatalf("OpenLibrary(%q) failed: %v", libFileName, err)
32 }33 }
34+ defer func() {
35+ if err := load.CloseLibrary(lib); err != nil {
36+ t.Fatalf("failed to close library: %v", err)
37+ }
38+ }()
33 39
34 const (40 const (
35 expectedUnsigned = 0xdeadbeef41 expectedUnsigned = 0xdeadbeef
@@ -41,8 +47,9 @@ func TestRegisterFunc_structArgs(t *testing.T) {
41 )47 )
42 48
43 implementations := []struct {49 implementations := []struct {
44- name string50+ name string
45- register func(fptr any, handle uintptr, name string, goFn any)51+ usesCallbacks bool
52+ register func(fptr any, handle uintptr, name string, goFn any)
46 }{53 }{
47 {54 {
48 name: "RegisterLibFunc",55 name: "RegisterLibFunc",
@@ -51,7 +58,8 @@ func TestRegisterFunc_structArgs(t *testing.T) {
51 },58 },
52 },59 },
53 {60 {
54- name: "GoCallbackFunc",61+ name: "GoCallbackFunc",
62+ usesCallbacks: true,
55 register: func(fptr any, handle uintptr, name string, goFn any) {63 register: func(fptr any, handle uintptr, name string, goFn any) {
56 fnType := reflect.TypeOf(fptr).Elem()64 fnType := reflect.TypeOf(fptr).Elem()
57 if fnType.NumOut() > 0 {65 if fnType.NumOut() > 0 {
@@ -69,6 +77,11 @@ func TestRegisterFunc_structArgs(t *testing.T) {
69 }77 }
70 for _, imp := range implementations {78 for _, imp := range implementations {
71 imp := imp79 imp := imp
80+ if imp.usesCallbacks && runtime.GOOS == "windows" {
81+ // Callbacks on Windows use the stdlib syscall.NewCallback, which does
82+ // not support struct arguments or returns.
83+ continue
84+ }
72 t.Run(imp.name, func(t *testing.T) {85 t.Run(imp.name, func(t *testing.T) {
73 register := imp.register86 register := imp.register
74 {87 {
@@ -865,20 +878,27 @@ func TestRegisterFunc_structReturns(t *testing.T) {
865 }878 }
866 defer os.Remove(libFileName)879 defer os.Remove(libFileName)
867 880
868- lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)881+ lib, err := load.OpenLibrary(libFileName)
869 if err != nil {882 if err != nil {
870- t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)883+ t.Fatalf("OpenLibrary(%q) failed: %v", libFileName, err)
871 }884 }
885+ defer func() {
886+ if err := load.CloseLibrary(lib); err != nil {
887+ t.Fatalf("failed to close library: %v", err)
888+ }
889+ }()
872 implementations := []struct {890 implementations := []struct {
873- name string891+ name string
874- register func(fptr any, handle uintptr, name string)892+ usesCallbacks bool
893+ register func(fptr any, handle uintptr, name string)
875 }{894 }{
876 {895 {
877 name: "RegisterLibFunc",896 name: "RegisterLibFunc",
878 register: purego.RegisterLibFunc,897 register: purego.RegisterLibFunc,
879 },898 },
880 {899 {
881- name: "GoCallbackFunc",900+ name: "GoCallbackFunc",
901+ usesCallbacks: true,
882 register: func(fptr any, _ uintptr, _ string) {902 register: func(fptr any, _ uintptr, _ string) {
883 fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {903 fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {
884 retType := reflect.TypeOf(fptr).Elem().Out(0)904 retType := reflect.TypeOf(fptr).Elem().Out(0)
@@ -899,6 +919,11 @@ func TestRegisterFunc_structReturns(t *testing.T) {
899 }919 }
900 for _, imp := range implementations {920 for _, imp := range implementations {
901 imp := imp921 imp := imp
922+ if imp.usesCallbacks && runtime.GOOS == "windows" {
923+ // Callbacks on Windows use the stdlib syscall.NewCallback, which does
924+ // not support struct arguments or returns.
925+ continue
926+ }
902 t.Run(imp.name, func(t *testing.T) {927 t.Run(imp.name, func(t *testing.T) {
903 register := imp.register928 register := imp.register
904 {929 {
M testdata/structtest/struct_test.c +33 -38
@@ -5,16 +5,11 @@
55 #include <stdio.h>
66 #include <stdlib.h>
77
8-#if defined(__x86_64__) || defined(__aarch64__)
9-typedef int64_t GoInt;
10-typedef uint64_t GoUint;
11-#endif
12-
138 // Empty is empty
149 struct Empty {};
1510
1611 // NoStruct tests that an empty struct doesn't cause issues
17-unsigned long NoStruct(struct Empty e) {
12+uint64_t NoStruct(struct Empty e) {
1813 return 0xdeadbeef;
1914 }
2015
@@ -22,28 +17,28 @@ struct EmptyEmpty {
2217 struct Empty x;
2318 };
2419
25-unsigned long EmptyEmpty(struct EmptyEmpty e) {
20+uint64_t EmptyEmpty(struct EmptyEmpty e) {
2621 return 0xdeadbeef;
2722 }
2823
29-unsigned long EmptyEmptyWithReg(unsigned int x, struct EmptyEmpty e, unsigned int y) {
24+uint64_t EmptyEmptyWithReg(unsigned int x, struct EmptyEmpty e, unsigned int y) {
3025 return (x << 16) | y;
3126 }
3227
3328 // GreaterThan16Bytes is 24 bytes on 64 bit systems
3429 struct GreaterThan16Bytes {
35- long *x, *y, *z;
30+ int64_t *x, *y, *z;
3631 };
3732
3833 // GreaterThan16Bytes is a basic test for structs bigger than 16 bytes
39-unsigned long GreaterThan16Bytes(struct GreaterThan16Bytes g) {
34+uint64_t GreaterThan16Bytes(struct GreaterThan16Bytes g) {
4035 return *g.x + *g.y + *g.z;
4136 }
4237
4338 // AfterRegisters tests to make sure that structs placed on the stack work properly
44-unsigned long AfterRegisters(long a, long b, long c, long d, long e, long f, long g, long h, struct GreaterThan16Bytes bytes) {
45- long registers = a + b + c + d + e + f + g + h;
46- long stack = *bytes.x + *bytes.y + *bytes.z;
39+uint64_t AfterRegisters(intptr_t a, intptr_t b, intptr_t c, intptr_t d, intptr_t e, intptr_t f, intptr_t g, intptr_t h, struct GreaterThan16Bytes bytes) {
40+ intptr_t registers = a + b + c + d + e + f + g + h;
41+ int64_t stack = *bytes.x + *bytes.y + *bytes.z;
4742 if (registers != stack) {
4843 return 0xbadbad;
4944 }
@@ -53,25 +48,25 @@ unsigned long AfterRegisters(long a, long b, long c, long d, long e, long f, lon
5348 return stack;
5449 }
5550
56-unsigned long BeforeRegisters(struct GreaterThan16Bytes bytes, long a, long b) {
51+uint64_t BeforeRegisters(struct GreaterThan16Bytes bytes, int64_t a, int64_t b) {
5752 return *bytes.x + *bytes.y + *bytes.z + a + b;
5853 }
5954
6055 struct GreaterThan16BytesStruct {
6156 struct {
62- long *x, *y, *z;
57+ int64_t *x, *y, *z;
6358 } a ;
6459 };
6560
66-unsigned long GreaterThan16BytesStruct(struct GreaterThan16BytesStruct g) {
61+uint64_t GreaterThan16BytesStruct(struct GreaterThan16BytesStruct g) {
6762 return *(g.a.x) + *(g.a.y) + *(g.a.z);
6863 }
6964
7065 struct IntLessThan16Bytes {
71- long x, y;
66+ int64_t x, y;
7267 };
7368
74-unsigned long IntLessThan16Bytes(struct IntLessThan16Bytes l) {
69+uint64_t IntLessThan16Bytes(struct IntLessThan16Bytes l) {
7570 return l.x + l.y;
7671 }
7772
@@ -201,23 +196,23 @@ struct Short {
201196 unsigned short a, b, c, d;
202197 };
203198
204-unsigned long Short(struct Short s) {
205- return (long)s.a << 48 | (long)s.b << 32 | (long)s.c << 16 | (long)s.d << 0;
199+uint64_t Short(struct Short s) {
200+ return (uint64_t)s.a << 48 | (uint64_t)s.b << 32 | (uint64_t)s.c << 16 | (uint64_t)s.d << 0;
206201 }
207202
208203 struct Int {
209204 unsigned int a, b;
210205 };
211206
212-unsigned long Int(struct Int i) {
213- return (long)i.a << 32 | (long)i.b << 0;
207+uint64_t Int(struct Int i) {
208+ return (uint64_t)i.a << 32 | (uint64_t)i.b << 0;
214209 }
215210
216211 struct Long {
217- unsigned long a;
212+ uint64_t a;
218213 };
219214
220-unsigned long Long(struct Long l) {
215+uint64_t Long(struct Long l) {
221216 return l.a;
222217 }
223218
@@ -327,31 +322,31 @@ struct Content {
327322 struct { double width, height; } size;
328323 };
329324
330-unsigned long InitWithContentRect(int *win, struct Content c, int style, int backing, _Bool flag) {
325+uint64_t InitWithContentRect(int *win, struct Content c, int style, int backing, _Bool flag) {
331326 if (win == 0)
332327 return 0xBAD;
333328 if (!flag)
334329 return 0xF1A6; // FLAG
335- return (unsigned long)(c.point.x + c.point.y + c.size.width + c.size.height) / (style - backing);
330+ return (uint64_t)(c.point.x + c.point.y + c.size.width + c.size.height) / (style - backing);
336331 }
337332
338333 struct GoInt4 {
339- GoInt a, b, c, d;
334+ intptr_t a, b, c, d;
340335 };
341336
342-GoInt GoInt4(struct GoInt4 g) {
337+intptr_t GoInt4(struct GoInt4 g) {
343338 return g.a + g.b - g.c + g.d;
344339 }
345340
346341 struct GoUint4 {
347- GoUint a, b, c, d;
342+ uintptr_t a, b, c, d;
348343 };
349344
350-GoUint GoUint4(struct GoUint4 g) {
345+uintptr_t GoUint4(struct GoUint4 g) {
351346 return g.a + g.b + g.c + g.d;
352347 }
353348
354-GoUint TakeGoUintAndReturn(GoUint a) {
349+uintptr_t TakeGoUintAndReturn(uintptr_t a) {
355350 return a;
356351 }
357352
@@ -395,7 +390,7 @@ uintptr_t AddPointers(struct TwoPointers wrapper) {
395390 // Identity functions for round-trip testing of struct arguments
396391
397392 struct OneInt64 {
398- long long a;
393+ int64_t a;
399394 };
400395
401396 struct OneInt64 IdentityOneInt64(struct OneInt64 s) {
@@ -423,7 +418,7 @@ struct FloatAndInt IdentityFloatAndInt(struct FloatAndInt s) {
423418 }
424419
425420 struct ThreeInt64 {
426- long long a, b, c;
421+ int64_t a, b, c;
427422 };
428423
429424 struct ThreeInt64 IdentityThreeInt64(struct ThreeInt64 s) {
@@ -431,16 +426,16 @@ struct ThreeInt64 IdentityThreeInt64(struct ThreeInt64 s) {
431426 }
432427
433428 struct PtrInt64Ptr {
434- long long *a;
435- long long b;
436- long long *c;
429+ int64_t *a;
430+ int64_t b;
431+ int64_t *c;
437432 };
438433
439434 struct PtrInt64Ptr IdentityPtrInt64Ptr(struct PtrInt64Ptr s) {
440435 return s;
441436 }
442437
443-struct IntLessThan16Bytes IdentityTwoInt64AfterPrims(long long x, double y, struct IntLessThan16Bytes s) {
438+struct IntLessThan16Bytes IdentityTwoInt64AfterPrims(int64_t x, double y, struct IntLessThan16Bytes s) {
444439 return s;
445440 }
446441
@@ -449,7 +444,7 @@ struct FloatLessThan16Bytes IdentityTwoFloat32AfterFloats(double x, double y, st
449444 }
450445
451446 struct Mixed5Args {
452- long long *a;
447+ int64_t *a;
453448 int32_t b;
454449 float c;
455450 int32_t d;
@@ -5,16 +5,11 @@
5 #include <stdio.h>5 #include <stdio.h>
6 #include <stdlib.h>6 #include <stdlib.h>
7 7
8-#if defined(__x86_64__) || defined(__aarch64__)
9-typedef int64_t GoInt;
10-typedef uint64_t GoUint;
11-#endif
12-
13 // Empty is empty8 // Empty is empty
14 struct Empty {};9 struct Empty {};
15 10
16 // NoStruct tests that an empty struct doesn't cause issues11 // NoStruct tests that an empty struct doesn't cause issues
17-unsigned long NoStruct(struct Empty e) {12+uint64_t NoStruct(struct Empty e) {
18 return 0xdeadbeef;13 return 0xdeadbeef;
19 }14 }
20 15
@@ -22,28 +17,28 @@ struct EmptyEmpty {
22 struct Empty x;17 struct Empty x;
23 };18 };
24 19
25-unsigned long EmptyEmpty(struct EmptyEmpty e) {20+uint64_t EmptyEmpty(struct EmptyEmpty e) {
26 return 0xdeadbeef;21 return 0xdeadbeef;
27 }22 }
28 23
29-unsigned long EmptyEmptyWithReg(unsigned int x, struct EmptyEmpty e, unsigned int y) {24+uint64_t EmptyEmptyWithReg(unsigned int x, struct EmptyEmpty e, unsigned int y) {
30 return (x << 16) | y;25 return (x << 16) | y;
31 }26 }
32 27
33 // GreaterThan16Bytes is 24 bytes on 64 bit systems28 // GreaterThan16Bytes is 24 bytes on 64 bit systems
34 struct GreaterThan16Bytes {29 struct GreaterThan16Bytes {
35- long *x, *y, *z;30+ int64_t *x, *y, *z;
36 };31 };
37 32
38 // GreaterThan16Bytes is a basic test for structs bigger than 16 bytes33 // GreaterThan16Bytes is a basic test for structs bigger than 16 bytes
39-unsigned long GreaterThan16Bytes(struct GreaterThan16Bytes g) {34+uint64_t GreaterThan16Bytes(struct GreaterThan16Bytes g) {
40 return *g.x + *g.y + *g.z;35 return *g.x + *g.y + *g.z;
41 }36 }
42 37
43 // AfterRegisters tests to make sure that structs placed on the stack work properly38 // AfterRegisters tests to make sure that structs placed on the stack work properly
44-unsigned long AfterRegisters(long a, long b, long c, long d, long e, long f, long g, long h, struct GreaterThan16Bytes bytes) {39+uint64_t AfterRegisters(intptr_t a, intptr_t b, intptr_t c, intptr_t d, intptr_t e, intptr_t f, intptr_t g, intptr_t h, struct GreaterThan16Bytes bytes) {
45- long registers = a + b + c + d + e + f + g + h;40+ intptr_t registers = a + b + c + d + e + f + g + h;
46- long stack = *bytes.x + *bytes.y + *bytes.z;41+ int64_t stack = *bytes.x + *bytes.y + *bytes.z;
47 if (registers != stack) {42 if (registers != stack) {
48 return 0xbadbad;43 return 0xbadbad;
49 }44 }
@@ -53,25 +48,25 @@ unsigned long AfterRegisters(long a, long b, long c, long d, long e, long f, lon
53 return stack;48 return stack;
54 }49 }
55 50
56-unsigned long BeforeRegisters(struct GreaterThan16Bytes bytes, long a, long b) {51+uint64_t BeforeRegisters(struct GreaterThan16Bytes bytes, int64_t a, int64_t b) {
57 return *bytes.x + *bytes.y + *bytes.z + a + b;52 return *bytes.x + *bytes.y + *bytes.z + a + b;
58 }53 }
59 54
60 struct GreaterThan16BytesStruct {55 struct GreaterThan16BytesStruct {
61 struct {56 struct {
62- long *x, *y, *z;57+ int64_t *x, *y, *z;
63 } a ;58 } a ;
64 };59 };
65 60
66-unsigned long GreaterThan16BytesStruct(struct GreaterThan16BytesStruct g) {61+uint64_t GreaterThan16BytesStruct(struct GreaterThan16BytesStruct g) {
67 return *(g.a.x) + *(g.a.y) + *(g.a.z);62 return *(g.a.x) + *(g.a.y) + *(g.a.z);
68 }63 }
69 64
70 struct IntLessThan16Bytes {65 struct IntLessThan16Bytes {
71- long x, y;66+ int64_t x, y;
72 };67 };
73 68
74-unsigned long IntLessThan16Bytes(struct IntLessThan16Bytes l) {69+uint64_t IntLessThan16Bytes(struct IntLessThan16Bytes l) {
75 return l.x + l.y;70 return l.x + l.y;
76 }71 }
77 72
@@ -201,23 +196,23 @@ struct Short {
201 unsigned short a, b, c, d;196 unsigned short a, b, c, d;
202 };197 };
203 198
204-unsigned long Short(struct Short s) {199+uint64_t Short(struct Short s) {
205- return (long)s.a << 48 | (long)s.b << 32 | (long)s.c << 16 | (long)s.d << 0;200+ return (uint64_t)s.a << 48 | (uint64_t)s.b << 32 | (uint64_t)s.c << 16 | (uint64_t)s.d << 0;
206 }201 }
207 202
208 struct Int {203 struct Int {
209 unsigned int a, b;204 unsigned int a, b;
210 };205 };
211 206
212-unsigned long Int(struct Int i) {207+uint64_t Int(struct Int i) {
213- return (long)i.a << 32 | (long)i.b << 0;208+ return (uint64_t)i.a << 32 | (uint64_t)i.b << 0;
214 }209 }
215 210
216 struct Long {211 struct Long {
217- unsigned long a;212+ uint64_t a;
218 };213 };
219 214
220-unsigned long Long(struct Long l) {215+uint64_t Long(struct Long l) {
221 return l.a;216 return l.a;
222 }217 }
223 218
@@ -327,31 +322,31 @@ struct Content {
327 struct { double width, height; } size;322 struct { double width, height; } size;
328 };323 };
329 324
330-unsigned long InitWithContentRect(int *win, struct Content c, int style, int backing, _Bool flag) {325+uint64_t InitWithContentRect(int *win, struct Content c, int style, int backing, _Bool flag) {
331 if (win == 0)326 if (win == 0)
332 return 0xBAD;327 return 0xBAD;
333 if (!flag)328 if (!flag)
334 return 0xF1A6; // FLAG329 return 0xF1A6; // FLAG
335- return (unsigned long)(c.point.x + c.point.y + c.size.width + c.size.height) / (style - backing);330+ return (uint64_t)(c.point.x + c.point.y + c.size.width + c.size.height) / (style - backing);
336 }331 }
337 332
338 struct GoInt4 {333 struct GoInt4 {
339- GoInt a, b, c, d;334+ intptr_t a, b, c, d;
340 };335 };
341 336
342-GoInt GoInt4(struct GoInt4 g) {337+intptr_t GoInt4(struct GoInt4 g) {
343 return g.a + g.b - g.c + g.d;338 return g.a + g.b - g.c + g.d;
344 }339 }
345 340
346 struct GoUint4 {341 struct GoUint4 {
347- GoUint a, b, c, d;342+ uintptr_t a, b, c, d;
348 };343 };
349 344
350-GoUint GoUint4(struct GoUint4 g) {345+uintptr_t GoUint4(struct GoUint4 g) {
351 return g.a + g.b + g.c + g.d;346 return g.a + g.b + g.c + g.d;
352 }347 }
353 348
354-GoUint TakeGoUintAndReturn(GoUint a) {349+uintptr_t TakeGoUintAndReturn(uintptr_t a) {
355 return a;350 return a;
356 }351 }
357 352
@@ -395,7 +390,7 @@ uintptr_t AddPointers(struct TwoPointers wrapper) {
395 // Identity functions for round-trip testing of struct arguments390 // Identity functions for round-trip testing of struct arguments
396 391
397 struct OneInt64 {392 struct OneInt64 {
398- long long a;393+ int64_t a;
399 };394 };
400 395
401 struct OneInt64 IdentityOneInt64(struct OneInt64 s) {396 struct OneInt64 IdentityOneInt64(struct OneInt64 s) {
@@ -423,7 +418,7 @@ struct FloatAndInt IdentityFloatAndInt(struct FloatAndInt s) {
423 }418 }
424 419
425 struct ThreeInt64 {420 struct ThreeInt64 {
426- long long a, b, c;421+ int64_t a, b, c;
427 };422 };
428 423
429 struct ThreeInt64 IdentityThreeInt64(struct ThreeInt64 s) {424 struct ThreeInt64 IdentityThreeInt64(struct ThreeInt64 s) {
@@ -431,16 +426,16 @@ struct ThreeInt64 IdentityThreeInt64(struct ThreeInt64 s) {
431 }426 }
432 427
433 struct PtrInt64Ptr {428 struct PtrInt64Ptr {
434- long long *a;429+ int64_t *a;
435- long long b;430+ int64_t b;
436- long long *c;431+ int64_t *c;
437 };432 };
438 433
439 struct PtrInt64Ptr IdentityPtrInt64Ptr(struct PtrInt64Ptr s) {434 struct PtrInt64Ptr IdentityPtrInt64Ptr(struct PtrInt64Ptr s) {
440 return s;435 return s;
441 }436 }
442 437
443-struct IntLessThan16Bytes IdentityTwoInt64AfterPrims(long long x, double y, struct IntLessThan16Bytes s) {438+struct IntLessThan16Bytes IdentityTwoInt64AfterPrims(int64_t x, double y, struct IntLessThan16Bytes s) {
444 return s;439 return s;
445 }440 }
446 441
@@ -449,7 +444,7 @@ struct FloatLessThan16Bytes IdentityTwoFloat32AfterFloats(double x, double y, st
449 }444 }
450 445
451 struct Mixed5Args {446 struct Mixed5Args {
452- long long *a;447+ int64_t *a;
453 int32_t b;448 int32_t b;
454 float c;449 float c;
455 int32_t d;450 int32_t d;

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