riscv64: improve disassembler support for FENCE

This commit adds disassembler support for FENCE.TSO in both gnu and
plan9 disassembly. It also adds support for FENCE operands in plan9
disassembly. These were previously supported for gnu syntax
disassembly but not for plan9 disassembly. We also add a few more
checks to ensure that reserved FENCE encodings are decoded as full
fences, rather than say FENCE ,.

Change-Id: I89fb8cef3fc57a2dcd282bd9c4c27d63f47eaea0
Reviewed-on: https://go-review.googlesource.com/c/arch/+/762800
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Joel Sing <joel@sing.id.au>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/riscv64/riscv64asm/gnu.go b/riscv64/riscv64asm/gnu.go
index 254bc1f..324b365 100644
--- a/riscv64/riscv64asm/gnu.go
+++ b/riscv64/riscv64asm/gnu.go
@@ -241,17 +241,25 @@
 			args = args[:len(args)-1]
 		}
 
-	// When both pred and succ equals to iorw, the GNU objdump will omit them.
 	case FENCE:
-		if inst.Args[0].(MemOrder).String() == "iorw" &&
-			inst.Args[1].(MemOrder).String() == "iorw" {
-			args = nil
+		fm := inst.Enc >> 28
+		pred := inst.Args[0].(MemOrder).String()
+		succ := inst.Args[1].(MemOrder).String()
+		if fm == 0b1000 {
+			if pred == "rw" && succ == "rw" {
+				return "fence.tso"
+			}
+			return op
 		}
 		// PAUSE is encoded as a FENCE instruction with pred=W, succ=0.
-		if inst.Args[0].(MemOrder).String() == "w" &&
-			inst.Args[1].(MemOrder).String() == "" {
-			op = "pause"
-			args = nil
+		if pred == "w" && succ == "" {
+			return "pause"
+		}
+		if fm != 0 || pred == "" || succ == "" || (pred == "iorw" && succ == "iorw") {
+			// We've either got a full fence or a reserved encoding which should be
+			// treated as a full fence. When both pred and succ equals to iorw, GNU
+			// objdump will omit them.
+			return op
 		}
 
 	case FSGNJX_D:
diff --git a/riscv64/riscv64asm/plan9x.go b/riscv64/riscv64asm/plan9x.go
index 2a20d0d..59f2a5b 100644
--- a/riscv64/riscv64asm/plan9x.go
+++ b/riscv64/riscv64asm/plan9x.go
@@ -199,14 +199,26 @@
 			}
 		}
 
-	// Fence instruction in plan9 doesn't have any operands.
 	case FENCE:
-		// PAUSE is encoded as a FENCE instruction with pred=W, succ=0.
-		if inst.Args[0].(MemOrder).String() == "w" &&
-			inst.Args[1].(MemOrder).String() == "" {
-			op = "PAUSE"
+		fm := inst.Enc >> 28
+		pred := inst.Args[0].(MemOrder).String()
+		succ := inst.Args[1].(MemOrder).String()
+		if fm == 0b1000 {
+			if pred == "rw" && succ == "rw" {
+				return "FENCE.TSO"
+			}
+			return op
 		}
-		args = nil
+		// PAUSE is encoded as a FENCE instruction with pred=W, succ=0.
+		if pred == "w" && succ == "" {
+			return "PAUSE"
+		}
+		if fm != 0 || pred == "" || succ == "" || (pred == "iorw" && succ == "iorw") {
+			// We've either got a full fence or a reserved encoding which should be
+			// treated as a full fence.
+			return op
+		}
+		args[0], args[1] = args[1], args[0]
 
 	case FMADD_D, FMADD_H, FMADD_Q, FMADD_S, FMSUB_D, FMSUB_H,
 		FMSUB_Q, FMSUB_S, FNMADD_D, FNMADD_H, FNMADD_Q, FNMADD_S,
diff --git a/riscv64/riscv64asm/testdata/gnucases.txt b/riscv64/riscv64asm/testdata/gnucases.txt
index a338493..ac9712c 100644
--- a/riscv64/riscv64asm/testdata/gnucases.txt
+++ b/riscv64/riscv64asm/testdata/gnucases.txt
@@ -176,6 +176,7 @@
 0f00f00d|	fence iow,iorw
 0f00f007|	fence orw,iorw
 0f00f00f|	fence
+0f000000|	fence
 0f001004|	fence o,w
 0f002004|	fence o,r
 0f004004|	fence o,o
@@ -184,6 +185,7 @@
 0f002008|	fence i,r
 0f004008|	fence i,o
 0f008008|	fence i,i
+0f003083|	fence.tso
 0f100000|	fence.i
 d32210a2|	feq.d x5,f0,f1
 d32210a4|	feq.h x5,f0,f1
diff --git a/riscv64/riscv64asm/testdata/plan9cases.txt b/riscv64/riscv64asm/testdata/plan9cases.txt
index 55a9260..8ad8c77 100644
--- a/riscv64/riscv64asm/testdata/plan9cases.txt
+++ b/riscv64/riscv64asm/testdata/plan9cases.txt
@@ -157,32 +157,33 @@
 53f0201c|	FDIVH F2, F1, F0
 53f0201e|	FDIVQ F2, F1, F0
 53f02018|	FDIVS F2, F1, F0
-# Fence instruction in plan9 doesn't have any operands.
-0f001001|	FENCE
-0f002001|	FENCE
-0f004001|	FENCE
-0f008001|	FENCE
-0f001002|	FENCE
-0f002002|	FENCE
-0f004002|	FENCE
-0f008002|	FENCE
-0f003003|	FENCE
-0f00f003|	FENCE
-0f00300f|	FENCE
-0f00300c|	FENCE
-0f00c003|	FENCE
-0f00f00e|	FENCE
-0f00f00d|	FENCE
-0f00f007|	FENCE
+0f001001|	FENCE W, W
+0f002001|	FENCE W, R
+0f004001|	FENCE W, O
+0f008001|	FENCE W, I
+0f001002|	FENCE R, W
+0f002002|	FENCE R, R
+0f004002|	FENCE R, O
+0f008002|	FENCE R, I
+0f003003|	FENCE RW, RW
+0f00f003|	FENCE RW, IORW
+0f00300f|	FENCE IORW, RW
+0f00300c|	FENCE IO, RW
+0f00c003|	FENCE RW, IO
+0f00f00e|	FENCE IOR, IORW
+0f00f00d|	FENCE IOW, IORW
+0f00f007|	FENCE ORW, IORW
 0f00f00f|	FENCE
-0f001004|	FENCE
-0f002004|	FENCE
-0f004004|	FENCE
-0f008004|	FENCE
-0f001008|	FENCE
-0f002008|	FENCE
-0f004008|	FENCE
-0f008008|	FENCE
+0f000000|	FENCE
+0f001004|	FENCE O, W
+0f002004|	FENCE O, R
+0f004004|	FENCE O, O
+0f008004|	FENCE O, I
+0f001008|	FENCE I, W
+0f002008|	FENCE I, R
+0f004008|	FENCE I, O
+0f008008|	FENCE I, I
+0f003083|	FENCE.TSO
 0f100000|	FENCEI
 d32210a2|	FEQD F1, F0, X5
 d32210a4|	FEQH F1, F0, X5