/*
* generate division.
- * caller must set:
- * ax = allocated AX register
- * dx = allocated DX register
* generates one of:
* res = nl / nr
* res = nl % nr
void
dodiv(int op, Node *nl, Node *nr, Node *res)
{
- int a;
- Node n3, n4;
+ int a, check;
+ Node n3, n4, n5;
Type *t;
Node ax, dx, oldax, olddx;
-
+ Prog *p1, *p2, *p3;
+
+ // Have to be careful about handling
+ // most negative int divided by -1 correctly.
+ // The hardware will trap.
+ // Also the byte divide instruction needs AH,
+ // which we otherwise don't have to deal with.
+ // Easiest way to avoid for int8, int16: use int32.
+ // For int32 and int64, use explicit test.
+ // Could use int64 hw for int32.
t = nl->type;
- if(t->width == 1) {
+ check = 0;
+ if(issigned[t->etype]) {
+ check = 1;
+ if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -1LL<<(t->width*8-1))
+ check = 0;
+ else if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) != -1)
+ check = 0;
+ }
+ if(t->width < 4) {
if(issigned[t->etype])
t = types[TINT32];
else
t = types[TUINT32];
+ check = 0;
}
a = optoas(op, t);
savex(D_AX, &ax, &oldax, res, t);
cgen(nl, &ax);
}
+ p3 = P;
+ if(check) {
+ nodconst(&n4, t, -1);
+ gins(optoas(OCMP, t), &n3, &n4);
+ p1 = gbranch(optoas(ONE, t), T);
+ nodconst(&n4, t, -1LL<<(t->width*8-1));
+ if(t->width == 8) {
+ n5 = n4;
+ regalloc(&n4, t, N);
+ gins(AMOVQ, &n5, &n4);
+ }
+ gins(optoas(OCMP, t), &ax, &n4);
+ p2 = gbranch(optoas(ONE, t), T);
+ if(op == ODIV)
+ gmove(&n4, res);
+ if(t->width == 8)
+ regfree(&n4);
+ if(op == OMOD) {
+ nodconst(&n4, t, 0);
+ gmove(&n4, res);
+ }
+ p3 = gbranch(AJMP, T);
+ patch(p1, pc);
+ patch(p2, pc);
+ }
savex(D_DX, &dx, &olddx, res, t);
if(!issigned[t->etype]) {
nodconst(&n4, t, 0);
gins(optoas(OEXTEND, t), N, N);
gins(a, &n3, N);
regfree(&n3);
-
if(op == ODIV)
gmove(&ax, res);
else
gmove(&dx, res);
- restx(&ax, &oldax);
restx(&dx, &olddx);
+ if(check)
+ patch(p3, pc);
+ restx(&ax, &oldax);
}
/*
* according to op.
*/
void
-dodiv(int op, Type *t, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
+dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
{
- Node n1, t1, t2, nz;
+ int check;
+ Node n1, t1, t2, n4, nz;
+ Type *t;
+ Prog *p1, *p2, *p3;
+
+ // Have to be careful about handling
+ // most negative int divided by -1 correctly.
+ // The hardware will trap.
+ // Also the byte divide instruction needs AH,
+ // which we otherwise don't have to deal with.
+ // Easiest way to avoid for int8, int16: use int32.
+ // For int32 and int64, use explicit test.
+ // Could use int64 hw for int32.
+ t = nl->type;
+ check = 0;
+ if(issigned[t->etype]) {
+ check = 1;
+ if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -1LL<<(t->width*8-1))
+ check = 0;
+ else if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) != -1)
+ check = 0;
+ }
+ if(t->width < 4) {
+ if(issigned[t->etype])
+ t = types[TINT32];
+ else
+ t = types[TUINT32];
+ check = 0;
+ }
- tempname(&t1, nl->type);
- tempname(&t2, nr->type);
+ tempname(&t1, t);
+ tempname(&t2, t);
cgen(nl, &t1);
cgen(nr, &t2);
regalloc(&n1, t, N);
gmove(&t2, &n1);
gmove(&t1, ax);
+ p3 = P;
+ if(check) {
+ nodconst(&n4, t, -1);
+ gins(optoas(OCMP, t), &n1, &n4);
+ p1 = gbranch(optoas(ONE, t), T);
+ nodconst(&n4, t, -1LL<<(t->width*8-1));
+ gins(optoas(OCMP, t), ax, &n4);
+ p2 = gbranch(optoas(ONE, t), T);
+ if(op == ODIV)
+ gmove(&n4, res);
+ if(op == OMOD) {
+ nodconst(&n4, t, 0);
+ gmove(&n4, res);
+ }
+ p3 = gbranch(AJMP, T);
+ patch(p1, pc);
+ patch(p2, pc);
+ }
if(!issigned[t->etype]) {
nodconst(&nz, t, 0);
gmove(&nz, dx);
gmove(ax, res);
else
gmove(dx, res);
+ if(check)
+ patch(p3, pc);
}
static void
if(is64(nl->type))
fatal("cgen_div %T", nl->type);
- t = nl->type;
- if(t->width == 1)
- t = types[t->etype+2]; // int8 -> int16, uint8 -> uint16
-
+ if(issigned[nl->type->etype])
+ t = types[TINT32];
+ else
+ t = types[TUINT32];
savex(D_AX, &ax, &oldax, res, t);
savex(D_DX, &dx, &olddx, res, t);
- dodiv(op, t, nl, nr, res, &ax, &dx);
+ dodiv(op, nl, nr, res, &ax, &dx);
restx(&dx, &olddx);
restx(&ax, &oldax);
}
--- /dev/null
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// Copyright 2011 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.
+
+// divide corner cases
+
+package main
+
+import "fmt"
+
+func f8(x, y, q, r int8) {
+ if t := x / y; t != q {
+ fmt.Printf("%d/%d = %d, want %d\n", x, y, t, q)
+ }
+ if t := x % y; t != r {
+ fmt.Printf("%d%%%d = %d, want %d\n", x, y, t, r)
+ }
+}
+
+func f16(x, y, q, r int16) {
+ if t := x / y; t != q {
+ fmt.Printf("%d/%d = %d, want %d\n", x, y, t, q)
+ }
+ if t := x % y; t != r {
+ fmt.Printf("%d%%%d = %d, want %d\n", x, y, t, r)
+ }
+}
+
+func f32(x, y, q, r int32) {
+ if t := x / y; t != q {
+ fmt.Printf("%d/%d = %d, want %d\n", x, y, t, q)
+ }
+ if t := x % y; t != r {
+ fmt.Printf("%d%%%d = %d, want %d\n", x, y, t, r)
+ }
+}
+
+func f64(x, y, q, r int64) {
+ if t := x / y; t != q {
+ fmt.Printf("%d/%d = %d, want %d\n", x, y, t, q)
+ }
+ if t := x % y; t != r {
+ fmt.Printf("%d%%%d = %d, want %d\n", x, y, t, r)
+ }
+}
+
+func main() {
+ f8(-1<<7, -1, -1<<7, 0)
+ f16(-1<<15, -1, -1<<15, 0)
+ f32(-1<<31, -1, -1<<31, 0)
+ f64(-1<<63, -1, -1<<63, 0)
+}