root/src/pkg/runtime/vlrt_386.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _d2v
  2. _f2v
  3. _v2d
  4. _v2f
  5. slowdodiv
  6. dodiv
  7. _divvu
  8. runtime·uint64div
  9. _modvu
  10. runtime·uint64mod
  11. vneg
  12. _divv
  13. runtime·int64div
  14. _modv
  15. runtime·int64mod
  16. _rshav
  17. _rshlv
  18. _lshv
  19. _andv
  20. _orv
  21. _xorv
  22. _vpp
  23. _vmm
  24. _ppv
  25. _mmv
  26. _vasop
  27. _p2v
  28. _sl2v
  29. _ul2v
  30. _si2v
  31. _ui2v
  32. _sh2v
  33. _uh2v
  34. _sc2v
  35. _uc2v
  36. _v2sc
  37. _v2uc
  38. _v2sh
  39. _v2uh
  40. _v2sl
  41. _v2ul
  42. _v2si
  43. _v2ui
  44. _testv
  45. _eqv
  46. _nev
  47. _ltv
  48. _lev
  49. _gtv
  50. _gev
  51. _lov
  52. _lsv
  53. _hiv
  54. _hsv

// Inferno's libkern/vlrt-386.c
// http://code.google.com/p/inferno-os/source/browse/libkern/vlrt-386.c
//
//         Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
//         Portions Copyright 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "../../cmd/ld/textflag.h"

/*
 * C runtime for 64-bit divide, others.
 *
 * TODO(rsc): The simple functions are dregs--8c knows how
 * to generate the code directly now.  Find and remove.
 */

extern void runtime·panicdivide(void);

typedef unsigned long   ulong;
typedef unsigned int    uint;
typedef unsigned short  ushort;
typedef unsigned char   uchar;
typedef signed char     schar;

#define SIGN(n) (1UL<<(n-1))

typedef struct  Vlong   Vlong;
struct  Vlong
{
        union
        {
                long long       v;
                struct
                {
                        ulong   lo;
                        ulong   hi;
                };
                struct
                {
                        ushort  lols;
                        ushort  loms;
                        ushort  hils;
                        ushort  hims;
                };
        };
};

void    runtime·abort(void);

void
_d2v(Vlong *y, double d)
{
        union { double d; struct Vlong; } x;
        ulong xhi, xlo, ylo, yhi;
        int sh;

        x.d = d;

        xhi = (x.hi & 0xfffff) | 0x100000;
        xlo = x.lo;
        sh = 1075 - ((x.hi >> 20) & 0x7ff);

        ylo = 0;
        yhi = 0;
        if(sh >= 0) {
                /* v = (hi||lo) >> sh */
                if(sh < 32) {
                        if(sh == 0) {
                                ylo = xlo;
                                yhi = xhi;
                        } else {
                                ylo = (xlo >> sh) | (xhi << (32-sh));
                                yhi = xhi >> sh;
                        }
                } else {
                        if(sh == 32) {
                                ylo = xhi;
                        } else
                        if(sh < 64) {
                                ylo = xhi >> (sh-32);
                        }
                }
        } else {
                /* v = (hi||lo) << -sh */
                sh = -sh;
                if(sh <= 10) {
                        ylo = xlo << sh;
                        yhi = (xhi << sh) | (xlo >> (32-sh));
                } else {
                        /* overflow */
                        yhi = d;        /* causes something awful */
                }
        }
        if(x.hi & SIGN(32)) {
                if(ylo != 0) {
                        ylo = -ylo;
                        yhi = ~yhi;
                } else
                        yhi = -yhi;
        }

        y->hi = yhi;
        y->lo = ylo;
}

void
_f2v(Vlong *y, float f)
{

        _d2v(y, f);
}

double
_v2d(Vlong x)
{
        if(x.hi & SIGN(32)) {
                if(x.lo) {
                        x.lo = -x.lo;
                        x.hi = ~x.hi;
                } else
                        x.hi = -x.hi;
                return -((long)x.hi*4294967296. + x.lo);
        }
        return (long)x.hi*4294967296. + x.lo;
}

float
_v2f(Vlong x)
{
        return _v2d(x);
}

ulong   _div64by32(Vlong, ulong, ulong*);
int     _mul64by32(Vlong*, Vlong, ulong);

