Emulate ldq and stq (load/store long double) instructions. GCC has started

using these to load long doubles, but they aren't implemented in hardware
on (at least) UltraSPARC I and II machines.
Emulate popc in the user trap handler as well.
Re-arrange slightly to make support functions more accessible.

Reviewed by:	tmm
This commit is contained in:
jake 2002-04-27 21:56:28 +00:00
parent 60ee831d3e
commit a1a94cce5c
12 changed files with 252 additions and 106 deletions

View File

@ -2,5 +2,7 @@
.PATH: ${.CURDIR}/../libc/sparc64/fpu/
CFLAGS+= -I${.CURDIR}/sparc64/sys
SRCS+= fpu.c fpu_add.c fpu_compare.c fpu_div.c fpu_explode.c fpu_implode.c \
fpu_mul.c fpu_reg.S fpu_sqrt.c fpu_subr.c

View File

@ -87,7 +87,7 @@ __FBSDID("$FreeBSD$");
#include <machine/pcb.h>
#include <machine/tstate.h>
#include "../sys/__sparc_utrap_private.h"
#include "__sparc_utrap_private.h"
#include "fpu_emu.h"
#include "fpu_extern.h"
@ -119,89 +119,52 @@ int __fpe_debug = 0;
#endif /* FPU_DEBUG */
static int __fpu_execute(struct utrapframe *, struct fpemu *, u_int32_t, u_long);
static void utrap_write(char *);
static void utrap_kill_self(int);
/*
* System call wrappers usable in an utrap environment.
*/
static void
utrap_write(char *str)
{
int berrno;
berrno = errno;
__sys_write(STDERR_FILENO, str, strlen(str));
errno = berrno;
}
static void
utrap_kill_self(sig)
{
int berrno;
berrno = errno;
__sys_kill(__sys_getpid(), sig);
errno = berrno;
}
void
__fpu_panic(char *msg)
{
utrap_write(msg);
utrap_write("\n");
utrap_kill_self(SIGKILL);
}
/*
* Need to use an fpstate on the stack; we could switch, so we cannot safely
* modify the pcb one, it might get overwritten.
*/
void
int
__fpu_exception(struct utrapframe *uf)
{
struct fpemu fe;
u_long fsr, tstate;
u_int insn;
int rv;
int sig;
fsr = uf->uf_fsr;
switch (FSR_GET_FTT(fsr)) {
case FSR_FTT_NONE:
utrap_write("lost FPU trap type\n");
return;
__utrap_write("lost FPU trap type\n");
return (0);
case FSR_FTT_IEEE:
goto fatal;
return (SIGFPE);
case FSR_FTT_SEQERR:
utrap_write("FPU sequence error\n");
goto fatal;
__utrap_write("FPU sequence error\n");
return (SIGFPE);
case FSR_FTT_HWERR:
utrap_write("FPU hardware error\n");
goto fatal;
__utrap_write("FPU hardware error\n");
return (SIGFPE);
case FSR_FTT_UNFIN:
case FSR_FTT_UNIMP:
break;
default:
utrap_write("unknown FPU error\n");
goto fatal;
__utrap_write("unknown FPU error\n");
return (SIGFPE);
}
fe.fe_fsr = fsr & ~FSR_FTT_MASK;
insn = *(u_int32_t *)uf->uf_pc;
if (IF_OP(insn) != IOP_MISC || (IF_F3_OP3(insn) != INS2_FPop1 &&
IF_F3_OP3(insn) != INS2_FPop2))
__fpu_panic("bogus FP fault");
__utrap_panic("bogus FP fault");
tstate = uf->uf_state;
rv = __fpu_execute(uf, &fe, insn, tstate);
if (rv != 0)
utrap_kill_self(rv);
sig = __fpu_execute(uf, &fe, insn, tstate);
if (sig != 0)
return (sig);
__asm __volatile("ldx %0, %%fsr" : : "m" (fe.fe_fsr));
return;
fatal:
utrap_kill_self(SIGFPE);
return;
return (0);
}
#ifdef FPU_DEBUG
@ -223,29 +186,6 @@ __fpu_dumpfpn(struct fpn *fp)
}
#endif
static u_long
fetch_reg(struct utrapframe *uf, int reg)
{
u_long offs;
struct frame *frm;
if (reg == IREG_G0)
return (0);
else if (reg < IREG_O0) /* global */
return (uf->uf_global[reg]);
else if (reg < IREG_L0) /* out */
return (uf->uf_out[reg - IREG_O0]);
else { /* local, in */
/*
* The in registers are immediately after the locals in
* the frame.
*/
frm = (struct frame *)(uf->uf_out[6] + SPOFF);
return (frm->fr_local[reg - IREG_L0]);
}
__fpu_panic("fetch_reg: bogus register");
}
static void
__fpu_mov(struct fpemu *fe, int type, int rd, int rs1, int rs2)
{
@ -361,32 +301,32 @@ __fpu_execute(struct utrapframe *uf, struct fpemu *fe, u_int32_t insn, u_long ts
(tstate & TSTATE_XCC_MASK) >> (TSTATE_XCC_SHIFT));
return (0);
case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_Z)):
reg = fetch_reg(uf, IF_F4_RS1(insn));
reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
if (reg == 0)
__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
return (0);
case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LEZ)):
reg = fetch_reg(uf, IF_F4_RS1(insn));
reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
if (reg <= 0)
__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
return (0);
case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_LZ)):
reg = fetch_reg(uf, IF_F4_RS1(insn));
reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
if (reg < 0)
__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
return (0);
case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_NZ)):
reg = fetch_reg(uf, IF_F4_RS1(insn));
reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
if (reg != 0)
__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
return (0);
case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GZ)):
reg = fetch_reg(uf, IF_F4_RS1(insn));
reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
if (reg > 0)
__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
return (0);
case FOP(INS2_FPop2, INSFP2_FMOV_RC(IRCOND_GEZ)):
reg = fetch_reg(uf, IF_F4_RS1(insn));
reg = __emul_fetch_reg(uf, IF_F4_RS1(insn));
if (reg >= 0)
__fpu_mov(fe, type, rd, __fpu_getreg(rs2), rs2);
return (0);

View File

@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
#include "fpu_arith.h"
#include "fpu_emu.h"
#include "fpu_extern.h"
#include "__sparc_utrap_private.h"
struct fpn *
__fpu_add(fe)
@ -198,7 +199,7 @@ __fpu_add(fe)
*/
#ifdef DIAGNOSTIC
if (x->fp_exp != y->fp_exp || r->fp_sticky)
__fpu_panic("fpu_add");
__utrap_panic("fpu_add");
#endif
r->fp_sign = y->fp_sign;
FPU_SUBS(r3, 0, r3);

View File

@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
#include "fpu_arith.h"
#include "fpu_emu.h"
#include "fpu_extern.h"
#include "__sparc_utrap_private.h"
/*
* N.B.: in all of the following, we assume the FP format is
@ -278,7 +279,7 @@ __fpu_explode(fe, fp, type, reg)
break;
default:
__fpu_panic("fpu_explode");
__utrap_panic("fpu_explode");
}
if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) {

View File

@ -40,16 +40,13 @@
#ifndef _SPARC64_FPU_FPU_EXTERN_H_
#define _SPARC64_FPU_FPU_EXTERN_H_
struct proc;
struct fpstate;
struct utrapframe;
union instr;
struct fpemu;
struct fpn;
/* fpu.c */
void __fpu_exception(struct utrapframe *tf);
void __fpu_panic(char *msg);
int __fpu_exception(struct utrapframe *tf);
/* fpu_add.c */
struct fpn *__fpu_add(struct fpemu *);

View File

@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
#include "fpu_arith.h"
#include "fpu_emu.h"
#include "fpu_extern.h"
#include "__sparc_utrap_private.h"
static int round(struct fpemu *, struct fpn *);
static int toinf(struct fpemu *, int);
@ -354,7 +355,7 @@ __fpu_ftos(fe, fp)
(void) __fpu_shr(fp, FP_NMANT - FP_NG - 1 - SNG_FRACBITS);
#ifdef DIAGNOSTIC
if ((fp->fp_mant[3] & SNG_EXP(1 << FP_NG)) == 0)
__fpu_panic("fpu_ftos");
__utrap_panic("fpu_ftos");
#endif
if (round(fe, fp) && fp->fp_mant[3] == SNG_EXP(2))
exp++;
@ -529,7 +530,7 @@ __fpu_implode(fe, fp, type, space)
break;
default:
__fpu_panic("fpu_implode");
__utrap_panic("fpu_implode");
}
DPRINTF(FPE_REG, ("fpu_implode: %x %x %x %x\n",
space[0], space[1], space[2], space[3]));

