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