static void
slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
{
        ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
        int i;

        numhi = num.hi;
        numlo = num.lo;
        denhi = den.hi;
        denlo = den.lo;

        /*
         * get a divide by zero
         */
        if(denlo==0 && denhi==0) {
                numlo = numlo / denlo;
        }

        /*
         * set up the divisor and find the number of iterations needed
         */
        if(numhi >= SIGN(32)) {
                quohi = SIGN(32);
                quolo = 0;
        } else {
                quohi = numhi;
                quolo = numlo;
        }
        i = 0;
        while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
                denhi = (denhi<<1) | (denlo>>31);
                denlo <<= 1;
                i++;
        }

        quohi = 0;
        quolo = 0;
        for(; i >= 0; i--) {
                quohi = (quohi<<1) | (quolo>>31);
                quolo <<= 1;
                if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
                        t = numlo;
                        numlo -= denlo;
                        if(numlo > t)
                                numhi--;
                        numhi -= denhi;
                        quolo |= 1;
                }
                denlo = (denlo>>1) | (denhi<<31);
                denhi >>= 1;
        }

        if(q) {
                q->lo = quolo;
                q->hi = quohi;
        }
        if(r) {
                r->lo = numlo;
                r->hi = numhi;
        }
}

static void
dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
{
        ulong n;
        Vlong x, q, r;

        if(den.hi > num.hi || (den.hi == num.hi && den.lo > num.lo)){
                if(qp) {
                        qp->hi = 0;
                        qp->lo = 0;
                }
                if(rp) {
                        rp->hi = num.hi;
                        rp->lo = num.lo;
                }
                return;
        }

        if(den.hi != 0){
                q.hi = 0;
                n = num.hi/den.hi;
                if(_mul64by32(&x, den, n) || x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo))
                        slowdodiv(num, den, &q, &r);
                else {
                        q.lo = n;
                        r.v = num.v - x.v;
                }
        } else {
                if(num.hi >= den.lo){
                        if(den.lo == 0)
                                runtime·panicdivide();
                        q.hi = n = num.hi/den.lo;
                        num.hi -= den.lo*n;
                } else {
                        q.hi = 0;
                }
                q.lo = _div64by32(num, den.lo, &r.lo);
                r.hi = 0;
        }
        if(qp) {
                qp->lo = q.lo;
                qp->hi = q.hi;
        }
        if(rp) {
                rp->lo = r.lo;
                rp->hi = r.hi;
        }
}

void
_divvu(Vlong *q, Vlong n, Vlong d)
{

        if(n.hi == 0 && d.hi == 0) {
                if(d.lo == 0)
                        runtime·panicdivide();
                q->hi = 0;
                q->lo = n.lo / d.lo;
                return;
        }
        dodiv(n, d, q, 0);
}

void
runtime·uint64div(Vlong n, Vlong d, Vlong q)
{
        _divvu(&q, n, d);
}

void
_modvu(Vlong *r, Vlong n, Vlong d)
{

        if(n.hi == 0 && d.hi == 0) {
                if(d.lo == 0)
                        runtime·panicdivide();
                r->hi = 0;
                r->lo = n.lo % d.lo;
                return;
        }
        dodiv(n, d, 0, r);
}

void
runtime·uint64mod(Vlong n, Vlong d, Vlong q)
{
        _modvu(&q, n, d);
}

static void
vneg(Vlong *v)
{

        if(v->lo == 0) {
                v->hi = -v->hi;
                return;
        }
        v->lo = -v->lo;
        v->hi = ~v->hi;
}

void
_divv(Vlong *q, Vlong n, Vlong d)
{
        long nneg, dneg;

        if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
                if((long)n.lo == -0x80000000 && (long)d.lo == -1) {
                        // special case: 32-bit -0x80000000 / -1 causes divide error,
                        // but it's okay in this 64-bit context.
                        q->lo = 0x80000000;
                        q->hi = 0;
                        return;
                }
                if(d.lo == 0)
                        runtime·panicdivide();
                q->lo = (long)n.lo / (long)d.lo;
                q->hi = ((long)q->lo) >> 31;
                return;
        }
        nneg = n.hi >> 31;
        if(nneg)
                vneg(&n);
        dneg = d.hi >> 31;
        if(dneg)
                vneg(&d);
        dodiv(n, d, q, 0);
        if(nneg != dneg)
                vneg(q);
}

void
runtime·int64div(Vlong n, Vlong d, Vlong q)
{
        _divv(&q, n, d);
}

void
_modv(Vlong *r, Vlong n, Vlong d)
{
        long nneg, dneg;

        if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
                if((long)n.lo == -0x80000000 && (long)d.lo == -1) {
                        // special case: 32-bit -0x80000000 % -1 causes divide error,
                        // but it's okay in this 64-bit context.
                        r->lo = 0;
                        r->hi = 0;
                        return;
                }
                if(d.lo == 0)
                        runtime·panicdivide();
                r->lo = (long)n.lo % (long)d.lo;
                r->hi = ((long)r->lo) >> 31;
                return;
        }
        nneg = n.hi >> 31;
        if(nneg)
                vneg(&n);
        dneg = d.hi >> 31;
        if(dneg)
                vneg(&d);
        dodiv(n, d, 0, r);
        if(nneg)
                vneg(r);
}

