2003-07-16 Andrew Cagney <cagney@redhat.com>
[deliverable/binutils-gdb.git] / gdb / i387-tdep.c
1 /* Intel 387 floating point stuff.
2
3 Copyright 1988, 1989, 1991, 1992, 1993, 1994, 1998, 1999, 2000,
4 2001, 2002, 2003 Free Software Foundation, Inc.
5
6 This file is part of GDB.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */
22
23 #include "defs.h"
24 #include "doublest.h"
25 #include "floatformat.h"
26 #include "frame.h"
27 #include "gdbcore.h"
28 #include "inferior.h"
29 #include "language.h"
30 #include "regcache.h"
31 #include "value.h"
32
33 #include "gdb_assert.h"
34 #include "gdb_string.h"
35
36 #include "i386-tdep.h"
37 #include "i387-tdep.h"
38
39 /* Implement the `info float' layout based on the register definitions
40 in `tm-i386.h'. */
41
42 /* Print the floating point number specified by RAW. */
43
44 static void
45 print_i387_value (char *raw, struct ui_file *file)
46 {
47 DOUBLEST value;
48
49 /* Using extract_typed_floating here might affect the representation
50 of certain numbers such as NaNs, even if GDB is running natively.
51 This is fine since our caller already detects such special
52 numbers and we print the hexadecimal representation anyway. */
53 value = extract_typed_floating (raw, builtin_type_i387_ext);
54
55 /* We try to print 19 digits. The last digit may or may not contain
56 garbage, but we'd better print one too many. We need enough room
57 to print the value, 1 position for the sign, 1 for the decimal
58 point, 19 for the digits and 6 for the exponent adds up to 27. */
59 #ifdef PRINTF_HAS_LONG_DOUBLE
60 fprintf_filtered (file, " %-+27.19Lg", (long double) value);
61 #else
62 fprintf_filtered (file, " %-+27.19g", (double) value);
63 #endif
64 }
65
66 /* Print the classification for the register contents RAW. */
67
68 static void
69 print_i387_ext (unsigned char *raw, struct ui_file *file)
70 {
71 int sign;
72 int integer;
73 unsigned int exponent;
74 unsigned long fraction[2];
75
76 sign = raw[9] & 0x80;
77 integer = raw[7] & 0x80;
78 exponent = (((raw[9] & 0x7f) << 8) | raw[8]);
79 fraction[0] = ((raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]);
80 fraction[1] = (((raw[7] & 0x7f) << 24) | (raw[6] << 16)
81 | (raw[5] << 8) | raw[4]);
82
83 if (exponent == 0x7fff && integer)
84 {
85 if (fraction[0] == 0x00000000 && fraction[1] == 0x00000000)
86 /* Infinity. */
87 fprintf_filtered (file, " %cInf", (sign ? '-' : '+'));
88 else if (sign && fraction[0] == 0x00000000 && fraction[1] == 0x40000000)
89 /* Real Indefinite (QNaN). */
90 fputs_unfiltered (" Real Indefinite (QNaN)", file);
91 else if (fraction[1] & 0x40000000)
92 /* QNaN. */
93 fputs_filtered (" QNaN", file);
94 else
95 /* SNaN. */
96 fputs_filtered (" SNaN", file);
97 }
98 else if (exponent < 0x7fff && exponent > 0x0000 && integer)
99 /* Normal. */
100 print_i387_value (raw, file);
101 else if (exponent == 0x0000)
102 {
103 /* Denormal or zero. */
104 print_i387_value (raw, file);
105
106 if (integer)
107 /* Pseudo-denormal. */
108 fputs_filtered (" Pseudo-denormal", file);
109 else if (fraction[0] || fraction[1])
110 /* Denormal. */
111 fputs_filtered (" Denormal", file);
112 }
113 else
114 /* Unsupported. */
115 fputs_filtered (" Unsupported", file);
116 }
117
118 /* Print the status word STATUS. */
119
120 static void
121 print_i387_status_word (unsigned int status, struct ui_file *file)
122 {
123 fprintf_filtered (file, "Status Word: %s",
124 local_hex_string_custom (status, "04"));
125 fputs_filtered (" ", file);
126 fprintf_filtered (file, " %s", (status & 0x0001) ? "IE" : " ");
127 fprintf_filtered (file, " %s", (status & 0x0002) ? "DE" : " ");
128 fprintf_filtered (file, " %s", (status & 0x0004) ? "ZE" : " ");
129 fprintf_filtered (file, " %s", (status & 0x0008) ? "OE" : " ");
130 fprintf_filtered (file, " %s", (status & 0x0010) ? "UE" : " ");
131 fprintf_filtered (file, " %s", (status & 0x0020) ? "PE" : " ");
132 fputs_filtered (" ", file);
133 fprintf_filtered (file, " %s", (status & 0x0080) ? "ES" : " ");
134 fputs_filtered (" ", file);
135 fprintf_filtered (file, " %s", (status & 0x0040) ? "SF" : " ");
136 fputs_filtered (" ", file);
137 fprintf_filtered (file, " %s", (status & 0x0100) ? "C0" : " ");
138 fprintf_filtered (file, " %s", (status & 0x0200) ? "C1" : " ");
139 fprintf_filtered (file, " %s", (status & 0x0400) ? "C2" : " ");
140 fprintf_filtered (file, " %s", (status & 0x4000) ? "C3" : " ");
141
142 fputs_filtered ("\n", file);
143
144 fprintf_filtered (file,
145 " TOP: %d\n", ((status >> 11) & 7));
146 }
147
148 /* Print the control word CONTROL. */
149
150 static void
151 print_i387_control_word (unsigned int control, struct ui_file *file)
152 {
153 fprintf_filtered (file, "Control Word: %s",
154 local_hex_string_custom (control, "04"));
155 fputs_filtered (" ", file);
156 fprintf_filtered (file, " %s", (control & 0x0001) ? "IM" : " ");
157 fprintf_filtered (file, " %s", (control & 0x0002) ? "DM" : " ");
158 fprintf_filtered (file, " %s", (control & 0x0004) ? "ZM" : " ");
159 fprintf_filtered (file, " %s", (control & 0x0008) ? "OM" : " ");
160 fprintf_filtered (file, " %s", (control & 0x0010) ? "UM" : " ");
161 fprintf_filtered (file, " %s", (control & 0x0020) ? "PM" : " ");
162
163 fputs_filtered ("\n", file);
164
165 fputs_filtered (" PC: ", file);
166 switch ((control >> 8) & 3)
167 {
168 case 0:
169 fputs_filtered ("Single Precision (24-bits)\n", file);
170 break;
171 case 1:
172 fputs_filtered ("Reserved\n", file);
173 break;
174 case 2:
175 fputs_filtered ("Double Precision (53-bits)\n", file);
176 break;
177 case 3:
178 fputs_filtered ("Extended Precision (64-bits)\n", file);
179 break;
180 }
181
182 fputs_filtered (" RC: ", file);
183 switch ((control >> 10) & 3)
184 {
185 case 0:
186 fputs_filtered ("Round to nearest\n", file);
187 break;
188 case 1:
189 fputs_filtered ("Round down\n", file);
190 break;
191 case 2:
192 fputs_filtered ("Round up\n", file);
193 break;
194 case 3:
195 fputs_filtered ("Round toward zero\n", file);
196 break;
197 }
198 }
199
200 /* Print out the i387 floating point state. Note that we ignore FRAME
201 in the code below. That's OK since floating-point registers are
202 never saved on the stack. */
203
204 void
205 i387_print_float_info (struct gdbarch *gdbarch, struct ui_file *file,
206 struct frame_info *frame, const char *args)
207 {
208 char buf[4];
209 ULONGEST fctrl;
210 ULONGEST fstat;
211 ULONGEST ftag;
212 ULONGEST fiseg;
213 ULONGEST fioff;
214 ULONGEST foseg;
215 ULONGEST fooff;
216 ULONGEST fop;
217 int fpreg;
218 int top;
219
220 frame_register_read (frame, FCTRL_REGNUM, buf);
221 fctrl = extract_unsigned_integer (buf, 4);
222 frame_register_read (frame, FSTAT_REGNUM, buf);
223 fstat = extract_unsigned_integer (buf, 4);
224 frame_register_read (frame, FTAG_REGNUM, buf);
225 ftag = extract_unsigned_integer (buf, 4);
226 frame_register_read (frame, FISEG_REGNUM, buf);
227 fiseg = extract_unsigned_integer (buf, 4);
228 frame_register_read (frame, FIOFF_REGNUM, buf);
229 fioff = extract_unsigned_integer (buf, 4);
230 frame_register_read (frame, FOSEG_REGNUM, buf);
231 foseg = extract_unsigned_integer (buf, 4);
232 frame_register_read (frame, FOOFF_REGNUM, buf);
233 fooff = extract_unsigned_integer (buf, 4);
234 frame_register_read (frame, FOP_REGNUM, buf);
235 fop = extract_unsigned_integer (buf, 4);
236
237 top = ((fstat >> 11) & 7);
238
239 for (fpreg = 7; fpreg >= 0; fpreg--)
240 {
241 unsigned char raw[FPU_REG_RAW_SIZE];
242 int tag = (ftag >> (fpreg * 2)) & 3;
243 int i;
244
245 fprintf_filtered (file, "%sR%d: ", fpreg == top ? "=>" : " ", fpreg);
246
247 switch (tag)
248 {
249 case 0:
250 fputs_filtered ("Valid ", file);
251 break;
252 case 1:
253 fputs_filtered ("Zero ", file);
254 break;
255 case 2:
256 fputs_filtered ("Special ", file);
257 break;
258 case 3:
259 fputs_filtered ("Empty ", file);
260 break;
261 }
262
263 frame_register_read (frame, (fpreg + 8 - top) % 8 + FP0_REGNUM, raw);
264
265 fputs_filtered ("0x", file);
266 for (i = 9; i >= 0; i--)
267 fprintf_filtered (file, "%02x", raw[i]);
268
269 if (tag != 3)
270 print_i387_ext (raw, file);
271
272 fputs_filtered ("\n", file);
273 }
274
275 fputs_filtered ("\n", file);
276
277 print_i387_status_word (fstat, file);
278 print_i387_control_word (fctrl, file);
279 fprintf_filtered (file, "Tag Word: %s\n",
280 local_hex_string_custom (ftag, "04"));
281 fprintf_filtered (file, "Instruction Pointer: %s:",
282 local_hex_string_custom (fiseg, "02"));
283 fprintf_filtered (file, "%s\n", local_hex_string_custom (fioff, "08"));
284 fprintf_filtered (file, "Operand Pointer: %s:",
285 local_hex_string_custom (foseg, "02"));
286 fprintf_filtered (file, "%s\n", local_hex_string_custom (fooff, "08"));
287 fprintf_filtered (file, "Opcode: %s\n",
288 local_hex_string_custom (fop ? (fop | 0xd800) : 0, "04"));
289 }
290 \f
291
292 /* Read a value of type TYPE from register REGNUM in frame FRAME, and
293 return its contents in TO. */
294
295 void
296 i387_register_to_value (struct frame_info *frame, int regnum,
297 struct type *type, void *to)
298 {
299 char from[I386_MAX_REGISTER_SIZE];
300
301 gdb_assert (i386_fp_regnum_p (regnum));
302
303 /* We only support floating-point values. */
304 if (TYPE_CODE (type) != TYPE_CODE_FLT)
305 {
306 warning ("Cannot convert floating-point register value "
307 "to non-floating-point type.");
308 return;
309 }
310
311 /* Convert to TYPE. This should be a no-op if TYPE is equivalent to
312 the extended floating-point format used by the FPU. */
313 frame_read_register (frame, regnum, from);
314 convert_typed_floating (from, builtin_type_i387_ext, to, type);
315 }
316
317 /* Write the contents FROM of a value of type TYPE into register
318 REGNUM in frame FRAME. */
319
320 void
321 i387_value_to_register (struct frame_info *frame, int regnum,
322 struct type *type, const void *from)
323 {
324 char to[I386_MAX_REGISTER_SIZE];
325
326 gdb_assert (i386_fp_regnum_p (regnum));
327
328 /* We only support floating-point values. */
329 if (TYPE_CODE (type) != TYPE_CODE_FLT)
330 {
331 warning ("Cannot convert non-floating-point type "
332 "to floating-point register value.");
333 return;
334 }
335
336 /* Convert from TYPE. This should be a no-op if TYPE is equivalent
337 to the extended floating-point format used by the FPU. */
338 convert_typed_floating (from, type, to, builtin_type_i387_ext);
339 put_frame_register (frame, regnum, to);
340 }
341 \f
342
343 /* Handle FSAVE and FXSAVE formats. */
344
345 /* At fsave_offset[REGNUM] you'll find the offset to the location in
346 the data structure used by the "fsave" instruction where GDB
347 register REGNUM is stored. */
348
349 static int fsave_offset[] =
350 {
351 28 + 0 * FPU_REG_RAW_SIZE, /* FP0_REGNUM through ... */
352 28 + 1 * FPU_REG_RAW_SIZE,
353 28 + 2 * FPU_REG_RAW_SIZE,
354 28 + 3 * FPU_REG_RAW_SIZE,
355 28 + 4 * FPU_REG_RAW_SIZE,
356 28 + 5 * FPU_REG_RAW_SIZE,
357 28 + 6 * FPU_REG_RAW_SIZE,
358 28 + 7 * FPU_REG_RAW_SIZE, /* ... FP7_REGNUM. */
359 0, /* FCTRL_REGNUM (16 bits). */
360 4, /* FSTAT_REGNUM (16 bits). */
361 8, /* FTAG_REGNUM (16 bits). */
362 16, /* FISEG_REGNUM (16 bits). */
363 12, /* FIOFF_REGNUM. */
364 24, /* FOSEG_REGNUM. */
365 20, /* FOOFF_REGNUM. */
366 18 /* FOP_REGNUM (bottom 11 bits). */
367 };
368
369 #define FSAVE_ADDR(fsave, regnum) (fsave + fsave_offset[regnum - FP0_REGNUM])
370 \f
371
372 /* Fill register REGNUM in GDB's register array with the appropriate
373 value from *FSAVE. This function masks off any of the reserved
374 bits in *FSAVE. */
375
376 void
377 i387_supply_register (int regnum, char *fsave)
378 {
379 if (fsave == NULL)
380 {
381 supply_register (regnum, NULL);
382 return;
383 }
384
385 /* Most of the FPU control registers occupy only 16 bits in
386 the fsave area. Give those a special treatment. */
387 if (regnum >= FPC_REGNUM
388 && regnum != FIOFF_REGNUM && regnum != FOOFF_REGNUM)
389 {
390 unsigned char val[4];
391
392 memcpy (val, FSAVE_ADDR (fsave, regnum), 2);
393 val[2] = val[3] = 0;
394 if (regnum == FOP_REGNUM)
395 val[1] &= ((1 << 3) - 1);
396 supply_register (regnum, val);
397 }
398 else
399 supply_register (regnum, FSAVE_ADDR (fsave, regnum));
400 }
401
402 /* Fill GDB's register array with the floating-point register values
403 in *FSAVE. This function masks off any of the reserved
404 bits in *FSAVE. */
405
406 void
407 i387_supply_fsave (char *fsave)
408 {
409 int i;
410
411 for (i = FP0_REGNUM; i < XMM0_REGNUM; i++)
412 i387_supply_register (i, fsave);
413 }
414
415 /* Fill register REGNUM (if it is a floating-point register) in *FSAVE
416 with the value in GDB's register array. If REGNUM is -1, do this
417 for all registers. This function doesn't touch any of the reserved
418 bits in *FSAVE. */
419
420 void
421 i387_fill_fsave (char *fsave, int regnum)
422 {
423 int i;
424
425 for (i = FP0_REGNUM; i < XMM0_REGNUM; i++)
426 if (regnum == -1 || regnum == i)
427 {
428 /* Most of the FPU control registers occupy only 16 bits in
429 the fsave area. Give those a special treatment. */
430 if (i >= FPC_REGNUM
431 && i != FIOFF_REGNUM && i != FOOFF_REGNUM)
432 {
433 unsigned char buf[4];
434
435 regcache_collect (i, buf);
436
437 if (i == FOP_REGNUM)
438 {
439 /* The opcode occupies only 11 bits. Make sure we
440 don't touch the other bits. */
441 buf[1] &= ((1 << 3) - 1);
442 buf[1] |= ((FSAVE_ADDR (fsave, i))[1] & ~((1 << 3) - 1));
443 }
444 memcpy (FSAVE_ADDR (fsave, i), buf, 2);
445 }
446 else
447 regcache_collect (i, FSAVE_ADDR (fsave, i));
448 }
449 }
450 \f
451
452 /* At fxsave_offset[REGNUM] you'll find the offset to the location in
453 the data structure used by the "fxsave" instruction where GDB
454 register REGNUM is stored. */
455
456 static int fxsave_offset[] =
457 {
458 32, /* FP0_REGNUM through ... */
459 48,
460 64,
461 80,
462 96,
463 112,
464 128,
465 144, /* ... FP7_REGNUM (80 bits each). */
466 0, /* FCTRL_REGNUM (16 bits). */
467 2, /* FSTAT_REGNUM (16 bits). */
468 4, /* FTAG_REGNUM (16 bits). */
469 12, /* FISEG_REGNUM (16 bits). */
470 8, /* FIOFF_REGNUM. */
471 20, /* FOSEG_REGNUM (16 bits). */
472 16, /* FOOFF_REGNUM. */
473 6, /* FOP_REGNUM (bottom 11 bits). */
474 160 + 0 * 16, /* XMM0_REGNUM through ... */
475 160 + 1 * 16,
476 160 + 2 * 16,
477 160 + 3 * 16,
478 160 + 4 * 16,
479 160 + 5 * 16,
480 160 + 6 * 16,
481 160 + 7 * 16,
482 160 + 8 * 16,
483 160 + 9 * 16,
484 160 + 10 * 16,
485 160 + 11 * 16,
486 160 + 12 * 16,
487 160 + 13 * 16,
488 160 + 14 * 16,
489 160 + 15 * 16, /* ... XMM15_REGNUM (128 bits each). */
490 24 /* MXCSR_REGNUM. */
491 };
492
493 /* FIXME: kettenis/20030430: We made an unfortunate choice in putting
494 %mxcsr after the SSE registers %xmm0-%xmm7 instead of before, since
495 it makes supporting the registers %xmm8-%xmm15 on x86-64 a bit
496 involved. Hack around it by explicitly overriding the offset for
497 %mxcsr here. */
498
499 #define FXSAVE_ADDR(fxsave, regnum) \
500 ((regnum == MXCSR_REGNUM) ? (fxsave + 24) : \
501 (fxsave + fxsave_offset[regnum - FP0_REGNUM]))
502
503 static int i387_tag (unsigned char *raw);
504 \f
505
506 /* Fill GDB's register array with the floating-point and SSE register
507 values in *FXSAVE. This function masks off any of the reserved
508 bits in *FXSAVE. */
509
510 void
511 i387_supply_fxsave (char *fxsave)
512 {
513 int i, last_regnum = MXCSR_REGNUM;
514
515 if (gdbarch_tdep (current_gdbarch)->num_xmm_regs == 0)
516 last_regnum = FOP_REGNUM;
517
518 for (i = FP0_REGNUM; i <= last_regnum; i++)
519 {
520 if (fxsave == NULL)
521 {
522 supply_register (i, NULL);
523 continue;
524 }
525
526 /* Most of the FPU control registers occupy only 16 bits in
527 the fxsave area. Give those a special treatment. */
528 if (i >= FPC_REGNUM && i < XMM0_REGNUM
529 && i != FIOFF_REGNUM && i != FOOFF_REGNUM)
530 {
531 unsigned char val[4];
532
533 memcpy (val, FXSAVE_ADDR (fxsave, i), 2);
534 val[2] = val[3] = 0;
535 if (i == FOP_REGNUM)
536 val[1] &= ((1 << 3) - 1);
537 else if (i== FTAG_REGNUM)
538 {
539 /* The fxsave area contains a simplified version of the
540 tag word. We have to look at the actual 80-bit FP
541 data to recreate the traditional i387 tag word. */
542
543 unsigned long ftag = 0;
544 int fpreg;
545 int top;
546
547 top = (((FXSAVE_ADDR (fxsave, FSTAT_REGNUM))[1] >> 3) & 0x7);
548
549 for (fpreg = 7; fpreg >= 0; fpreg--)
550 {
551 int tag;
552
553 if (val[0] & (1 << fpreg))
554 {
555 int regnum = (fpreg + 8 - top) % 8 + FP0_REGNUM;
556 tag = i387_tag (FXSAVE_ADDR (fxsave, regnum));
557 }
558 else
559 tag = 3; /* Empty */
560
561 ftag |= tag << (2 * fpreg);
562 }
563 val[0] = ftag & 0xff;
564 val[1] = (ftag >> 8) & 0xff;
565 }
566 supply_register (i, val);
567 }
568 else
569 supply_register (i, FXSAVE_ADDR (fxsave, i));
570 }
571 }
572
573 /* Fill register REGNUM (if it is a floating-point or SSE register) in
574 *FXSAVE with the value in GDB's register array. If REGNUM is -1, do
575 this for all registers. This function doesn't touch any of the
576 reserved bits in *FXSAVE. */
577
578 void
579 i387_fill_fxsave (char *fxsave, int regnum)
580 {
581 int i, last_regnum = MXCSR_REGNUM;
582
583 if (gdbarch_tdep (current_gdbarch)->num_xmm_regs == 0)
584 last_regnum = FOP_REGNUM;
585
586 for (i = FP0_REGNUM; i <= last_regnum; i++)
587 if (regnum == -1 || regnum == i)
588 {
589 /* Most of the FPU control registers occupy only 16 bits in
590 the fxsave area. Give those a special treatment. */
591 if (i >= FPC_REGNUM && i < XMM0_REGNUM
592 && i != FIOFF_REGNUM && i != FOOFF_REGNUM)
593 {
594 unsigned char buf[4];
595
596 regcache_collect (i, buf);
597
598 if (i == FOP_REGNUM)
599 {
600 /* The opcode occupies only 11 bits. Make sure we
601 don't touch the other bits. */
602 buf[1] &= ((1 << 3) - 1);
603 buf[1] |= ((FXSAVE_ADDR (fxsave, i))[1] & ~((1 << 3) - 1));
604 }
605 else if (i == FTAG_REGNUM)
606 {
607 /* Converting back is much easier. */
608
609 unsigned short ftag;
610 int fpreg;
611
612 ftag = (buf[1] << 8) | buf[0];
613 buf[0] = 0;
614 buf[1] = 0;
615
616 for (fpreg = 7; fpreg >= 0; fpreg--)
617 {
618 int tag = (ftag >> (fpreg * 2)) & 3;
619
620 if (tag != 3)
621 buf[0] |= (1 << fpreg);
622 }
623 }
624 memcpy (FXSAVE_ADDR (fxsave, i), buf, 2);
625 }
626 else
627 regcache_collect (i, FXSAVE_ADDR (fxsave, i));
628 }
629 }
630
631 /* Recreate the FTW (tag word) valid bits from the 80-bit FP data in
632 *RAW. */
633
634 static int
635 i387_tag (unsigned char *raw)
636 {
637 int integer;
638 unsigned int exponent;
639 unsigned long fraction[2];
640
641 integer = raw[7] & 0x80;
642 exponent = (((raw[9] & 0x7f) << 8) | raw[8]);
643 fraction[0] = ((raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]);
644 fraction[1] = (((raw[7] & 0x7f) << 24) | (raw[6] << 16)
645 | (raw[5] << 8) | raw[4]);
646
647 if (exponent == 0x7fff)
648 {
649 /* Special. */
650 return (2);
651 }
652 else if (exponent == 0x0000)
653 {
654 if (fraction[0] == 0x0000 && fraction[1] == 0x0000 && !integer)
655 {
656 /* Zero. */
657 return (1);
658 }
659 else
660 {
661 /* Special. */
662 return (2);
663 }
664 }
665 else
666 {
667 if (integer)
668 {
669 /* Valid. */
670 return (0);
671 }
672 else
673 {
674 /* Special. */
675 return (2);
676 }
677 }
678 }
This page took 0.060965 seconds and 4 git commands to generate.