Bug#892088: golang-1.10: FTBFS on mips when built on Octeon III buildds

James Cowgill <[email protected]>
Newsgroups gmane.linux.debian.ports.mips
Message-ID <082fd79b-f3a0-bfa3-249c-0ec9af9586ca__40000.5961921581$1520246075$gmane$org@debian.org>
Source: golang-1.10
Version: 1.10-1
Severity: serious
Tags: upstream patch
Forwarded: https://go-review.googlesource.com/c/go/+/97735
X-Debbugs-CC: [email protected]

[CC the list for the HW issue]

Hi,

golang-1.10 (and 1.9) FTBFS on mips big-endian with various floating
point test errors, but only when built on the Octeon III buildds.

After tearing my head out investigating this, I have concluded that
there is a hardware bug in the Octeon IIIs. The bug occurs when:
- You run a big floating point double operation (like div.d).
- Wait a few instructions (a store here seems to be important).
- Read the *odd* register which the above float operation stored into.
This ends up reading the value from before the double operation instead
of the result of the operation. I've attached a small C program which
reproduces this. It should happen 99% of the time (it will only fail if
you are unlucky and get an interrupt at the wrong time).

I think this does not affect the wider Debian archive because:
- MFHC1 (move from high float) seems to be unaffected (probably...)
- If mips32r2 and fpxx are enabled, GCC only reads the high part of a
float register using MFHC1 (in the few cases it needs to do this - like
integer <-> float conversion).

However, golang is affected because it uses FP32 and on big endian, all
double loads are split into word loads with the odd register loaded
first. I have attached a patch which fixes this which I have also
submitted upstream.

Thanks,
James
test-octeon-bug.c (text/x-csrc, 647 B)
#include <stdint.h>
#include <stdio.h>

static void div_octeon_bug(double a, double b, uint32_t* buf)
{
	asm(
		"mtc1 %[fake_clobber], $f5\n"
		"div.d $f4, %[a], %[b]\n"
		"nop\n"
		"nop\n"
		"nop\n"
		"nop\n"
		"swc1 $f0, 0(%[buf])\n"
		"swc1 $f0, 0(%[buf])\n"
		"swc1 $f5, 0(%[buf])\n"
		"swc1 $f4, 4(%[buf])\n"
		"swc1 $f5, 8(%[buf])\n"
		"swc1 $f4, 12(%[buf])\n"
		:
		: [buf]"r"(buf), [fake_clobber]"r"(0xdeadbeef), [a]"f"(a), [b]"f"(b)
		: "memory", "f4", "$f5");
}

int main(void)
{
	uint32_t buf[4];
	div_octeon_bug(1.0, 21.63538985889851, buf);
	printf("%08x %08x\n", buf[0], buf[1]);
	printf("%08x %08x\n", buf[2], buf[3]);
	return 0;
}
0001-cmd-internal-obj-mips-load-store-even-float-register.patch (text/x-patch, 2.3 KB)
From 5ab26b4e5996a3557a1d6f1af7f1e54104448a79 Mon Sep 17 00:00:00 2001
From: James Cowgill <[email protected]>
Date: Wed, 28 Feb 2018 16:10:14 +0000
Subject: [PATCH] cmd/internal/obj/mips: load/store even float registers first

There is a bug in Octeon III processors where storing an odd floating
point register after it has recently been written to by a double
floating point operation will store the old value from before the double
operation (there are some extra details - the operation and store
must be a certain number of cycles apart). However, this bug does not
occur if the even register is stored first. Currently the bug only
happens on big endian because go always loads the even register first on
little endian.

Workaround the bug by always loading / storing the even floating point
register first. Since this is just an instruction reordering, it should
have no performance penalty. This follows other compilers like GCC which
will always store the even register first (although you do have to set
the ISA level to MIPS I to prevent it from using SDC1).

Change-Id: I5e73daa4d724ca1df7bf5228aab19f53f26a4976
---
 src/cmd/internal/obj/mips/obj0.go | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go
index 2b9f18c942..c3bab5c48e 100644
--- a/src/cmd/internal/obj/mips/obj0.go
+++ b/src/cmd/internal/obj/mips/obj0.go
@@ -558,20 +558,22 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 			p.Link = q
 			p1 = q.Link
 
-			var regOff int16
+			var addrOff int64
 			if c.ctxt.Arch.ByteOrder == binary.BigEndian {
-				regOff = 1 // load odd register first
+				addrOff = 4 // swap load/save order
 			}
 			if p.From.Type == obj.TYPE_MEM {
 				reg := REG_F0 + (p.To.Reg-REG_F0)&^1
-				p.To.Reg = reg + regOff
-				q.To.Reg = reg + 1 - regOff
-				q.From.Offset += 4
+				p.To.Reg = reg
+				q.To.Reg = reg + 1
+				p.From.Offset += addrOff
+				q.From.Offset += 4 - addrOff
 			} else if p.To.Type == obj.TYPE_MEM {
 				reg := REG_F0 + (p.From.Reg-REG_F0)&^1
-				p.From.Reg = reg + regOff
-				q.From.Reg = reg + 1 - regOff
-				q.To.Offset += 4
+				p.From.Reg = reg
+				q.From.Reg = reg + 1
+				p.To.Offset += addrOff
+				q.To.Offset += 4 - addrOff
 			}
 		}
 	}
-- 
2.16.2
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEE+Ixt5DaZ6POztUwQx/FnbeotAe8FAlqdHQoACgkQx/Fnbeot
Ae9K+Q//WYLplZCwT7RWpJYs0kc3AaHm3043JFF2iw2g8Xj4YCVJP84jBjX6nlBo
7D0UKNH2mosCxj8IppWER12g0/1VaiLBKc+ZE7j7cOsO0m7xYCiGbbKLqgOFvz+4
vnhC+MWqqKZTZq/mszJ3ODzp6VfW2/uAgY6nc8573x9JMZloZZSEa7Oj0opKh/Bu
xiFeOhycZElMVLEK5p7X/xC6FupJAbeWPl0ZzfT5CzJJtt1xvwWkk67a+/9Nnbgj
1zQm6/7LKAe9CaOlvFSpQoAWJbpGroHp/AvUkyA9g/ZdvKCVp21fPDYo8h1Rjv4e
ETS/GyIi+0Thf3JIcwN49CX7fRs/QClK1CZ6pE5+aHSf5pgk5Txwcifm07Ndde7y
sFeb2cpnWF40a2vYyTh1aLzWkQ2GBHfeqdCs2WM3sSwpHX8A0tJufCb0BQZwfmsG
FluoCCs/7rdEhWUiOeeFvhxtF/K7rOr2UPWdCGvRkkMHxtcDlDCbcDljMNsp+Yk1
M5Q2NCPAFRh4/9B8EE6HWeFAk1fIlX7QELLsDBtLeRdKwWUw1llwEjozKoFC3cMQ
oR7gvLDY+PNh56AVFumFdQ5+QXu34rJD3OryNcFvjtHJkvPiOarup8AoS+RuLZyx
/gSOnEbI4ea330fCV5i5xPxzL1M/04LY02n0FdPY3TEZXFBYNVc=
=GU1Q
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.