OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [alpha/] [math-emu/] [math.c] - Blame information for rev 1781

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
#include <linux/module.h>
2
#include <linux/types.h>
3
#include <linux/kernel.h>
4
#include <linux/sched.h>
5
 
6
#include <asm/uaccess.h>
7
 
8
#include "sfp-util.h"
9
#include <math-emu/soft-fp.h>
10
#include <math-emu/single.h>
11
#include <math-emu/double.h>
12
 
13
#define OPC_PAL         0x00
14
#define OPC_INTA        0x10
15
#define OPC_INTL        0x11
16
#define OPC_INTS        0x12
17
#define OPC_INTM        0x13
18
#define OPC_FLTC        0x14
19
#define OPC_FLTV        0x15
20
#define OPC_FLTI        0x16
21
#define OPC_FLTL        0x17
22
#define OPC_MISC        0x18
23
#define OPC_JSR         0x1a
24
 
25
#define FOP_SRC_S       0
26
#define FOP_SRC_T       2
27
#define FOP_SRC_Q       3
28
 
29
#define FOP_FNC_ADDx    0
30
#define FOP_FNC_CVTQL   0
31
#define FOP_FNC_SUBx    1
32
#define FOP_FNC_MULx    2
33
#define FOP_FNC_DIVx    3
34
#define FOP_FNC_CMPxUN  4
35
#define FOP_FNC_CMPxEQ  5
36
#define FOP_FNC_CMPxLT  6
37
#define FOP_FNC_CMPxLE  7
38
#define FOP_FNC_SQRTx   11
39
#define FOP_FNC_CVTxS   12
40
#define FOP_FNC_CVTxT   14
41
#define FOP_FNC_CVTxQ   15
42
 
43
#define MISC_TRAPB      0x0000
44
#define MISC_EXCB       0x0400
45
 
46
extern unsigned long alpha_read_fp_reg (unsigned long reg);
47
extern void alpha_write_fp_reg (unsigned long reg, unsigned long val);
48
extern unsigned long alpha_read_fp_reg_s (unsigned long reg);
49
extern void alpha_write_fp_reg_s (unsigned long reg, unsigned long val);
50
 
51
 
52
#ifdef MODULE
53
 
54
MODULE_DESCRIPTION("FP Software completion module");
55
 
56
extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long);
57
extern long (*alpha_fp_emul) (unsigned long pc);
58
 
59
static long (*save_emul_imprecise)(struct pt_regs *, unsigned long);
60
static long (*save_emul) (unsigned long pc);
61
 
62
long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long);
63
long do_alpha_fp_emul(unsigned long);
64
 
65
int init_module(void)
66
{
67
        save_emul_imprecise = alpha_fp_emul_imprecise;
68
        save_emul = alpha_fp_emul;
69
        alpha_fp_emul_imprecise = do_alpha_fp_emul_imprecise;
70
        alpha_fp_emul = do_alpha_fp_emul;
71
        return 0;
72
}
73
 
74
void cleanup_module(void)
75
{
76
        alpha_fp_emul_imprecise = save_emul_imprecise;
77
        alpha_fp_emul = save_emul;
78
}
79
 
80
#undef  alpha_fp_emul_imprecise
81
#define alpha_fp_emul_imprecise         do_alpha_fp_emul_imprecise
82
#undef  alpha_fp_emul
83
#define alpha_fp_emul                   do_alpha_fp_emul
84
 
85
#endif /* MODULE */
86
 
87
 
88
/*
89
 * Emulate the floating point instruction at address PC.  Returns 0 if
90
 * emulation fails.  Notice that the kernel does not and cannot use FP
91
 * regs.  This is good because it means that instead of
92
 * saving/restoring all fp regs, we simply stick the result of the
93
 * operation into the appropriate register.
94
 */
