/* $Id: softfloat.c,v 1.4 2009/07/29 12:32:34 ragge Exp $ */ /* * Copyright (c) 2008 Anders Magnusson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef SOFTFLOAT #include "pass1.h" /* * Floating point emulation to be used when cross-compiling. * Currently only supports F- and D-float, used in DEC machines. * Should be trivial to add other emulations. * * XXX - assumes that: * - long long is (at least) 64 bits * - int is at least 32 bits. * - short is 16 bits. */ #ifdef FDFLOAT /* * Useful macros to manipulate the float. */ #define DSIGN(w) (((w).fd1 >> 15) & 1) #define DSIGNSET(w,s) ((w).fd1 = (s << 15) | ((w).fd1 & 077777)) #define DEXP(w) (((w).fd1 >> 7) & 0377) #define DEXPSET(w,e) ((w).fd1 = (((e) & 0377) << 7) | ((w).fd1 & 0100177)) #define DMANTH(w) ((w).fd1 & 0177) #define DMANTHSET(w,m) ((w).fd1 = ((m) & 0177) | ((w).fd1 & 0177600)) typedef unsigned int lword; typedef unsigned long long dword; #define MAXMANT 0x100000000000000LL /* * Returns a zero dfloat. */ static SF nulldf(void) { SF rv; rv.fd1 = rv.fd2 = rv.fd3 = rv.fd4 = 0; return rv; } /* * Convert a (u)longlong to dfloat. * XXX - fails on too large (> 55 bits) numbers. */ SF soft_cast(CONSZ ll, TWORD t) { int i; SF rv; rv = nulldf(); if (ll == 0) return rv; /* fp is zero */ if (ll < 0) DSIGNSET(rv,1), ll = -ll; for (i = 0; ll > 0; i++, ll <<= 1) ; DEXPSET(rv, 192-i); DMANTHSET(rv, ll >> 56); rv.fd2 = ll >> 40; rv.fd3 = ll >> 24; rv.fd4 = ll >> 8; return rv; } /* * multiply two dfloat. Use chop, not round. */ SF soft_mul(SF p1, SF p2) { SF rv; lword a1[2], a2[2], res[4]; dword sum; res[0] = res[1] = res[2] = res[3] = 0; /* move mantissa into lwords */ a1[0] = p1.fd4 | (p1.fd3 << 16); a1[1] = p1.fd2 | DMANTH(p1) << 16 | 0x800000; a2[0] = p2.fd4 | (p2.fd3 << 16); a2[1] = p2.fd2 | DMANTH(p2) << 16 | 0x800000; #define MULONE(x,y,r) sum += (dword)a1[x] * (dword)a2[y]; sum += res[r]; \ res[r] = sum; sum >>= 32; sum = 0; MULONE(0, 0, 0); MULONE(1, 0, 1); res[2] = sum; sum = 0; MULONE(0, 1, 1); MULONE(1, 1, 2); res[3] = sum; rv.fd1 = 0; DSIGNSET(rv, DSIGN(p1) ^ DSIGN(p2)); DEXPSET(rv, DEXP(p1) + DEXP(p2) - 128); if (res[3] & 0x8000) { res[3] = (res[3] << 8) | (res[2] >> 24); res[2] = (res[2] << 8) | (res[1] >> 24); } else { DEXPSET(rv, DEXP(rv) - 1); res[3] = (res[3] << 9) | (res[2] >> 23); res[2] = (res[2] << 9) | (res[1] >> 23); } DMANTHSET(rv, res[3] >> 16); rv.fd2 = res[3]; rv.fd3 = res[2] >> 16; rv.fd4 = res[2]; return rv; } SF soft_div(SF t, SF n) { SF rv; dword T, N, K; int c; #define SHL(x,b) ((dword)(x) << b) T = SHL(1,55) | SHL(DMANTH(t), 48) | SHL(t.fd2, 32) | SHL(t.fd3, 16) | t.fd4; N = SHL(1,55) | SHL(DMANTH(n), 48) | SHL(n.fd2, 32) | SHL(n.fd3, 16) | n.fd4; c = T > N; for (K = 0; (K & 0x80000000000000ULL) == 0; ) { if (T >= N) { T -= N; K |= 1; } T <<= 1; K <<= 1; } rv.fd1 = 0; DSIGNSET(rv, DSIGN(t) ^ DSIGN(n)); DEXPSET(rv, DEXP(t) - DEXP(n) + 128 + c); DMANTHSET(rv, K >> 48); rv.fd2 = K >> 32; rv.fd3 = K >> 16; rv.fd4 = K; return rv; } /* * Negate a float number. Easy. */ SF soft_neg(SF sf) { int sign = DSIGN(sf) == 0; DSIGNSET(sf, sign); return sf; } /* * Return true if fp number is zero. */ int soft_isz(SF sf) { return (DEXP(sf) == 0); } int soft_cmp_eq(SF x1, SF x2) { cerror("soft_cmp_eq"); return 0; } int soft_cmp_ne(SF x1, SF x2) { cerror("soft_cmp_ne"); return 0; } int soft_cmp_le(SF x1, SF x2) { cerror("soft_cmp_le"); return 0; } int soft_cmp_lt(SF x1, SF x2) { cerror("soft_cmp_lt"); return 0; } int soft_cmp_ge(SF x1, SF x2) { cerror("soft_cmp_ge"); return 0; } int soft_cmp_gt(SF x1, SF x2) { cerror("soft_cmp_gt"); return 0; } /* * Convert a fp number to a CONSZ. */ CONSZ soft_val(SF sf) { CONSZ mant; int exp = DEXP(sf) - 128; mant = SHL(1,55) | SHL(DMANTH(sf), 48) | SHL(sf.fd2, 32) | SHL(sf.fd3, 16) | sf.fd4; while (exp < 0) mant >>= 1, exp++; while (exp > 0) mant <<= 1, exp--; return mant; } SF soft_plus(SF x1, SF x2) { cerror("soft_plus"); return x1; } SF soft_minus(SF x1, SF x2) { cerror("soft_minus"); return x1; } /* * Convert a hex constant to floating point number. */ NODE * fhexcon(char *s) { cerror("fhexcon"); return NULL; } /* * Convert a floating-point constant to D-float and store it in a NODE. */ NODE * floatcon(char *s) { NODE *p; dword mant; SF fl, flexp, exp5; int exp, negexp, bexp; exp = 0; mant = 0; #define ADDTO(sum, val) sum = sum * 10 + val - '0' for (; *s >= '0' && *s <= '9'; s++) { if (mant= '0' && *s <= '9'; s++) { if (mant= '0' && *s <= '9'; s++) ADDTO(eexp, *s); if (sign) eexp = -eexp; exp = exp + eexp; } negexp = 1; if (exp<0) { negexp = -1; exp = -exp; } flexp = soft_cast(1, INT); exp5 = soft_cast(5, INT); bexp = exp; fl = soft_cast(mant, INT); for (; exp; exp >>= 1) { if (exp&01) flexp = soft_mul(flexp, exp5); exp5 = soft_mul(exp5, exp5); } if (negexp<0) fl = soft_div(fl, flexp); else fl = soft_mul(fl, flexp); DEXPSET(fl, DEXP(fl) + negexp*bexp); p = block(FCON, NIL, NIL, DOUBLE, 0, MKSUE(DOUBLE)); /* XXX type */ p->n_dcon = fl; return p; } #else #error missing softfloat definition #endif #endif