d8307d
/* Emulate power6 mf[tf]gpr and fri[zpmn] instructions.
d8307d
   Copyright (C) 2006 Red Hat, Inc.
d8307d
   Contributed by Jakub Jelinek <jakub@redhat.com>, 2006.
d8307d
d8307d
   This library is free software; you can redistribute it and/or
d8307d
   modify it under the terms of the GNU Lesser General Public
d8307d
   License as published by the Free Software Foundation; either
d8307d
   version 2.1 of the License, or (at your option) any later version.
d8307d
d8307d
   It is distributed in the hope that it will be useful,
d8307d
   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
   Lesser General Public License for more details.
d8307d
d8307d
   You should have received a copy of the GNU Lesser General Public
d8307d
   License along with the GNU C Library; if not, write to the Free
d8307d
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
d8307d
   02111-1307 USA.  */
d8307d
d8307d
#include <signal.h>
d8307d
#include <stdio.h>
d8307d
d8307d
extern double frip (double), friz (double), frin (double), frim (double);
d8307d
asm (".globl frip, friz, frin, frim\n.hidden frip, friz, frin, frim\n\t"
d8307d
#ifdef __powerpc64__
d8307d
	".section \".toc\",\"aw\"\n"
d8307d
"8:"	".tc FD_43300000_0[TC],0x4330000000000000\n"
d8307d
"9:"	".tc FD_3fe00000_0[TC],0x3fe0000000000000\n\t"
d8307d
	".previous\n\t"
d8307d
#else
d8307d
	".rodata\n\t"
d8307d
	".align 2\n"
d8307d
"8:"	".long 0x59800000\n"
d8307d
"9:"	".long 0x3f000000\n\t"
d8307d
	".previous\n\t"
d8307d
#endif
d8307d
	"# frip == ceil\n"
d8307d
"frip:"	"mffs    11\n\t"
d8307d
#ifdef __powerpc64__
d8307d
	"lfd     13,8b@toc(2)\n\t"
d8307d
#else
d8307d
	"mflr    11\n\t"
d8307d
	"bcl     20,31,1f\n"
d8307d
"1:"	"mflr    9\n\t"
d8307d
	"addis   9,9,8b-1b@ha\n\t"
d8307d
	"lfs     13,8b-1b@l(9)\n\t"
d8307d
	"mtlr    11\n\t"
d8307d
#endif
d8307d
	"fabs    0,1\n\t"
d8307d
	"fsub    12,13,13\n\t"
d8307d
	"fcmpu   7,0,13\n\t"
d8307d
	"fcmpu   6,1,12\n\t"
d8307d
	"bnllr-  7\n\t"
d8307d
	"mtfsfi  7,2\n\t"
d8307d
	"ble-    6,2f\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fsub    1,1,13\n\t"
d8307d
	"fabs    1,1\n\t"
d8307d
	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n"
d8307d
"2:"	"bge-    6,3f\n\t"
d8307d
	"fsub    1,1,13\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fnabs   1,1\n"
d8307d
"3:"	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n\t"
d8307d
	"# friz == trunc\n"
d8307d
"friz:"	"mffs    11\n\t"
d8307d
#ifdef __powerpc64__
d8307d
	"lfd     13,8b@toc(2)\n\t"
d8307d
#else
d8307d
	"mflr    11\n\t"
d8307d
	"bcl     20,31,1f\n"
d8307d
"1:"	"mflr    9\n\t"
d8307d
	"addis   9,9,8b-1b@ha\n\t"
d8307d
	"lfs     13,8b-1b@l(9)\n\t"
d8307d
	"mtlr    11\n\t"
d8307d
#endif
d8307d
	"fabs    0,1\n\t"
d8307d
	"fsub    12,13,13\n\t"
d8307d
	"fcmpu   7,0,13\n\t"
d8307d
	"fcmpu   6,1,12\n\t"
d8307d
	"bnllr-  7\n\t"
d8307d
	"mtfsfi  7,1\n\t"
d8307d
	"ble-    6,2f\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fsub    1,1,13\n\t"
d8307d
	"fabs    1,1\n\t"
d8307d
	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n"
d8307d
"2:"	"bge-    6,3f\n\t"
d8307d
	"fsub    1,1,13\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fnabs   1,1\n"
d8307d
"3:"	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n\t"
d8307d
	"# frin == round\n"
d8307d
"frin:"	"mffs    11\n\t"
d8307d
#ifdef __powerpc64__
d8307d
	"lfd     13,8b@toc(2)\n\t"
d8307d
#else
d8307d
	"mflr    11\n\t"
d8307d
	"bcl     20,31,1f\n"
d8307d
"1:"	"mflr    9\n\t"
d8307d
	"addis   9,9,8b-1b@ha\n\t"
d8307d
	"addi    9,9,8b-1b@l\n\t"
d8307d
	"mtlr    11\n\t"
d8307d
	"lfs     13,0(9)\n\t"
d8307d
#endif
d8307d
	"fabs    0,1\n\t"
d8307d
	"fsub    12,13,13\n\t"
d8307d
	"fcmpu   7,0,13\n\t"
d8307d
	"fcmpu   6,1,12\n\t"
d8307d
	"bnllr-  7\n\t"
d8307d
	"mtfsfi  7,1\n\t"
d8307d
#ifdef __powerpc64__
d8307d
	"lfd     10,9b@toc(2)\n\t"
d8307d
#else
d8307d
	"lfs     10,9b-8b(9)\n\t"
d8307d
#endif
d8307d
	"ble-    6,2f\n\t"
d8307d
	"fadd    1,1,10\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fsub    1,1,13\n\t"
d8307d
	"fabs    1,1\n\t"
d8307d
	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n"
d8307d
"2:"	"fsub    9,1,10\n\t"
d8307d
	"bge-    6,3f\n\t"
d8307d
	"fsub    1,9,13\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fnabs   1,1\n"
d8307d
"3:"	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n\t"
d8307d
	"# frim == floor\n"
d8307d
"frim:"	"mffs    11\n\t"
d8307d
#ifdef __powerpc64__
d8307d
	"lfd     13,8b@toc(2)\n\t"
d8307d
#else
d8307d
	"mflr    11\n\t"
d8307d
	"bcl     20,31,1f\n"
d8307d
"1:"	"mflr    9\n\t"
d8307d
	"addis   9,9,8b-1b@ha\n\t"
d8307d
	"lfs     13,8b-1b@l(9)\n\t"
d8307d
	"mtlr    11\n\t"
d8307d
#endif
d8307d
	"fabs    0,1\n\t"
d8307d
	"fsub    12,13,13\n\t"
d8307d
	"fcmpu   7,0,13\n\t"
d8307d
	"fcmpu   6,1,12\n\t"
d8307d
	"bnllr-  7\n\t"
d8307d
	"mtfsfi  7,3\n\t"
d8307d
	"ble-    6,2f\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fsub    1,1,13\n\t"
d8307d
	"fabs    1,1\n\t"
d8307d
	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n"
d8307d
"2:"	"bge-    6,3f\n\t"
d8307d
	"fsub    1,1,13\n\t"
d8307d
	"fadd    1,1,13\n\t"
d8307d
	"fnabs   1,1\n"
d8307d
"3:"	"mtfsf   0x01,11\n\t"
d8307d
	"blr\n");
