/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Main procedures for sparc FPU simulator. */ #include #include #include #include #include #include #include #include #include #include #include #include #define FPUINFO_KSTAT(opcode) { \ extern void __dtrace_probe___fpuinfo_##opcode(uint64_t *); \ uint64_t *stataddr = &fpuinfo.opcode.value.ui64; \ __dtrace_probe___fpuinfo_##opcode(stataddr); \ atomic_add_64(&fpuinfo.opcode.value.ui64, 1); \ } #define FPUINFO_KSTAT_PREC(prec, kstat_s, kstat_d, kstat_q) \ if (prec < 2) { \ FPUINFO_KSTAT(kstat_s); \ } else if (prec == 2) { \ FPUINFO_KSTAT(kstat_d); \ } else { \ FPUINFO_KSTAT(kstat_q); \ } /* * FPU simulator global kstat data */ struct fpuinfo_kstat fpuinfo = { { "fpu_sim_fmovs", KSTAT_DATA_UINT64}, { "fpu_sim_fmovd", KSTAT_DATA_UINT64}, { "fpu_sim_fmovq", KSTAT_DATA_UINT64}, { "fpu_sim_fnegs", KSTAT_DATA_UINT64}, { "fpu_sim_fnegd", KSTAT_DATA_UINT64}, { "fpu_sim_fnegq", KSTAT_DATA_UINT64}, { "fpu_sim_fabss", KSTAT_DATA_UINT64}, { "fpu_sim_fabsd", KSTAT_DATA_UINT64}, { "fpu_sim_fabsq", KSTAT_DATA_UINT64}, { "fpu_sim_fsqrts", KSTAT_DATA_UINT64}, { "fpu_sim_fsqrtd", KSTAT_DATA_UINT64}, { "fpu_sim_fsqrtq", KSTAT_DATA_UINT64}, { "fpu_sim_fadds", KSTAT_DATA_UINT64}, { "fpu_sim_faddd", KSTAT_DATA_UINT64}, { "fpu_sim_faddq", KSTAT_DATA_UINT64}, { "fpu_sim_fsubs", KSTAT_DATA_UINT64}, { "fpu_sim_fsubd", KSTAT_DATA_UINT64}, { "fpu_sim_fsubq", KSTAT_DATA_UINT64}, { "fpu_sim_fmuls", KSTAT_DATA_UINT64}, { "fpu_sim_fmuld", KSTAT_DATA_UINT64}, { "fpu_sim_fmulq", KSTAT_DATA_UINT64}, { "fpu_sim_fdivs", KSTAT_DATA_UINT64}, { "fpu_sim_fdivd", KSTAT_DATA_UINT64}, { "fpu_sim_fdivq", KSTAT_DATA_UINT64}, { "fpu_sim_fcmps", KSTAT_DATA_UINT64}, { "fpu_sim_fcmpd", KSTAT_DATA_UINT64}, { "fpu_sim_fcmpq", KSTAT_DATA_UINT64}, { "fpu_sim_fcmpes", KSTAT_DATA_UINT64}, { "fpu_sim_fcmped", KSTAT_DATA_UINT64}, { "fpu_sim_fcmpeq", KSTAT_DATA_UINT64}, { "fpu_sim_fsmuld", KSTAT_DATA_UINT64}, { "fpu_sim_fdmulx", KSTAT_DATA_UINT64}, { "fpu_sim_fstox", KSTAT_DATA_UINT64}, { "fpu_sim_fdtox", KSTAT_DATA_UINT64}, { "fpu_sim_fqtox", KSTAT_DATA_UINT64}, { "fpu_sim_fxtos", KSTAT_DATA_UINT64}, { "fpu_sim_fxtod", KSTAT_DATA_UINT64}, { "fpu_sim_fxtoq", KSTAT_DATA_UINT64}, { "fpu_sim_fitos", KSTAT_DATA_UINT64}, { "fpu_sim_fitod", KSTAT_DATA_UINT64}, { "fpu_sim_fitoq", KSTAT_DATA_UINT64}, { "fpu_sim_fstoi", KSTAT_DATA_UINT64}, { "fpu_sim_fdtoi", KSTAT_DATA_UINT64}, { "fpu_sim_fqtoi", KSTAT_DATA_UINT64}, { "fpu_sim_fmovcc", KSTAT_DATA_UINT64}, { "fpu_sim_fmovr", KSTAT_DATA_UINT64}, { "fpu_sim_fmadds", KSTAT_DATA_UINT64}, { "fpu_sim_fmaddd", KSTAT_DATA_UINT64}, { "fpu_sim_fmsubs", KSTAT_DATA_UINT64}, { "fpu_sim_fmsubd", KSTAT_DATA_UINT64}, { "fpu_sim_fnmadds", KSTAT_DATA_UINT64}, { "fpu_sim_fnmaddd", KSTAT_DATA_UINT64}, { "fpu_sim_fnmsubs", KSTAT_DATA_UINT64}, { "fpu_sim_fnmsubd", KSTAT_DATA_UINT64}, { "fpu_sim_fumadds", KSTAT_DATA_UINT64}, { "fpu_sim_fumaddd", KSTAT_DATA_UINT64}, { "fpu_sim_fumsubs", KSTAT_DATA_UINT64}, { "fpu_sim_fumsubd", KSTAT_DATA_UINT64}, { "fpu_sim_fnumadds", KSTAT_DATA_UINT64}, { "fpu_sim_fnumaddd", KSTAT_DATA_UINT64}, { "fpu_sim_fnumsubs", KSTAT_DATA_UINT64}, { "fpu_sim_fnumsubd", KSTAT_DATA_UINT64}, { "fpu_sim_invalid", KSTAT_DATA_UINT64}, }; struct visinfo_kstat visinfo = { { "vis_edge8", KSTAT_DATA_UINT64}, { "vis_edge8n", KSTAT_DATA_UINT64}, { "vis_edge8l", KSTAT_DATA_UINT64}, { "vis_edge8ln", KSTAT_DATA_UINT64}, { "vis_edge16", KSTAT_DATA_UINT64}, { "vis_edge16n", KSTAT_DATA_UINT64}, { "vis_edge16l", KSTAT_DATA_UINT64}, { "vis_edge16ln", KSTAT_DATA_UINT64}, { "vis_edge32", KSTAT_DATA_UINT64}, { "vis_edge32n", KSTAT_DATA_UINT64}, { "vis_edge32l", KSTAT_DATA_UINT64}, { "vis_edge32ln", KSTAT_DATA_UINT64}, { "vis_array8", KSTAT_DATA_UINT64}, { "vis_array16", KSTAT_DATA_UINT64}, { "vis_array32", KSTAT_DATA_UINT64}, { "vis_bmask", KSTAT_DATA_UINT64}, { "vis_fcmple16", KSTAT_DATA_UINT64}, { "vis_fcmpne16", KSTAT_DATA_UINT64}, { "vis_fcmpgt16", KSTAT_DATA_UINT64}, { "vis_fcmpeq16", KSTAT_DATA_UINT64}, { "vis_fcmple32", KSTAT_DATA_UINT64}, { "vis_fcmpne32", KSTAT_DATA_UINT64}, { "vis_fcmpgt32", KSTAT_DATA_UINT64}, { "vis_fcmpeq32", KSTAT_DATA_UINT64}, { "vis_fmul8x16", KSTAT_DATA_UINT64}, { "vis_fmul8x16au", KSTAT_DATA_UINT64}, { "vis_fmul8x16al", KSTAT_DATA_UINT64}, { "vis_fmul8sux16", KSTAT_DATA_UINT64}, { "vis_fmul8ulx16", KSTAT_DATA_UINT64}, { "vis_fmuld8sux16", KSTAT_DATA_UINT64}, { "vis_fmuld8ulx16", KSTAT_DATA_UINT64}, { "vis_fpack16", KSTAT_DATA_UINT64}, { "vis_fpack32", KSTAT_DATA_UINT64}, { "vis_fpackfix", KSTAT_DATA_UINT64}, { "vis_fexpand", KSTAT_DATA_UINT64}, { "vis_fpmerge", KSTAT_DATA_UINT64}, { "vis_pdist", KSTAT_DATA_UINT64}, { "vis_pdistn", KSTAT_DATA_UINT64}, { "vis_bshuffle", KSTAT_DATA_UINT64}, }; /* PUBLIC FUNCTIONS */ int fp_notp = 1; /* fp checking not a problem */ /* ARGSUSED */ static enum ftt_type _fp_fpu_simulator( fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ fp_inst_type inst, /* FPU instruction to simulate. */ fsr_type *pfsr, /* Pointer to image of FSR to read and write. */ uint64_t gsr) /* Image of GSR to read */ { unpacked us1, us2, ud; /* Unpacked operands and result. */ uint32_t nrs1, nrs2, nrd; /* Register number fields. */ uint32_t usr, andexcep; fsr_type fsr; enum fcc_type cc; uint32_t nfcc; /* fcc number field. */ uint64_t lusr; uint_t fmau_mul_exceptions; nrs1 = inst.rs1; nrs2 = inst.rs2; nrd = inst.rd; fsr = *pfsr; pfpsd->fp_current_exceptions = 0; /* Init current exceptions. */ fmau_mul_exceptions = 0; pfpsd->fp_fsrtem = fsr.tem; /* Obtain fsr's tem */ /* * Obtain rounding direction and precision */ pfpsd->fp_direction = GSR_IM(gsr) ? GSR_IRND(gsr) : fsr.rnd; pfpsd->fp_precision = fsr.rnp; if (inst.op3 == 0x37) { /* FMA-fused opcode */ fp_fma_inst_type *fma_inst; uint32_t nrs3; unpacked us3; unpacked ust; fma_inst = (fp_fma_inst_type *) &inst; nrs2 = fma_inst->rs2; nrs3 = fma_inst->rs3; switch (fma_inst->var) { case fmadd: _fp_unpack(pfpsd, &us1, nrs1, fma_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fma_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { _fp_unpack(pfpsd, &us3, nrs3, fma_inst->sz); _fp_add(pfpsd, &ust, &us3, &ud); _fp_pack(pfpsd, &ud, nrd, fma_inst->sz); } FPUINFO_KSTAT_PREC(fma_inst->sz, fpu_sim_fmadds, fpu_sim_fmaddd, fpu_sim_invalid); break; case fmsub: _fp_unpack(pfpsd, &us1, nrs1, fma_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fma_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { _fp_unpack(pfpsd, &us3, nrs3, fma_inst->sz); _fp_sub(pfpsd, &ust, &us3, &ud); _fp_pack(pfpsd, &ud, nrd, fma_inst->sz); } FPUINFO_KSTAT_PREC(fma_inst->sz, fpu_sim_fmsubs, fpu_sim_fmsubd, fpu_sim_invalid); break; case fnmadd: _fp_unpack(pfpsd, &us1, nrs1, fma_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fma_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { _fp_unpack(pfpsd, &us3, nrs3, fma_inst->sz); if (ust.fpclass != fp_quiet && ust.fpclass != fp_signaling) ust.sign ^= 1; _fp_sub(pfpsd, &ust, &us3, &ud); _fp_pack(pfpsd, &ud, nrd, fma_inst->sz); } FPUINFO_KSTAT_PREC(fma_inst->sz, fpu_sim_fnmadds, fpu_sim_fnmaddd, fpu_sim_invalid); break; case fnmsub: _fp_unpack(pfpsd, &us1, nrs1, fma_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fma_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { _fp_unpack(pfpsd, &us3, nrs3, fma_inst->sz); if (ust.fpclass != fp_quiet && ust.fpclass != fp_signaling) ust.sign ^= 1; _fp_add(pfpsd, &ust, &us3, &ud); _fp_pack(pfpsd, &ud, nrd, fma_inst->sz); } FPUINFO_KSTAT_PREC(fma_inst->sz, fpu_sim_fnmsubs, fpu_sim_fnmsubd, fpu_sim_invalid); } } else if (inst.op3 == fmau) { /* FMA-unfused opcode */ fp_fma_inst_type *fmau_inst; uint32_t nrs3; unpacked us3; unpacked ust; /* * For FMA-unfused, if either the multiply part or the add * part raises an exception whose trap is enabled, we trap * with cexc indicating only that exception and aexc un- * changed. If neither part raises an exception whose trap * is enabled, the instruction completes with cexc indicating * just those exceptions that occurred in the add part and * aexc accumulating all exceptions that occurred in either * part. We use fmau_mul_exceptions to keep track of the * exceptions that occurred in the multiply part while we * simulate the add part. */ fmau_inst = (fp_fma_inst_type *) &inst; nrs2 = fmau_inst->rs2; nrs3 = fmau_inst->rs3; switch (fmau_inst->var) { case fmadd: _fp_unpack(pfpsd, &us1, nrs1, fmau_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fmau_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); _fp_pack(pfpsd, &ust, nrd, fmau_inst->sz); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { fmau_mul_exceptions = pfpsd->fp_current_exceptions; pfpsd->fp_current_exceptions = 0; _fp_unpack(pfpsd, &us3, nrs3, fmau_inst->sz); _fp_unpack(pfpsd, &ust, nrd, fmau_inst->sz); _fp_add(pfpsd, &ust, &us3, &ud); /* ensure QSNaN1 has precedence over QNaN3 */ if ((us3.fpclass == fp_quiet) && ((us1.fpclass == fp_signaling) || (us2.fpclass == fp_signaling))) ud = ust; _fp_pack(pfpsd, &ud, nrd, fmau_inst->sz); } FPUINFO_KSTAT_PREC(fmau_inst->sz, fpu_sim_fumadds, fpu_sim_fumaddd, fpu_sim_invalid); break; case fmsub: _fp_unpack(pfpsd, &us1, nrs1, fmau_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fmau_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); _fp_pack(pfpsd, &ust, nrd, fmau_inst->sz); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { fmau_mul_exceptions = pfpsd->fp_current_exceptions; pfpsd->fp_current_exceptions = 0; _fp_unpack(pfpsd, &us3, nrs3, fmau_inst->sz); _fp_unpack(pfpsd, &ust, nrd, fmau_inst->sz); _fp_sub(pfpsd, &ust, &us3, &ud); /* ensure QSNaN1 has precedence over QNaN3 */ if ((us3.fpclass == fp_quiet) && ((us1.fpclass == fp_signaling) || (us2.fpclass == fp_signaling))) ud = ust; _fp_pack(pfpsd, &ud, nrd, fmau_inst->sz); } FPUINFO_KSTAT_PREC(fmau_inst->sz, fpu_sim_fumsubs, fpu_sim_fumsubd, fpu_sim_invalid); break; case fnmadd: _fp_unpack(pfpsd, &us1, nrs1, fmau_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fmau_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); _fp_pack(pfpsd, &ust, nrd, fmau_inst->sz); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { fmau_mul_exceptions = pfpsd->fp_current_exceptions; pfpsd->fp_current_exceptions = 0; _fp_unpack(pfpsd, &us3, nrs3, fmau_inst->sz); _fp_unpack(pfpsd, &ust, nrd, fmau_inst->sz); if (ust.fpclass != fp_quiet && ust.fpclass != fp_signaling) ust.sign ^= 1; _fp_sub(pfpsd, &ust, &us3, &ud); /* ensure QSNaN1 has precedence over QNaN3 */ if ((us3.fpclass == fp_quiet) && ((us1.fpclass == fp_signaling) || (us2.fpclass == fp_signaling))) ud = ust; _fp_pack(pfpsd, &ud, nrd, fmau_inst->sz); } FPUINFO_KSTAT_PREC(fmau_inst->sz, fpu_sim_fnumadds, fpu_sim_fnumaddd, fpu_sim_invalid); break; case fnmsub: _fp_unpack(pfpsd, &us1, nrs1, fmau_inst->sz); _fp_unpack(pfpsd, &us2, nrs2, fmau_inst->sz); _fp_mul(pfpsd, &us1, &us2, &ust); _fp_pack(pfpsd, &ust, nrd, fmau_inst->sz); if ((pfpsd->fp_current_exceptions & fsr.tem) == 0) { fmau_mul_exceptions = pfpsd->fp_current_exceptions; pfpsd->fp_current_exceptions = 0; _fp_unpack(pfpsd, &us3, nrs3, fmau_inst->sz); _fp_unpack(pfpsd, &ust, nrd, fmau_inst->sz); if (ust.fpclass != fp_quiet && ust.fpclass != fp_signaling) ust.sign ^= 1; _fp_add(pfpsd, &ust, &us3, &ud); /* ensure QSNaN1 has precedence over QNaN3 */ if ((us3.fpclass == fp_quiet) && ((us1.fpclass == fp_signaling) || (us2.fpclass == fp_signaling))) ud = ust; _fp_pack(pfpsd, &ud, nrd, fmau_inst->sz); } FPUINFO_KSTAT_PREC(fmau_inst->sz, fpu_sim_fnumsubs, fpu_sim_fnumsubd, fpu_sim_invalid); } } else { nfcc = nrd & 0x3; if (inst.op3 == 0x35) { /* fpop2 */ fsr.cexc = 0; *pfsr = fsr; if ((inst.opcode & 0xf) == 0) { if ((fp_notp) && (inst.prec == 0)) return (ftt_unimplemented); FPUINFO_KSTAT(fpu_sim_fmovcc); return (fmovcc(pfpsd, inst, pfsr)); /* fmovcc */ } else if ((inst.opcode & 0x7) == 1) { if ((fp_notp) && (inst.prec == 0)) return (ftt_unimplemented); FPUINFO_KSTAT(fpu_sim_fmovr); return (fmovr(pfpsd, inst)); /* fmovr */ } } /* ibit not valid for fpop1 instructions */ if ((fp_notp) && (inst.ibit != 0)) return (ftt_unimplemented); if ((fp_notp) && (inst.prec == 0)) { /* fxto[sdq], fito[sdq] */ if ((inst.opcode != flltos) && (inst.opcode != flltod) && (inst.opcode != flltox) && (inst.opcode != fitos) && (inst.opcode != fitod) && (inst.opcode != fitox)) { return (ftt_unimplemented); } } switch (inst.opcode) { case fmovs: /* also covers fmovd, fmovq */ if (inst.prec < 2) { /* fmovs */ _fp_unpack_word(pfpsd, &usr, nrs2); _fp_pack_word(pfpsd, &usr, nrd); FPUINFO_KSTAT(fpu_sim_fmovs); } else { /* fmovd */ _fp_unpack_extword(pfpsd, &lusr, nrs2); _fp_pack_extword(pfpsd, &lusr, nrd); if (inst.prec > 2) { /* fmovq */ _fp_unpack_extword(pfpsd, &lusr, nrs2+2); _fp_pack_extword(pfpsd, &lusr, nrd+2); FPUINFO_KSTAT(fpu_sim_fmovq); } else { FPUINFO_KSTAT(fpu_sim_fmovd); } } break; case fabss: /* also covers fabsd, fabsq */ if (inst.prec < 2) { /* fabss */ _fp_unpack_word(pfpsd, &usr, nrs2); usr &= 0x7fffffff; _fp_pack_word(pfpsd, &usr, nrd); FPUINFO_KSTAT(fpu_sim_fabss); } else { /* fabsd */ _fp_unpack_extword(pfpsd, &lusr, nrs2); lusr &= 0x7fffffffffffffff; _fp_pack_extword(pfpsd, &lusr, nrd); if (inst.prec > 2) { /* fabsq */ _fp_unpack_extword(pfpsd, &lusr, nrs2+2); _fp_pack_extword(pfpsd, &lusr, nrd+2); FPUINFO_KSTAT(fpu_sim_fabsq); } else { FPUINFO_KSTAT(fpu_sim_fabsd); } } break; case fnegs: /* also covers fnegd, fnegq */ if (inst.prec < 2) { /* fnegs */ _fp_unpack_word(pfpsd, &usr, nrs2); usr ^= 0x80000000; _fp_pack_word(pfpsd, &usr, nrd); FPUINFO_KSTAT(fpu_sim_fnegs); } else { /* fnegd */ _fp_unpack_extword(pfpsd, &lusr, nrs2); lusr ^= 0x8000000000000000; _fp_pack_extword(pfpsd, &lusr, nrd); if (inst.prec > 2) { /* fnegq */ _fp_unpack_extword(pfpsd, &lusr, nrs2+2); lusr ^= 0x0000000000000000; _fp_pack_extword(pfpsd, &lusr, nrd+2); FPUINFO_KSTAT(fpu_sim_fnegq); } else { FPUINFO_KSTAT(fpu_sim_fnegd); } } break; case fadd: _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); _fp_add(pfpsd, &us1, &us2, &ud); _fp_pack(pfpsd, &ud, nrd, inst.prec); FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fadds, fpu_sim_faddd, fpu_sim_faddq); break; case fsub: _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); _fp_sub(pfpsd, &us1, &us2, &ud); _fp_pack(pfpsd, &ud, nrd, inst.prec); FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fsubs, fpu_sim_fsubd, fpu_sim_fsubq); break; case fmul: _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); _fp_mul(pfpsd, &us1, &us2, &ud); _fp_pack(pfpsd, &ud, nrd, inst.prec); FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fmuls, fpu_sim_fmuld, fpu_sim_fmulq); break; case fsmuld: if ((fp_notp) && (inst.prec != 1)) return (ftt_unimplemented); _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); _fp_mul(pfpsd, &us1, &us2, &ud); _fp_pack(pfpsd, &ud, nrd, (enum fp_op_type) ((int)inst.prec+1)); FPUINFO_KSTAT(fpu_sim_fsmuld); break; case fdmulx: if ((fp_notp) && (inst.prec != 2)) return (ftt_unimplemented); _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); _fp_mul(pfpsd, &us1, &us2, &ud); _fp_pack(pfpsd, &ud, nrd, (enum fp_op_type) ((int)inst.prec+1)); FPUINFO_KSTAT(fpu_sim_fdmulx); break; case fdiv: _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); _fp_div(pfpsd, &us1, &us2, &ud); _fp_pack(pfpsd, &ud, nrd, inst.prec); FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fdivs, fpu_sim_fdivd, fpu_sim_fdivq); break; case fcmp: _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); cc = _fp_compare(pfpsd, &us1, &us2, 0); if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem)) switch (nfcc) { case fcc_0: fsr.fcc0 = cc; break; case fcc_1: fsr.fcc1 = cc; break; case fcc_2: fsr.fcc2 = cc; break; case fcc_3: fsr.fcc3 = cc; break; } FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fcmps, fpu_sim_fcmpd, fpu_sim_fcmpq); break; case fcmpe: _fp_unpack(pfpsd, &us1, nrs1, inst.prec); _fp_unpack(pfpsd, &us2, nrs2, inst.prec); cc = _fp_compare(pfpsd, &us1, &us2, 1); if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem)) switch (nfcc) { case fcc_0: fsr.fcc0 = cc; break; case fcc_1: fsr.fcc1 = cc; break; case fcc_2: fsr.fcc2 = cc; break; case fcc_3: fsr.fcc3 = cc; break; } FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fcmpes, fpu_sim_fcmped, fpu_sim_fcmpeq); break; case fsqrt: _fp_unpack(pfpsd, &us1, nrs2, inst.prec); _fp_sqrt(pfpsd, &us1, &ud); _fp_pack(pfpsd, &ud, nrd, inst.prec); FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fsqrts, fpu_sim_fsqrtd, fpu_sim_fsqrtq); break; case ftoi: _fp_unpack(pfpsd, &us1, nrs2, inst.prec); pfpsd->fp_direction = fp_tozero; /* Force rounding toward zero. */ _fp_pack(pfpsd, &us1, nrd, fp_op_int32); FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fstoi, fpu_sim_fdtoi, fpu_sim_fqtoi); break; case ftoll: _fp_unpack(pfpsd, &us1, nrs2, inst.prec); pfpsd->fp_direction = fp_tozero; /* Force rounding toward zero. */ _fp_pack(pfpsd, &us1, nrd, fp_op_int64); FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fstox, fpu_sim_fdtox, fpu_sim_fqtox); break; case flltos: _fp_unpack(pfpsd, &us1, nrs2, fp_op_int64); _fp_pack(pfpsd, &us1, nrd, fp_op_single); FPUINFO_KSTAT(fpu_sim_fxtos); break; case flltod: _fp_unpack(pfpsd, &us1, nrs2, fp_op_int64); _fp_pack(pfpsd, &us1, nrd, fp_op_double); FPUINFO_KSTAT(fpu_sim_fxtod); break; case flltox: _fp_unpack(pfpsd, &us1, nrs2, fp_op_int64); _fp_pack(pfpsd, &us1, nrd, fp_op_extended); FPUINFO_KSTAT(fpu_sim_fxtoq); break; case fitos: _fp_unpack(pfpsd, &us1, nrs2, inst.prec); _fp_pack(pfpsd, &us1, nrd, fp_op_single); FPUINFO_KSTAT(fpu_sim_fitos); break; case fitod: _fp_unpack(pfpsd, &us1, nrs2, inst.prec); _fp_pack(pfpsd, &us1, nrd, fp_op_double); FPUINFO_KSTAT(fpu_sim_fitod); break; case fitox: _fp_unpack(pfpsd, &us1, nrs2, inst.prec); _fp_pack(pfpsd, &us1, nrd, fp_op_extended); FPUINFO_KSTAT(fpu_sim_fitoq); break; default: return (ftt_unimplemented); } } fsr.cexc = pfpsd->fp_current_exceptions; andexcep = pfpsd->fp_current_exceptions & fsr.tem; if (andexcep != 0) { /* Signal an IEEE SIGFPE here. */ if (andexcep & (1 << fp_invalid)) { pfpsd->fp_trapcode = FPE_FLTINV; fsr.cexc = FSR_CEXC_NV; } else if (andexcep & (1 << fp_overflow)) { pfpsd->fp_trapcode = FPE_FLTOVF; fsr.cexc = FSR_CEXC_OF; } else if (andexcep & (1 << fp_underflow)) { pfpsd->fp_trapcode = FPE_FLTUND; fsr.cexc = FSR_CEXC_UF; } else if (andexcep & (1 << fp_division)) { pfpsd->fp_trapcode = FPE_FLTDIV; fsr.cexc = FSR_CEXC_DZ; } else if (andexcep & (1 << fp_inexact)) { pfpsd->fp_trapcode = FPE_FLTRES; fsr.cexc = FSR_CEXC_NX; } else { pfpsd->fp_trapcode = 0; } *pfsr = fsr; return (ftt_ieee); } else { /* Just set accrued exception field. */ fsr.aexc |= pfpsd->fp_current_exceptions | fmau_mul_exceptions; } *pfsr = fsr; return (ftt_none); } /* * fpu_vis_sim simulates fpu and vis instructions; * It can work with both real and pcb image registers. */ enum ftt_type fpu_vis_sim( fp_simd_type *pfpsd, /* Pointer to simulator data */ fp_inst_type *pinst, /* Address of FPU instruction to simulate */ struct regs *pregs, /* Pointer to PCB image of registers. */ fsr_type *pfsr, /* Pointer to image of FSR to read and write */ uint64_t gsr, /* Image of GSR to read */ uint32_t inst) /* The FPU instruction to simulate */ { klwp_id_t lwp = ttolwp(curthread); union { uint32_t i; fp_inst_type inst; } fp; kfpu_t *pfp = lwptofpu(lwp); enum ftt_type ftt; fp.i = inst; pfpsd->fp_trapaddr = (caddr_t)pinst; if (fpu_exists) { pfpsd->fp_current_read_freg = _fp_read_pfreg; pfpsd->fp_current_write_freg = _fp_write_pfreg; pfpsd->fp_current_read_dreg = _fp_read_pdreg; pfpsd->fp_current_write_dreg = _fp_write_pdreg; pfpsd->fp_current_read_gsr = _fp_read_pgsr; pfpsd->fp_current_write_gsr = _fp_write_pgsr; } else { pfpsd->fp_current_pfregs = pfp; pfpsd->fp_current_read_freg = _fp_read_vfreg; pfpsd->fp_current_write_freg = _fp_write_vfreg; pfpsd->fp_current_read_dreg = _fp_read_vdreg; pfpsd->fp_current_write_dreg = _fp_write_vdreg; pfpsd->fp_current_read_gsr = get_gsr; pfpsd->fp_current_write_gsr = set_gsr; } if ((fp.inst.hibits == 2) && (fp.inst.op3 == 0x36)) { ftt = vis_fpu_simulator(pfpsd, fp.inst, pregs, (ulong_t *)pregs->r_sp, pfp); return (ftt); } else if ((fp.inst.hibits == 2) && ((fp.inst.op3 == 0x34) || (fp.inst.op3 == 0x35) || (fp.inst.op3 == 0x37) || (fp.inst.op3 == 0x3f))) { ftt = _fp_fpu_simulator(pfpsd, fp.inst, pfsr, gsr); if (ftt == ftt_none || ftt == ftt_ieee) { pregs->r_pc = pregs->r_npc; pregs->r_npc += 4; } return (ftt); } else { ftt = _fp_iu_simulator(pfpsd, fp.inst, pregs, (ulong_t *)pregs->r_sp, pfp); return (ftt); } } /* * fpu_simulator simulates FPU instructions only; * reads and writes FPU data registers directly. */ enum ftt_type fpu_simulator( fp_simd_type *pfpsd, /* Pointer to simulator data */ fp_inst_type *pinst, /* Address of FPU instruction to simulate */ fsr_type *pfsr, /* Pointer to image of FSR to read and write */ uint64_t gsr, /* Image of GSR to read */ uint32_t inst) /* The FPU instruction to simulate */ { union { uint32_t i; fp_inst_type inst; } fp; fp.i = inst; pfpsd->fp_trapaddr = (caddr_t)pinst; pfpsd->fp_current_read_freg = _fp_read_pfreg; pfpsd->fp_current_write_freg = _fp_write_pfreg; pfpsd->fp_current_read_dreg = _fp_read_pdreg; pfpsd->fp_current_write_dreg = _fp_write_pdreg; pfpsd->fp_current_read_gsr = _fp_read_pgsr; pfpsd->fp_current_write_gsr = _fp_write_pgsr; return (_fp_fpu_simulator(pfpsd, fp.inst, pfsr, gsr)); } /* * fp_emulator simulates FPU and CPU-FPU instructions; reads and writes FPU * data registers from image in pfpu. */ enum ftt_type fp_emulator( fp_simd_type *pfpsd, /* Pointer to simulator data */ fp_inst_type *pinst, /* Pointer to FPU instruction to simulate. */ struct regs *pregs, /* Pointer to PCB image of registers. */ void *prw, /* Pointer to locals and ins. */ kfpu_t *pfpu) /* Pointer to FPU register block. */ { klwp_id_t lwp = ttolwp(curthread); union { uint32_t i; fp_inst_type inst; } fp; enum ftt_type ftt; uint64_t gsr = get_gsr(pfpu); kfpu_t *pfp = lwptofpu(lwp); uint64_t tfsr; tfsr = pfpu->fpu_fsr; pfpsd->fp_current_pfregs = pfpu; pfpsd->fp_current_read_freg = _fp_read_vfreg; pfpsd->fp_current_write_freg = _fp_write_vfreg; pfpsd->fp_current_read_dreg = _fp_read_vdreg; pfpsd->fp_current_write_dreg = _fp_write_vdreg; pfpsd->fp_current_read_gsr = get_gsr; pfpsd->fp_current_write_gsr = set_gsr; pfpsd->fp_trapaddr = (caddr_t)pinst; /* bad inst addr in case we trap */ ftt = _fp_read_inst((uint32_t *)pinst, &(fp.i), pfpsd); if (ftt != ftt_none) return (ftt); if ((fp.inst.hibits == 2) && ((fp.inst.op3 == 0x34) || (fp.inst.op3 == 0x35) || (fp.inst.op3 == 0x37) || (fp.inst.op3 == 0x3f))) { ftt = _fp_fpu_simulator(pfpsd, fp.inst, (fsr_type *)&tfsr, gsr); /* Do not retry emulated instruction. */ pregs->r_pc = pregs->r_npc; pregs->r_npc += 4; pfpu->fpu_fsr = tfsr; if (ftt != ftt_none) { /* * Simulation generated an exception of some kind, * simulate the fp queue for a signal. */ pfpu->fpu_q->FQu.fpq.fpq_addr = (uint32_t *)pinst; pfpu->fpu_q->FQu.fpq.fpq_instr = fp.i; pfpu->fpu_qcnt = 1; } } else if ((fp.inst.hibits == 2) && (fp.inst.op3 == 0x36)) { ftt = vis_fpu_simulator(pfpsd, fp.inst, pregs, prw, pfp); } else ftt = _fp_iu_simulator(pfpsd, fp.inst, pregs, prw, pfpu); if (ftt != ftt_none) return (ftt); /* * If we are single-stepping, don't emulate any more instructions. */ if (lwp->lwp_pcb.pcb_step != STEP_NONE) return (ftt); again: /* * now read next instruction and see if it can be emulated */ pinst = (fp_inst_type *)pregs->r_pc; pfpsd->fp_trapaddr = (caddr_t)pinst; /* bad inst addr in case we trap */ ftt = _fp_read_inst((uint32_t *)pinst, &(fp.i), pfpsd); if (ftt != ftt_none) return (ftt); if ((fp.inst.hibits == 2) && /* fpops */ ((fp.inst.op3 == 0x34) || (fp.inst.op3 == 0x35) || (fp.inst.op3 == 0x37) || (fp.inst.op3 == 0x3f))) { ftt = _fp_fpu_simulator(pfpsd, fp.inst, (fsr_type *)&tfsr, gsr); /* Do not retry emulated instruction. */ pfpu->fpu_fsr = tfsr; pregs->r_pc = pregs->r_npc; pregs->r_npc += 4; if (ftt != ftt_none) { /* * Simulation generated an exception of some kind, * simulate the fp queue for a signal. */ pfpu->fpu_q->FQu.fpq.fpq_addr = (uint32_t *)pinst; pfpu->fpu_q->FQu.fpq.fpq_instr = fp.i; pfpu->fpu_qcnt = 1; } } else if ((fp.inst.hibits == 2) && (fp.inst.op3 == 0x36)) { ftt = vis_fpu_simulator(pfpsd, fp.inst, pregs, prw, pfp); } else if ( /* rd %gsr */ ((fp.inst.hibits == 2) && ((fp.inst.op3 & 0x3f) == 0x28) && (fp.inst.rs1 == 0x13)) || /* wr %gsr */ ((fp.inst.hibits == 2) && ((fp.inst.op3 & 0x3f) == 0x30) && (fp.inst.rd == 0x13)) || /* movcc */ ((fp.inst.hibits == 2) && ((fp.inst.op3 & 0x3f) == 0x2c) && (((fp.i>>18) & 0x1) == 0)) || /* fbpcc */ ((fp.inst.hibits == 0) && (((fp.i>>22) & 0x7) == 5)) || /* fldst */ ((fp.inst.hibits == 3) && ((fp.inst.op3 & 0x38) == 0x20)) || /* fbcc */ ((fp.inst.hibits == 0) && (((fp.i>>22) & 0x7) == 6))) { ftt = _fp_iu_simulator(pfpsd, fp.inst, pregs, prw, pfpu); } else return (ftt); if (ftt != ftt_none) return (ftt); else goto again; } /* * FPU simulator global kstat data */ struct fpustat_kstat fpustat = { { "fpu_ieee_traps", KSTAT_DATA_UINT64 }, { "fpu_unfinished_traps", KSTAT_DATA_UINT64 }, { "fpu_unimplemented", KSTAT_DATA_UINT64 }, }; kstat_t *fpu_kstat = NULL; kstat_t *fpuinfo_kstat = NULL; kstat_t *visinfo_kstat = NULL; void fp_kstat_init(void) { const uint_t fpustat_ndata = sizeof (fpustat) / sizeof (kstat_named_t); const uint_t fpuinfo_ndata = sizeof (fpuinfo) / sizeof (kstat_named_t); const uint_t visinfo_ndata = sizeof (visinfo) /sizeof (kstat_named_t); ASSERT(fpu_kstat == NULL); if ((fpu_kstat = kstat_create("unix", 0, "fpu_traps", "misc", KSTAT_TYPE_NAMED, fpustat_ndata, KSTAT_FLAG_VIRTUAL)) == NULL) { cmn_err(CE_WARN, "CPU%d: kstat_create for fpu_traps failed", CPU->cpu_id); } else { fpu_kstat->ks_data = (void *)&fpustat; kstat_install(fpu_kstat); } ASSERT(fpuinfo_kstat == NULL); if ((fpuinfo_kstat = kstat_create("unix", 0, "fpu_info", "misc", KSTAT_TYPE_NAMED, fpuinfo_ndata, KSTAT_FLAG_VIRTUAL)) == NULL) { cmn_err(CE_WARN, "CPU%d: kstat_create for fpu_info failed", CPU->cpu_id); } else { fpuinfo_kstat->ks_data = (void *)&fpuinfo; kstat_install(fpuinfo_kstat); } ASSERT(visinfo_kstat == NULL); if ((visinfo_kstat = kstat_create("unix", 0, "vis_info", "misc", KSTAT_TYPE_NAMED, visinfo_ndata, KSTAT_FLAG_VIRTUAL)) == NULL) { cmn_err(CE_WARN, "CPU%d: kstat_create for vis_info failed", CPU->cpu_id); } else { visinfo_kstat->ks_data = (void *)&visinfo; kstat_install(visinfo_kstat); } } void fp_kstat_update(enum ftt_type ftt) { ASSERT((ftt == ftt_ieee) || (ftt == ftt_unfinished) || (ftt == ftt_unimplemented)); if (ftt == ftt_ieee) atomic_add_64(&fpustat.fpu_ieee_traps.value.ui64, 1); else if (ftt == ftt_unfinished) atomic_add_64(&fpustat.fpu_unfinished_traps.value.ui64, 1); else if (ftt == ftt_unimplemented) atomic_add_64(&fpustat.fpu_unimplemented_traps.value.ui64, 1); }