fix: correct loong64 and s390x struct packing
- commit
- 0879f2b44707ee29762604e56f6c803c49da199f
- author
- brice · · 2026-04-13 10:03 UTC
- parent
- 5e664bd
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +63 -12
M struct_loong64.go +6 -11
| @@ -19,7 +19,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 19 | 19 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | |
| 20 | 20 | r1 = syscall.f1 | |
| 21 | 21 | if numFields == 2 { | |
| 22 | - | r1 = syscall.f2<<32 | syscall.f1 | |
| 22 | + | r1 = packFloat32Pair(syscall.f1, syscall.f2) | |
| 23 | 23 | } | |
| 24 | 24 | } | |
| 25 | 25 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |
| @@ -28,10 +28,10 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 28 | 28 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | |
| 29 | 29 | switch numFields { | |
| 30 | 30 | case 4: | |
| 31 | - | r1 = syscall.f2<<32 | syscall.f1 | |
| 32 | - | r2 = syscall.f4<<32 | syscall.f3 | |
| 31 | + | r1 = packFloat32Pair(syscall.f1, syscall.f2) | |
| 32 | + | r2 = packFloat32Pair(syscall.f3, syscall.f4) | |
| 33 | 33 | case 3: | |
| 34 | - | r1 = syscall.f2<<32 | syscall.f1 | |
| 34 | + | r1 = packFloat32Pair(syscall.f1, syscall.f2) | |
| 35 | 35 | r2 = syscall.f3 | |
| 36 | 36 | case 2: | |
| 37 | 37 | r1 = syscall.f1 | |
| @@ -91,13 +91,8 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||
| 91 | 91 | align := byte(f.Type().Align()*8 - 1) | |
| 92 | 92 | shift = (shift + align) &^ align | |
| 93 | 93 | if shift >= 64 { | |
| 94 | - | shift = 0 | |
| 95 | - | flushed = true | |
| 96 | - | if class == _FLOAT { | |
| 97 | - | addFloat(uintptr(val)) | |
| 98 | - | } else { | |
| 99 | - | addInt(uintptr(val)) | |
| 100 | - | } | |
| 94 | + | flushPackedWord(&val, &shift, &flushed, class == _FLOAT, addFloat, addInt) | |
| 95 | + | class = _NO_CLASS | |
| 101 | 96 | } | |
| 102 | 97 | switch f.Type().Kind() { | |
| 103 | 98 | case reflect.Struct: | |
| @@ -19,7 +19,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||||
| 19 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | 19 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | ||
| 20 | r1 = syscall.f1 | 20 | r1 = syscall.f1 | ||
| 21 | if numFields == 2 { | 21 | if numFields == 2 { | ||
| 22 | - | r1 = syscall.f2<<32 | syscall.f1 | 22 | + | r1 = packFloat32Pair(syscall.f1, syscall.f2) |
| 23 | } | 23 | } | ||
| 24 | } | 24 | } | ||
| 25 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | 25 | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | ||
| @@ -28,10 +28,10 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||||
| 28 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | 28 | if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats { | ||
| 29 | switch numFields { | 29 | switch numFields { | ||
| 30 | case 4: | 30 | case 4: | ||
| 31 | - | r1 = syscall.f2<<32 | syscall.f1 | 31 | + | r1 = packFloat32Pair(syscall.f1, syscall.f2) |
| 32 | - | r2 = syscall.f4<<32 | syscall.f3 | 32 | + | r2 = packFloat32Pair(syscall.f3, syscall.f4) |
| 33 | case 3: | 33 | case 3: | ||
| 34 | - | r1 = syscall.f2<<32 | syscall.f1 | 34 | + | r1 = packFloat32Pair(syscall.f1, syscall.f2) |
| 35 | r2 = syscall.f3 | 35 | r2 = syscall.f3 | ||
| 36 | case 2: | 36 | case 2: | ||
| 37 | r1 = syscall.f1 | 37 | r1 = syscall.f1 | ||
| @@ -91,13 +91,8 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr | |||||
| 91 | align := byte(f.Type().Align()*8 - 1) | 91 | align := byte(f.Type().Align()*8 - 1) | ||
| 92 | shift = (shift + align) &^ align | 92 | shift = (shift + align) &^ align | ||
| 93 | if shift >= 64 { | 93 | if shift >= 64 { | ||
| 94 | - | shift = 0 | 94 | + | flushPackedWord(&val, &shift, &flushed, class == _FLOAT, addFloat, addInt) |
| 95 | - | flushed = true | 95 | + | class = _NO_CLASS |
| 96 | - | if class == _FLOAT { | |||
| 97 | - | addFloat(uintptr(val)) | |||
| 98 | - | } else { | |||
| 99 | - | addInt(uintptr(val)) | |||
| 100 | - | } | |||
| 101 | } | 96 | } | ||
| 102 | switch f.Type().Kind() { | 97 | switch f.Type().Kind() { | ||
| 103 | case reflect.Struct: | 98 | case reflect.Struct: | ||
A struct_loong64_regression_test.go +35 -0
| @@ -0,0 +1,35 @@ | |||
| 1 | + | //go:build loong64 && linux | |
| 2 | + | ||
| 3 | + | package purego | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "math" | |
| 7 | + | "reflect" | |
| 8 | + | "testing" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | func TestLoong64GetStructMasksFloat32Pairs(t *testing.T) { | |
| 12 | + | type pair struct{ X, Y float32 } | |
| 13 | + | sys := syscall15Args{ | |
| 14 | + | f1: uintptr(0xDEADBEEF00000000 | uint64(math.Float32bits(1.5))), | |
| 15 | + | f2: uintptr(math.Float32bits(2.5)), | |
| 16 | + | } | |
| 17 | + | got := getStruct(reflect.TypeOf(pair{}), sys).Interface().(pair) | |
| 18 | + | if got.X != 1.5 || got.Y != 2.5 { | |
| 19 | + | t.Fatalf("got %+v, want {1.5 2.5}", got) | |
| 20 | + | } | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | func TestLoong64PlaceRegistersResetsAfterFlush(t *testing.T) { | |
| 24 | + | type triple struct{ A, B, C uint32 } | |
| 25 | + | var ints []uintptr | |
| 26 | + | placeRegisters(reflect.ValueOf(triple{A: 1, B: 2, C: 3}), func(uintptr) { | |
| 27 | + | t.Fatal("unexpected float register") | |
| 28 | + | }, func(v uintptr) { | |
| 29 | + | ints = append(ints, v) | |
| 30 | + | }) | |
| 31 | + | want := []uintptr{0x0000000200000001, 0x0000000000000003} | |
| 32 | + | if len(ints) != len(want) || ints[0] != want[0] || ints[1] != want[1] { | |
| 33 | + | t.Fatalf("placeRegisters() = %#v, want %#v", ints, want) | |
| 34 | + | } | |
| 35 | + | } | |
| @@ -0,0 +1,35 @@ | |||||
| 1 | + | //go:build loong64 && linux | |||
| 2 | + | ||||
| 3 | + | package purego | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "math" | |||
| 7 | + | "reflect" | |||
| 8 | + | "testing" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | func TestLoong64GetStructMasksFloat32Pairs(t *testing.T) { | |||
| 12 | + | type pair struct{ X, Y float32 } | |||
| 13 | + | sys := syscall15Args{ | |||
| 14 | + | f1: uintptr(0xDEADBEEF00000000 | uint64(math.Float32bits(1.5))), | |||
| 15 | + | f2: uintptr(math.Float32bits(2.5)), | |||
| 16 | + | } | |||
| 17 | + | got := getStruct(reflect.TypeOf(pair{}), sys).Interface().(pair) | |||
| 18 | + | if got.X != 1.5 || got.Y != 2.5 { | |||
| 19 | + | t.Fatalf("got %+v, want {1.5 2.5}", got) | |||
| 20 | + | } | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | func TestLoong64PlaceRegistersResetsAfterFlush(t *testing.T) { | |||
| 24 | + | type triple struct{ A, B, C uint32 } | |||
| 25 | + | var ints []uintptr | |||
| 26 | + | placeRegisters(reflect.ValueOf(triple{A: 1, B: 2, C: 3}), func(uintptr) { | |||
| 27 | + | t.Fatal("unexpected float register") | |||
| 28 | + | }, func(v uintptr) { | |||
| 29 | + | ints = append(ints, v) | |||
| 30 | + | }) | |||
| 31 | + | want := []uintptr{0x0000000200000001, 0x0000000000000003} | |||
| 32 | + | if len(ints) != len(want) || ints[0] != want[0] || ints[1] != want[1] { | |||
| 33 | + | t.Fatalf("placeRegisters() = %#v, want %#v", ints, want) | |||
| 34 | + | } | |||
| 35 | + | } | |||
M struct_s390x.go +1 -1
| @@ -31,7 +31,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) reflect.Value { | |||
| 31 | 31 | // float32 values in FP regs | |
| 32 | 32 | f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4} | |
| 33 | 33 | for i := 0; i < numFields; i++ { | |
| 34 | - | *(*uint32)(unsafe.Pointer(&buf[i*4])) = uint32(f[i]) | |
| 34 | + | *(*uint32)(unsafe.Pointer(&buf[i*4])) = highFloat32Bits(f[i]) | |
| 35 | 35 | } | |
| 36 | 36 | } else { | |
| 37 | 37 | // float64: whole register value is valid | |
| @@ -31,7 +31,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) reflect.Value { | |||||
| 31 | // float32 values in FP regs | 31 | // float32 values in FP regs | ||
| 32 | f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4} | 32 | f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4} | ||
| 33 | for i := 0; i < numFields; i++ { | 33 | for i := 0; i < numFields; i++ { | ||
| 34 | - | *(*uint32)(unsafe.Pointer(&buf[i*4])) = uint32(f[i]) | 34 | + | *(*uint32)(unsafe.Pointer(&buf[i*4])) = highFloat32Bits(f[i]) |
| 35 | } | 35 | } | ||
| 36 | } else { | 36 | } else { | ||
| 37 | // float64: whole register value is valid | 37 | // float64: whole register value is valid | ||
A struct_s390x_regression_test.go +21 -0
| @@ -0,0 +1,21 @@ | |||
| 1 | + | //go:build s390x && linux | |
| 2 | + | ||
| 3 | + | package purego | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "math" | |
| 7 | + | "reflect" | |
| 8 | + | "testing" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | func TestS390xGetStructUsesHighFloat32Bits(t *testing.T) { | |
| 12 | + | type pair struct{ X, Y float32 } | |
| 13 | + | sys := syscall15Args{ | |
| 14 | + | f1: uintptr(uint64(math.Float32bits(1.5)) << 32), | |
| 15 | + | f2: uintptr(uint64(math.Float32bits(2.5)) << 32), | |
| 16 | + | } | |
| 17 | + | got := getStruct(reflect.TypeOf(pair{}), sys).Interface().(pair) | |
| 18 | + | if got.X != 1.5 || got.Y != 2.5 { | |
| 19 | + | t.Fatalf("got %+v, want {1.5 2.5}", got) | |
| 20 | + | } | |
| 21 | + | } | |
| @@ -0,0 +1,21 @@ | |||||
| 1 | + | //go:build s390x && linux | |||
| 2 | + | ||||
| 3 | + | package purego | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "math" | |||
| 7 | + | "reflect" | |||
| 8 | + | "testing" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | func TestS390xGetStructUsesHighFloat32Bits(t *testing.T) { | |||
| 12 | + | type pair struct{ X, Y float32 } | |||
| 13 | + | sys := syscall15Args{ | |||
| 14 | + | f1: uintptr(uint64(math.Float32bits(1.5)) << 32), | |||
| 15 | + | f2: uintptr(uint64(math.Float32bits(2.5)) << 32), | |||
| 16 | + | } | |||
| 17 | + | got := getStruct(reflect.TypeOf(pair{}), sys).Interface().(pair) | |||
| 18 | + | if got.X != 1.5 || got.Y != 2.5 { | |||
| 19 | + | t.Fatalf("got %+v, want {1.5 2.5}", got) | |||
| 20 | + | } | |||
| 21 | + | } | |||