2002-02-17 Radek Doulik <rodo@ximian.com>
[mono.git] / mono / arch / ppc / tramp.c
1 /*
2  * Create trampolines to invoke arbitrary functions.
3  * 
4  * Copyright (C) Radek Doulik
5  * 
6  */
7
8 #include "config.h"
9 #include <stdlib.h>
10 #include "ppc-codegen.h"
11 #include "mono/metadata/class.h"
12 #include "mono/metadata/tabledefs.h"
13 #include "mono/interpreter/interp.h"
14
15 #ifdef NEED_MPROTECT
16 #include <sys/mman.h>
17 #include <limits.h>    /* for PAGESIZE */
18 #ifndef PAGESIZE
19 #define PAGESIZE 4096
20 #endif
21 #endif
22
23 /* void
24 fake_func (gdouble (*callme)(), stackval *retval, void *this_obj, stackval *arguments)
25 {
26         guint32 i = 0xc002becd;
27
28         callme = (gpointer) 0x100fabcd;
29
30         *(gpointer*)retval = (gpointer)(*callme) (arguments [0].data.p, arguments [1].data.p, arguments [2].data.p);
31         *(gdouble*) retval = (gdouble)(*callme) (arguments [0].data.f);
32 } */
33
34 #define MIN_CACHE_LINE 8
35
36 static void inline
37 flush_icache (guint8 *code, guint size)
38 {
39         guint i;
40         guint8 *p;
41
42         p = code;
43         for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) {
44                 asm ("dcbst 0,%0;" : : "r"(p) : "memory");
45         }
46         asm ("sync");
47         p = code;
48         for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) {
49                 asm ("icbi 0,%0; sync;" : : "r"(p) : "memory");
50         }
51         asm ("sync");
52         asm ("isync");
53 }
54
55 #define NOT_IMPLEMENTED(x) \
56                 g_error ("FIXME: %s is not yet implemented. (trampoline)", x);
57
58 #define PROLOG_INS 8
59 #define CALL_INS   2
60 #define EPILOG_INS 6
61 #define MINIMAL_STACK_SIZE 5
62 #define FLOAT_REGS 8
63 #define GENERAL_REGS 8
64
65 static void inline
66 add_general (guint *gr, guint *stack_size, guint *code_size, gboolean simple)
67 {
68         if (simple) {
69                 if (*gr >= GENERAL_REGS) {
70                         *stack_size += 4;
71                         *code_size += 8;    /* load from stack, save on stack */
72                 } else {
73                         *code_size += 4;    /* load from stack */
74                 }
75         } else {
76                 if (*gr >= GENERAL_REGS - 1) {
77                         *stack_size += 8 + (*stack_size % 8);
78                         *code_size += 16;   /* 2x load from stack, save to stack */
79                 } else {
80                         *code_size += 16;   /* 2x load from stack */
81                 }
82                 (*gr) ++;
83         }
84         (*gr) ++;
85 }
86
87 static void inline
88 calculate_sizes (MonoMethod *method, guint *stack_size, guint *code_size, guint *strings, gint runtime)
89 {
90         MonoMethodSignature *sig;
91         guint i, fr, gr;
92         guint32 simpletype;
93
94         fr = gr = 0;
95         *stack_size = MINIMAL_STACK_SIZE*4;
96         *code_size  = (PROLOG_INS + CALL_INS + EPILOG_INS)*4;
97         *strings = 0;
98
99         sig = method->signature;
100         if (sig->hasthis) {
101                 add_general (&gr, stack_size, code_size, TRUE);
102         }
103
104         for (i = 0; i < sig->param_count; ++i) {
105                 if (sig->params [i]->byref) {
106                         add_general (&gr, stack_size, code_size, TRUE);
107                         continue;
108                 }
109                 simpletype = sig->params [i]->type;
110         enum_calc_size:
111                 switch (simpletype) {
112                 case MONO_TYPE_BOOLEAN:
113                 case MONO_TYPE_CHAR:
114                 case MONO_TYPE_I1:
115                 case MONO_TYPE_U1:
116                 case MONO_TYPE_I2:
117                 case MONO_TYPE_U2:
118                 case MONO_TYPE_I4:
119                 case MONO_TYPE_U4:
120                 case MONO_TYPE_I:
121                 case MONO_TYPE_U:
122                 case MONO_TYPE_PTR:
123                 case MONO_TYPE_SZARRAY:
124                 case MONO_TYPE_CLASS:
125                 case MONO_TYPE_OBJECT:
126                         add_general (&gr, stack_size, code_size, TRUE);
127                         break;
128                 case MONO_TYPE_VALUETYPE:
129                         if (sig->params [i]->data.klass->enumtype) {
130                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
131                                 goto enum_calc_size;
132                         }
133                         if (mono_class_value_size (sig->params [i]->data.klass, NULL) != 4)
134                                 g_error ("can only marshal enums, not generic structures (size: %d)",
135                                          mono_class_value_size (sig->params [i]->data.klass, NULL));
136                         add_general (&gr, stack_size, code_size, TRUE);
137                         *code_size += 4;
138                         break;
139                 case MONO_TYPE_STRING:
140                         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || runtime) {
141                                 add_general (&gr, stack_size, code_size, TRUE);
142                                 break;
143                         }
144                         (*strings) ++;
145                         *code_size += 12*4;
146                         *stack_size += 4;
147                         break;
148                 case MONO_TYPE_I8:
149                         add_general (&gr, stack_size, code_size, FALSE);
150                         break;
151                 case MONO_TYPE_R4:
152                 case MONO_TYPE_R8:
153                         if (fr < 7) {
154                                 *code_size += 4;
155                                 fr ++;
156                         } else {
157                                 NOT_IMPLEMENTED ("R8 arg");
158                         }
159                         break;
160                 default:
161                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
162                 }
163         }
164
165         if (sig->ret->byref) {
166                 *code_size += 8;
167         } else {
168                 simpletype = sig->ret->type;
169 enum_retvalue:
170                 switch (simpletype) {
171                 case MONO_TYPE_BOOLEAN:
172                 case MONO_TYPE_I1:
173                 case MONO_TYPE_U1:
174                 case MONO_TYPE_I2:
175                 case MONO_TYPE_U2:
176                 case MONO_TYPE_CHAR:
177                 case MONO_TYPE_I4:
178                 case MONO_TYPE_U4:
179                 case MONO_TYPE_I:
180                 case MONO_TYPE_U:
181                 case MONO_TYPE_CLASS:
182                 case MONO_TYPE_OBJECT:
183                 case MONO_TYPE_R4:
184                 case MONO_TYPE_R8:
185                 case MONO_TYPE_SZARRAY:
186                 case MONO_TYPE_ARRAY:
187                 case MONO_TYPE_STRING:
188                         *code_size += 8;
189                         break;
190                 case MONO_TYPE_I8:
191                         *code_size += 12;
192                         break;
193                 case MONO_TYPE_VALUETYPE:
194                         if (sig->ret->data.klass->enumtype) {
195                                 simpletype = sig->ret->data.klass->enum_basetype->type;
196                                 goto enum_retvalue;
197                         }
198                         NOT_IMPLEMENTED ("valuetype");
199                         break;
200                 case MONO_TYPE_VOID:
201                         break;
202                 default:
203                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
204                 }
205         }
206
207         if (*strings) {
208                 /* space to keep parameters and prepared strings */
209                  *stack_size += 8;
210                  *code_size += 16;
211                  if (sig->hasthis) {
212                          *stack_size += 4;
213                          *code_size  += 12;
214                  }
215         }
216         /* align stack size to 16 */
217         printf ("      stack size: %d (%d)\n       code size: %d\n", (*stack_size + 15) & ~15, *stack_size, *code_size);
218         *stack_size = (*stack_size + 15) & ~15;
219
220 }
221
222 static inline guint8 *
223 emit_prolog (guint8 *p, MonoMethod *method, guint stack_size, guint strings)
224 {
225         /* function prolog */
226         ppc_stwu (p, ppc_r1, -stack_size, ppc_r1);     /* sp      <--- sp - stack_size, sp[0] <---- sp save sp, alloc stack */
227         ppc_mflr (p, ppc_r0);                          /* r0      <--- LR */
228         ppc_stw  (p, ppc_r31, stack_size - 4, ppc_r1); /* sp[+4]  <--- r31     save r31 */
229         ppc_stw  (p, ppc_r0, stack_size + 4, ppc_r1);  /* sp[-4]  <--- LR      save return address for "callme" */
230         ppc_mr   (p, ppc_r31, ppc_r1);                 /* r31     <--- sp */
231
232         /* handle our parameters */
233         if (strings) {
234                 ppc_stw  (p, ppc_r30, stack_size - 16, ppc_r1);
235                 ppc_stw  (p, ppc_r29, stack_size - 12, ppc_r1);
236                 if (method->signature->hasthis) {
237                         ppc_stw  (p, ppc_r28, 24, ppc_r1);
238                 }
239                 ppc_mr   (p, ppc_r30, ppc_r6);                        /* args */
240                 ppc_mr   (p, ppc_r29, ppc_r3);                        /* callme */
241                 if (method->signature->hasthis) {
242                         ppc_mr   (p, ppc_r28, ppc_r5);                /* this */
243                 }
244         } else {
245                 ppc_mr   (p, ppc_r12, ppc_r6);                        /* keep "arguments" in register */
246                 ppc_mr   (p, ppc_r0, ppc_r3);                         /* keep "callme" in register */
247         }
248         ppc_stw  (p, ppc_r4, 8, ppc_r31);                             /* preserve "retval", sp[+8] */
249
250         return p;
251 }
252
253 #define ARG_BASE strings ? ppc_r30 : ppc_r12
254 #define SAVE_4_IN_GENERIC_REGISTER \
255                         if (gr < GENERAL_REGS) { \
256                                 ppc_lwz  (p, ppc_r3 + gr, i*16, ARG_BASE); \
257                                 gr ++; \
258                         } else { \
259                                 NOT_IMPLEMENTED("save on stack"); \
260                         }
261 #define SAVE_2_IN_GENERIC_REGISTER \
262                         if (gr < GENERAL_REGS) { \
263                                 ppc_lhz  (p, ppc_r3 + gr, i*16, ARG_BASE); \
264                                 gr ++; \
265                         } else { \
266                                 NOT_IMPLEMENTED("save on stack"); \
267                         }
268 #define SAVE_1_IN_GENERIC_REGISTER \
269                         if (gr < GENERAL_REGS) { \
270                                 ppc_lbz  (p, ppc_r3 + gr, i*16, ARG_BASE); \
271                                 gr ++; \
272                         } else { \
273                                 NOT_IMPLEMENTED("save on stack"); \
274                         }
275
276 inline static guint8*
277 emit_save_parameters (guint8 *p, MonoMethod *method, guint stack_size, guint strings, gint runtime)
278 {
279         MonoMethodSignature *sig;
280         guint i, fr, gr, act_strs;
281         guint32 simpletype;
282
283         fr = gr = 0;
284         act_strs = 0;
285         sig = method->signature;
286
287         if (strings) {
288                 for (i = 0; i < sig->param_count; ++i) {
289                         if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_STRING
290                             && !((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || runtime)) {
291                                 ppc_lis  (p, ppc_r0,     (guint32) mono_string_to_utf8 >> 16);
292                                 ppc_lwz  (p, ppc_r3, i*16, ppc_r30);
293                                 ppc_ori  (p, ppc_r0, ppc_r0, (guint32) mono_string_to_utf8 & 0xffff);
294                                 ppc_mtlr (p, ppc_r0);
295                                 ppc_blrl (p);
296                                 ppc_stw  (p, ppc_r3, stack_size - 20 - act_strs, ppc_r31);
297                                 act_strs += 4;
298                         }
299                 }
300         }
301
302         if (sig->hasthis) {
303                 ppc_mr (p, ppc_r3, ppc_r5);
304                 gr ++;
305         }
306
307         act_strs = 0;
308         for (i = 0; i < sig->param_count; ++i) {
309                 if (sig->params [i]->byref) {
310                         SAVE_4_IN_GENERIC_REGISTER;
311                         continue;
312                 }
313                 simpletype = sig->params [i]->type;
314         enum_calc_size:
315                 switch (simpletype) {
316                 case MONO_TYPE_BOOLEAN:
317                 case MONO_TYPE_I1:
318                 case MONO_TYPE_U1:
319                         SAVE_1_IN_GENERIC_REGISTER;
320                         break;
321                 case MONO_TYPE_I2:
322                 case MONO_TYPE_U2:
323                 case MONO_TYPE_CHAR:
324                         SAVE_2_IN_GENERIC_REGISTER;
325                         break;
326                 case MONO_TYPE_I4:
327                 case MONO_TYPE_U4:
328                 case MONO_TYPE_I:
329                 case MONO_TYPE_U:
330                 case MONO_TYPE_PTR:
331                 case MONO_TYPE_SZARRAY:
332                 case MONO_TYPE_CLASS:
333                 case MONO_TYPE_OBJECT:
334                         SAVE_4_IN_GENERIC_REGISTER;
335                         break;
336                 case MONO_TYPE_VALUETYPE:
337                         if (sig->params [i]->data.klass->enumtype) {
338                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
339                                 goto enum_calc_size;
340                         }
341                         if (mono_class_value_size (sig->params [i]->data.klass, NULL) != 4)
342                                 g_error ("can only marshal enums, not generic structures (size: %d)",
343                                          mono_class_value_size (sig->params [i]->data.klass, NULL));
344                         if (gr < GENERAL_REGS) {
345                                 ppc_lwz  (p, ppc_r3 + gr, i*16, ARG_BASE);
346                                 ppc_lwz  (p, ppc_r3 + gr, 0, ppc_r3 + gr);
347                                 gr ++;
348                         } else {
349                                 NOT_IMPLEMENTED ("save value type on stack");
350                         }
351                         break;
352                 case MONO_TYPE_STRING:
353                         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || runtime) {
354                                 SAVE_4_IN_GENERIC_REGISTER;
355                         } else {
356                                 if (gr < 8) {
357                                         ppc_lwz (p, ppc_r3 + gr, stack_size - 20 - act_strs, ppc_r31);
358                                         gr ++;
359                                         act_strs += 4;
360                                 } else
361                                         NOT_IMPLEMENTED ("string on stack");
362                         }
363                         break;
364                 case MONO_TYPE_I8:
365                         if (gr < 7) {
366                                 g_warning ("check endianess");
367                                 ppc_lwz  (p, ppc_r3 + gr, i*16, ARG_BASE);
368                                 gr ++;
369                                 ppc_lwz  (p, ppc_r3 + gr, i*17, ARG_BASE);
370                                 gr ++;
371                         } else {
372                                 NOT_IMPLEMENTED ("i8 on stack");
373                         }
374                         break;
375                 case MONO_TYPE_R4:
376                         if (fr < 7) {
377                                 ppc_lfs  (p, ppc_f1 + fr, i*16, ARG_BASE);
378                                 fr ++;
379                         } else {
380                                 NOT_IMPLEMENTED ("r4 on stack");
381                         }
382                         break;
383                 case MONO_TYPE_R8:
384                         if (fr < 7) {
385                                 ppc_lfd  (p, ppc_f1 + fr, i*16, ARG_BASE);
386                                 fr ++;
387                         } else {
388                                 NOT_IMPLEMENTED ("r8 on stack");
389                         }
390                         break;
391                 default:
392                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
393                 }
394         }
395
396         return p;
397 }
398
399 static inline guint8 *
400 alloc_code_memory (guint code_size)
401 {
402         guint8 *p;
403
404 #ifdef NEED_MPROTECT
405         p = g_malloc (code_size + PAGESIZE - 1);
406
407         /* Align to a multiple of PAGESIZE, assumed to be a power of two */
408         p = (char *)(((int) p + PAGESIZE-1) & ~(PAGESIZE-1));
409 #else
410         p = g_malloc (code_size);
411 #endif
412         printf ("           align: %p (%d)\n", p, (guint)p % 4);
413
414         return p;
415 }
416
417 static inline guint8 *
418 emit_call_and_store_retval (guint8 *p, MonoMethod *method, guint strings)
419 {
420         MonoMethodSignature *sig = method->signature;
421         guint32 simpletype;
422
423         /* call "callme" */
424         ppc_mtlr (p, strings ? ppc_r29 : ppc_r0);
425         ppc_blrl (p);
426
427         /* get return value */
428         if (sig->ret->byref) {
429                 ppc_lwz  (p, ppc_r9, 8, ppc_r31);        /* load "retval" address */
430                 ppc_stw  (p, ppc_r3, 0, ppc_r9);         /* save return value (r3) to "retval" */
431         } else {
432                 simpletype = sig->ret->type;
433 enum_retvalue:
434                 switch (simpletype) {
435                 case MONO_TYPE_BOOLEAN:
436                 case MONO_TYPE_I1:
437                 case MONO_TYPE_U1:
438                         ppc_lwz  (p, ppc_r9, 8, ppc_r31);        /* load "retval" address */
439                         ppc_stb  (p, ppc_r3, 0, ppc_r9);         /* save return value (r3) to "retval" */
440                         break;
441                 case MONO_TYPE_I2:
442                 case MONO_TYPE_U2:
443                 case MONO_TYPE_CHAR:
444                         ppc_lwz  (p, ppc_r9, 8, ppc_r31);        /* load "retval" address */
445                         ppc_sth  (p, ppc_r3, 0, ppc_r9);         /* save return value (r3) to "retval" */
446                         break;
447                 case MONO_TYPE_I4:
448                 case MONO_TYPE_U4:
449                 case MONO_TYPE_I:
450                 case MONO_TYPE_U:
451                 case MONO_TYPE_CLASS:
452                 case MONO_TYPE_OBJECT:
453                 case MONO_TYPE_SZARRAY:
454                 case MONO_TYPE_ARRAY:
455                 case MONO_TYPE_STRING:
456                         ppc_lwz  (p, ppc_r9, 8, ppc_r31);        /* load "retval" address */
457                         ppc_stw  (p, ppc_r3, 0, ppc_r9);         /* save return value (r3) to "retval" */
458                         break;
459                 case MONO_TYPE_R4:
460                         ppc_lwz  (p, ppc_r9, 8, ppc_r31);        /* load "retval" address */
461                         ppc_stfs (p, ppc_f1, 0, ppc_r9);         /* save return value (f1) to "retval" */
462                         break;
463                 case MONO_TYPE_R8:
464                         ppc_lwz  (p, ppc_r9, 8, ppc_r31);        /* load "retval" address */
465                         ppc_stfd (p, ppc_f1, 0, ppc_r9);         /* save return value (f1) to "retval" */
466                         break;
467                 case MONO_TYPE_I8:
468                         g_warning ("check endianess");
469                         ppc_lwz  (p, ppc_r9, 8, ppc_r31);        /* load "retval" address */
470                         ppc_stw  (p, ppc_r3, 0, ppc_r9);         /* save return value (r3) to "retval" */
471                         ppc_stw  (p, ppc_r4, 4, ppc_r9);         /* save return value (r3) to "retval" */
472                         break;
473                 case MONO_TYPE_VALUETYPE:
474                         if (sig->ret->data.klass->enumtype) {
475                                 simpletype = sig->ret->data.klass->enum_basetype->type;
476                                 goto enum_retvalue;
477                         }
478                         NOT_IMPLEMENTED ("retval valuetype");
479                         break;
480                 case MONO_TYPE_VOID:
481                         break;
482                 default:
483                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
484                 }
485         }
486
487         return p;
488 }
489
490 static inline guint8 *
491 emit_epilog (guint8 *p, MonoMethod *method, guint stack_size, guint strings, gboolean runtime)
492 {
493         if (strings) {
494                 MonoMethodSignature *sig = method->signature;
495                 guint i, act_strs;
496
497                 /* free allocated memory */
498                 act_strs = 0;
499                 for (i = 0; i < sig->param_count; ++i) {
500                         if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_STRING
501                             && !((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || runtime)) {
502                                 ppc_lis  (p, ppc_r0,     (guint32) g_free >> 16);
503                                 ppc_lwz  (p, ppc_r3, stack_size - 20 - act_strs, ppc_r31);
504                                 ppc_ori  (p, ppc_r0, ppc_r0, (guint32) g_free & 0xffff);
505                                 ppc_mtlr (p, ppc_r0);
506                                 ppc_blrl (p);
507                                 act_strs += 4;
508                         }
509                 }
510
511                 /* restore volatile registers */
512                 ppc_lwz  (p, ppc_r30, stack_size - 16, ppc_r1);
513                 ppc_lwz  (p, ppc_r29, stack_size - 12, ppc_r1);
514                 if (method->signature->hasthis) {
515                         ppc_lwz  (p, ppc_r28, 24, ppc_r1);
516                 }
517         }
518
519         /* function epilog */
520         ppc_lwz  (p, ppc_r11, 0,  ppc_r1);        /* r11     <--- sp[0]   load backchain from caller's function */
521         ppc_lwz  (p, ppc_r0, 4, ppc_r11);         /* r0      <--- r11[4]  load return address */
522         ppc_mtlr (p, ppc_r0);                     /* LR      <--- r0      set return address */
523         ppc_lwz  (p, ppc_r31, -4, ppc_r11);       /* r31     <--- r11[-4] restore r31 */
524         ppc_mr   (p, ppc_r1, ppc_r11);            /* sp      <--- r11     restore stack */
525         ppc_blr  (p);                             /* return */
526
527         return p;
528 }
529
530 MonoPIFunc
531 mono_create_trampoline (MonoMethod *method, int runtime)
532 {
533         guint8 *p, *code_buffer;
534         guint stack_size, code_size, strings;
535
536         printf ("\nPInvoke [start emiting] %s\n", method->name);
537         calculate_sizes (method, &stack_size, &code_size, &strings, runtime);
538
539         p = code_buffer = alloc_code_memory (code_size);
540         p = emit_prolog (p, method, stack_size, strings);
541         p = emit_save_parameters (p, method, stack_size, strings, runtime);
542         p = emit_call_and_store_retval (p, method, strings);
543         p = emit_epilog (p, method, stack_size, strings, runtime);
544
545         /* {
546                 guchar *cp;
547                 printf (".text\n.align 4\n.globl main\n.type main,@function\nmain:\n");
548                 for (cp = code_buffer; cp < p; cp++) {
549                         printf (".byte 0x%x\n", *cp);
550                 }
551                 } */
552
553 #ifdef NEED_MPROTECT
554         if (mprotect (code_buffer, 1024, PROT_READ | PROT_WRITE | PROT_EXEC)) {
555                 g_error ("Cannot mprotect trampoline\n");
556         }
557 #endif
558
559         printf ("emited code size: %d\n", p - code_buffer);
560         flush_icache (code_buffer, p - code_buffer);
561
562         printf ("PInvoke [end emiting]\n");
563
564         return (MonoPIFunc) code_buffer;
565         /* return fake_func; */
566 }
567
568
569 #define MINV_POS  (- sizeof (MonoInvocation))
570 #define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count)
571 #define OBJ_POS   8
572 #define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type))
573
574 /*
575  * Returns a pointer to a native function that can be used to
576  * call the specified method.
577  * The function created will receive the arguments according
578  * to the call convention specified in the method.
579  * This function works by creating a MonoInvocation structure,
580  * filling the fields in and calling ves_exec_method on it.
581  * Still need to figure out how to handle the exception stuff
582  * across the managed/unmanaged boundary.
583  */
584 void *
585 mono_create_method_pointer (MonoMethod *method)
586 {
587         return NULL;
588 }
589
590
591 /*
592  * mono_create_method_pointer () will insert a pointer to the MonoMethod
593  * so that the interp can easily get at the data: this function will retrieve 
594  * the method from the code stream.
595  */
596 MonoMethod*
597 mono_method_pointer_get (void *code)
598 {
599         return NULL;
600 }
601