shift for non-64 bit integers.

R=rsc
http://go/go-review/1015017
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index e213ddf..bcbc979 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -244,11 +244,14 @@
 
 	// asymmetric binary
 	case OSUB:
-	case OLSH:
-	case ORSH:
 		a = optoas(n->op, nl->type);
 		goto abop;
 
+	case OLSH:
+	case ORSH:
+		cgen_shift(n->op, nl, nr, res);
+		break;
+
 	case OCONV:
 		if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) {
 			cgen(nl, res);
diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h
index 04b16d2..98e5278 100644
--- a/src/cmd/5g/gg.h
+++ b/src/cmd/5g/gg.h
@@ -102,6 +102,7 @@
 Prog *	gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs);
 void	naddr(Node*, Addr*, int);
 void	cgen_aret(Node*, Node*);
+void	cgen_shift(int, Node*, Node*, Node*);
 
 /*
  * cgen64.c
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c
index ddd6936..a224320 100644
--- a/src/cmd/5g/ggen.c
+++ b/src/cmd/5g/ggen.c
@@ -536,6 +536,95 @@
 	return 1;
 }
 
+/*
+ * generate shift according to op, one of:
+ *	res = nl << nr
+ *	res = nl >> nr
+ */
+void
+cgen_shift(int op, Node *nl, Node *nr, Node *res)
+{
+	Node n1, n2, n3, t;
+	int w;
+	Prog *p1, *p2, *p3;
+	uvlong sc;
+
+	if(nl->type->width > 4)
+		fatal("cgen_shift %T", nl->type);
+
+	w = nl->type->width * 8;
+
+	if(nr->op == OLITERAL) {
+		regalloc(&n1, nl->type, res);
+		cgen(nl, &n1);
+		sc = mpgetfix(nr->val.u.xval);
+		if(sc == 0) {
+			return;
+		} else if(sc >= nl->type->width*8) {
+			if(op == ORSH && issigned[nl->type->etype])
+				gshift(AMOVW, &n1, SHIFT_AR, w, &n1);
+			else
+				gins(AEOR, &n1, &n1);
+		} else {
+			if(op == ORSH && issigned[nl->type->etype])
+				gshift(AMOVW, &n1, SHIFT_AR, sc, &n1);
+			else if(op == ORSH)
+				gshift(AMOVW, &n1, SHIFT_LR, sc, &n1);
+			else // OLSH
+				gshift(AMOVW, &n1, SHIFT_LL, sc, &n1);
+		}
+		gmove(&n1, res);
+		regfree(&n1);
+		return;
+	}
+
+	if(nl->ullman >= nr->ullman) {
+		regalloc(&n2, nl->type, res);
+		cgen(nl, &n2);
+		regalloc(&n1, nr->type, N);
+		cgen(nr, &n1);
+	} else {
+		regalloc(&n1, nr->type, N);
+		cgen(nr, &n1);
+		regalloc(&n2, nl->type, res);
+		cgen(nl, &n2);
+	}
+
+	// test for shift being 0
+	p1 = gins(AMOVW, &n1, &n1);
+	p1->scond |= C_SBIT;
+	p3 = gbranch(ABEQ, T);
+
+	// test and fix up large shifts
+	regalloc(&n3, nr->type, N);
+	nodconst(&t, types[TUINT32], w);
+	gmove(&t, &n3);
+	gcmp(ACMP, &n1, &n3);
+	if(op == ORSH) {
+		if(issigned[nl->type->etype]) {
+			p1 = gshift(AMOVW, &n2, SHIFT_AR, w-1, &n2);
+			p2 = gregshift(AMOVW, &n2, SHIFT_AR, &n1, &n2);
+		} else {
+			p1 = gins(AEOR, &n2, &n2);
+			p2 = gregshift(AMOVW, &n2, SHIFT_LR, &n1, &n2);
+		}
+		p1->scond = C_SCOND_HS;
+		p2->scond = C_SCOND_LO;
+	} else {
+		p1 = gins(AEOR, &n2, &n2);
+		p2 = gregshift(AMOVW, &n2, SHIFT_LL, &n1, &n2);
+		p1->scond = C_SCOND_HS;
+		p2->scond = C_SCOND_LO;
+	}
+	regfree(&n3);
+
+	patch(p3, pc);
+	gmove(&n2, res);
+
+	regfree(&n1);
+	regfree(&n2);
+}
+
 void
 clearfat(Node *nl)
 {
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index bd5f0de..b14c7d2 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -923,6 +923,7 @@
 }
 
 /* generate a comparison
+TODO(kaib): one of the args can actually be a small constant. relax the constraint and fix call sites.
  */
 Prog*
 gcmp(int as, Node *lhs, Node *rhs)
diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c
index 3f535cf..99c8b78 100644
--- a/src/cmd/8g/ggen.c
+++ b/src/cmd/8g/ggen.c
@@ -627,7 +627,7 @@
 	uvlong sc;
 
 	if(nl->type->width > 4)
-		fatal("cgen_shift %T", nl->type->width);
+		fatal("cgen_shift %T", nl->type);
 
 	w = nl->type->width * 8;