cmd/internal/obj: make arm64 use RegTo2 instead of a full fledged Addr To2

It shrinks Prog type from 448 bytes down to 376 bytes on amd64.

It also makes sense, because I don't know of any modern architecture
that have instructions which can write to two destinations, none of
which is a register (even x86 doesn't have such instructions).

Change-Id: I3061f1c9ac93d79ee2b92ecb9049641d0e0f6300
Reviewed-on: https://go-review.googlesource.com/10330
Reviewed-by: Aram Hăvărneanu <aram@mgk.ro>
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go
index 725c635..d5d2772 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -493,7 +493,10 @@
 			if arch.IsARM64STLXR(op) {
 				prog.From = a[0]
 				prog.To = a[1]
-				prog.To2 = a[2]
+				if a[2].Type != obj.TYPE_REG {
+					p.errorf("invalid addressing modes for third operand to %s instruction, must be register", obj.Aconv(op))
+				}
+				prog.RegTo2 = a[2].Reg
 				break
 			}
 			prog.From = a[0]
diff --git a/src/cmd/compile/internal/arm64/peep.go b/src/cmd/compile/internal/arm64/peep.go
index 1c3b289..3dbccb7 100644
--- a/src/cmd/compile/internal/arm64/peep.go
+++ b/src/cmd/compile/internal/arm64/peep.go
@@ -422,9 +422,9 @@
 		// 7g never generates a from3
 		fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3))
 	}
-	if p.To2.Type != obj.TYPE_NONE {
+	if p.RegTo2 != obj.REG_NONE {
 		// 7g never generates a to2
-		fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2))
+		fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2)))
 	}
 
 	switch p.As {
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 6e00cb5..9e64393 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -2677,8 +2677,8 @@
 	case 59: /* stxr/stlxr */
 		o1 = opstore(ctxt, int(p.As))
 
-		if p.To2.Type != obj.TYPE_NONE {
-			o1 |= uint32(p.To2.Reg&31) << 16
+		if p.RegTo2 != obj.REG_NONE {
+			o1 |= uint32(p.RegTo2&31) << 16
 		} else {
 			o1 |= 0x1F << 16
 		}
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index b0c7a55..2fc12c1 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -206,7 +206,6 @@
 	From     Addr
 	From3    Addr
 	To       Addr
-	To2      Addr
 	Opt      interface{}
 	Forwd    *Prog
 	Pcond    *Prog
@@ -217,6 +216,7 @@
 	Spadj    int32
 	As       int16
 	Reg      int16
+	RegTo2   int16 // 2nd register output operand
 	Mark     uint16
 	Optab    uint16
 	Scond    uint8
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index 317ee4f..efecae6 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -327,8 +327,8 @@
 	if p.To.Type != TYPE_NONE {
 		fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To))
 	}
-	if p.To2.Type != TYPE_NONE {
-		fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To2))
+	if p.RegTo2 != REG_NONE {
+		fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2)))
 	}
 	return buf.String()
 }
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 7a4fc12..4798c8f 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -350,9 +350,6 @@
 		if p.From3.Name == obj.NAME_EXTERN {
 			ctxt.Diag("don't know how to handle %v with -dynlink", p)
 		}
-		if p.To2.Name == obj.NAME_EXTERN {
-			ctxt.Diag("don't know how to handle %v with -dynlink", p)
-		}
 		var source *obj.Addr
 		if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
 			if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {