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