d8307d
d8307d
#ifdef __powerpc64__
d8307d
#define m1 0x5555555555555555L
d8307d
#define m2 0x3333333333333333L
d8307d
#define m3 0x0f0f0f0f0f0f0f0fL
d8307d
#else
d8307d
#define m1 0x55555555
d8307d
#define m2 0x33333333
d8307d
#define m3 0x0f0f0f0f
d8307d
#endif
d8307d
d8307d
static inline unsigned long
d8307d
popcntb (unsigned long n)
d8307d
{
d8307d
  n -= (n >> 1) & m1;
d8307d
  n = (n & m2) + ((n >> 2) & m2);
d8307d
  n = (n + (n >> 4)) & m3;
d8307d
  return n;
d8307d
}
d8307d
d8307d
static void
d8307d
catch_sigill (int signal, struct sigcontext *ctx)
d8307d
{
d8307d
  unsigned int insn = *(unsigned int *) (ctx->regs->nip);
d8307d
#ifdef __powerpc64__
d8307d
  if ((insn & 0xfc1f07ff) == 0x7c0005be) /* mftgpr */
d8307d
    {
d8307d
      unsigned long *regs = (unsigned long *) ctx->regs;
d8307d
      unsigned fpr = (insn >> 11) & 0x1f;
d8307d
      unsigned gpr = (insn >> 21) & 0x1f;
d8307d
      regs[gpr] = regs[fpr + 0x30];
d8307d
      ctx->regs->nip += 4;
d8307d
      return;
d8307d
    }
d8307d
  if ((insn & 0xfc1f07ff) == 0x7c0004be) /*mffgpr */
d8307d
    {
d8307d
      unsigned long *regs = (unsigned long *) ctx->regs;
d8307d
      unsigned fpr = (insn >> 21) & 0x1f;
d8307d
      unsigned gpr = (insn >> 11) & 0x1f;
d8307d
      regs[fpr + 0x30] = regs[gpr];
d8307d
      ctx->regs->nip += 4;
d8307d
      return;
d8307d
    }
d8307d
#endif
d8307d
  if ((insn & 0xfc1f073f) == 0xfc000310) /* fri[pznm] */
d8307d
    {
d8307d
#ifdef __powerpc64__
d8307d
      double *regs = (double *) (((char *) ctx->regs) + 0x30 * 8);
d8307d
      unsigned int *fpscr = (unsigned int *) (((char *) ctx->regs) + 0x50 * 8 + 4);
d8307d
#else
d8307d
      double *regs = (double *) (((char *) ctx->regs) + 0x30 * 4);
d8307d
      unsigned int *fpscr = (unsigned int *) (((char *) ctx->regs) + 0x30 * 4 + 0x20 * 8 + 4);
d8307d
#endif
d8307d
      unsigned dest = (insn >> 21) & 0x1f;
d8307d
      unsigned src = (insn >> 11) & 0x1f;
d8307d
      switch (insn & 0xc0)
d8307d
	{
d8307d
	case 0:
d8307d
	  regs[dest] = frin (regs[src]);
d8307d
	  break;
d8307d
	case 0x40:
d8307d
	  regs[dest] = friz (regs[src]);
d8307d
	  break;
d8307d
	case 0x80:
d8307d
	  regs[dest] = frip (regs[src]);
d8307d
	  break;
d8307d
	case 0xc0:
d8307d
	  regs[dest] = frim (regs[src]);
d8307d
	  break;
d8307d
	}
d8307d
      /* Update raised exceptions.  */
d8307d
      union { unsigned int i[2]; double d; } u;
d8307d
      asm volatile ("mffs %0" : "=f" (u.d));
d8307d
      u.i[1] &= 0xfffe0000; /* Is this correct?  */
d8307d
      *fpscr |= u.i[1];
d8307d
      ctx->regs->nip += 4;
d8307d
      return;
d8307d
    }
d8307d
  if ((insn & 0xfc00ffff) == 0x7c0000f4) /* popcntb */
d8307d
    {
d8307d
      unsigned long *regs = (unsigned long *) ctx->regs;
d8307d
      unsigned dest = (insn >> 16) & 0x1f;
d8307d
      unsigned src = (insn >> 21) & 0x1f;
d8307d
      unsigned long res = 0;
d8307d
      int i;
d8307d
d8307d
      regs[dest] = popcntb (regs[src]);
d8307d
      ctx->regs->nip += 4;
d8307d
      return;
d8307d
    }
d8307d
d8307d
  struct sigaction sa;
d8307d
  sa.sa_handler = SIG_DFL;
d8307d
  sigemptyset (&sa.sa_mask);
d8307d
  sa.sa_flags = 0;
d8307d
  sigaction (signal, &sa, NULL);
d8307d
  raise (signal);
d8307d
}
d8307d
d8307d
static void
d8307d
__attribute__ ((constructor))
d8307d
install_handler (void)
d8307d
{
d8307d
  struct sigaction sa;
d8307d
  sa.sa_handler = (void *) catch_sigill;
d8307d
  sigemptyset (&sa.sa_mask);
d8307d
  sa.sa_flags = SA_RESTART;
d8307d
  sigaction (SIGILL, &sa, NULL);
d8307d
}