summaryrefslogtreecommitdiff
path: root/src/cmd/8g/ggen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/8g/ggen.c')
-rw-r--r--src/cmd/8g/ggen.c96
1 files changed, 81 insertions, 15 deletions
diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c
index 6db0474c9..108c493aa 100644
--- a/src/cmd/8g/ggen.c
+++ b/src/cmd/8g/ggen.c
@@ -480,12 +480,40 @@ samereg(Node *a, Node *b)
* 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);
@@ -495,6 +523,24 @@ dodiv(int op, Type *t, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
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);
@@ -507,6 +553,8 @@ dodiv(int op, Type *t, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
gmove(ax, res);
else
gmove(dx, res);
+ if(check)
+ patch(p3, pc);
}
static void
@@ -553,13 +601,13 @@ cgen_div(int op, Node *nl, Node *nr, Node *res)
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);
}
@@ -572,9 +620,9 @@ cgen_div(int op, Node *nl, Node *nr, Node *res)
void
cgen_shift(int op, Node *nl, Node *nr, Node *res)
{
- Node n1, n2, cx, oldcx;
+ Node n1, n2, nt, cx, oldcx, hi, lo;
int a, w;
- Prog *p1;
+ Prog *p1, *p2;
uvlong sc;
if(nl->type->width > 4)
@@ -608,8 +656,13 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res)
gmove(&cx, &oldcx);
}
- nodreg(&n1, types[TUINT32], D_CX);
- regalloc(&n1, nr->type, &n1); // to hold the shift type in CX
+ if(nr->type->width > 4) {
+ tempname(&nt, nr->type);
+ n1 = nt;
+ } else {
+ nodreg(&n1, types[TUINT32], D_CX);
+ regalloc(&n1, nr->type, &n1); // to hold the shift type in CX
+ }
if(samereg(&cx, res))
regalloc(&n2, nl->type, N);
@@ -624,8 +677,21 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res)
}
// test and fix up large shifts
- gins(optoas(OCMP, nr->type), &n1, ncon(w));
- p1 = gbranch(optoas(OLT, types[TUINT32]), T);
+ if(nr->type->width > 4) {
+ // delayed reg alloc
+ nodreg(&n1, types[TUINT32], D_CX);
+ regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX
+ split64(&nt, &lo, &hi);
+ gmove(&lo, &n1);
+ gins(optoas(OCMP, types[TUINT32]), &hi, ncon(0));
+ p2 = gbranch(optoas(ONE, types[TUINT32]), T);
+ gins(optoas(OCMP, types[TUINT32]), &n1, ncon(w));
+ p1 = gbranch(optoas(OLT, types[TUINT32]), T);
+ patch(p2, pc);
+ } else {
+ gins(optoas(OCMP, nr->type), &n1, ncon(w));
+ p1 = gbranch(optoas(OLT, types[TUINT32]), T);
+ }
if(op == ORSH && issigned[nl->type->etype]) {
gins(a, ncon(w-1), &n2);
} else {