void
runtime·int64mod(Vlong n, Vlong d, Vlong q)
{
        _modv(&q, n, d);
}

void
_rshav(Vlong *r, Vlong a, int b)
{
        long t;

        t = a.hi;
        if(b >= 32) {
                r->hi = t>>31;
                if(b >= 64) {
                        /* this is illegal re C standard */
                        r->lo = t>>31;
                        return;
                }
                r->lo = t >> (b-32);
                return;
        }
        if(b <= 0) {
                r->hi = t;
                r->lo = a.lo;
                return;
        }
        r->hi = t >> b;
        r->lo = (t << (32-b)) | (a.lo >> b);
}

void
_rshlv(Vlong *r, Vlong a, int b)
{
        ulong t;

        t = a.hi;
        if(b >= 32) {
                r->hi = 0;
                if(b >= 64) {
                        /* this is illegal re C standard */
                        r->lo = 0;
                        return;
                }
                r->lo = t >> (b-32);
                return;
        }
        if(b <= 0) {
                r->hi = t;
                r->lo = a.lo;
                return;
        }
        r->hi = t >> b;
        r->lo = (t << (32-b)) | (a.lo >> b);
}

#pragma textflag NOSPLIT
void
_lshv(Vlong *r, Vlong a, int b)
{
        ulong t;

        t = a.lo;
        if(b >= 32) {
                r->lo = 0;
                if(b >= 64) {
                        /* this is illegal re C standard */
                        r->hi = 0;
                        return;
                }
                r->hi = t << (b-32);
                return;
        }
        if(b <= 0) {
                r->lo = t;
                r->hi = a.hi;
                return;
        }
        r->lo = t << b;
        r->hi = (t >> (32-b)) | (a.hi << b);
}

void
_andv(Vlong *r, Vlong a, Vlong b)
{
        r->hi = a.hi & b.hi;
        r->lo = a.lo & b.lo;
}

void
_orv(Vlong *r, Vlong a, Vlong b)
{
        r->hi = a.hi | b.hi;
        r->lo = a.lo | b.lo;
}

void
_xorv(Vlong *r, Vlong a, Vlong b)
{
        r->hi = a.hi ^ b.hi;
        r->lo = a.lo ^ b.lo;
}

void
_vpp(Vlong *l, Vlong *r)
{

        l->hi = r->hi;
        l->lo = r->lo;
        r->lo++;
        if(r->lo == 0)
                r->hi++;
}

void
_vmm(Vlong *l, Vlong *r)
{

        l->hi = r->hi;
        l->lo = r->lo;
        if(r->lo == 0)
                r->hi--;
        r->lo--;
}

void
_ppv(Vlong *l, Vlong *r)
{

        r->lo++;
        if(r->lo == 0)
                r->hi++;
        l->hi = r->hi;
        l->lo = r->lo;
}

void
_mmv(Vlong *l, Vlong *r)
{

        if(r->lo == 0)
                r->hi--;
        r->lo--;
        l->hi = r->hi;
        l->lo = r->lo;
}

void
_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
{
        Vlong t, u;

        u.lo = 0;
        u.hi = 0;
        switch(type) {
        default:
                runtime·abort();
                break;

        case 1: /* schar */
                t.lo = *(schar*)lv;
                t.hi = t.lo >> 31;
                fn(&u, t, rv);
                *(schar*)lv = u.lo;
                break;

        case 2: /* uchar */
                t.lo = *(uchar*)lv;
                t.hi = 0;
                fn(&u, t, rv);
                *(uchar*)lv = u.lo;
                break;

        case 3: /* short */
                t.lo = *(short*)lv;
                t.hi = t.lo >> 31;
                fn(&u, t, rv);
                *(short*)lv = u.lo;
                break;

        case 4: /* ushort */
                t.lo = *(ushort*)lv;
                t.hi = 0;
                fn(&u, t, rv);
                *(ushort*)lv = u.lo;
                break;

        case 9: /* int */
                t.lo = *(int*)lv;
                t.hi = t.lo >> 31;
                fn(&u, t, rv);
                *(int*)lv = u.lo;
                break;

        case 10:        /* uint */
                t.lo = *(uint*)lv;
                t.hi = 0;
                fn(&u, t, rv);
                *(uint*)lv = u.lo;
                break;

        case 5: /* long */
                t.lo = *(long*)lv;
                t.hi = t.lo >> 31;
                fn(&u, t, rv);
                *(long*)lv = u.lo;
                break;

        case 6: /* ulong */
                t.lo = *(ulong*)lv;
                t.hi = 0;
                fn(&u, t, rv);
                *(ulong*)lv = u.lo;
                break;

        case 7: /* vlong */
        case 8: /* uvlong */
                fn(&u, *(Vlong*)lv, rv);
                *(Vlong*)lv = u;
                break;
        }
        *ret = u;
}

