source: trunk/gcc/libffi/src/x86/ffi64.c

Last change on this file was 1389, checked in by bird, 21 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 18.2 KB
Line 
1/* -----------------------------------------------------------------------
2 ffi.c - Copyright (c) 2002 Bo Thorsen <bo@suse.de>
3
4 x86-64 Foreign Function Interface
5
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 ``Software''), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
24 ----------------------------------------------------------------------- */
25
26#include <ffi.h>
27#include <ffi_common.h>
28
29#include <stdlib.h>
30#include <stdarg.h>
31
32/* ffi_prep_args is called by the assembly routine once stack space
33 has been allocated for the function's arguments */
34
35#ifdef __x86_64__
36
37#define MAX_GPR_REGS 6
38#define MAX_SSE_REGS 8
39typedef struct
40{
41 /* Registers for argument passing. */
42 long gpr[MAX_GPR_REGS];
43 __int128_t sse[MAX_SSE_REGS];
44
45 /* Stack space for arguments. */
46 char argspace[0];
47} stackLayout;
48
49/* All reference to register classes here is identical to the code in
50 gcc/config/i386/i386.c. Do *not* change one without the other. */
51
52/* Register class used for passing given 64bit part of the argument.
53 These represent classes as documented by the PS ABI, with the exception
54 of SSESF, SSEDF classes, that are basically SSE class, just gcc will
55 use SF or DFmode move instead of DImode to avoid reformating penalties.
56
57 Similary we play games with INTEGERSI_CLASS to use cheaper SImode moves
58 whenever possible (upper half does contain padding).
59 */
60enum x86_64_reg_class
61 {
62 X86_64_NO_CLASS,
63 X86_64_INTEGER_CLASS,
64 X86_64_INTEGERSI_CLASS,
65 X86_64_SSE_CLASS,
66 X86_64_SSESF_CLASS,
67 X86_64_SSEDF_CLASS,
68 X86_64_SSEUP_CLASS,
69 X86_64_X87_CLASS,
70 X86_64_X87UP_CLASS,
71 X86_64_MEMORY_CLASS
72 };
73
74#define MAX_CLASSES 4
75
76/* x86-64 register passing implementation. See x86-64 ABI for details. Goal
77 of this code is to classify each 8bytes of incoming argument by the register
78 class and assign registers accordingly. */
79
80/* Return the union class of CLASS1 and CLASS2.
81 See the x86-64 PS ABI for details. */
82
83static enum x86_64_reg_class
84merge_classes (enum x86_64_reg_class class1, enum x86_64_reg_class class2)
85{
86 /* Rule #1: If both classes are equal, this is the resulting class. */
87 if (class1 == class2)
88 return class1;
89
90 /* Rule #2: If one of the classes is NO_CLASS, the resulting class is
91 the other class. */
92 if (class1 == X86_64_NO_CLASS)
93 return class2;
94 if (class2 == X86_64_NO_CLASS)
95 return class1;
96
97 /* Rule #3: If one of the classes is MEMORY, the result is MEMORY. */
98 if (class1 == X86_64_MEMORY_CLASS || class2 == X86_64_MEMORY_CLASS)
99 return X86_64_MEMORY_CLASS;
100
101 /* Rule #4: If one of the classes is INTEGER, the result is INTEGER. */
102 if ((class1 == X86_64_INTEGERSI_CLASS && class2 == X86_64_SSESF_CLASS)
103 || (class2 == X86_64_INTEGERSI_CLASS && class1 == X86_64_SSESF_CLASS))
104 return X86_64_INTEGERSI_CLASS;
105 if (class1 == X86_64_INTEGER_CLASS || class1 == X86_64_INTEGERSI_CLASS
106 || class2 == X86_64_INTEGER_CLASS || class2 == X86_64_INTEGERSI_CLASS)
107 return X86_64_INTEGER_CLASS;
108
109 /* Rule #5: If one of the classes is X87 or X87UP class, MEMORY is used. */
110 if (class1 == X86_64_X87_CLASS || class1 == X86_64_X87UP_CLASS
111 || class2 == X86_64_X87_CLASS || class2 == X86_64_X87UP_CLASS)
112 return X86_64_MEMORY_CLASS;
113
114 /* Rule #6: Otherwise class SSE is used. */
115 return X86_64_SSE_CLASS;
116}
117
118/* Classify the argument of type TYPE and mode MODE.
119 CLASSES will be filled by the register class used to pass each word
120 of the operand. The number of words is returned. In case the parameter
121 should be passed in memory, 0 is returned. As a special case for zero
122 sized containers, classes[0] will be NO_CLASS and 1 is returned.
123
124 See the x86-64 PS ABI for details.
125*/
126static int
127classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
128 int *byte_offset)
129{
130 /* First, align to the right place. */
131 *byte_offset = ALIGN(*byte_offset, type->alignment);
132
133 switch (type->type)
134 {
135 case FFI_TYPE_UINT8:
136 case FFI_TYPE_SINT8:
137 case FFI_TYPE_UINT16:
138 case FFI_TYPE_SINT16:
139 case FFI_TYPE_UINT32:
140 case FFI_TYPE_SINT32:
141 case FFI_TYPE_UINT64:
142 case FFI_TYPE_SINT64:
143 case FFI_TYPE_POINTER:
144 if (((*byte_offset) % 8 + type->size) <= 4)
145 classes[0] = X86_64_INTEGERSI_CLASS;
146 else
147 classes[0] = X86_64_INTEGER_CLASS;
148 return 1;
149 case FFI_TYPE_FLOAT:
150 if (((*byte_offset) % 8) == 0)
151 classes[0] = X86_64_SSESF_CLASS;
152 else
153 classes[0] = X86_64_SSE_CLASS;
154 return 1;
155 case FFI_TYPE_DOUBLE:
156 classes[0] = X86_64_SSEDF_CLASS;
157 return 1;
158 case FFI_TYPE_LONGDOUBLE:
159 classes[0] = X86_64_X87_CLASS;
160 classes[1] = X86_64_X87UP_CLASS;
161 return 2;
162 case FFI_TYPE_STRUCT:
163 {
164 const int UNITS_PER_WORD = 8;
165 int words = (type->size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
166 ffi_type **ptr;
167 int i;
168 enum x86_64_reg_class subclasses[MAX_CLASSES];
169
170 /* If the struct is larger than 16 bytes, pass it on the stack. */
171 if (type->size > 16)
172 return 0;
173
174 for (i = 0; i < words; i++)
175 classes[i] = X86_64_NO_CLASS;
176
177 /* Merge the fields of structure. */
178 for (ptr=type->elements; (*ptr)!=NULL; ptr++)
179 {
180 int num;
181
182 num = classify_argument (*ptr, subclasses, byte_offset);
183 if (num == 0)
184 return 0;
185 for (i = 0; i < num; i++)
186 {
187 int pos = *byte_offset / 8;
188 classes[i + pos] =
189 merge_classes (subclasses[i], classes[i + pos]);
190 }
191
192 if ((*ptr)->type != FFI_TYPE_STRUCT)
193 *byte_offset += (*ptr)->size;
194 }
195
196 /* Final merger cleanup. */
197 for (i = 0; i < words; i++)
198 {
199 /* If one class is MEMORY, everything should be passed in
200 memory. */
201 if (classes[i] == X86_64_MEMORY_CLASS)
202 return 0;
203
204 /* The X86_64_SSEUP_CLASS should be always preceded by
205 X86_64_SSE_CLASS. */
206 if (classes[i] == X86_64_SSEUP_CLASS
207 && (i == 0 || classes[i - 1] != X86_64_SSE_CLASS))
208 classes[i] = X86_64_SSE_CLASS;
209
210 /* X86_64_X87UP_CLASS should be preceded by X86_64_X87_CLASS. */
211 if (classes[i] == X86_64_X87UP_CLASS
212 && (i == 0 || classes[i - 1] != X86_64_X87_CLASS))
213 classes[i] = X86_64_SSE_CLASS;
214 }
215 return words;
216 }
217
218 default:
219 FFI_ASSERT(0);
220 }
221 return 0; /* Never reached. */
222}
223
224/* Examine the argument and return set number of register required in each
225 class. Return 0 iff parameter should be passed in memory. */
226static int
227examine_argument (ffi_type *type, int in_return, int *int_nregs,int *sse_nregs)
228{
229 enum x86_64_reg_class class[MAX_CLASSES];
230 int offset = 0;
231 int n;
232
233 n = classify_argument (type, class, &offset);
234
235 if (n == 0)
236 return 0;
237
238 *int_nregs = 0;
239 *sse_nregs = 0;
240 for (n--; n>=0; n--)
241 switch (class[n])
242 {
243 case X86_64_INTEGER_CLASS:
244 case X86_64_INTEGERSI_CLASS:
245 (*int_nregs)++;
246 break;
247 case X86_64_SSE_CLASS:
248 case X86_64_SSESF_CLASS:
249 case X86_64_SSEDF_CLASS:
250 (*sse_nregs)++;
251 break;
252 case X86_64_NO_CLASS:
253 case X86_64_SSEUP_CLASS:
254 break;
255 case X86_64_X87_CLASS:
256 case X86_64_X87UP_CLASS:
257 if (!in_return)
258 return 0;
259 break;
260 default:
261 abort ();
262 }
263 return 1;
264}
265
266/* Functions to load floats and double to an SSE register placeholder. */
267extern void float2sse (float, __int128_t *);
268extern void double2sse (double, __int128_t *);
269extern void floatfloat2sse (void *, __int128_t *);
270
271/* Functions to put the floats and doubles back. */
272extern float sse2float (__int128_t *);
273extern double sse2double (__int128_t *);
274extern void sse2floatfloat(__int128_t *, void *);
275
276/*@-exportheader@*/
277void
278ffi_prep_args (stackLayout *stack, extended_cif *ecif)
279/*@=exportheader@*/
280{
281 int gprcount, ssecount, i, g, s;
282 void **p_argv;
283 void *argp = &stack->argspace;
284 ffi_type **p_arg;
285
286 /* First check if the return value should be passed in memory. If so,
287 pass the pointer as the first argument. */
288 gprcount = ssecount = 0;
289 if (ecif->cif->rtype->type != FFI_TYPE_VOID
290 && examine_argument (ecif->cif->rtype, 1, &g, &s) == 0)
291 (void *)stack->gpr[gprcount++] = ecif->rvalue;
292
293 for (i=ecif->cif->nargs, p_arg=ecif->cif->arg_types, p_argv = ecif->avalue;
294 i!=0; i--, p_arg++, p_argv++)
295 {
296 int in_register = 0;
297
298 switch ((*p_arg)->type)
299 {
300 case FFI_TYPE_SINT8:
301 case FFI_TYPE_SINT16:
302 case FFI_TYPE_SINT32:
303 case FFI_TYPE_SINT64:
304 case FFI_TYPE_UINT8:
305 case FFI_TYPE_UINT16:
306 case FFI_TYPE_UINT32:
307 case FFI_TYPE_UINT64:
308 case FFI_TYPE_POINTER:
309 if (gprcount < MAX_GPR_REGS)
310 {
311 stack->gpr[gprcount] = 0;
312 stack->gpr[gprcount++] = *(long long *)(*p_argv);
313 in_register = 1;
314 }
315 break;
316
317 case FFI_TYPE_FLOAT:
318 if (ssecount < MAX_SSE_REGS)
319 {
320 float2sse (*(float *)(*p_argv), &stack->sse[ssecount++]);
321 in_register = 1;
322 }
323 break;
324
325 case FFI_TYPE_DOUBLE:
326 if (ssecount < MAX_SSE_REGS)
327 {
328 double2sse (*(double *)(*p_argv), &stack->sse[ssecount++]);
329 in_register = 1;
330 }
331 break;
332 }
333
334 if (in_register)
335 continue;
336
337 /* Either all places in registers where filled, or this is a
338 type that potentially goes into a memory slot. */
339 if (examine_argument (*p_arg, 0, &g, &s) == 0
340 || gprcount + g > MAX_GPR_REGS || ssecount + s > MAX_SSE_REGS)
341 {
342 /* Pass this argument in memory. */
343 argp = (void *)ALIGN(argp, (*p_arg)->alignment);
344 memcpy (argp, *p_argv, (*p_arg)->size);
345 argp += (*p_arg)->size;
346 }
347 else
348 {
349 /* All easy cases are eliminated. Now fire the big guns. */
350
351 enum x86_64_reg_class classes[MAX_CLASSES];
352 int offset = 0, j, num;
353 void *a;
354
355 num = classify_argument (*p_arg, classes, &offset);
356 for (j=0, a=*p_argv; j<num; j++, a+=8)
357 {
358 switch (classes[j])
359 {
360 case X86_64_INTEGER_CLASS:
361 case X86_64_INTEGERSI_CLASS:
362 stack->gpr[gprcount++] = *(long long *)a;
363 break;
364 case X86_64_SSE_CLASS:
365 floatfloat2sse (a, &stack->sse[ssecount++]);
366 break;
367 case X86_64_SSESF_CLASS:
368 float2sse (*(float *)a, &stack->sse[ssecount++]);
369 break;
370 case X86_64_SSEDF_CLASS:
371 double2sse (*(double *)a, &stack->sse[ssecount++]);
372 break;
373 default:
374 abort();
375 }
376 }
377 }
378 }
379}
380
381/* Perform machine dependent cif processing. */
382ffi_status
383ffi_prep_cif_machdep (ffi_cif *cif)
384{
385 int gprcount, ssecount, i, g, s;
386
387 gprcount = ssecount = 0;
388
389 /* Reset the byte count. We handle this size estimation here. */
390 cif->bytes = 0;
391
392 /* If the return value should be passed in memory, pass the pointer
393 as the first argument. The actual memory isn't allocated here. */
394 if (cif->rtype->type != FFI_TYPE_VOID
395 && examine_argument (cif->rtype, 1, &g, &s) == 0)
396 gprcount = 1;
397
398 /* Go over all arguments and determine the way they should be passed.
399 If it's in a register and there is space for it, let that be so. If
400 not, add it's size to the stack byte count. */
401 for (i=0; i<cif->nargs; i++)
402 {
403 if (examine_argument (cif->arg_types[i], 0, &g, &s) == 0
404 || gprcount + g > MAX_GPR_REGS || ssecount + s > MAX_SSE_REGS)
405 {
406 /* This is passed in memory. First align to the basic type. */
407 cif->bytes = ALIGN(cif->bytes, cif->arg_types[i]->alignment);
408
409 /* Stack arguments are *always* at least 8 byte aligned. */
410 cif->bytes = ALIGN(cif->bytes, 8);
411
412 /* Now add the size of this argument. */
413 cif->bytes += cif->arg_types[i]->size;
414 }
415 else
416 {
417 gprcount += g;
418 ssecount += s;
419 }
420 }
421
422 /* Set the flag for the closures return. */
423 switch (cif->rtype->type)
424 {
425 case FFI_TYPE_VOID:
426 case FFI_TYPE_STRUCT:
427 case FFI_TYPE_SINT64:
428 case FFI_TYPE_FLOAT:
429 case FFI_TYPE_DOUBLE:
430 case FFI_TYPE_LONGDOUBLE:
431 cif->flags = (unsigned) cif->rtype->type;
432 break;
433
434 case FFI_TYPE_UINT64:
435 cif->flags = FFI_TYPE_SINT64;
436 break;
437
438 default:
439 cif->flags = FFI_TYPE_INT;
440 break;
441 }
442
443 return FFI_OK;
444}
445
446typedef struct
447{
448 long gpr[2];
449 __int128_t sse[2];
450 long double st0;
451} return_value;
452
453void
454ffi_fill_return_value (return_value *rv, extended_cif *ecif)
455{
456 enum x86_64_reg_class classes[MAX_CLASSES];
457 int i = 0, num;
458 long *gpr = rv->gpr;
459 __int128_t *sse = rv->sse;
460 signed char sc;
461 signed short ss;
462
463 /* This is needed because of the way x86-64 handles signed short
464 integers. */
465 switch (ecif->cif->rtype->type)
466 {
467 case FFI_TYPE_SINT8:
468 sc = *(signed char *)gpr;
469 *(long long *)ecif->rvalue = (long long)sc;
470 return;
471 case FFI_TYPE_SINT16:
472 ss = *(signed short *)gpr;
473 *(long long *)ecif->rvalue = (long long)ss;
474 return;
475 default:
476 /* Just continue. */
477 ;
478 }
479
480 num = classify_argument (ecif->cif->rtype, classes, &i);
481
482 if (num == 0)
483 /* Return in memory. */
484 ecif->rvalue = (void *) rv->gpr[0];
485 else if (num == 2 && classes[0] == X86_64_X87_CLASS &&
486 classes[1] == X86_64_X87UP_CLASS)
487 /* This is a long double (this is easiest to handle this way instead
488 of an eightbyte at a time as in the loop below. */
489 *((long double *)ecif->rvalue) = rv->st0;
490 else
491 {
492 void *a;
493
494 for (i=0, a=ecif->rvalue; i<num; i++, a+=8)
495 {
496 switch (classes[i])
497 {
498 case X86_64_INTEGER_CLASS:
499 case X86_64_INTEGERSI_CLASS:
500 *(long long *)a = *gpr;
501 gpr++;
502 break;
503 case X86_64_SSE_CLASS:
504 sse2floatfloat (sse++, a);
505 break;
506 case X86_64_SSESF_CLASS:
507 *(float *)a = sse2float (sse++);
508 break;
509 case X86_64_SSEDF_CLASS:
510 *(double *)a = sse2double (sse++);
511 break;
512 default:
513 abort();
514 }
515 }
516 }
517}
518
519/*@-declundef@*/
520/*@-exportheader@*/
521extern void ffi_call_UNIX64(void (*)(stackLayout *, extended_cif *),
522 void (*) (return_value *, extended_cif *),
523 /*@out@*/ extended_cif *,
524 unsigned, /*@out@*/ unsigned *, void (*fn)());
525/*@=declundef@*/
526/*@=exportheader@*/
527
528void ffi_call(/*@dependent@*/ ffi_cif *cif,
529 void (*fn)(),
530 /*@out@*/ void *rvalue,
531 /*@dependent@*/ void **avalue)
532{
533 extended_cif ecif;
534 int dummy;
535
536 ecif.cif = cif;
537 ecif.avalue = avalue;
538
539 /* If the return value is a struct and we don't have a return */
540 /* value address then we need to make one */
541
542 if ((rvalue == NULL) &&
543 (examine_argument (cif->rtype, 1, &dummy, &dummy) == 0))
544 {
545 /*@-sysunrecog@*/
546 ecif.rvalue = alloca(cif->rtype->size);
547 /*@=sysunrecog@*/
548 }
549 else
550 ecif.rvalue = rvalue;
551
552 /* Stack must always be 16byte aligned. Make it so. */
553 cif->bytes = ALIGN(cif->bytes, 16);
554
555 switch (cif->abi)
556 {
557 case FFI_SYSV:
558 /* Calling 32bit code from 64bit is not possible */
559 FFI_ASSERT(0);
560 break;
561
562 case FFI_UNIX64:
563 /*@-usedef@*/
564 ffi_call_UNIX64 (ffi_prep_args, ffi_fill_return_value, &ecif,
565 cif->bytes, ecif.rvalue, fn);
566 /*@=usedef@*/
567 break;
568
569 default:
570 FFI_ASSERT(0);
571 break;
572 }
573}
574
575extern void ffi_closure_UNIX64(void);
576
577ffi_status
578ffi_prep_closure (ffi_closure* closure,
579 ffi_cif* cif,
580 void (*fun)(ffi_cif*, void*, void**, void*),
581 void *user_data)
582{
583 volatile unsigned short *tramp;
584
585 /* FFI_ASSERT (cif->abi == FFI_OSF); */
586
587 tramp = (volatile unsigned short *) &closure->tramp[0];
588 tramp[0] = 0xbb49; /* mov <code>, %r11 */
589 tramp[5] = 0xba49; /* mov <data>, %r10 */
590 tramp[10] = 0xff49; /* jmp *%r11 */
591 tramp[11] = 0x00e3;
592 *(void * volatile *) &tramp[1] = ffi_closure_UNIX64;
593 *(void * volatile *) &tramp[6] = closure;
594
595 closure->cif = cif;
596 closure->fun = fun;
597 closure->user_data = user_data;
598
599 return FFI_OK;
600}
601
602int
603ffi_closure_UNIX64_inner(ffi_closure *closure, va_list l, void *rp)
604{
605 ffi_cif *cif;
606 void **avalue;
607 ffi_type **arg_types;
608 long i, avn, argn;
609
610 cif = closure->cif;
611 avalue = alloca(cif->nargs * sizeof(void *));
612
613 argn = 0;
614
615 i = 0;
616 avn = cif->nargs;
617 arg_types = cif->arg_types;
618
619 /* Grab the addresses of the arguments from the stack frame. */
620 while (i < avn)
621 {
622 switch (arg_types[i]->type)
623 {
624 case FFI_TYPE_SINT8:
625 case FFI_TYPE_UINT8:
626 case FFI_TYPE_SINT16:
627 case FFI_TYPE_UINT16:
628 case FFI_TYPE_SINT32:
629 case FFI_TYPE_UINT32:
630 case FFI_TYPE_SINT64:
631 case FFI_TYPE_UINT64:
632 case FFI_TYPE_POINTER:
633 {
634 if (l->gp_offset > 48-8)
635 {
636 avalue[i] = l->overflow_arg_area;
637 l->overflow_arg_area = (char *)l->overflow_arg_area + 8;
638 }
639 else
640 {
641 avalue[i] = (char *)l->reg_save_area + l->gp_offset;
642 l->gp_offset += 8;
643 }
644 }
645 break;
646
647 case FFI_TYPE_STRUCT:
648 /* FIXME */
649 FFI_ASSERT(0);
650 break;
651
652 case FFI_TYPE_DOUBLE:
653 {
654 if (l->fp_offset > 176-16)
655 {
656 avalue[i] = l->overflow_arg_area;
657 l->overflow_arg_area = (char *)l->overflow_arg_area + 8;
658 }
659 else
660 {
661 avalue[i] = (char *)l->reg_save_area + l->fp_offset;
662 l->fp_offset += 16;
663 }
664 }
665#if DEBUG_FFI
666 fprintf (stderr, "double arg %d = %g\n", i, *(double *)avalue[i]);
667#endif
668 break;
669
670 case FFI_TYPE_FLOAT:
671 {
672 if (l->fp_offset > 176-16)
673 {
674 avalue[i] = l->overflow_arg_area;
675 l->overflow_arg_area = (char *)l->overflow_arg_area + 8;
676 }
677 else
678 {
679 avalue[i] = (char *)l->reg_save_area + l->fp_offset;
680 l->fp_offset += 16;
681 }
682 }
683#if DEBUG_FFI
684 fprintf (stderr, "float arg %d = %g\n", i, *(float *)avalue[i]);
685#endif
686 break;
687
688 default:
689 FFI_ASSERT(0);
690 }
691
692 argn += ALIGN(arg_types[i]->size, SIZEOF_ARG) / SIZEOF_ARG;
693 i++;
694 }
695
696 /* Invoke the closure. */
697 (closure->fun) (cif, rp, avalue, closure->user_data);
698
699 /* FIXME: Structs not supported. */
700 FFI_ASSERT(cif->rtype->type != FFI_TYPE_STRUCT);
701
702 /* Tell ffi_closure_UNIX64 how to perform return type promotions. */
703
704 return cif->rtype->type;
705}
706#endif /* ifndef __x86_64__ */
Note: See TracBrowser for help on using the repository browser.