View File

@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$");
#include "fpu_arith.h"
#include "fpu_emu.h"
#include "fpu_extern.h"
#include "__sparc_utrap_private.h"
/*
* Shift the given number right rsh bits. Any bits that `fall off' will get
@ -75,7 +76,7 @@ __fpu_shr(struct fpn *fp, int rsh)
#ifdef DIAGNOSTIC
if (rsh <= 0 || (fp->fp_class != FPC_NUM && !ISNAN(fp)))
__fpu_panic("fpu_rightshift 1");
__utrap_panic("fpu_rightshift 1");
#endif
m0 = fp->fp_mant[0];
@ -87,7 +88,7 @@ __fpu_shr(struct fpn *fp, int rsh)
if (rsh >= FP_NMANT) {
#ifdef DIAGNOSTIC
if ((m0 | m1 | m2 | m3) == 0)
__fpu_panic("fpu_rightshift 2");
__utrap_panic("fpu_rightshift 2");
#endif
fp->fp_mant[0] = 0;
fp->fp_mant[1] = 0;

View File

@ -1,7 +1,9 @@
# $FreeBSD$
SRCS+= __sparc_utrap.c __sparc_utrap_fp_disabled.S __sparc_utrap_gen.S \
__sparc_utrap_install.c __sparc_utrap_setup.c
SRCS+= __sparc_utrap.c __sparc_utrap_emul.c __sparc_utrap_fp_disabled.S \
__sparc_utrap_gen.S __sparc_utrap_install.c __sparc_utrap_setup.c
CFLAGS+= -I${.CURDIR}/sparc64/fpu
MDASM+= brk.S cerror.S exect.S pipe.S ptrace.S sbrk.S setlogin.S

View File

@ -32,8 +32,11 @@ __FBSDID("$FreeBSD$");
#include <machine/utrap.h>
#include <machine/sysarch.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "__sparc_utrap_private.h"
@ -77,23 +80,55 @@ static const char *utrap_msg[] = {
void
__sparc_utrap(struct utrapframe *uf)
{
int sig;
switch (uf->uf_type) {
case UT_FP_EXCEPTION_IEEE_754:
case UT_FP_EXCEPTION_OTHER:
__fpu_exception(uf);
UF_DONE(uf);
return;
sig = __fpu_exception(uf);
break;
case UT_ILLEGAL_INSTRUCTION:
sig = __emul_insn(uf);
break;
case UT_MEM_ADDRESS_NOT_ALIGNED:
break;
case UT_TRAP_INSTRUCTION_16:
UF_DONE(uf);
return;
default:
break;
}
printf("__sparc_utrap: type=%s pc=%#lx npc=%#lx\n",
utrap_msg[uf->uf_type], uf->uf_pc, uf->uf_npc);
abort();
if (sig) {
__utrap_write("__sparc_utrap: fatal ");
__utrap_write(utrap_msg[uf->uf_type]);
__utrap_write("\n");
__utrap_kill_self(sig);
}
UF_DONE(uf);
}
void
__utrap_write(const char *str)
{
int berrno;
berrno = errno;
__sys_write(STDERR_FILENO, str, strlen(str));
errno = berrno;
}
void
__utrap_kill_self(sig)
{
int berrno;
berrno = errno;
__sys_kill(__sys_getpid(), sig);
errno = berrno;
}
void
__utrap_panic(const char *msg)
{
__utrap_write(msg);
__utrap_write("\n");
__utrap_kill_self(SIGKILL);
}

View File

@ -0,0 +1,157 @@
/*-
* Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
* 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.
*
* 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 OR CONTRIBUTORS 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <machine/cpufunc.h>
#include <machine/frame.h>
#include <machine/instr.h>
#include <signal.h>
#include "__sparc_utrap_private.h"
#include "fpu_reg.h"
int
__emul_insn(struct utrapframe *uf)
{
u_long reg, res;
u_long *addr;
u_int insn;
int sig;
int rd;
int i;
sig = 0;
insn = *(u_int *)uf->uf_pc;
flushw();
switch (IF_OP(insn)) {
case IOP_MISC:
switch (IF_F3_OP3(insn)) {
case INS2_POPC:
if (IF_F3_RS1(insn) != 0) {
sig = SIGILL;
break;
}
reg = __emul_f3_op2(uf, insn);
for (i = 0; i < 64; i++)
res += (reg >> i) & 1;
__emul_store_reg(uf, IF_F3_RD(insn), res);
break;
default:
sig = SIGILL;
break;
}
break;
case IOP_LDST:
switch (IF_F3_OP3(insn)) {
case INS3_LDQF:
rd = IF_F3_RD(insn);
rd = (rd & ~3) | ((rd & 1) << 5);
addr = (u_long *)__emul_f3_memop_addr(uf, insn);
__fpu_setreg64(rd, addr[0]);
__fpu_setreg64(rd + 2, addr[1]);
break;
case INS3_STQF:
rd = IF_F3_RD(insn);
rd = (rd & ~3) | ((rd & 1) << 5);
addr = (u_long *)__emul_f3_memop_addr(uf, insn);
addr[0] = __fpu_getreg64(rd);
addr[1] = __fpu_getreg64(rd + 2);
break;
default:
sig = SIGILL;
break;
}
break;
default:
sig = SIGILL;
break;
}
return (sig);
}
u_long
__emul_fetch_reg(struct utrapframe *uf, int reg)
{
struct frame *frm;
if (reg == IREG_G0)
return (0);
else if (reg < IREG_O0) /* global */
return (uf->uf_global[reg]);
else if (reg < IREG_L0) /* out */
return (uf->uf_out[reg - IREG_O0]);
else { /* local, in */
/*
* The in registers are immediately after the locals in
* the frame.
*/
frm = (struct frame *)(uf->uf_out[6] + SPOFF);
return (frm->fr_local[reg - IREG_L0]);
}
}
void
__emul_store_reg(struct utrapframe *uf, int reg, u_long val)
{
struct frame *frm;
if (reg == IREG_G0)
return;
if (reg < IREG_O0) /* global */
uf->uf_global[reg] = val;
else if (reg < IREG_L0) /* out */
uf->uf_out[reg - IREG_O0] = val;
else {
/*
* The in registers are immediately after the locals in
* the frame.
*/
frm = (struct frame *)(uf->uf_out[6] + SPOFF);
frm->fr_local[reg - IREG_L0] = val;
}
}
u_long
__emul_f3_op2(struct utrapframe *uf, u_int insn)
{
if (IF_F3_I(insn) != 0)
return (IF_SIMM(insn, 13));
else
return (__emul_fetch_reg(uf, IF_F3_RS2(insn)));
}
u_long
__emul_f3_memop_addr(struct utrapframe *uf, u_int insn)
{
u_long addr;
addr = __emul_f3_op2(uf, insn) + __emul_fetch_reg(uf, IF_F3_RS1(insn));
return (addr);
}