95
long
96
alpha_fp_emul (unsigned long pc)
97
{
98
        FP_DECL_EX;
99
        FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
100
        FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
101
 
102
        unsigned long fa, fb, fc, func, mode, src;
103
        unsigned long res, va, vb, vc, swcr, fpcr;
104
        __u32 insn;
105
 
106
        MOD_INC_USE_COUNT;
107
 
108
        get_user(insn, (__u32*)pc);
109
        fc     = (insn >>  0) & 0x1f;    /* destination register */
110
        fb     = (insn >> 16) & 0x1f;
111
        fa     = (insn >> 21) & 0x1f;
112
        func   = (insn >>  5) & 0xf;
113
        src    = (insn >>  9) & 0x3;
114
        mode   = (insn >> 11) & 0x3;
115
 
116
        fpcr = rdfpcr();
117
        swcr = swcr_update_status(current->thread.flags, fpcr);
118
 
119
        if (mode == 3) {
120
                /* Dynamic -- get rounding mode from fpcr.  */
121
                mode = (fpcr >> FPCR_DYN_SHIFT) & 3;
122
        }
123
 
124
        switch (src) {
125
        case FOP_SRC_S:
126
                va = alpha_read_fp_reg_s(fa);
127
                vb = alpha_read_fp_reg_s(fb);
128
 
129
                FP_UNPACK_SP(SA, &va);
130
                FP_UNPACK_SP(SB, &vb);
131
 
132
                switch (func) {
133
                case FOP_FNC_SUBx:
134
                        FP_SUB_S(SR, SA, SB);
135
                        goto pack_s;
136
 
137
                case FOP_FNC_ADDx:
138
                        FP_ADD_S(SR, SA, SB);
139
                        goto pack_s;
140
 
141
                case FOP_FNC_MULx:
142
                        FP_MUL_S(SR, SA, SB);
143
                        goto pack_s;
144
 
145
                case FOP_FNC_DIVx:
146
                        FP_DIV_S(SR, SA, SB);
147
                        goto pack_s;
148
 
149
                case FOP_FNC_SQRTx:
150
                        FP_SQRT_S(SR, SB);
151
                        goto pack_s;
152
                }
153
                goto bad_insn;
154
 
155
        case FOP_SRC_T:
156
                va = alpha_read_fp_reg(fa);
157
                vb = alpha_read_fp_reg(fb);
158
 
159
                if ((func & ~3) == FOP_FNC_CMPxUN) {
160
                        FP_UNPACK_RAW_DP(DA, &va);
161
                        FP_UNPACK_RAW_DP(DB, &vb);
162
                        if (!DA_e && !_FP_FRAC_ZEROP_1(DA)) {
163
                                FP_SET_EXCEPTION(FP_EX_DENORM);
164
                                if (FP_DENORM_ZERO)
165
                                        _FP_FRAC_SET_1(DA, _FP_ZEROFRAC_1);
166
                        }
167
                        if (!DB_e && !_FP_FRAC_ZEROP_1(DB)) {
168
                                FP_SET_EXCEPTION(FP_EX_DENORM);
169
                                if (FP_DENORM_ZERO)
170
                                        _FP_FRAC_SET_1(DB, _FP_ZEROFRAC_1);
171
                        }
172
                        FP_CMP_D(res, DA, DB, 3);
173
                        vc = 0x4000000000000000;
174
                        /* CMPTEQ, CMPTUN don't trap on QNaN,
175
                           while CMPTLT and CMPTLE do */
176
                        if (res == 3
177
                            && ((func & 3) >= 2
178
                                || FP_ISSIGNAN_D(DA)
179
                                || FP_ISSIGNAN_D(DB))) {
180
                                FP_SET_EXCEPTION(FP_EX_INVALID);
181
                        }
182
                        switch (func) {
183
                        case FOP_FNC_CMPxUN: if (res != 3) vc = 0; break;
184
                        case FOP_FNC_CMPxEQ: if (res) vc = 0; break;
185
                        case FOP_FNC_CMPxLT: if (res != -1) vc = 0; break;
186
                        case FOP_FNC_CMPxLE: if ((long)res > 0) vc = 0; break;
187
                        }
188
                        goto done_d;
189
                }
190
 
191
                FP_UNPACK_DP(DA, &va);
192
                FP_UNPACK_DP(DB, &vb);
193
 
194
                switch (func) {
195
                case FOP_FNC_SUBx:
196
                        FP_SUB_D(DR, DA, DB);
197
                        goto pack_d;
198
 
199
                case FOP_FNC_ADDx:
200
                        FP_ADD_D(DR, DA, DB);
201
                        goto pack_d;
202
 
203
                case FOP_FNC_MULx:
204
                        FP_MUL_D(DR, DA, DB);
205
                        goto pack_d;
206
 
207
                case FOP_FNC_DIVx:
208
                        FP_DIV_D(DR, DA, DB);
209
                        goto pack_d;
210
 
211
                case FOP_FNC_SQRTx:
212
                        FP_SQRT_D(DR, DB);
213
                        goto pack_d;
214
 
215
                case FOP_FNC_CVTxS:
216
                        /* It is irritating that DEC encoded CVTST with
217
                           SRC == T_floating.  It is also interesting that
218
                           the bit used to tell the two apart is /U... */
219
                        if (insn & 0x2000) {
220
                                FP_CONV(S,D,1,1,SR,DB);
221
                                goto pack_s;
222
                        } else {
223
                                vb = alpha_read_fp_reg_s(fb);
224
                                FP_UNPACK_SP(SB, &vb);
225
                                DR_c = DB_c;
226
                                DR_s = DB_s;
227
                                DR_e = DB_e;
228
                                DR_f = SB_f << (52 - 23);
229
                                goto pack_d;
230
                        }
231
 
232
                case FOP_FNC_CVTxQ:
233
                        if (DB_c == FP_CLS_NAN
234
                            && (_FP_FRAC_HIGH_RAW_D(DB) & _FP_QNANBIT_D)) {
235
                          /* AAHB Table B-2 says QNaN should not trigger INV */
236
                                vc = 0;
237
                        } else
238
                                FP_TO_INT_ROUND_D(vc, DB, 64, 2);
239
                        goto done_d;
240
                }
241
                goto bad_insn;
242
 
243
        case FOP_SRC_Q:
244
                vb = alpha_read_fp_reg(fb);
245
 
246
                switch (func) {
247
                case FOP_FNC_CVTQL:
248
                        /* Notice: We can get here only due to an integer
249
                           overflow.  Such overflows are reported as invalid
250
                           ops.  We return the result the hw would have
251
                           computed.  */
252
                        vc = ((vb & 0xc0000000) << 32 | /* sign and msb */
253
                              (vb & 0x3fffffff) << 29); /* rest of the int */
254
                        FP_SET_EXCEPTION (FP_EX_INVALID);
255
                        goto done_d;
256
 
257
                case FOP_FNC_CVTxS:
258
                        FP_FROM_INT_S(SR, ((long)vb), 64, long);
259
                        goto pack_s;
260
 
261
                case FOP_FNC_CVTxT:
262
                        FP_FROM_INT_D(DR, ((long)vb), 64, long);
263
                        goto pack_d;
264
                }
265
                goto bad_insn;
266
        }
267
        goto bad_insn;
268
 
269
pack_s:
270
        FP_PACK_SP(&vc, SR);
271
        if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ))
