cmd/compile: eliminate copy for static literals

*p = [5]byte{1,2,3,4,5}

First we allocate a global containing the RHS.  Then we copy
that global to a local stack variable, and then copy that local
stack variable to *p.  The intermediate copy is unnecessary.

Note that this only works if the RHS is completely constant.
If the code was:
*p = [5]byte{1,2,x,4,5}
this optimization doesn't apply as we have to construct the
RHS on the stack before copying it to *p.

Fixes #12841

Change-Id: I7cd0404ecc7a2d1750cbd8fe1222dba0fa44611f
Reviewed-on: https://go-review.googlesource.com/22192
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
index 85ef78b..1021609 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -563,6 +563,32 @@
 	return mode
 }
 
+// isStaticCompositeLiteral reports whether n is a compile-time constant.
+// n must be a struct or array literal.
+func isStaticCompositeLiteral(n *Node) bool {
+	for _, r := range n.List.Slice() {
+		if r.Op != OKEY {
+			Fatalf("isStaticCompositeLiteral: rhs not OKEY: %v", r)
+		}
+		index := r.Left
+		if n.Op == OARRAYLIT && index.Op != OLITERAL {
+			return false
+		}
+		value := r.Right
+		switch value.Op {
+		case OSTRUCTLIT, OARRAYLIT:
+			if !isStaticCompositeLiteral(value) {
+				return false
+			}
+		default:
+			if value.Op != OLITERAL {
+				return false
+			}
+		}
+	}
+	return true
+}
+
 func structlit(ctxt int, pass int, n *Node, var_ *Node, init *Nodes) {
 	for _, r := range n.List.Slice() {
 		if r.Op != OKEY {
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 78bad8d..1a15bd9 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -1531,6 +1531,19 @@
 		n = r
 
 	case OARRAYLIT, OMAPLIT, OSTRUCTLIT, OPTRLIT:
+		if (n.Op == OSTRUCTLIT || (n.Op == OARRAYLIT && !n.Type.IsSlice())) && isStaticCompositeLiteral(n) {
+			// n can be directly represented in the read-only data section.
+			// Make direct reference to the static data. See issue 12841.
+			vstat := staticname(n.Type, 0)
+			if n.Op == OSTRUCTLIT {
+				structlit(0, 1, n, vstat, init)
+			} else {
+				arraylit(0, 1, n, vstat, init)
+			}
+			n = vstat
+			n = typecheck(n, Erv)
+			break
+		}
 		var_ := temp(n.Type)
 		anylit(0, n, var_, init)
 		n = var_