Fix alignment of stack args on macOS (#310)
Fixes #309
- commit
- 9059adfae616e486aa4145d6f4d5fefaa1b47a61
- author
- TotallyGamerJet · · 2025-04-11 14:46 UTC
- committer
- GitHub · · 2025-04-11 14:46 UTC
- parent
- d99d2be
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown - refs
- tag: v0.9.0-alpha.3
12 files · +189 -58
M .github/workflows/test.yml +5 -2
| @@ -34,10 +34,13 @@ jobs: | |||
| 34 | 34 | sudo apt-get update | |
| 35 | 35 | sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user | |
| 36 | 36 | ||
| 37 | + | - name: Set up the prerequisites | |
| 38 | + | if: runner.os == 'Windows' | |
| 39 | + | uses: msys2/setup-msys2@v2 | |
| 40 | + | ||
| 37 | 41 | - name: go vet | |
| 38 | - | if: runner.os != 'Windows' && runner.os != 'macOS' | |
| 39 | 42 | run: | | |
| 40 | - | go vet -v ./... | |
| 43 | + | env CGO_ENABLED=0 go vet -v ./... | |
| 41 | 44 | ||
| 42 | 45 | - name: go build | |
| 43 | 46 | run: | | |
| @@ -34,10 +34,13 @@ jobs: | |||||
| 34 | sudo apt-get update | 34 | sudo apt-get update | ||
| 35 | sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user | 35 | sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user | ||
| 36 | 36 | ||||
| 37 | + | - name: Set up the prerequisites | |||
| 38 | + | if: runner.os == 'Windows' | |||
| 39 | + | uses: msys2/setup-msys2@v2 | |||
| 40 | + | ||||
| 37 | - name: go vet | 41 | - name: go vet | ||
| 38 | - | if: runner.os != 'Windows' && runner.os != 'macOS' | |||
| 39 | run: | | 42 | run: | | ||
| 40 | - | go vet -v ./... | 43 | + | env CGO_ENABLED=0 go vet -v ./... |
| 41 | 44 | ||||
| 42 | - name: go build | 45 | - name: go build | ||
| 43 | run: | | 46 | run: | | ||
M dlfcn_test.go +0 -43
| @@ -6,13 +6,8 @@ | |||
| 6 | 6 | package purego_test | |
| 7 | 7 | ||
| 8 | 8 | import ( | |
| 9 | - | "errors" | |
| 10 | - | "fmt" | |
| 11 | 9 | "os" | |
| 12 | - | "os/exec" | |
| 13 | 10 | "path/filepath" | |
| 14 | - | "runtime" | |
| 15 | - | "strings" | |
| 16 | 11 | "testing" | |
| 17 | 12 | "unsafe" | |
| 18 | 13 | ||
| @@ -42,44 +37,6 @@ func TestNestedDlopenCall(t *testing.T) { | |||
| 42 | 37 | purego.Dlclose(lib) | |
| 43 | 38 | } | |
| 44 | 39 | ||
| 45 | - | func buildSharedLib(compilerEnv, libFile string, sources ...string) error { | |
| 46 | - | out, err := exec.Command("go", "env", compilerEnv).Output() | |
| 47 | - | if err != nil { | |
| 48 | - | return fmt.Errorf("go env %s error: %w", compilerEnv, err) | |
| 49 | - | } | |
| 50 | - | ||
| 51 | - | compiler := strings.TrimSpace(string(out)) | |
| 52 | - | if compiler == "" { | |
| 53 | - | return errors.New("compiler not found") | |
| 54 | - | } | |
| 55 | - | ||
| 56 | - | args := []string{"-shared", "-Wall", "-Werror", "-fPIC", "-o", libFile} | |
| 57 | - | if runtime.GOARCH == "386" { | |
| 58 | - | args = append(args, "-m32") | |
| 59 | - | } | |
| 60 | - | // macOS arm64 can run amd64 tests through Rossetta. | |
| 61 | - | // Build the shared library based on the GOARCH and not | |
| 62 | - | // the default behavior of the compiler. | |
| 63 | - | if runtime.GOOS == "darwin" { | |
| 64 | - | var arch string | |
| 65 | - | switch runtime.GOARCH { | |
| 66 | - | case "arm64": | |
| 67 | - | arch = "arm64" | |
| 68 | - | case "amd64": | |
| 69 | - | arch = "x86_64" | |
| 70 | - | default: | |
| 71 | - | return fmt.Errorf("unknown macOS architecture %s", runtime.GOARCH) | |
| 72 | - | } | |
| 73 | - | args = append(args, "-arch", arch) | |
| 74 | - | } | |
| 75 | - | cmd := exec.Command(compiler, append(args, sources...)...) | |
| 76 | - | if out, err := cmd.CombinedOutput(); err != nil { | |
| 77 | - | return fmt.Errorf("compile lib: %w\n%q\n%s", err, cmd, string(out)) | |
| 78 | - | } | |
| 79 | - | ||
| 80 | - | return nil | |
| 81 | - | } | |
| 82 | - | ||
| 83 | 40 | func TestSyscallN(t *testing.T) { | |
| 84 | 41 | var dlsym uintptr | |
| 85 | 42 | var err error | |
| @@ -6,13 +6,8 @@ | |||||
| 6 | package purego_test | 6 | package purego_test | ||
| 7 | 7 | ||||
| 8 | import ( | 8 | import ( | ||
| 9 | - | "errors" | |||
| 10 | - | "fmt" | |||
| 11 | "os" | 9 | "os" | ||
| 12 | - | "os/exec" | |||
| 13 | "path/filepath" | 10 | "path/filepath" | ||
| 14 | - | "runtime" | |||
| 15 | - | "strings" | |||
| 16 | "testing" | 11 | "testing" | ||
| 17 | "unsafe" | 12 | "unsafe" | ||
| 18 | 13 | ||||
| @@ -42,44 +37,6 @@ func TestNestedDlopenCall(t *testing.T) { | |||||
| 42 | purego.Dlclose(lib) | 37 | purego.Dlclose(lib) | ||
| 43 | } | 38 | } | ||
| 44 | 39 | ||||
| 45 | - | func buildSharedLib(compilerEnv, libFile string, sources ...string) error { | |||
| 46 | - | out, err := exec.Command("go", "env", compilerEnv).Output() | |||
| 47 | - | if err != nil { | |||
| 48 | - | return fmt.Errorf("go env %s error: %w", compilerEnv, err) | |||
| 49 | - | } | |||
| 50 | - | ||||
| 51 | - | compiler := strings.TrimSpace(string(out)) | |||
| 52 | - | if compiler == "" { | |||
| 53 | - | return errors.New("compiler not found") | |||
| 54 | - | } | |||
| 55 | - | ||||
| 56 | - | args := []string{"-shared", "-Wall", "-Werror", "-fPIC", "-o", libFile} | |||
| 57 | - | if runtime.GOARCH == "386" { | |||
| 58 | - | args = append(args, "-m32") | |||
| 59 | - | } | |||
| 60 | - | // macOS arm64 can run amd64 tests through Rossetta. | |||
| 61 | - | // Build the shared library based on the GOARCH and not | |||
| 62 | - | // the default behavior of the compiler. | |||
| 63 | - | if runtime.GOOS == "darwin" { | |||
| 64 | - | var arch string | |||
| 65 | - | switch runtime.GOARCH { | |||
| 66 | - | case "arm64": | |||
| 67 | - | arch = "arm64" | |||
| 68 | - | case "amd64": | |||
| 69 | - | arch = "x86_64" | |||
| 70 | - | default: | |||
| 71 | - | return fmt.Errorf("unknown macOS architecture %s", runtime.GOARCH) | |||
| 72 | - | } | |||
| 73 | - | args = append(args, "-arch", arch) | |||
| 74 | - | } | |||
| 75 | - | cmd := exec.Command(compiler, append(args, sources...)...) | |||
| 76 | - | if out, err := cmd.CombinedOutput(); err != nil { | |||
| 77 | - | return fmt.Errorf("compile lib: %w\n%q\n%s", err, cmd, string(out)) | |||
| 78 | - | } | |||
| 79 | - | ||||
| 80 | - | return nil | |||
| 81 | - | } | |||
| 82 | - | ||||
| 83 | func TestSyscallN(t *testing.T) { | 40 | func TestSyscallN(t *testing.T) { | ||
| 84 | var dlsym uintptr | 41 | var dlsym uintptr | ||
| 85 | var err error | 42 | var err error | ||
M func.go +28 -4
| @@ -10,6 +10,7 @@ import ( | |||
| 10 | 10 | "math" | |
| 11 | 11 | "reflect" | |
| 12 | 12 | "runtime" | |
| 13 | + | "strconv" | |
| 13 | 14 | "sync" | |
| 14 | 15 | "unsafe" | |
| 15 | 16 | ||
| @@ -161,7 +162,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 161 | 162 | if is32bit { | |
| 162 | 163 | panic("purego: floats only supported on 64bit platforms") | |
| 163 | 164 | } | |
| 164 | - | if floats < numOfFloats { | |
| 165 | + | if floats < numOfFloatRegisters { | |
| 165 | 166 | floats++ | |
| 166 | 167 | } else { | |
| 167 | 168 | stack++ | |
| @@ -206,8 +207,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 206 | 207 | } | |
| 207 | 208 | v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) { | |
| 208 | 209 | var sysargs [maxArgs]uintptr | |
| 209 | - | stack := sysargs[numOfIntegerRegisters():] | |
| 210 | - | var floats [numOfFloats]uintptr | |
| 210 | + | var floats [numOfFloatRegisters]uintptr | |
| 211 | 211 | var numInts int | |
| 212 | 212 | var numFloats int | |
| 213 | 213 | var numStack int | |
| @@ -215,7 +215,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 215 | 215 | if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { | |
| 216 | 216 | // Windows arm64 uses the same calling convention as macOS and Linux | |
| 217 | 217 | addStack = func(x uintptr) { | |
| 218 | - | stack[numStack] = x | |
| 218 | + | sysargs[numOfIntegerRegisters()+numStack] = x | |
| 219 | 219 | numStack++ | |
| 220 | 220 | } | |
| 221 | 221 | addInt = func(x uintptr) { | |
| @@ -280,6 +280,30 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 280 | 280 | } | |
| 281 | 281 | continue | |
| 282 | 282 | } | |
| 283 | + | if runtime.GOARCH == "arm64" && runtime.GOOS == "darwin" && | |
| 284 | + | (numInts >= numOfIntegerRegisters() || numFloats >= numOfFloatRegisters) && v.Kind() != reflect.Struct { // hit the stack | |
| 285 | + | fields := make([]reflect.StructField, len(args[i:])) | |
| 286 | + | ||
| 287 | + | for j, val := range args[i:] { | |
| 288 | + | if val.Kind() == reflect.String { | |
| 289 | + | ptr := strings.CString(v.String()) | |
| 290 | + | keepAlive = append(keepAlive, ptr) | |
| 291 | + | val = reflect.ValueOf(ptr) | |
| 292 | + | args[i+j] = val | |
| 293 | + | } | |
| 294 | + | fields[j] = reflect.StructField{ | |
| 295 | + | Name: "X" + strconv.Itoa(j), | |
| 296 | + | Type: val.Type(), | |
| 297 | + | } | |
| 298 | + | } | |
| 299 | + | structType := reflect.StructOf(fields) | |
| 300 | + | structInstance := reflect.New(structType).Elem() | |
| 301 | + | for j, val := range args[i:] { | |
| 302 | + | structInstance.Field(j).Set(val) | |
| 303 | + | } | |
| 304 | + | placeRegisters(structInstance, addFloat, addInt) | |
| 305 | + | break | |
| 306 | + | } | |
| 283 | 307 | keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) | |
| 284 | 308 | } | |
| 285 | 309 | ||
| @@ -10,6 +10,7 @@ import ( | |||||
| 10 | "math" | 10 | "math" | ||
| 11 | "reflect" | 11 | "reflect" | ||
| 12 | "runtime" | 12 | "runtime" | ||
| 13 | + | "strconv" | |||
| 13 | "sync" | 14 | "sync" | ||
| 14 | "unsafe" | 15 | "unsafe" | ||
| 15 | 16 | ||||
| @@ -161,7 +162,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 161 | if is32bit { | 162 | if is32bit { | ||
| 162 | panic("purego: floats only supported on 64bit platforms") | 163 | panic("purego: floats only supported on 64bit platforms") | ||
| 163 | } | 164 | } | ||
| 164 | - | if floats < numOfFloats { | 165 | + | if floats < numOfFloatRegisters { |
| 165 | floats++ | 166 | floats++ | ||
| 166 | } else { | 167 | } else { | ||
| 167 | stack++ | 168 | stack++ | ||
| @@ -206,8 +207,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 206 | } | 207 | } | ||
| 207 | v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) { | 208 | v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) { | ||
| 208 | var sysargs [maxArgs]uintptr | 209 | var sysargs [maxArgs]uintptr | ||
| 209 | - | stack := sysargs[numOfIntegerRegisters():] | 210 | + | var floats [numOfFloatRegisters]uintptr |
| 210 | - | var floats [numOfFloats]uintptr | |||
| 211 | var numInts int | 211 | var numInts int | ||
| 212 | var numFloats int | 212 | var numFloats int | ||
| 213 | var numStack int | 213 | var numStack int | ||
| @@ -215,7 +215,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 215 | if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { | 215 | if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { | ||
| 216 | // Windows arm64 uses the same calling convention as macOS and Linux | 216 | // Windows arm64 uses the same calling convention as macOS and Linux | ||
| 217 | addStack = func(x uintptr) { | 217 | addStack = func(x uintptr) { | ||
| 218 | - | stack[numStack] = x | 218 | + | sysargs[numOfIntegerRegisters()+numStack] = x |
| 219 | numStack++ | 219 | numStack++ | ||
| 220 | } | 220 | } | ||
| 221 | addInt = func(x uintptr) { | 221 | addInt = func(x uintptr) { | ||
| @@ -280,6 +280,30 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 280 | } | 280 | } | ||
| 281 | continue | 281 | continue | ||
| 282 | } | 282 | } | ||
| 283 | + | if runtime.GOARCH == "arm64" && runtime.GOOS == "darwin" && | |||
| 284 | + | (numInts >= numOfIntegerRegisters() || numFloats >= numOfFloatRegisters) && v.Kind() != reflect.Struct { // hit the stack | |||
| 285 | + | fields := make([]reflect.StructField, len(args[i:])) | |||
| 286 | + | ||||
| 287 | + | for j, val := range args[i:] { | |||
| 288 | + | if val.Kind() == reflect.String { | |||
| 289 | + | ptr := strings.CString(v.String()) | |||
| 290 | + | keepAlive = append(keepAlive, ptr) | |||
| 291 | + | val = reflect.ValueOf(ptr) | |||
| 292 | + | args[i+j] = val | |||
| 293 | + | } | |||
| 294 | + | fields[j] = reflect.StructField{ | |||
| 295 | + | Name: "X" + strconv.Itoa(j), | |||
| 296 | + | Type: val.Type(), | |||
| 297 | + | } | |||
| 298 | + | } | |||
| 299 | + | structType := reflect.StructOf(fields) | |||
| 300 | + | structInstance := reflect.New(structType).Elem() | |||
| 301 | + | for j, val := range args[i:] { | |||
| 302 | + | structInstance.Field(j).Set(val) | |||
| 303 | + | } | |||
| 304 | + | placeRegisters(structInstance, addFloat, addInt) | |||
| 305 | + | break | |||
| 306 | + | } | |||
| 283 | keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) | 307 | keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack) | ||
| 284 | } | 308 | } | ||
| 285 | 309 | ||||
M func_test.go +94 -0
| @@ -4,8 +4,12 @@ | |||
| 4 | 4 | package purego_test | |
| 5 | 5 | ||
| 6 | 6 | import ( | |
| 7 | + | "errors" | |
| 7 | 8 | "fmt" | |
| 9 | + | "os/exec" | |
| 10 | + | "path/filepath" | |
| 8 | 11 | "runtime" | |
| 12 | + | "strings" | |
| 9 | 13 | "testing" | |
| 10 | 14 | "unsafe" | |
| 11 | 15 | ||
| @@ -130,3 +134,93 @@ func TestRegisterLibFunc_Bool(t *testing.T) { | |||
| 130 | 134 | t.Errorf("runFalse failed. got %t but wanted %t", got, expected) | |
| 131 | 135 | } | |
| 132 | 136 | } | |
| 137 | + | ||
| 138 | + | func TestABI(t *testing.T) { | |
| 139 | + | if runtime.GOOS == "windows" && runtime.GOARCH == "386" { | |
| 140 | + | t.Skip("need a 32bit gcc to run this test") // TODO: find 32bit gcc for test | |
| 141 | + | } | |
| 142 | + | libFileName := filepath.Join(t.TempDir(), "abitest.so") | |
| 143 | + | t.Logf("Build %v", libFileName) | |
| 144 | + | ||
| 145 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "abitest", "abi_test.c")); err != nil { | |
| 146 | + | t.Fatal(err) | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | lib, err := load.OpenLibrary(libFileName) | |
| 150 | + | if err != nil { | |
| 151 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |
| 152 | + | } | |
| 153 | + | defer func() { | |
| 154 | + | if err := load.CloseLibrary(lib); err != nil { | |
| 155 | + | t.Fatalf("failed to close library: %s", err) | |
| 156 | + | } | |
| 157 | + | }() | |
| 158 | + | { | |
| 159 | + | const cName = "stack_uint8_t" | |
| 160 | + | const expect = 2047 | |
| 161 | + | var fn func(a, b, c, d, e, f, g, h uint32, i, j uint8, k uint32) uint32 | |
| 162 | + | purego.RegisterLibFunc(&fn, lib, cName) | |
| 163 | + | res := fn(256, 512, 4, 8, 16, 32, 64, 128, 1, 2, 1024) | |
| 164 | + | if res != expect { | |
| 165 | + | t.Fatalf("%s: got %d, want %d", cName, res, expect) | |
| 166 | + | } | |
| 167 | + | } | |
| 168 | + | { | |
| 169 | + | const cName = "reg_uint8_t" | |
| 170 | + | const expect = 1027 | |
| 171 | + | var fn func(a, b uint8, c uint32) uint32 | |
| 172 | + | purego.RegisterLibFunc(&fn, lib, cName) | |
| 173 | + | res := fn(1, 2, 1024) | |
| 174 | + | if res != expect { | |
| 175 | + | t.Fatalf("%s: got %d, want %d", cName, res, expect) | |
| 176 | + | } | |
| 177 | + | } | |
| 178 | + | { | |
| 179 | + | const cName = "stack_string" | |
| 180 | + | const expect = 255 | |
| 181 | + | var fn func(a, b, c, d, e, f, g, h uint32, i string) uint32 | |
| 182 | + | purego.RegisterLibFunc(&fn, lib, cName) | |
| 183 | + | res := fn(1, 2, 4, 8, 16, 32, 64, 128, "test") | |
| 184 | + | if res != expect { | |
| 185 | + | t.Fatalf("%s: got %d, want %d", cName, res, expect) | |
| 186 | + | } | |
| 187 | + | } | |
| 188 | + | } | |
| 189 | + | ||
| 190 | + | func buildSharedLib(compilerEnv, libFile string, sources ...string) error { | |
| 191 | + | out, err := exec.Command("go", "env", compilerEnv).Output() | |
| 192 | + | if err != nil { | |
| 193 | + | return fmt.Errorf("go env %s error: %w", compilerEnv, err) | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | compiler := strings.TrimSpace(string(out)) | |
| 197 | + | if compiler == "" { | |
| 198 | + | return errors.New("compiler not found") | |
| 199 | + | } | |
| 200 | + | ||
| 201 | + | args := []string{"-shared", "-Wall", "-Werror", "-fPIC", "-o", libFile} | |
| 202 | + | if runtime.GOARCH == "386" { | |
| 203 | + | args = append(args, "-m32") | |
| 204 | + | } | |
| 205 | + | // macOS arm64 can run amd64 tests through Rossetta. | |
| 206 | + | // Build the shared library based on the GOARCH and not | |
| 207 | + | // the default behavior of the compiler. | |
| 208 | + | if runtime.GOOS == "darwin" { | |
| 209 | + | var arch string | |
| 210 | + | switch runtime.GOARCH { | |
| 211 | + | case "arm64": | |
| 212 | + | arch = "arm64" | |
| 213 | + | case "amd64": | |
| 214 | + | arch = "x86_64" | |
| 215 | + | default: | |
| 216 | + | return fmt.Errorf("unknown macOS architecture %s", runtime.GOARCH) | |
| 217 | + | } | |
| 218 | + | args = append(args, "-arch", arch) | |
| 219 | + | } | |
| 220 | + | cmd := exec.Command(compiler, append(args, sources...)...) | |
| 221 | + | if out, err := cmd.CombinedOutput(); err != nil { | |
| 222 | + | return fmt.Errorf("compile lib: %w\n%q\n%s", err, cmd, string(out)) | |
| 223 | + | } | |
| 224 | + | ||
| 225 | + | return nil | |
| 226 | + | } | |
| @@ -4,8 +4,12 @@ | |||||
| 4 | package purego_test | 4 | package purego_test | ||
| 5 | 5 | ||||
| 6 | import ( | 6 | import ( | ||
| 7 | + | "errors" | |||
| 7 | "fmt" | 8 | "fmt" | ||
| 9 | + | "os/exec" | |||
| 10 | + | "path/filepath" | |||
| 8 | "runtime" | 11 | "runtime" | ||
| 12 | + | "strings" | |||
| 9 | "testing" | 13 | "testing" | ||
| 10 | "unsafe" | 14 | "unsafe" | ||
| 11 | 15 | ||||
| @@ -130,3 +134,93 @@ func TestRegisterLibFunc_Bool(t *testing.T) { | |||||
| 130 | t.Errorf("runFalse failed. got %t but wanted %t", got, expected) | 134 | t.Errorf("runFalse failed. got %t but wanted %t", got, expected) | ||
| 131 | } | 135 | } | ||
| 132 | } | 136 | } | ||
| 137 | + | ||||
| 138 | + | func TestABI(t *testing.T) { | |||
| 139 | + | if runtime.GOOS == "windows" && runtime.GOARCH == "386" { | |||
| 140 | + | t.Skip("need a 32bit gcc to run this test") // TODO: find 32bit gcc for test | |||
| 141 | + | } | |||
| 142 | + | libFileName := filepath.Join(t.TempDir(), "abitest.so") | |||
| 143 | + | t.Logf("Build %v", libFileName) | |||
| 144 | + | ||||
| 145 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "abitest", "abi_test.c")); err != nil { | |||
| 146 | + | t.Fatal(err) | |||
| 147 | + | } | |||
| 148 | + | ||||
| 149 | + | lib, err := load.OpenLibrary(libFileName) | |||
| 150 | + | if err != nil { | |||
| 151 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |||
| 152 | + | } | |||
| 153 | + | defer func() { | |||
| 154 | + | if err := load.CloseLibrary(lib); err != nil { | |||
| 155 | + | t.Fatalf("failed to close library: %s", err) | |||
| 156 | + | } | |||
| 157 | + | }() | |||
| 158 | + | { | |||
| 159 | + | const cName = "stack_uint8_t" | |||
| 160 | + | const expect = 2047 | |||
| 161 | + | var fn func(a, b, c, d, e, f, g, h uint32, i, j uint8, k uint32) uint32 | |||
| 162 | + | purego.RegisterLibFunc(&fn, lib, cName) | |||
| 163 | + | res := fn(256, 512, 4, 8, 16, 32, 64, 128, 1, 2, 1024) | |||
| 164 | + | if res != expect { | |||
| 165 | + | t.Fatalf("%s: got %d, want %d", cName, res, expect) | |||
| 166 | + | } | |||
| 167 | + | } | |||
| 168 | + | { | |||
| 169 | + | const cName = "reg_uint8_t" | |||
| 170 | + | const expect = 1027 | |||
| 171 | + | var fn func(a, b uint8, c uint32) uint32 | |||
| 172 | + | purego.RegisterLibFunc(&fn, lib, cName) | |||
| 173 | + | res := fn(1, 2, 1024) | |||
| 174 | + | if res != expect { | |||
| 175 | + | t.Fatalf("%s: got %d, want %d", cName, res, expect) | |||
| 176 | + | } | |||
| 177 | + | } | |||
| 178 | + | { | |||
| 179 | + | const cName = "stack_string" | |||
| 180 | + | const expect = 255 | |||
| 181 | + | var fn func(a, b, c, d, e, f, g, h uint32, i string) uint32 | |||
| 182 | + | purego.RegisterLibFunc(&fn, lib, cName) | |||
| 183 | + | res := fn(1, 2, 4, 8, 16, 32, 64, 128, "test") | |||
| 184 | + | if res != expect { | |||
| 185 | + | t.Fatalf("%s: got %d, want %d", cName, res, expect) | |||
| 186 | + | } | |||
| 187 | + | } | |||
| 188 | + | } | |||
| 189 | + | ||||
| 190 | + | func buildSharedLib(compilerEnv, libFile string, sources ...string) error { | |||
| 191 | + | out, err := exec.Command("go", "env", compilerEnv).Output() | |||
| 192 | + | if err != nil { | |||
| 193 | + | return fmt.Errorf("go env %s error: %w", compilerEnv, err) | |||
| 194 | + | } | |||
| 195 | + | ||||
| 196 | + | compiler := strings.TrimSpace(string(out)) | |||
| 197 | + | if compiler == "" { | |||
| 198 | + | return errors.New("compiler not found") | |||
| 199 | + | } | |||
| 200 | + | ||||
| 201 | + | args := []string{"-shared", "-Wall", "-Werror", "-fPIC", "-o", libFile} | |||
| 202 | + | if runtime.GOARCH == "386" { | |||
| 203 | + | args = append(args, "-m32") | |||
| 204 | + | } | |||
| 205 | + | // macOS arm64 can run amd64 tests through Rossetta. | |||
| 206 | + | // Build the shared library based on the GOARCH and not | |||
| 207 | + | // the default behavior of the compiler. | |||
| 208 | + | if runtime.GOOS == "darwin" { | |||
| 209 | + | var arch string | |||
| 210 | + | switch runtime.GOARCH { | |||
| 211 | + | case "arm64": | |||
| 212 | + | arch = "arm64" | |||
| 213 | + | case "amd64": | |||
| 214 | + | arch = "x86_64" | |||
| 215 | + | default: | |||
| 216 | + | return fmt.Errorf("unknown macOS architecture %s", runtime.GOARCH) | |||
| 217 | + | } | |||
| 218 | + | args = append(args, "-arch", arch) | |||
| 219 | + | } | |||
| 220 | + | cmd := exec.Command(compiler, append(args, sources...)...) | |||
| 221 | + | if out, err := cmd.CombinedOutput(); err != nil { | |||
| 222 | + | return fmt.Errorf("compile lib: %w\n%q\n%s", err, cmd, string(out)) | |||
| 223 | + | } | |||
| 224 | + | ||||
| 225 | + | return nil | |||
| 226 | + | } | |||
M internal/load/load_unix.go +4 -0
| @@ -11,6 +11,10 @@ func OpenLibrary(name string) (uintptr, error) { | |||
| 11 | 11 | return purego.Dlopen(name, purego.RTLD_NOW|purego.RTLD_GLOBAL) | |
| 12 | 12 | } | |
| 13 | 13 | ||
| 14 | + | func CloseLibrary(handle uintptr) error { | |
| 15 | + | return purego.Dlclose(handle) | |
| 16 | + | } | |
| 17 | + | ||
| 14 | 18 | func OpenSymbol(lib uintptr, name string) (uintptr, error) { | |
| 15 | 19 | return purego.Dlsym(lib, name) | |
| 16 | 20 | } | |
| @@ -11,6 +11,10 @@ func OpenLibrary(name string) (uintptr, error) { | |||||
| 11 | return purego.Dlopen(name, purego.RTLD_NOW|purego.RTLD_GLOBAL) | 11 | return purego.Dlopen(name, purego.RTLD_NOW|purego.RTLD_GLOBAL) | ||
| 12 | } | 12 | } | ||
| 13 | 13 | ||||
| 14 | + | func CloseLibrary(handle uintptr) error { | |||
| 15 | + | return purego.Dlclose(handle) | |||
| 16 | + | } | |||
| 17 | + | ||||
| 14 | func OpenSymbol(lib uintptr, name string) (uintptr, error) { | 18 | func OpenSymbol(lib uintptr, name string) (uintptr, error) { | ||
| 15 | return purego.Dlsym(lib, name) | 19 | return purego.Dlsym(lib, name) | ||
| 16 | } | 20 | } | ||
M internal/load/load_windows.go +4 -0
| @@ -12,6 +12,10 @@ func OpenLibrary(name string) (uintptr, error) { | |||
| 12 | 12 | return uintptr(handle), err | |
| 13 | 13 | } | |
| 14 | 14 | ||
| 15 | + | func CloseLibrary(handle uintptr) error { | |
| 16 | + | return syscall.FreeLibrary(syscall.Handle(handle)) | |
| 17 | + | } | |
| 18 | + | ||
| 15 | 19 | func OpenSymbol(lib uintptr, name string) (uintptr, error) { | |
| 16 | 20 | return syscall.GetProcAddress(syscall.Handle(lib), name) | |
| 17 | 21 | } | |
| @@ -12,6 +12,10 @@ func OpenLibrary(name string) (uintptr, error) { | |||||
| 12 | return uintptr(handle), err | 12 | return uintptr(handle), err | ||
| 13 | } | 13 | } | ||
| 14 | 14 | ||||
| 15 | + | func CloseLibrary(handle uintptr) error { | |||
| 16 | + | return syscall.FreeLibrary(syscall.Handle(handle)) | |||
| 17 | + | } | |||
| 18 | + | ||||
| 15 | func OpenSymbol(lib uintptr, name string) (uintptr, error) { | 19 | func OpenSymbol(lib uintptr, name string) (uintptr, error) { | ||
| 16 | return syscall.GetProcAddress(syscall.Handle(lib), name) | 20 | return syscall.GetProcAddress(syscall.Handle(lib), name) | ||
| 17 | } | 21 | } | ||
M struct_amd64.go +4 -0
| @@ -258,3 +258,7 @@ func placeStack(v reflect.Value, addStack func(uintptr)) { | |||
| 258 | 258 | } | |
| 259 | 259 | } | |
| 260 | 260 | } | |
| 261 | + | ||
| 262 | + | func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) { | |
| 263 | + | panic("purego: not needed on amd64") | |
| 264 | + | } | |
| @@ -258,3 +258,7 @@ func placeStack(v reflect.Value, addStack func(uintptr)) { | |||||
| 258 | } | 258 | } | ||
| 259 | } | 259 | } | ||
| 260 | } | 260 | } | ||
| 261 | + | ||||
| 262 | + | func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) { | |||
| 263 | + | panic("purego: not needed on amd64") | |||
| 264 | + | } | |||
M struct_arm64.go +14 -4
| @@ -73,8 +73,8 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl | |||
| 73 | 73 | if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 { | |
| 74 | 74 | // if this doesn't fit entirely in registers then | |
| 75 | 75 | // each element goes onto the stack | |
| 76 | - | if hfa && *numFloats+v.NumField() > numOfFloats { | |
| 77 | - | *numFloats = numOfFloats | |
| 76 | + | if hfa && *numFloats+v.NumField() > numOfFloatRegisters { | |
| 77 | + | *numFloats = numOfFloatRegisters | |
| 78 | 78 | } else if hva && *numInts+v.NumField() > numOfIntegerRegisters() { | |
| 79 | 79 | *numInts = numOfIntegerRegisters() | |
| 80 | 80 | } | |
| @@ -107,6 +107,8 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||
| 107 | 107 | } else { | |
| 108 | 108 | f = v.Index(k) | |
| 109 | 109 | } | |
| 110 | + | align := byte(f.Type().Align()*8 - 1) | |
| 111 | + | shift = (shift + align) &^ align | |
| 110 | 112 | if shift >= 64 { | |
| 111 | 113 | shift = 0 | |
| 112 | 114 | flushed = true | |
| @@ -137,10 +139,11 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||
| 137 | 139 | val |= f.Uint() << shift | |
| 138 | 140 | shift += 32 | |
| 139 | 141 | class |= _INT | |
| 140 | - | case reflect.Uint64: | |
| 142 | + | case reflect.Uint64, reflect.Uint, reflect.Uintptr: | |
| 141 | 143 | addInt(uintptr(f.Uint())) | |
| 142 | 144 | shift = 0 | |
| 143 | 145 | flushed = true | |
| 146 | + | class = _NO_CLASS | |
| 144 | 147 | case reflect.Int8: | |
| 145 | 148 | val |= uint64(f.Int()&0xFF) << shift | |
| 146 | 149 | shift += 8 | |
| @@ -153,10 +156,11 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||
| 153 | 156 | val |= uint64(f.Int()&0xFFFF_FFFF) << shift | |
| 154 | 157 | shift += 32 | |
| 155 | 158 | class |= _INT | |
| 156 | - | case reflect.Int64: | |
| 159 | + | case reflect.Int64, reflect.Int: | |
| 157 | 160 | addInt(uintptr(f.Int())) | |
| 158 | 161 | shift = 0 | |
| 159 | 162 | flushed = true | |
| 163 | + | class = _NO_CLASS | |
| 160 | 164 | case reflect.Float32: | |
| 161 | 165 | if class == _FLOAT { | |
| 162 | 166 | addFloat(uintptr(val)) | |
| @@ -170,6 +174,12 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||
| 170 | 174 | addFloat(uintptr(math.Float64bits(float64(f.Float())))) | |
| 171 | 175 | shift = 0 | |
| 172 | 176 | flushed = true | |
| 177 | + | class = _NO_CLASS | |
| 178 | + | case reflect.Ptr: | |
| 179 | + | addInt(f.Pointer()) | |
| 180 | + | shift = 0 | |
| 181 | + | flushed = true | |
| 182 | + | class = _NO_CLASS | |
| 173 | 183 | case reflect.Array: | |
| 174 | 184 | place(f) | |
| 175 | 185 | default: | |
| @@ -73,8 +73,8 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl | |||||
| 73 | if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 { | 73 | if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 { | ||
| 74 | // if this doesn't fit entirely in registers then | 74 | // if this doesn't fit entirely in registers then | ||
| 75 | // each element goes onto the stack | 75 | // each element goes onto the stack | ||
| 76 | - | if hfa && *numFloats+v.NumField() > numOfFloats { | 76 | + | if hfa && *numFloats+v.NumField() > numOfFloatRegisters { |
| 77 | - | *numFloats = numOfFloats | 77 | + | *numFloats = numOfFloatRegisters |
| 78 | } else if hva && *numInts+v.NumField() > numOfIntegerRegisters() { | 78 | } else if hva && *numInts+v.NumField() > numOfIntegerRegisters() { | ||
| 79 | *numInts = numOfIntegerRegisters() | 79 | *numInts = numOfIntegerRegisters() | ||
| 80 | } | 80 | } | ||
| @@ -107,6 +107,8 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||||
| 107 | } else { | 107 | } else { | ||
| 108 | f = v.Index(k) | 108 | f = v.Index(k) | ||
| 109 | } | 109 | } | ||
| 110 | + | align := byte(f.Type().Align()*8 - 1) | |||
| 111 | + | shift = (shift + align) &^ align | |||
| 110 | if shift >= 64 { | 112 | if shift >= 64 { | ||
| 111 | shift = 0 | 113 | shift = 0 | ||
| 112 | flushed = true | 114 | flushed = true | ||
| @@ -137,10 +139,11 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||||
| 137 | val |= f.Uint() << shift | 139 | val |= f.Uint() << shift | ||
| 138 | shift += 32 | 140 | shift += 32 | ||
| 139 | class |= _INT | 141 | class |= _INT | ||
| 140 | - | case reflect.Uint64: | 142 | + | case reflect.Uint64, reflect.Uint, reflect.Uintptr: |
| 141 | addInt(uintptr(f.Uint())) | 143 | addInt(uintptr(f.Uint())) | ||
| 142 | shift = 0 | 144 | shift = 0 | ||
| 143 | flushed = true | 145 | flushed = true | ||
| 146 | + | class = _NO_CLASS | |||
| 144 | case reflect.Int8: | 147 | case reflect.Int8: | ||
| 145 | val |= uint64(f.Int()&0xFF) << shift | 148 | val |= uint64(f.Int()&0xFF) << shift | ||
| 146 | shift += 8 | 149 | shift += 8 | ||
| @@ -153,10 +156,11 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||||
| 153 | val |= uint64(f.Int()&0xFFFF_FFFF) << shift | 156 | val |= uint64(f.Int()&0xFFFF_FFFF) << shift | ||
| 154 | shift += 32 | 157 | shift += 32 | ||
| 155 | class |= _INT | 158 | class |= _INT | ||
| 156 | - | case reflect.Int64: | 159 | + | case reflect.Int64, reflect.Int: |
| 157 | addInt(uintptr(f.Int())) | 160 | addInt(uintptr(f.Int())) | ||
| 158 | shift = 0 | 161 | shift = 0 | ||
| 159 | flushed = true | 162 | flushed = true | ||
| 163 | + | class = _NO_CLASS | |||
| 160 | case reflect.Float32: | 164 | case reflect.Float32: | ||
| 161 | if class == _FLOAT { | 165 | if class == _FLOAT { | ||
| 162 | addFloat(uintptr(val)) | 166 | addFloat(uintptr(val)) | ||
| @@ -170,6 +174,12 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||||
| 170 | addFloat(uintptr(math.Float64bits(float64(f.Float())))) | 174 | addFloat(uintptr(math.Float64bits(float64(f.Float())))) | ||
| 171 | shift = 0 | 175 | shift = 0 | ||
| 172 | flushed = true | 176 | flushed = true | ||
| 177 | + | class = _NO_CLASS | |||
| 178 | + | case reflect.Ptr: | |||
| 179 | + | addInt(f.Pointer()) | |||
| 180 | + | shift = 0 | |||
| 181 | + | flushed = true | |||
| 182 | + | class = _NO_CLASS | |||
| 173 | case reflect.Array: | 183 | case reflect.Array: | ||
| 174 | place(f) | 184 | place(f) | ||
| 175 | default: | 185 | default: | ||
M struct_other.go +4 -0
| @@ -14,3 +14,7 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl | |||
| 14 | 14 | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |
| 15 | 15 | panic("purego: struct returns are not supported") | |
| 16 | 16 | } | |
| 17 | + | ||
| 18 | + | func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) { | |
| 19 | + | panic("purego: not needed on other platforms") | |
| 20 | + | } | |
| @@ -14,3 +14,7 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl | |||||
| 14 | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | 14 | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | ||
| 15 | panic("purego: struct returns are not supported") | 15 | panic("purego: struct returns are not supported") | ||
| 16 | } | 16 | } | ||
| 17 | + | ||||
| 18 | + | func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) { | |||
| 19 | + | panic("purego: not needed on other platforms") | |||
| 20 | + | } | |||
M syscall.go +2 -2
| @@ -13,8 +13,8 @@ package purego | |||
| 13 | 13 | type CDecl struct{} | |
| 14 | 14 | ||
| 15 | 15 | const ( | |
| 16 | - | maxArgs = 15 | |
| 17 | - | numOfFloats = 8 // arm64 and amd64 both have 8 float registers | |
| 16 | + | maxArgs = 15 | |
| 17 | + | numOfFloatRegisters = 8 // arm64 and amd64 both have 8 float registers | |
| 18 | 18 | ) | |
| 19 | 19 | ||
| 20 | 20 | type syscall15Args struct { | |
| @@ -13,8 +13,8 @@ package purego | |||||
| 13 | type CDecl struct{} | 13 | type CDecl struct{} | ||
| 14 | 14 | ||||
| 15 | const ( | 15 | const ( | ||
| 16 | - | maxArgs = 15 | 16 | + | maxArgs = 15 |
| 17 | - | numOfFloats = 8 // arm64 and amd64 both have 8 float registers | 17 | + | numOfFloatRegisters = 8 // arm64 and amd64 both have 8 float registers |
| 18 | ) | 18 | ) | ||
| 19 | 19 | ||||
| 20 | type syscall15Args struct { | 20 | type syscall15Args struct { | ||
M syscall_sysv.go +3 -3
| @@ -146,12 +146,12 @@ func callbackWrap(a *callbackArgs) { | |||
| 146 | 146 | var intsN int // intsN represents the number of integer arguments processed | |
| 147 | 147 | // stack points to the index into frame of the current stack element. | |
| 148 | 148 | // The stack begins after the float and integer registers. | |
| 149 | - | stack := numOfIntegerRegisters() + numOfFloats | |
| 149 | + | stack := numOfIntegerRegisters() + numOfFloatRegisters | |
| 150 | 150 | for i := range args { | |
| 151 | 151 | var pos int | |
| 152 | 152 | switch fnType.In(i).Kind() { | |
| 153 | 153 | case reflect.Float32, reflect.Float64: | |
| 154 | - | if floatsN >= numOfFloats { | |
| 154 | + | if floatsN >= numOfFloatRegisters { | |
| 155 | 155 | pos = stack | |
| 156 | 156 | stack++ | |
| 157 | 157 | } else { | |
| @@ -169,7 +169,7 @@ func callbackWrap(a *callbackArgs) { | |||
| 169 | 169 | stack++ | |
| 170 | 170 | } else { | |
| 171 | 171 | // the integers begin after the floats in frame | |
| 172 | - | pos = intsN + numOfFloats | |
| 172 | + | pos = intsN + numOfFloatRegisters | |
| 173 | 173 | } | |
| 174 | 174 | intsN++ | |
| 175 | 175 | } | |
| @@ -146,12 +146,12 @@ func callbackWrap(a *callbackArgs) { | |||||
| 146 | var intsN int // intsN represents the number of integer arguments processed | 146 | var intsN int // intsN represents the number of integer arguments processed | ||
| 147 | // stack points to the index into frame of the current stack element. | 147 | // stack points to the index into frame of the current stack element. | ||
| 148 | // The stack begins after the float and integer registers. | 148 | // The stack begins after the float and integer registers. | ||
| 149 | - | stack := numOfIntegerRegisters() + numOfFloats | 149 | + | stack := numOfIntegerRegisters() + numOfFloatRegisters |
| 150 | for i := range args { | 150 | for i := range args { | ||
| 151 | var pos int | 151 | var pos int | ||
| 152 | switch fnType.In(i).Kind() { | 152 | switch fnType.In(i).Kind() { | ||
| 153 | case reflect.Float32, reflect.Float64: | 153 | case reflect.Float32, reflect.Float64: | ||
| 154 | - | if floatsN >= numOfFloats { | 154 | + | if floatsN >= numOfFloatRegisters { |
| 155 | pos = stack | 155 | pos = stack | ||
| 156 | stack++ | 156 | stack++ | ||
| 157 | } else { | 157 | } else { | ||
| @@ -169,7 +169,7 @@ func callbackWrap(a *callbackArgs) { | |||||
| 169 | stack++ | 169 | stack++ | ||
| 170 | } else { | 170 | } else { | ||
| 171 | // the integers begin after the floats in frame | 171 | // the integers begin after the floats in frame | ||
| 172 | - | pos = intsN + numOfFloats | 172 | + | pos = intsN + numOfFloatRegisters |
| 173 | } | 173 | } | ||
| 174 | intsN++ | 174 | intsN++ | ||
| 175 | } | 175 | } | ||
A testdata/abitest/abi_test.c +27 -0
| @@ -0,0 +1,27 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2025 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | #include <stdint.h> | |
| 5 | + | #include <assert.h> | |
| 6 | + | #include <stdio.h> | |
| 7 | + | #include <string.h> | |
| 8 | + | ||
| 9 | + | uint32_t stack_uint8_t(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f, uint32_t g, uint32_t h, uint8_t i, uint8_t j, uint32_t k ) { | |
| 10 | + | assert(i == 1); | |
| 11 | + | assert(j == 2); | |
| 12 | + | assert(k == 1024); | |
| 13 | + | return a | b | c | d | e | f | g | h | (uint32_t) i | (uint32_t) j | k; | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | uint32_t reg_uint8_t(uint8_t a, uint8_t b, uint32_t c) { | |
| 17 | + | assert(a == 1); | |
| 18 | + | assert(b == 2); | |
| 19 | + | assert(c == 1024); | |
| 20 | + | return a | b | c; | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | uint32_t stack_string(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f, uint32_t g, uint32_t h, const char * i) { | |
| 24 | + | assert(i != 0); | |
| 25 | + | assert(strcmp(i, "test") == 0); | |
| 26 | + | return a | b | c | d | e | f | g | h; | |
| 27 | + | } | |
| @@ -0,0 +1,27 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2025 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | #include <stdint.h> | |||
| 5 | + | #include <assert.h> | |||
| 6 | + | #include <stdio.h> | |||
| 7 | + | #include <string.h> | |||
| 8 | + | ||||
| 9 | + | uint32_t stack_uint8_t(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f, uint32_t g, uint32_t h, uint8_t i, uint8_t j, uint32_t k ) { | |||
| 10 | + | assert(i == 1); | |||
| 11 | + | assert(j == 2); | |||
| 12 | + | assert(k == 1024); | |||
| 13 | + | return a | b | c | d | e | f | g | h | (uint32_t) i | (uint32_t) j | k; | |||
| 14 | + | } | |||
| 15 | + | ||||
| 16 | + | uint32_t reg_uint8_t(uint8_t a, uint8_t b, uint32_t c) { | |||
| 17 | + | assert(a == 1); | |||
| 18 | + | assert(b == 2); | |||
| 19 | + | assert(c == 1024); | |||
| 20 | + | return a | b | c; | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | uint32_t stack_string(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f, uint32_t g, uint32_t h, const char * i) { | |||
| 24 | + | assert(i != 0); | |||
| 25 | + | assert(strcmp(i, "test") == 0); | |||
| 26 | + | return a | b | c | d | e | f | g | h; | |||
| 27 | + | } | |||