void
_p2v(Vlong *ret, void *p)
{
        long t;

        t = (ulong)p;
        ret->lo = t;
        ret->hi = 0;
}

void
_sl2v(Vlong *ret, long sl)
{
        long t;

        t = sl;
        ret->lo = t;
        ret->hi = t >> 31;
}

void
_ul2v(Vlong *ret, ulong ul)
{
        long t;

        t = ul;
        ret->lo = t;
        ret->hi = 0;
}

void
_si2v(Vlong *ret, int si)
{
        long t;

        t = si;
        ret->lo = t;
        ret->hi = t >> 31;
}

void
_ui2v(Vlong *ret, uint ui)
{
        long t;

        t = ui;
        ret->lo = t;
        ret->hi = 0;
}

void
_sh2v(Vlong *ret, long sh)
{
        long t;

        t = (sh << 16) >> 16;
        ret->lo = t;
        ret->hi = t >> 31;
}

void
_uh2v(Vlong *ret, ulong ul)
{
        long t;

        t = ul & 0xffff;
        ret->lo = t;
        ret->hi = 0;
}

void
_sc2v(Vlong *ret, long uc)
{
        long t;

        t = (uc << 24) >> 24;
        ret->lo = t;
        ret->hi = t >> 31;
}

void
_uc2v(Vlong *ret, ulong ul)
{
        long t;

        t = ul & 0xff;
        ret->lo = t;
        ret->hi = 0;
}

long
_v2sc(Vlong rv)
{
        long t;

        t = rv.lo & 0xff;
        return (t << 24) >> 24;
}

long
_v2uc(Vlong rv)
{

        return rv.lo & 0xff;
}

long
_v2sh(Vlong rv)
{
        long t;

        t = rv.lo & 0xffff;
        return (t << 16) >> 16;
}

long
_v2uh(Vlong rv)
{

        return rv.lo & 0xffff;
}

long
_v2sl(Vlong rv)
{

        return rv.lo;
}

long
_v2ul(Vlong rv)
{

        return rv.lo;
}

long
_v2si(Vlong rv)
{

        return rv.lo;
}

long
_v2ui(Vlong rv)
{

        return rv.lo;
}

int
_testv(Vlong rv)
{
        return rv.lo || rv.hi;
}

int
_eqv(Vlong lv, Vlong rv)
{
        return lv.lo == rv.lo && lv.hi == rv.hi;
}

int
_nev(Vlong lv, Vlong rv)
{
        return lv.lo != rv.lo || lv.hi != rv.hi;
}

int
_ltv(Vlong lv, Vlong rv)
{
        return (long)lv.hi < (long)rv.hi ||
                (lv.hi == rv.hi && lv.lo < rv.lo);
}

int
_lev(Vlong lv, Vlong rv)
{
        return (long)lv.hi < (long)rv.hi ||
                (lv.hi == rv.hi && lv.lo <= rv.lo);
}

int
_gtv(Vlong lv, Vlong rv)
{
        return (long)lv.hi > (long)rv.hi ||
                (lv.hi == rv.hi && lv.lo > rv.lo);
}

int
_gev(Vlong lv, Vlong rv)
{
        return (long)lv.hi > (long)rv.hi ||
                (lv.hi == rv.hi && lv.lo >= rv.lo);
}

int
_lov(Vlong lv, Vlong rv)
{
        return lv.hi < rv.hi ||
                (lv.hi == rv.hi && lv.lo < rv.lo);
}

int
_lsv(Vlong lv, Vlong rv)
{
        return lv.hi < rv.hi ||
                (lv.hi == rv.hi && lv.lo <= rv.lo);
}

int
_hiv(Vlong lv, Vlong rv)
{
        return lv.hi > rv.hi ||
                (lv.hi == rv.hi && lv.lo > rv.lo);
}

int
_hsv(Vlong lv, Vlong rv)
{
        return lv.hi > rv.hi ||
                (lv.hi == rv.hi && lv.lo >= rv.lo);
}

/* [<][>][^][v][top][bottom][index][help] */