cmd/gc: use 100x less memory for []byte("string")

[]byte("string") was simplifying to
[]byte{0: 0x73, 1: 0x74, 2: 0x72, 3: 0x69, 4: 0x6e, 5: 0x67},
but that latter form takes up much more memory in the compiler.
Preserve the string form and recognize it to turn global variables
initialized this way into linker-initialized data.

Reduces the compiler memory footprint for a large []byte initialized
this way from approximately 10 kB/B to under 100 B/B.

See also issue 6643.

R=golang-codereviews, r, iant, oleku.konko, dave, gobot, bradfitz
CC=golang-codereviews
https://golang.org/cl/15930045
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 2e03898..a00f5c8 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -1263,6 +1263,7 @@
 int	dsname(Sym *s, int off, char *dat, int ndat);
 void	dumpobj(void);
 Sym*	stringsym(char*, int);
+void	slicebytes(Node*, char*, int);
 LSym*	linksym(Sym*);
 
 /*
diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c
index c17be5c..c6ba367 100644
--- a/src/cmd/gc/obj.c
+++ b/src/cmd/gc/obj.c
@@ -253,3 +253,31 @@
 
 	return sym;	
 }
+
+void
+slicebytes(Node *nam, char *s, int len)
+{
+	int off, n, m;
+	static int gen;
+	Sym *sym;
+
+	snprint(namebuf, sizeof(namebuf), ".gobytes.%d", ++gen);
+	sym = pkglookup(namebuf, localpkg);
+	sym->def = newname(sym);
+
+	off = 0;
+	for(n=0; n<len; n+=m) {
+		m = 8;
+		if(m > len-n)
+			m = len-n;
+		off = dsname(sym, off, s+n, m);
+	}
+	ggloblsym(sym, off, 0, 0);
+	
+	if(nam->op != ONAME)
+		fatal("slicebytes %N", nam);
+	off = nam->xoffset;
+	off = dsymptr(nam->sym, off, sym, 0);
+	off = duintxx(nam->sym, off, len, widthint);
+	duintxx(nam->sym, off, len, widthint);
+}
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
index 446b111..59c5097 100644
--- a/src/cmd/gc/sinit.c
+++ b/src/cmd/gc/sinit.c
@@ -378,6 +378,7 @@
 	InitPlan *p;
 	InitEntry *e;
 	int i;
+	Strlit *sval;
 	
 	switch(r->op) {
 	default:
@@ -426,6 +427,14 @@
 		}
 		break;
 
+	case OSTRARRAYBYTE:
+		if(l->class == PEXTERN && r->left->op == OLITERAL) {
+			sval = r->left->val.u.sval;
+			slicebytes(l, sval->s, sval->len);
+			return 1;
+		}
+		break;
+
 	case OARRAYLIT:
 		initplan(r);
 		if(isslice(r->type)) {
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 6f8b6ad..4d0a636 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -1406,6 +1406,9 @@
 			}
 			break;
 		case OSTRARRAYBYTE:
+			// do not use stringtoarraylit.
+			// generated code and compiler memory footprint is better without it.
+			break;
 		case OSTRARRAYRUNE:
 			if(n->left->op == OLITERAL)
 				stringtoarraylit(&n);