272
                vc = 0;
273
        alpha_write_fp_reg_s(fc, vc);
274
        goto done;
275
 
276
pack_d:
277
        FP_PACK_DP(&vc, DR);
278
        if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ))
279
                vc = 0;
280
done_d:
281
        alpha_write_fp_reg(fc, vc);
282
        goto done;
283
 
284
        /*
285
         * Take the appropriate action for each possible
286
         * floating-point result:
287
         *
288
         *      - Set the appropriate bits in the FPCR
289
         *      - If the specified exception is enabled in the FPCR,
290
         *        return.  The caller (entArith) will dispatch
291
         *        the appropriate signal to the translated program.
292
         *
293
         * In addition, properly track the exception state in software
294
         * as described in the Alpha Architectre Handbook section 4.7.7.3.
295
         */
296
done:
297
        if (_fex) {
298
                /* Record exceptions in software control word.  */
299
                swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
300
                current->thread.flags |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT);
301
 
302
                /* Update hardware control register.  */
303
                fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
304
                fpcr |= ieee_swcr_to_fpcr(swcr);
305
                wrfpcr(fpcr);
306
 
307
                /* Do we generate a signal?  */
308
                if (_fex & swcr & IEEE_TRAP_ENABLE_MASK) {
309
                        MOD_DEC_USE_COUNT;
310
                        return 0;
311
                }
312
        }
313
 
314
        /* We used to write the destination register here, but DEC FORTRAN
315
           requires that the result *always* be written... so we do the write
316
           immediately after the operations above.  */
317
 
318
        MOD_DEC_USE_COUNT;
319
        return 1;
320
 
321
bad_insn:
322
        printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n",
323
               insn, pc);
324
        MOD_DEC_USE_COUNT;
325
        return 0;
326
}
327
 
328
long
329
alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
330
{
331
        unsigned long trigger_pc = regs->pc - 4;
332
        unsigned long insn, opcode, rc, no_signal = 0;
333
 
334
        MOD_INC_USE_COUNT;
335
 
336
        /*
337
         * Turn off the bits corresponding to registers that are the
338
         * target of instructions that set bits in the exception
339
         * summary register.  We have some slack doing this because a
340
         * register that is the target of a trapping instruction can
341
         * be written at most once in the trap shadow.
342
         *
343
         * Branches, jumps, TRAPBs, EXCBs and calls to PALcode all
344
         * bound the trap shadow, so we need not look any further than
345
         * up to the first occurrence of such an instruction.
346
         */
347
        while (write_mask) {
348
                get_user(insn, (__u32*)(trigger_pc));
349
                opcode = insn >> 26;
350
                rc = insn & 0x1f;
351
 
352
                switch (opcode) {
353
                      case OPC_PAL:
354
                      case OPC_JSR:
355
                      case 0x30 ... 0x3f:       /* branches */
356
                        goto egress;
357
 
358
                      case OPC_MISC:
359
                        switch (insn & 0xffff) {
360
                              case MISC_TRAPB:
361
                              case MISC_EXCB:
362
                                goto egress;
363
 
364
                              default:
365
                                break;
366
                        }
367
                        break;
368
 
369
                      case OPC_INTA:
370
                      case OPC_INTL:
371
                      case OPC_INTS:
372
                      case OPC_INTM:
373
                        write_mask &= ~(1UL << rc);
374
                        break;
375
 
376
                      case OPC_FLTC:
377
                      case OPC_FLTV:
378
                      case OPC_FLTI:
379
                      case OPC_FLTL:
380
                        write_mask &= ~(1UL << (rc + 32));
381
                        break;
382
                }
383
                if (!write_mask) {
384
                        /* Re-execute insns in the trap-shadow.  */
385
                        regs->pc = trigger_pc + 4;
386
                        no_signal = alpha_fp_emul(trigger_pc);
387
                        goto egress;
388
                }
389
                trigger_pc -= 4;
390
        }
391
 
392
egress:
393
        MOD_DEC_USE_COUNT;
394
        return no_signal;
395
}

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.