Skip to content

Commit 4f1b698

Browse files
aykevldeadprogram
authored andcommitted
reflect: support big-endian systems
The reflect package needs to know the endianness of the system in a few places. Before this patch, it assumed little-endian systems. But with GOARCH=mips we now have a big-endian system which also needs to be supported. So this patch fixes the reflect package to work on big-endian systems. Also, I've updated the tests for MIPS: instead of running the little-endian tests, I've changed it to run the big-endian tests instead. The two are very similar except for endianness so this should be fine. To be sure we won't accidentally break little-endian support, I've kept a single MIPS little-endian test (the CGo test, which doesn't yet work on big-endian systems anyway).
1 parent 73f519b commit 4f1b698

File tree

4 files changed

+90
-38
lines changed

4 files changed

+90
-38
lines changed

main_test.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var supportedLinuxArches = map[string]string{
3636
"X86Linux": "linux/386",
3737
"ARMLinux": "linux/arm/6",
3838
"ARM64Linux": "linux/arm64",
39-
"MIPSLinux": "linux/mipsle/hardfloat",
39+
"MIPSLinux": "linux/mips/hardfloat",
4040
"WASIp1": "wasip1/wasm",
4141
}
4242

@@ -177,17 +177,14 @@ func TestBuild(t *testing.T) {
177177
})
178178
}
179179
}
180-
t.Run("MIPS big-endian", func(t *testing.T) {
181-
// Run a single test for GOARCH=mips to see whether it works at all.
182-
// Big-endian MIPS isn't fully supported yet, but simple examples
183-
// should work.
184-
// Once big-endian is fully supported, we can probably flip this
185-
// around and do full testing of MIPS big-endian support and only do
186-
// limited testing of MIPS little-endian (because the two are some
187-
// similar).
180+
t.Run("MIPS little-endian", func(t *testing.T) {
181+
// Run a single test for GOARCH=mipsle to see whether it works at
182+
// all. It is already mostly tested because GOARCH=mips and
183+
// GOARCH=mipsle are so similar, but it's good to have an extra test
184+
// to be sure.
188185
t.Parallel()
189-
options := optionsFromOSARCH("linux/mips/softfloat", sema)
190-
runTest("map.go", options, t, nil, nil)
186+
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
187+
runTest("cgo/", options, t, nil, nil)
191188
})
192189
t.Run("WebAssembly", func(t *testing.T) {
193190
t.Parallel()
@@ -232,6 +229,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
232229
continue
233230
}
234231
}
232+
if options.GOOS == "linux" && options.GOARCH == "mips" {
233+
if name == "cgo/" {
234+
// CGo isn't supported yet on big-endian systems (needs updates
235+
// to bitfield access methods).
236+
continue
237+
}
238+
}
235239
if options.Target == "simavr" {
236240
// Not all tests are currently supported on AVR.
237241
// Skip the ones that aren't.

src/reflect/endian-big.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//go:build mips
2+
3+
package reflect
4+
5+
import "unsafe"
6+
7+
// loadValue loads a value that may or may not be word-aligned. The number of
8+
// bytes given in size are loaded. The biggest possible size it can load is that
9+
// of an uintptr.
10+
func loadValue(ptr unsafe.Pointer, size uintptr) uintptr {
11+
loadedValue := uintptr(0)
12+
for i := uintptr(0); i < size; i++ {
13+
loadedValue <<= 8
14+
loadedValue |= uintptr(*(*byte)(ptr))
15+
ptr = unsafe.Add(ptr, 1)
16+
}
17+
return loadedValue
18+
}
19+
20+
// storeValue is the inverse of loadValue. It stores a value to a pointer that
21+
// doesn't need to be aligned.
22+
func storeValue(ptr unsafe.Pointer, size, value uintptr) {
23+
// This could perhaps be optimized using bits.ReverseBytes32 if needed.
24+
value <<= (unsafe.Sizeof(uintptr(0)) - size) * 8
25+
for i := uintptr(0); i < size; i++ {
26+
*(*byte)(ptr) = byte(value >> ((unsafe.Sizeof(uintptr(0)) - 1) * 8))
27+
ptr = unsafe.Add(ptr, 1)
28+
value <<= 8
29+
}
30+
}
31+
32+
// maskAndShift cuts out a part of a uintptr. Note that the offset may not be 0.
33+
func maskAndShift(value, offset, size uintptr) uintptr {
34+
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - size) * 8)
35+
return (uintptr(value) >> ((unsafe.Sizeof(uintptr(0)) - offset - size) * 8)) & mask
36+
}

src/reflect/endian-little.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build !mips
2+
3+
package reflect
4+
5+
import "unsafe"
6+
7+
// loadValue loads a value that may or may not be word-aligned. The number of
8+
// bytes given in size are loaded. The biggest possible size it can load is that
9+
// of an uintptr.
10+
func loadValue(ptr unsafe.Pointer, size uintptr) uintptr {
11+
loadedValue := uintptr(0)
12+
shift := uintptr(0)
13+
for i := uintptr(0); i < size; i++ {
14+
loadedValue |= uintptr(*(*byte)(ptr)) << shift
15+
shift += 8
16+
ptr = unsafe.Add(ptr, 1)
17+
}
18+
return loadedValue
19+
}
20+
21+
// storeValue is the inverse of loadValue. It stores a value to a pointer that
22+
// doesn't need to be aligned.
23+
func storeValue(ptr unsafe.Pointer, size, value uintptr) {
24+
for i := uintptr(0); i < size; i++ {
25+
*(*byte)(ptr) = byte(value)
26+
ptr = unsafe.Add(ptr, 1)
27+
value >>= 8
28+
}
29+
}
30+
31+
// maskAndShift cuts out a part of a uintptr. Note that the offset may not be 0.
32+
func maskAndShift(value, offset, size uintptr) uintptr {
33+
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - size) * 8)
34+
return (uintptr(value) >> (offset * 8)) & mask
35+
}

