divide by a constant power of 2

R=rsc
OCL=32858
CL=32858
diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h
index ce5f6c8..f9f50cc 100644
--- a/src/cmd/6g/gg.h
+++ b/src/cmd/6g/gg.h
@@ -125,6 +125,8 @@
 int	sudoaddable(int, Node*, Addr*);
 void	afunclit(Addr*);
 void	datagostring(Strlit*, Addr*);
+int	powtwo(Node*);
+Type*	tounsigned(Type*);
 
 /*
  * obj.c
diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c
index 64220bc..4e71f75 100644
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -533,8 +533,8 @@
 void
 cgen_div(int op, Node *nl, Node *nr, Node *res)
 {
-	Node ax, dx, oldax, olddx, n1, n2;
-	int rax, rdx;
+	Node ax, dx, oldax, olddx, n1, n2, n3;
+	int rax, rdx, n, w;
 
 	if(nl->ullman >= UINF) {
 		tempname(&n1, nl->type);
@@ -547,6 +547,86 @@
 		nr = &n2;
 	}
 
+	if(nr->op != OLITERAL)
+		goto longdiv;
+
+	// special cases of mod/div
+	// by a constant
+	n = powtwo(nr);
+	w = nl->type->width*8;
+
+	if(n+1 >= w) {
+		// just sign bit
+		goto longdiv;
+	}
+
+	if(n < 0)
+		goto divbymul;
+
+	if(op == OMOD) {
+		// todo
+		goto longdiv;
+	}
+
+	switch(n) {
+	case 0:
+		// divide by 1
+		cgen(nl, res);
+		return;
+	case 1:
+		// divide by 2
+		regalloc(&n1, nl->type, res);
+		cgen(nl, &n1);
+		if(issigned[nl->type->etype]) {
+			// develop -1 iff nl is negative
+			regalloc(&n2, nl->type, N);
+			gmove(&n1, &n2);
+			nodconst(&n3, nl->type, w-1);
+			gins(optoas(ORSH, nl->type), &n3, &n2);
+			gins(optoas(OSUB, nl->type), &n2, &n1);
+			regfree(&n2);
+		}
+		nodconst(&n2, nl->type, n);
+		gins(optoas(ORSH, nl->type), &n2, &n1);
+		gmove(&n1, res);
+		regfree(&n1);
+		return;
+	default:
+		regalloc(&n1, nl->type, res);
+		cgen(nl, &n1);
+		if(issigned[nl->type->etype]) {
+			// develop (2^k)-1 iff nl is negative
+			regalloc(&n2, nl->type, N);
+			gmove(&n1, &n2);
+			nodconst(&n3, nl->type, w-1);
+			gins(optoas(ORSH, nl->type), &n3, &n2);
+			nodconst(&n3, nl->type, w-n);
+			gins(optoas(ORSH, tounsigned(nl->type)), &n3, &n2);
+			gins(optoas(OADD, nl->type), &n2, &n1);
+			regfree(&n2);
+		}
+		nodconst(&n2, nl->type, n);
+		gins(optoas(ORSH, nl->type), &n2, &n1);
+		gmove(&n1, res);
+		regfree(&n1);
+	}
+	return;
+
+divbymul:
+	switch(simtype[nl->type->etype]) {
+	default:
+		goto longdiv;
+
+	case TINT32:
+	case TUINT32:
+	case TINT64:
+	case TUINT64:
+		break;
+	}
+	// todo
+	goto longdiv;
+
+longdiv:
 	rax = reg[D_AX];
 	rdx = reg[D_DX];
 
diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c
index 5ed0a81..136a8d5 100644
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -1861,3 +1861,55 @@
 	sudoclean();
 	return 0;
 }
+
+int
+powtwo(Node *n)
+{
+	uvlong v, b;
+	int i;
+
+	if(n == N || n->op != OLITERAL || n->type == T)
+		goto no;
+	if(!isint[n->type->etype])
+		goto no;
+
+	v = mpgetfix(n->val.u.xval);
+	b = 1ULL;
+	for(i=0; i<64; i++) {
+		if(b == v)
+			return i;
+		b = b<<1;
+	}
+
+no:
+	return -1;
+}
+
+Type*
+tounsigned(Type *t)
+{
+
+	// this is types[et+1], but not sure
+	// that this relation is immutable
+	switch(t->etype) {
+	default:
+		print("tounsigned: unknown type %T\n", t);
+		break;
+	case TINT:
+		t = types[TUINT];
+		break;
+	case TINT8:
+		t = types[TUINT8];
+		break;
+	case TINT16:
+		t = types[TUINT16];
+		break;
+	case TINT32:
+		t = types[TUINT32];
+		break;
+	case TINT64:
+		t = types[TUINT64];
+		break;
+	}
+	return t;
+}
diff --git a/test/ken/divconst.go b/test/ken/divconst.go
new file mode 100644
index 0000000..9042b1e
--- /dev/null
+++ b/test/ken/divconst.go
@@ -0,0 +1,45 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+
+package main
+
+import	"rand"
+
+func	test(a,b,c int64);
+
+func
+main()
+{
+	var a, b int64;
+
+	for i:=0; i<1e6; i++ {
+		a := rand.Int63() - 1<<62;
+		b = a/1;	test(a,b,1);
+		b = a/2;	test(a,b,2);
+		b = a/3;	test(a,b,3);
+		b = a/4;	test(a,b,4);
+		b = a/5;	test(a,b,5);
+		b = a/6;	test(a,b,6);
+		b = a/7;	test(a,b,7);
+		b = a/8;	test(a,b,8);
+		b = a/16;	test(a,b,16);
+		b = a/32;	test(a,b,32);
+		b = a/64;	test(a,b,64);
+		b = a/128;	test(a,b,128);
+		b = a/256;	test(a,b,256);
+		b = a/16384;	test(a,b,16384);
+	}
+}
+
+func
+test(a,b,c int64)
+{
+	d := a/c;
+	if d != b {
+		panicln(a, b, c, d);
+	}
+}