View File

@ -50,6 +50,16 @@ struct utrapframe {
extern char __sparc_utrap_fp_disabled[];
extern char __sparc_utrap_gen[];
int __emul_insn(struct utrapframe *uf);
u_long __emul_fetch_reg(struct utrapframe *uf, int reg);
void __emul_store_reg(struct utrapframe *uf, int reg, u_long val);
u_long __emul_f3_op2(struct utrapframe *uf, u_int insn);
u_long __emul_f3_memop_addr(struct utrapframe *uf, u_int insn);
void __sparc_utrap(struct utrapframe *);
void __utrap_write(const char *);
void __utrap_kill_self(int);
void __utrap_panic(const char *);
#endif

View File

@ -40,11 +40,10 @@ static const struct sparc_utrap_args ua[] = {
{ UT_FP_DISABLED, __sparc_utrap_fp_disabled, NULL, NULL, NULL },
{ UT_FP_EXCEPTION_IEEE_754, __sparc_utrap_gen, NULL, NULL, NULL },
{ UT_FP_EXCEPTION_OTHER, __sparc_utrap_gen, NULL, NULL, NULL },
#if 0
{ UT_ILLEGAL_INSTRUCTION, __sparc_utrap_gen, NULL, NULL, NULL },
#if 0
{ UT_MEM_ADDRESS_NOT_ALIGNED, __sparc_utrap_gen, NULL, NULL, NULL },
#endif
{ UT_TRAP_INSTRUCTION_16, __sparc_utrap_gen, NULL, NULL, NULL },
};
static const struct sparc_utrap_install_args uia[] = {