1 /* -----------------------------------------------------------------------
2 ffi.c - Copyright (c) 2011, 2013 Anthony Green
3 Copyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc.
4
5 SPARC Foreign Function Interface
6
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 ``Software''), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 ----------------------------------------------------------------------- */
27
28 #include <ffi.h>
29 #include <ffi_common.h>
30
31 #include <stdlib.h>
32
33
34 /* ffi_prep_args is called by the assembly routine once stack space
35 has been allocated for the function's arguments */
36
ffi_prep_args_v8(char * stack,extended_cif * ecif)37 void ffi_prep_args_v8(char *stack, extended_cif *ecif)
38 {
39 int i;
40 void **p_argv;
41 char *argp;
42 ffi_type **p_arg;
43
44 /* Skip 16 words for the window save area */
45 argp = stack + 16*sizeof(int);
46
47 /* This should only really be done when we are returning a structure,
48 however, it's faster just to do it all the time...
49
50 if ( ecif->cif->rtype->type == FFI_TYPE_STRUCT ) */
51 *(int *) argp = (long)ecif->rvalue;
52
53 /* And 1 word for the structure return value. */
54 argp += sizeof(int);
55
56 #ifdef USING_PURIFY
57 /* Purify will probably complain in our assembly routine, unless we
58 zero out this memory. */
59
60 ((int*)argp)[0] = 0;
61 ((int*)argp)[1] = 0;
62 ((int*)argp)[2] = 0;
63 ((int*)argp)[3] = 0;
64 ((int*)argp)[4] = 0;
65 ((int*)argp)[5] = 0;
66 #endif
67
68 p_argv = ecif->avalue;
69
70 for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; i; i--, p_arg++)
71 {
72 size_t z;
73
74 if ((*p_arg)->type == FFI_TYPE_STRUCT
75 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
76 || (*p_arg)->type == FFI_TYPE_LONGDOUBLE
77 #endif
78 )
79 {
80 *(unsigned int *) argp = (unsigned long)(* p_argv);
81 z = sizeof(int);
82 }
83 else
84 {
85 z = (*p_arg)->size;
86 if (z < sizeof(int))
87 {
88 z = sizeof(int);
89 switch ((*p_arg)->type)
90 {
91 case FFI_TYPE_SINT8:
92 *(signed int *) argp = *(SINT8 *)(* p_argv);
93 break;
94
95 case FFI_TYPE_UINT8:
96 *(unsigned int *) argp = *(UINT8 *)(* p_argv);
97 break;
98
99 case FFI_TYPE_SINT16:
100 *(signed int *) argp = *(SINT16 *)(* p_argv);
101 break;
102
103 case FFI_TYPE_UINT16:
104 *(unsigned int *) argp = *(UINT16 *)(* p_argv);
105 break;
106
107 default:
108 FFI_ASSERT(0);
109 }
110 }
111 else
112 {
113 memcpy(argp, *p_argv, z);
114 }
115 }
116 p_argv++;
117 argp += z;
118 }
119
120 return;
121 }
122
ffi_prep_args_v9(char * stack,extended_cif * ecif)123 int ffi_prep_args_v9(char *stack, extended_cif *ecif)
124 {
125 int i, ret = 0;
126 int tmp;
127 void **p_argv;
128 char *argp;
129 ffi_type **p_arg;
130
131 tmp = 0;
132
133 /* Skip 16 words for the window save area */
134 argp = stack + 16*sizeof(long long);
135
136 #ifdef USING_PURIFY
137 /* Purify will probably complain in our assembly routine, unless we
138 zero out this memory. */
139
140 ((long long*)argp)[0] = 0;
141 ((long long*)argp)[1] = 0;
142 ((long long*)argp)[2] = 0;
143 ((long long*)argp)[3] = 0;
144 ((long long*)argp)[4] = 0;
145 ((long long*)argp)[5] = 0;
146 #endif
147
148 p_argv = ecif->avalue;
149
150 if (ecif->cif->rtype->type == FFI_TYPE_STRUCT &&
151 ecif->cif->rtype->size > 32)
152 {
153 *(unsigned long long *) argp = (unsigned long)ecif->rvalue;
154 argp += sizeof(long long);
155 tmp = 1;
156 }
157
158 for (i = 0, p_arg = ecif->cif->arg_types; i < ecif->cif->nargs;
159 i++, p_arg++)
160 {
161 size_t z;
162
163 z = (*p_arg)->size;
164 switch ((*p_arg)->type)
165 {
166 case FFI_TYPE_STRUCT:
167 if (z > 16)
168 {
169 /* For structures larger than 16 bytes we pass reference. */
170 *(unsigned long long *) argp = (unsigned long)* p_argv;
171 argp += sizeof(long long);
172 tmp++;
173 p_argv++;
174 continue;
175 }
176 /* FALLTHROUGH */
177 case FFI_TYPE_FLOAT:
178 case FFI_TYPE_DOUBLE:
179 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
180 case FFI_TYPE_LONGDOUBLE:
181 #endif
182 ret = 1; /* We should promote into FP regs as well as integer. */
183 break;
184 }
185 if (z < sizeof(long long))
186 {
187 switch ((*p_arg)->type)
188 {
189 case FFI_TYPE_SINT8:
190 *(signed long long *) argp = *(SINT8 *)(* p_argv);
191 break;
192
193 case FFI_TYPE_UINT8:
194 *(unsigned long long *) argp = *(UINT8 *)(* p_argv);
195 break;
196
197 case FFI_TYPE_SINT16:
198 *(signed long long *) argp = *(SINT16 *)(* p_argv);
199 break;
200
201 case FFI_TYPE_UINT16:
202 *(unsigned long long *) argp = *(UINT16 *)(* p_argv);
203 break;
204
205 case FFI_TYPE_SINT32:
206 *(signed long long *) argp = *(SINT32 *)(* p_argv);
207 break;
208
209 case FFI_TYPE_UINT32:
210 *(unsigned long long *) argp = *(UINT32 *)(* p_argv);
211 break;
212
213 case FFI_TYPE_FLOAT:
214 *(float *) (argp + 4) = *(FLOAT32 *)(* p_argv); /* Right justify */
215 break;
216
217 case FFI_TYPE_STRUCT:
218 memcpy(argp, *p_argv, z);
219 break;
220
221 default:
222 FFI_ASSERT(0);
223 }
224 z = sizeof(long long);
225 tmp++;
226 }
227 else if (z == sizeof(long long))
228 {
229 memcpy(argp, *p_argv, z);
230 z = sizeof(long long);
231 tmp++;
232 }
233 else
234 {
235 if ((tmp & 1) && (*p_arg)->alignment > 8)
236 {
237 tmp++;
238 argp += sizeof(long long);
239 }
240 memcpy(argp, *p_argv, z);
241 z = 2 * sizeof(long long);
242 tmp += 2;
243 }
244 p_argv++;
245 argp += z;
246 }
247
248 return ret;
249 }
250
251 /* Perform machine dependent cif processing */
ffi_prep_cif_machdep(ffi_cif * cif)252 ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
253 {
254 int wordsize;
255
256 if (cif->abi != FFI_V9)
257 {
258 wordsize = 4;
259
260 /* If we are returning a struct, this will already have been added.
261 Otherwise we need to add it because it's always got to be there! */
262
263 if (cif->rtype->type != FFI_TYPE_STRUCT)
264 cif->bytes += wordsize;
265
266 /* sparc call frames require that space is allocated for 6 args,
267 even if they aren't used. Make that space if necessary. */
268
269 if (cif->bytes < 4*6+4)
270 cif->bytes = 4*6+4;
271 }
272 else
273 {
274 wordsize = 8;
275
276 /* sparc call frames require that space is allocated for 6 args,
277 even if they aren't used. Make that space if necessary. */
278
279 if (cif->bytes < 8*6)
280 cif->bytes = 8*6;
281 }
282
283 /* Adjust cif->bytes. to include 16 words for the window save area,
284 and maybe the struct/union return pointer area, */
285
286 cif->bytes += 16 * wordsize;
287
288 /* The stack must be 2 word aligned, so round bytes up
289 appropriately. */
290
291 cif->bytes = ALIGN(cif->bytes, 2 * wordsize);
292
293 /* Set the return type flag */
294 switch (cif->rtype->type)
295 {
296 case FFI_TYPE_VOID:
297 case FFI_TYPE_FLOAT:
298 case FFI_TYPE_DOUBLE:
299 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
300 case FFI_TYPE_LONGDOUBLE:
301 #endif
302 cif->flags = cif->rtype->type;
303 break;
304
305 case FFI_TYPE_STRUCT:
306 if (cif->abi == FFI_V9 && cif->rtype->size > 32)
307 cif->flags = FFI_TYPE_VOID;
308 else
309 cif->flags = FFI_TYPE_STRUCT;
310 break;
311
312 case FFI_TYPE_SINT8:
313 case FFI_TYPE_UINT8:
314 case FFI_TYPE_SINT16:
315 case FFI_TYPE_UINT16:
316 if (cif->abi == FFI_V9)
317 cif->flags = FFI_TYPE_INT;
318 else
319 cif->flags = cif->rtype->type;
320 break;
321
322 case FFI_TYPE_SINT64:
323 case FFI_TYPE_UINT64:
324 if (cif->abi == FFI_V9)
325 cif->flags = FFI_TYPE_INT;
326 else
327 cif->flags = FFI_TYPE_SINT64;
328 break;
329
330 default:
331 cif->flags = FFI_TYPE_INT;
332 break;
333 }
334 return FFI_OK;
335 }
336
ffi_v9_layout_struct(ffi_type * arg,int off,char * ret,char * intg,char * flt)337 int ffi_v9_layout_struct(ffi_type *arg, int off, char *ret, char *intg, char *flt)
338 {
339 ffi_type **ptr = &arg->elements[0];
340
341 while (*ptr != NULL)
342 {
343 if (off & ((*ptr)->alignment - 1))
344 off = ALIGN(off, (*ptr)->alignment);
345
346 switch ((*ptr)->type)
347 {
348 case FFI_TYPE_STRUCT:
349 off = ffi_v9_layout_struct(*ptr, off, ret, intg, flt);
350 off = ALIGN(off, FFI_SIZEOF_ARG);
351 break;
352 case FFI_TYPE_FLOAT:
353 case FFI_TYPE_DOUBLE:
354 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
355 case FFI_TYPE_LONGDOUBLE:
356 #endif
357 memmove(ret + off, flt + off, (*ptr)->size);
358 off += (*ptr)->size;
359 break;
360 default:
361 memmove(ret + off, intg + off, (*ptr)->size);
362 off += (*ptr)->size;
363 break;
364 }
365 ptr++;
366 }
367 return off;
368 }
369
370
371 #ifdef SPARC64
372 extern int ffi_call_v9(void *, extended_cif *, unsigned,
373 unsigned, unsigned *, void (*fn)(void));
374 #else
375 extern int ffi_call_v8(void *, extended_cif *, unsigned,
376 unsigned, unsigned *, void (*fn)(void));
377 #endif
378
379 #ifndef __GNUC__
380 void ffi_flush_icache (void *, size_t);
381 #endif
382
ffi_call(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue)383 void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
384 {
385 extended_cif ecif;
386 void *rval = rvalue;
387
388 ecif.cif = cif;
389 ecif.avalue = avalue;
390
391 /* If the return value is a struct and we don't have a return */
392 /* value address then we need to make one */
393
394 ecif.rvalue = rvalue;
395 if (cif->rtype->type == FFI_TYPE_STRUCT)
396 {
397 if (cif->rtype->size <= 32)
398 rval = alloca(64);
399 else
400 {
401 rval = NULL;
402 if (rvalue == NULL)
403 ecif.rvalue = alloca(cif->rtype->size);
404 }
405 }
406
407 switch (cif->abi)
408 {
409 case FFI_V8:
410 #ifdef SPARC64
411 /* We don't yet support calling 32bit code from 64bit */
412 FFI_ASSERT(0);
413 #else
414 if (rvalue && (cif->rtype->type == FFI_TYPE_STRUCT
415 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
416 || cif->flags == FFI_TYPE_LONGDOUBLE
417 #endif
418 ))
419 {
420 /* For v8, we need an "unimp" with size of returning struct */
421 /* behind "call", so we alloc some executable space for it. */
422 /* l7 is used, we need to make sure v8.S doesn't use %l7. */
423 unsigned int *call_struct = NULL;
424 ffi_closure_alloc(32, (void **)&call_struct);
425 if (call_struct)
426 {
427 unsigned long f = (unsigned long)fn;
428 call_struct[0] = 0xae10001f; /* mov %i7, %l7 */
429 call_struct[1] = 0xbe10000f; /* mov %o7, %i7 */
430 call_struct[2] = 0x03000000 | f >> 10; /* sethi %hi(fn), %g1 */
431 call_struct[3] = 0x9fc06000 | (f & 0x3ff); /* jmp %g1+%lo(fn), %o7 */
432 call_struct[4] = 0x01000000; /* nop */
433 if (cif->rtype->size < 0x7f)
434 call_struct[5] = cif->rtype->size; /* unimp */
435 else
436 call_struct[5] = 0x01000000; /* nop */
437 call_struct[6] = 0x81c7e008; /* ret */
438 call_struct[7] = 0xbe100017; /* mov %l7, %i7 */
439 #ifdef __GNUC__
440 asm volatile ("iflush %0; iflush %0+8; iflush %0+16; iflush %0+24" : :
441 "r" (call_struct) : "memory");
442 /* SPARC v8 requires 5 instructions for flush to be visible */
443 asm volatile ("nop; nop; nop; nop; nop");
444 #else
445 ffi_flush_icache (call_struct, 32);
446 #endif
447 ffi_call_v8(ffi_prep_args_v8, &ecif, cif->bytes,
448 cif->flags, rvalue, call_struct);
449 ffi_closure_free(call_struct);
450 }
451 else
452 {
453 ffi_call_v8(ffi_prep_args_v8, &ecif, cif->bytes,
454 cif->flags, rvalue, fn);
455 }
456 }
457 else
458 {
459 ffi_call_v8(ffi_prep_args_v8, &ecif, cif->bytes,
460 cif->flags, rvalue, fn);
461 }
462 #endif
463 break;
464 case FFI_V9:
465 #ifdef SPARC64
466 ffi_call_v9(ffi_prep_args_v9, &ecif, cif->bytes,
467 cif->flags, rval, fn);
468 if (rvalue && rval && cif->rtype->type == FFI_TYPE_STRUCT)
469 ffi_v9_layout_struct(cif->rtype, 0, (char *)rvalue, (char *)rval, ((char *)rval)+32);
470 #else
471 /* And vice versa */
472 FFI_ASSERT(0);
473 #endif
474 break;
475 default:
476 FFI_ASSERT(0);
477 break;
478 }
479 }
480
481
482 #ifdef SPARC64
483 extern void ffi_closure_v9(void);
484 #else
485 extern void ffi_closure_v8(void);
486 #endif
487
488 ffi_status
ffi_prep_closure_loc(ffi_closure * closure,ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *),void * user_data,void * codeloc)489 ffi_prep_closure_loc (ffi_closure* closure,
490 ffi_cif* cif,
491 void (*fun)(ffi_cif*, void*, void**, void*),
492 void *user_data,
493 void *codeloc)
494 {
495 unsigned int *tramp = (unsigned int *) &closure->tramp[0];
496 unsigned long fn;
497 #ifdef SPARC64
498 /* Trampoline address is equal to the closure address. We take advantage
499 of that to reduce the trampoline size by 8 bytes. */
500 if (cif->abi != FFI_V9)
501 return FFI_BAD_ABI;
502 fn = (unsigned long) ffi_closure_v9;
503 tramp[0] = 0x83414000; /* rd %pc, %g1 */
504 tramp[1] = 0xca586010; /* ldx [%g1+16], %g5 */
505 tramp[2] = 0x81c14000; /* jmp %g5 */
506 tramp[3] = 0x01000000; /* nop */
507 *((unsigned long *) &tramp[4]) = fn;
508 #else
509 unsigned long ctx = (unsigned long) codeloc;
510 if (cif->abi != FFI_V8)
511 return FFI_BAD_ABI;
512 fn = (unsigned long) ffi_closure_v8;
513 tramp[0] = 0x03000000 | fn >> 10; /* sethi %hi(fn), %g1 */
514 tramp[1] = 0x05000000 | ctx >> 10; /* sethi %hi(ctx), %g2 */
515 tramp[2] = 0x81c06000 | (fn & 0x3ff); /* jmp %g1+%lo(fn) */
516 tramp[3] = 0x8410a000 | (ctx & 0x3ff);/* or %g2, %lo(ctx) */
517 #endif
518
519 closure->cif = cif;
520 closure->fun = fun;
521 closure->user_data = user_data;
522
523 /* Flush the Icache. closure is 8 bytes aligned. */
524 #ifdef __GNUC__
525 #ifdef SPARC64
526 asm volatile ("flush %0; flush %0+8" : : "r" (closure) : "memory");
527 #else
528 asm volatile ("iflush %0; iflush %0+8" : : "r" (closure) : "memory");
529 /* SPARC v8 requires 5 instructions for flush to be visible */
530 asm volatile ("nop; nop; nop; nop; nop");
531 #endif
532 #else
533 ffi_flush_icache (closure, 16);
534 #endif
535
536 return FFI_OK;
537 }
538
539 int
ffi_closure_sparc_inner_v8(ffi_closure * closure,void * rvalue,unsigned long * gpr,unsigned long * scratch)540 ffi_closure_sparc_inner_v8(ffi_closure *closure,
541 void *rvalue, unsigned long *gpr, unsigned long *scratch)
542 {
543 ffi_cif *cif;
544 ffi_type **arg_types;
545 void **avalue;
546 int i, argn;
547
548 cif = closure->cif;
549 arg_types = cif->arg_types;
550 avalue = alloca(cif->nargs * sizeof(void *));
551
552 /* Copy the caller's structure return address so that the closure
553 returns the data directly to the caller. */
554 if (cif->flags == FFI_TYPE_STRUCT
555 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
556 || cif->flags == FFI_TYPE_LONGDOUBLE
557 #endif
558 )
559 rvalue = (void *) gpr[0];
560
561 /* Always skip the structure return address. */
562 argn = 1;
563
564 /* Grab the addresses of the arguments from the stack frame. */
565 for (i = 0; i < cif->nargs; i++)
566 {
567 if (arg_types[i]->type == FFI_TYPE_STRUCT
568 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
569 || arg_types[i]->type == FFI_TYPE_LONGDOUBLE
570 #endif
571 )
572 {
573 /* Straight copy of invisible reference. */
574 avalue[i] = (void *)gpr[argn++];
575 }
576 else if ((arg_types[i]->type == FFI_TYPE_DOUBLE
577 || arg_types[i]->type == FFI_TYPE_SINT64
578 || arg_types[i]->type == FFI_TYPE_UINT64)
579 /* gpr is 8-byte aligned. */
580 && (argn % 2) != 0)
581 {
582 /* Align on a 8-byte boundary. */
583 scratch[0] = gpr[argn];
584 scratch[1] = gpr[argn+1];
585 avalue[i] = scratch;
586 scratch -= 2;
587 argn += 2;
588 }
589 else
590 {
591 /* Always right-justify. */
592 argn += ALIGN(arg_types[i]->size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
593 avalue[i] = ((char *) &gpr[argn]) - arg_types[i]->size;
594 }
595 }
596
597 /* Invoke the closure. */
598 (closure->fun) (cif, rvalue, avalue, closure->user_data);
599
600 /* Tell ffi_closure_sparc how to perform return type promotions. */
601 return cif->rtype->type;
602 }
603
604 int
ffi_closure_sparc_inner_v9(ffi_closure * closure,void * rvalue,unsigned long * gpr,double * fpr)605 ffi_closure_sparc_inner_v9(ffi_closure *closure,
606 void *rvalue, unsigned long *gpr, double *fpr)
607 {
608 ffi_cif *cif;
609 ffi_type **arg_types;
610 void **avalue;
611 int i, argn, fp_slot_max;
612
613 cif = closure->cif;
614 arg_types = cif->arg_types;
615 avalue = alloca(cif->nargs * sizeof(void *));
616
617 /* Copy the caller's structure return address so that the closure
618 returns the data directly to the caller. */
619 if (cif->flags == FFI_TYPE_VOID
620 && cif->rtype->type == FFI_TYPE_STRUCT)
621 {
622 rvalue = (void *) gpr[0];
623 /* Skip the structure return address. */
624 argn = 1;
625 }
626 else
627 argn = 0;
628
629 fp_slot_max = 16 - argn;
630
631 /* Grab the addresses of the arguments from the stack frame. */
632 for (i = 0; i < cif->nargs; i++)
633 {
634 if (arg_types[i]->type == FFI_TYPE_STRUCT)
635 {
636 if (arg_types[i]->size > 16)
637 {
638 /* Straight copy of invisible reference. */
639 avalue[i] = (void *)gpr[argn++];
640 }
641 else
642 {
643 /* Left-justify. */
644 ffi_v9_layout_struct(arg_types[i],
645 0,
646 (char *) &gpr[argn],
647 (char *) &gpr[argn],
648 (char *) &fpr[argn]);
649 avalue[i] = &gpr[argn];
650 argn += ALIGN(arg_types[i]->size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
651 }
652 }
653 else
654 {
655 /* Right-justify. */
656 argn += ALIGN(arg_types[i]->size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
657
658 /* Align on a 16-byte boundary. */
659 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
660 if (arg_types[i]->type == FFI_TYPE_LONGDOUBLE && (argn % 2) != 0)
661 argn++;
662 #endif
663 if (i < fp_slot_max
664 && (arg_types[i]->type == FFI_TYPE_FLOAT
665 || arg_types[i]->type == FFI_TYPE_DOUBLE
666 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
667 || arg_types[i]->type == FFI_TYPE_LONGDOUBLE
668 #endif
669 ))
670 avalue[i] = ((char *) &fpr[argn]) - arg_types[i]->size;
671 else
672 avalue[i] = ((char *) &gpr[argn]) - arg_types[i]->size;
673 }
674 }
675
676 /* Invoke the closure. */
677 (closure->fun) (cif, rvalue, avalue, closure->user_data);
678
679 /* Tell ffi_closure_sparc how to perform return type promotions. */
680 return cif->rtype->type;
681 }
682