src/reflect/value.go

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -887,26 +887,6 @@ func (v Value) Index(i int) Value {
887887
}
888888
}
889889

890-
// loadValue loads a value that may or may not be word-aligned. The number of
891-
// bytes given in size are loaded. The biggest possible size it can load is that
892-
// of an uintptr.
893-
func loadValue(ptr unsafe.Pointer, size uintptr) uintptr {
894-
loadedValue := uintptr(0)
895-
shift := uintptr(0)
896-
for i := uintptr(0); i < size; i++ {
897-
loadedValue |= uintptr(*(*byte)(ptr)) << shift
898-
shift += 8
899-
ptr = unsafe.Add(ptr, 1)
900-
}
901-
return loadedValue
902-
}
903-
904-
// maskAndShift cuts out a part of a uintptr. Note that the offset may not be 0.
905-
func maskAndShift(value, offset, size uintptr) uintptr {
906-
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - size) * 8)
907-
return (uintptr(value) >> (offset * 8)) & mask
908-
}
909-
910890
func (v Value) NumMethod() int {
911891
return v.typecode.NumMethod()
912892
}
@@ -1088,9 +1068,7 @@ func (v Value) Set(x Value) {
10881068
if v.typecode.Kind() == Interface && x.typecode.Kind() != Interface {
10891069
// move the value of x back into the interface, if possible
10901070
if x.isIndirect() && x.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
1091-
var value uintptr
1092-
memcpy(unsafe.Pointer(&value), x.value, x.typecode.Size())
1093-
x.value = unsafe.Pointer(value)
1071+
x.value = unsafe.Pointer(loadValue(x.value, x.typecode.Size()))
10941072
}
10951073

10961074
intf := composeInterface(unsafe.Pointer(x.typecode), x.value)
@@ -1101,12 +1079,11 @@ func (v Value) Set(x Value) {
11011079
}
11021080

11031081
size := v.typecode.Size()
1104-
xptr := x.value
11051082
if size <= unsafe.Sizeof(uintptr(0)) && !x.isIndirect() {
1106-
value := x.value
1107-
xptr = unsafe.Pointer(&value)
1083+
storeValue(v.value, size, uintptr(x.value))
1084+
} else {
1085+
memcpy(v.value, x.value, size)
11081086
}
1109-
memcpy(v.value, xptr, size)
11101087
}
11111088

11121089
func (v Value) SetZero() {

0 commit comments

Comments
 (0)