Home | History | Annotate | Line # | Download | only in ppc
gen-icache.c revision 1.1.1.3
      1 /*  This file is part of the program psim.
      2 
      3     Copyright (C) 1994-1997, Andrew Cagney <cagney (at) highland.com.au>
      4 
      5     This program is free software; you can redistribute it and/or modify
      6     it under the terms of the GNU General Public License as published by
      7     the Free Software Foundation; either version 3 of the License, or
      8     (at your option) any later version.
      9 
     10     This program is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13     GNU General Public License for more details.
     14 
     15     You should have received a copy of the GNU General Public License
     16     along with this program; if not, see <http://www.gnu.org/licenses/>.
     17 
     18     */
     19 
     20 #include <stdlib.h>
     21 
     22 #include "misc.h"
     23 #include "lf.h"
     24 #include "lf-ppc.h"
     25 #include "table.h"
     26 
     27 #include "filter.h"
     28 #include "filter-ppc.h"
     29 
     30 #include "ld-decode.h"
     31 #include "ld-cache.h"
     32 #include "ld-insn.h"
     33 
     34 #include "igen.h"
     35 
     36 #include "gen-semantics.h"
     37 #include "gen-idecode.h"
     38 #include "gen-icache.h"
     39 
     40 
     41 
     42 static void
     43 print_icache_function_header(lf *file,
     44 			     const char *basename,
     45 			     insn_bits *expanded_bits,
     46 			     int is_function_definition)
     47 {
     48   lf_printf(file, "\n");
     49   lf_print__function_type(file, ICACHE_FUNCTION_TYPE, "EXTERN_ICACHE", " ");
     50   print_function_name(file,
     51 		      basename,
     52 		      expanded_bits,
     53 		      function_name_prefix_icache);
     54   lf_printf(file, "\n(%s)", ICACHE_FUNCTION_FORMAL);
     55   if (!is_function_definition)
     56     lf_printf(file, ";");
     57   lf_printf(file, "\n");
     58 }
     59 
     60 
     61 void
     62 print_icache_declaration(insn_table *entry,
     63 			 lf *file,
     64 			 void *data,
     65 			 insn *instruction,
     66 			 int depth)
     67 {
     68   if (generate_expanded_instructions) {
     69     ASSERT(entry->nr_insn == 1);
     70     print_icache_function_header(file,
     71 				 entry->insns->file_entry->fields[insn_name],
     72 				 entry->expanded_bits,
     73 				 0/* is not function definition */);
     74   }
     75   else {
     76     print_icache_function_header(file,
     77 				 instruction->file_entry->fields[insn_name],
     78 				 NULL,
     79 				 0/* is not function definition */);
     80   }
     81 }
     82 
     83 
     84 
     85 static void
     86 print_icache_extraction(lf *file,
     87 			insn *instruction,
     88 			const char *entry_name,
     89 			const char *entry_type,
     90 			const char *entry_expression,
     91 			const char *original_name,
     92 			const char *file_name,
     93 			int line_nr,
     94 			insn_field *cur_field,
     95 			insn_bits *bits,
     96 			icache_decl_type what_to_declare,
     97 			icache_body_type what_to_do,
     98 			const char *reason)
     99 {
    100   const char *expression;
    101   ASSERT(entry_name != NULL);
    102 
    103   /* Define a storage area for the cache element */
    104   if (what_to_declare == undef_variables) {
    105     /* We've finished with the value - destory it */
    106     lf_indent_suppress(file);
    107     lf_printf(file, "#undef %s\n", entry_name);
    108     return;
    109   }
    110   else if (what_to_declare == define_variables) {
    111     lf_indent_suppress(file);
    112     lf_printf(file, "#define %s ", entry_name);
    113   }
    114   else {
    115     if (file_name != NULL)
    116       lf_print__external_ref(file, line_nr, file_name);
    117     lf_printf(file, "%s const %s ATTRIBUTE_UNUSED = ",
    118 	      entry_type == NULL ? "unsigned" : entry_type,
    119 	      entry_name);
    120   }
    121 
    122   /* define a value for that storage area as determined by what is in
    123      the cache */
    124   if (bits != NULL
    125       && strcmp(entry_name, cur_field->val_string) == 0
    126       && ((bits->opcode->is_boolean && bits->value == 0)
    127 	  || (!bits->opcode->is_boolean))) {
    128     /* The simple field has been made constant (as a result of
    129        expanding instructions or similar).  Remember that for a
    130        boolean field, value is either 0 (implying the required
    131        boolean_constant) or nonzero (implying some other value and
    132        handled later below) - Define the variable accordingly */
    133     expression = "constant field";
    134     ASSERT(bits->field == cur_field);
    135     ASSERT(entry_type == NULL);
    136     if (bits->opcode->is_boolean)
    137       lf_printf(file, "%d", bits->opcode->boolean_constant);
    138     else if (bits->opcode->last < bits->field->last)
    139       lf_printf(file, "%d",
    140 		bits->value << (bits->field->last - bits->opcode->last));
    141     else
    142       lf_printf(file, "%d", bits->value);
    143   }
    144   else if (bits != NULL
    145 	   && original_name != NULL
    146 	   && strncmp(entry_name,
    147 		      original_name, strlen(original_name)) == 0
    148 	   && strncmp(entry_name + strlen(original_name),
    149 		      "_is_", strlen("_is_")) == 0
    150 	   && ((bits->opcode->is_boolean
    151 		&& (atol(entry_name + strlen(original_name) + strlen("_is_"))
    152 		    == bits->opcode->boolean_constant))
    153 	       || (!bits->opcode->is_boolean))) {
    154     expression = "constant compare";
    155     /* An entry, derived from ORIGINAL_NAME, is testing to see of the
    156        ORIGINAL_NAME has a specific constant value.  That value
    157        matching a boolean or constant field */
    158     if (bits->opcode->is_boolean)
    159       lf_printf(file, "%d /* %s == %d */",
    160 		bits->value == 0,
    161 		original_name,
    162 		bits->opcode->boolean_constant);
    163     else if (bits->opcode->last < bits->field->last)
    164       lf_printf(file, "%d /* %s == %d */",
    165 		(atol(entry_name + strlen(original_name) + strlen("_is_"))
    166 		 == (bits->value << (bits->field->last - bits->opcode->last))),
    167 		original_name,
    168 		(bits->value << (bits->field->last - bits->opcode->last)));
    169     else
    170       lf_printf(file, "%d /* %s == %d */",
    171 		(atol(entry_name + strlen(original_name) + strlen("_is_"))
    172 		 == bits->value),
    173 		original_name,
    174 		bits->value);
    175   }
    176   else {
    177     /* put the field in the local variable, possibly also enter it
    178        into the cache */
    179     expression = "extraction";
    180     /* handle the cache */
    181     if ((what_to_do & get_values_from_icache)
    182 	|| (what_to_do & put_values_in_icache)) {
    183       lf_printf(file, "cache_entry->crack.%s.%s",
    184 		instruction->file_entry->fields[insn_form],
    185 		entry_name);
    186       if (what_to_do & put_values_in_icache) /* also put it in the cache? */
    187 	lf_printf(file, " = ");
    188     }
    189     if ((what_to_do & put_values_in_icache)
    190 	|| what_to_do == do_not_use_icache) {
    191       if (cur_field != NULL && strcmp(entry_name, cur_field->val_string) == 0)
    192 	lf_printf(file, "EXTRACTED32(instruction, %d, %d)",
    193 		  i2target(hi_bit_nr, cur_field->first),
    194 		  i2target(hi_bit_nr, cur_field->last));
    195       else if (entry_expression != NULL)
    196 	lf_printf(file, "%s", entry_expression);
    197       else
    198 	lf_printf(file, "eval_%s", entry_name);
    199     }
    200   }
    201 
    202   if (!((what_to_declare == define_variables)
    203 	|| (what_to_declare == undef_variables)))
    204     lf_printf(file, ";");
    205   if (reason != NULL)
    206     lf_printf(file, " /* %s - %s */", reason, expression);
    207   lf_printf(file, "\n");
    208 }
    209 
    210 
    211 void
    212 print_icache_body(lf *file,
    213 		  insn *instruction,
    214 		  insn_bits *expanded_bits,
    215 		  cache_table *cache_rules,
    216 		  icache_decl_type what_to_declare,
    217 		  icache_body_type what_to_do)
    218 {
    219   insn_field *cur_field;
    220 
    221   /* extract instruction fields */
    222   lf_printf(file, "/* extraction: %s ",
    223 	    instruction->file_entry->fields[insn_format]);
    224   switch (what_to_declare) {
    225   case define_variables:
    226     lf_printf(file, "#define");
    227     break;
    228   case declare_variables:
    229     lf_printf(file, "declare");
    230     break;
    231   case undef_variables:
    232     lf_printf(file, "#undef");
    233     break;
    234   }
    235   lf_printf(file, " ");
    236   switch (what_to_do) {
    237   case get_values_from_icache:
    238     lf_printf(file, "get-values-from-icache");
    239     break;
    240   case put_values_in_icache:
    241     lf_printf(file, "put-values-in-icache");
    242     break;
    243   case both_values_and_icache:
    244     lf_printf(file, "get-values-from-icache|put-values-in-icache");
    245     break;
    246   case do_not_use_icache:
    247     lf_printf(file, "do-not-use-icache");
    248     break;
    249   }
    250   lf_printf(file, " */\n");
    251 
    252   for (cur_field = instruction->fields->first;
    253        cur_field->first < insn_bit_size;
    254        cur_field = cur_field->next) {
    255     if (cur_field->is_string) {
    256       insn_bits *bits;
    257       int found_rule = 0;
    258       /* find any corresponding value */
    259       for (bits = expanded_bits;
    260 	   bits != NULL;
    261 	   bits = bits->last) {
    262 	if (bits->field == cur_field)
    263 	  break;
    264       }
    265       /* try the cache rule table for what to do */
    266       {
    267 	cache_table *cache_rule;
    268 	for (cache_rule = cache_rules;
    269 	     cache_rule != NULL;
    270 	     cache_rule = cache_rule->next) {
    271 	  if (strcmp(cur_field->val_string, cache_rule->field_name) == 0) {
    272 	    found_rule = 1;
    273 	    if (cache_rule->type == scratch_value
    274 		&& ((what_to_do & put_values_in_icache)
    275 		    || what_to_do == do_not_use_icache))
    276 	      print_icache_extraction(file,
    277 				      instruction,
    278 				      cache_rule->derived_name,
    279 				      cache_rule->type_def,
    280 				      cache_rule->expression,
    281 				      cache_rule->field_name,
    282 				      cache_rule->file_entry->file_name,
    283 				      cache_rule->file_entry->line_nr,
    284 				      cur_field,
    285 				      bits,
    286 				      what_to_declare,
    287 				      do_not_use_icache,
    288 				      "icache scratch");
    289 	    else if (cache_rule->type == compute_value
    290 		     && ((what_to_do & get_values_from_icache)
    291 			 || what_to_do == do_not_use_icache))
    292 	      print_icache_extraction(file,
    293 				      instruction,
    294 				      cache_rule->derived_name,
    295 				      cache_rule->type_def,
    296 				      cache_rule->expression,
    297 				      cache_rule->field_name,
    298 				      cache_rule->file_entry->file_name,
    299 				      cache_rule->file_entry->line_nr,
    300 				      cur_field,
    301 				      bits,
    302 				      what_to_declare,
    303 				      do_not_use_icache,
    304 				      "semantic compute");
    305 	    else if (cache_rule->type == cache_value
    306 		     && ((what_to_declare != undef_variables)
    307 			 || !(what_to_do & put_values_in_icache)))
    308 	      print_icache_extraction(file,
    309 				      instruction,
    310 				      cache_rule->derived_name,
    311 				      cache_rule->type_def,
    312 				      cache_rule->expression,
    313 				      cache_rule->field_name,
    314 				      cache_rule->file_entry->file_name,
    315 				      cache_rule->file_entry->line_nr,
    316 				      cur_field,
    317 				      bits,
    318 				      ((what_to_do & put_values_in_icache)
    319 				       ? declare_variables
    320 				       : what_to_declare),
    321 				      what_to_do,
    322 				      "in icache");
    323 	  }
    324 	}
    325       }
    326       /* No rule at all, assume that this is needed in the semantic
    327          function (when values are extracted from the icache) and
    328          hence must be put into the cache */
    329       if (found_rule == 0
    330 	  && ((what_to_declare != undef_variables)
    331 	      || !(what_to_do & put_values_in_icache)))
    332 	print_icache_extraction(file,
    333 				instruction,
    334 				cur_field->val_string,
    335 				NULL, NULL, NULL, /* type, exp, orig */
    336 				instruction->file_entry->file_name,
    337 				instruction->file_entry->line_nr,
    338 				cur_field,
    339 				bits,
    340 				((what_to_do & put_values_in_icache)
    341 				 ? declare_variables
    342 				 : what_to_declare),
    343 				what_to_do,
    344 				"default in icache");
    345       /* any thing else ... */
    346     }
    347   }
    348 
    349   lf_print__internal_ref(file);
    350 
    351   if ((code & generate_with_insn_in_icache)) {
    352     lf_printf(file, "\n");
    353     print_icache_extraction(file,
    354 			    instruction,
    355 			    "insn",
    356 			    "instruction_word",
    357 			    "instruction",
    358 			    NULL, /* origin */
    359 			    NULL, 0, /* file_name & line_nr */
    360 			    NULL, NULL,
    361 			    what_to_declare,
    362 			    what_to_do,
    363 			    NULL);
    364   }
    365 }
    366 
    367 
    368 
    369 typedef struct _icache_tree icache_tree;
    370 struct _icache_tree {
    371   const char *name;
    372   icache_tree *next;
    373   icache_tree *children;
    374 };
    375 
    376 static icache_tree *
    377 icache_tree_insert(icache_tree *tree,
    378 		   const char *name)
    379 {
    380   icache_tree *new_tree;
    381   /* find it */
    382   icache_tree **ptr_to_cur_tree = &tree->children;
    383   icache_tree *cur_tree = *ptr_to_cur_tree;
    384   while (cur_tree != NULL
    385 	 && strcmp(cur_tree->name, name) < 0) {
    386     ptr_to_cur_tree = &cur_tree->next;
    387     cur_tree = *ptr_to_cur_tree;
    388   }
    389   ASSERT(cur_tree == NULL
    390 	 || strcmp(cur_tree->name, name) >= 0);
    391   /* already in the tree */
    392   if (cur_tree != NULL
    393       && strcmp(cur_tree->name, name) == 0)
    394     return cur_tree;
    395   /* missing, insert it */
    396   ASSERT(cur_tree == NULL
    397 	 || strcmp(cur_tree->name, name) > 0);
    398   new_tree = ZALLOC(icache_tree);
    399   new_tree->name = name;
    400   new_tree->next = cur_tree;
    401   *ptr_to_cur_tree = new_tree;
    402   return new_tree;
    403 }
    404 
    405 
    406 static icache_tree *
    407 insn_table_cache_fields(insn_table *table)
    408 {
    409   icache_tree *tree = ZALLOC(icache_tree);
    410   insn *instruction;
    411   for (instruction = table->insns;
    412        instruction != NULL;
    413        instruction = instruction->next) {
    414     insn_field *field;
    415     icache_tree *form =
    416       icache_tree_insert(tree,
    417 			 instruction->file_entry->fields[insn_form]);
    418     for (field = instruction->fields->first;
    419 	 field != NULL;
    420 	 field = field->next) {
    421       if (field->is_string)
    422 	icache_tree_insert(form, field->val_string);
    423     }
    424   }
    425   return tree;
    426 }
    427 
    428 
    429 
    430 extern void
    431 print_icache_struct(insn_table *instructions,
    432 		    cache_table *cache_rules,
    433 		    lf *file)
    434 {
    435   icache_tree *tree = insn_table_cache_fields(instructions);
    436 
    437   lf_printf(file, "\n");
    438   lf_printf(file, "#define WITH_IDECODE_CACHE_SIZE %d\n",
    439 	    (code & generate_with_icache) ? icache_size : 0);
    440   lf_printf(file, "\n");
    441 
    442   /* create an instruction cache if being used */
    443   if ((code & generate_with_icache)) {
    444     icache_tree *form;
    445     lf_printf(file, "typedef struct _idecode_cache {\n");
    446     lf_printf(file, "  unsigned_word address;\n");
    447     lf_printf(file, "  void *semantic;\n");
    448     lf_printf(file, "  union {\n");
    449     for (form = tree->children;
    450 	 form != NULL;
    451 	 form = form->next) {
    452       icache_tree *field;
    453       lf_printf(file, "    struct {\n");
    454       if (code & generate_with_insn_in_icache)
    455 	lf_printf(file, "      instruction_word insn;\n");
    456       for (field = form->children;
    457 	   field != NULL;
    458 	   field = field->next) {
    459 	cache_table *cache_rule;
    460 	int found_rule = 0;
    461 	for (cache_rule = cache_rules;
    462 	     cache_rule != NULL;
    463 	     cache_rule = cache_rule->next) {
    464 	  if (strcmp(field->name, cache_rule->field_name) == 0) {
    465 	    found_rule = 1;
    466 	    if (cache_rule->derived_name != NULL)
    467 	      lf_printf(file, "      %s %s; /* %s */\n",
    468 			(cache_rule->type_def == NULL
    469 			 ? "unsigned"
    470 			 : cache_rule->type_def),
    471 			cache_rule->derived_name,
    472 			cache_rule->field_name);
    473 	  }
    474 	}
    475 	if (!found_rule)
    476 	  lf_printf(file, "      unsigned %s;\n", field->name);
    477       }
    478       lf_printf(file, "    } %s;\n", form->name);
    479     }
    480     lf_printf(file, "  } crack;\n");
    481     lf_printf(file, "} idecode_cache;\n");
    482   }
    483   else {
    484     /* alernativly, since no cache, emit a dummy definition for
    485        idecode_cache so that code refering to the type can still compile */
    486     lf_printf(file, "typedef void idecode_cache;\n");
    487   }
    488   lf_printf(file, "\n");
    489 }
    490 
    491 
    492 
    493 static void
    494 print_icache_function(lf *file,
    495 		      insn *instruction,
    496 		      insn_bits *expanded_bits,
    497 		      opcode_field *opcodes,
    498 		      cache_table *cache_rules)
    499 {
    500   int indent;
    501 
    502   /* generate code to enter decoded instruction into the icache */
    503   lf_printf(file, "\n");
    504   lf_print__function_type(file, ICACHE_FUNCTION_TYPE, "EXTERN_ICACHE", "\n");
    505   indent = print_function_name(file,
    506 			       instruction->file_entry->fields[insn_name],
    507 			       expanded_bits,
    508 			       function_name_prefix_icache);
    509   lf_indent(file, +indent);
    510   lf_printf(file, "(%s)\n", ICACHE_FUNCTION_FORMAL);
    511   lf_indent(file, -indent);
    512 
    513   /* function header */
    514   lf_printf(file, "{\n");
    515   lf_indent(file, +2);
    516 
    517   print_my_defines(file, expanded_bits, instruction->file_entry);
    518   print_itrace(file, instruction->file_entry, 1/*putting-value-in-cache*/);
    519 
    520   print_idecode_validate(file, instruction, opcodes);
    521 
    522   lf_printf(file, "\n");
    523   lf_printf(file, "{\n");
    524   lf_indent(file, +2);
    525   if ((code & generate_with_semantic_icache))
    526     lf_printf(file, "unsigned_word nia;\n");
    527   print_icache_body(file,
    528 		    instruction,
    529 		    expanded_bits,
    530 		    cache_rules,
    531 		    ((code & generate_with_direct_access)
    532 		     ? define_variables
    533 		     : declare_variables),
    534 		    ((code & generate_with_semantic_icache)
    535 		     ? both_values_and_icache
    536 		     : put_values_in_icache));
    537 
    538   lf_printf(file, "\n");
    539   lf_printf(file, "cache_entry->address = cia;\n");
    540   lf_printf(file, "cache_entry->semantic = ");
    541   print_function_name(file,
    542 		      instruction->file_entry->fields[insn_name],
    543 		      expanded_bits,
    544 		      function_name_prefix_semantics);
    545   lf_printf(file, ";\n");
    546   lf_printf(file, "\n");
    547 
    548   if ((code & generate_with_semantic_icache)) {
    549     lf_printf(file, "/* semantic routine */\n");
    550     print_semantic_body(file,
    551 			instruction,
    552 			expanded_bits,
    553 			opcodes);
    554     lf_printf(file, "return nia;\n");
    555   }
    556 
    557   if (!(code & generate_with_semantic_icache)) {
    558     lf_printf(file, "/* return the function proper */\n");
    559     lf_printf(file, "return ");
    560     print_function_name(file,
    561 			instruction->file_entry->fields[insn_name],
    562 			expanded_bits,
    563 			function_name_prefix_semantics);
    564     lf_printf(file, ";\n");
    565   }
    566 
    567   if ((code & generate_with_direct_access))
    568     print_icache_body(file,
    569 		      instruction,
    570 		      expanded_bits,
    571 		      cache_rules,
    572 		      undef_variables,
    573 		      ((code & generate_with_semantic_icache)
    574 		       ? both_values_and_icache
    575 		       : put_values_in_icache));
    576 
    577   lf_indent(file, -2);
    578   lf_printf(file, "}\n");
    579   lf_indent(file, -2);
    580   lf_printf(file, "}\n");
    581 }
    582 
    583 
    584 void
    585 print_icache_definition(insn_table *entry,
    586 			lf *file,
    587 			void *data,
    588 			insn *instruction,
    589 			int depth)
    590 {
    591   cache_table *cache_rules = (cache_table*)data;
    592   if (generate_expanded_instructions) {
    593     ASSERT(entry->nr_insn == 1
    594 	   && entry->opcode == NULL
    595 	   && entry->parent != NULL
    596 	   && entry->parent->opcode != NULL);
    597     ASSERT(entry->nr_insn == 1
    598 	   && entry->opcode == NULL
    599 	   && entry->parent != NULL
    600 	   && entry->parent->opcode != NULL
    601 	   && entry->parent->opcode_rule != NULL);
    602     print_icache_function(file,
    603 			  entry->insns,
    604 			  entry->expanded_bits,
    605 			  entry->opcode,
    606 			  cache_rules);
    607   }
    608   else {
    609     print_icache_function(file,
    610 			  instruction,
    611 			  NULL,
    612 			  NULL,
    613 			  cache_rules);
    614   }
    615 }
    616 
    617 
    618 
    619 void
    620 print_icache_internal_function_declaration(insn_table *table,
    621 					   lf *file,
    622 					   void *data,
    623 					   table_entry *function)
    624 {
    625   ASSERT((code & generate_with_icache) != 0);
    626   if (it_is("internal", function->fields[insn_flags])) {
    627     lf_printf(file, "\n");
    628     lf_print__function_type(file, ICACHE_FUNCTION_TYPE, "PSIM_INLINE_ICACHE",
    629 			   "\n");
    630     print_function_name(file,
    631 			function->fields[insn_name],
    632 			NULL,
    633 			function_name_prefix_icache);
    634     lf_printf(file, "\n(%s);\n", ICACHE_FUNCTION_FORMAL);
    635   }
    636 }
    637 
    638 
    639 void
    640 print_icache_internal_function_definition(insn_table *table,
    641 					  lf *file,
    642 					  void *data,
    643 					  table_entry *function)
    644 {
    645   ASSERT((code & generate_with_icache) != 0);
    646   if (it_is("internal", function->fields[insn_flags])) {
    647     lf_printf(file, "\n");
    648     lf_print__function_type(file, ICACHE_FUNCTION_TYPE, "PSIM_INLINE_ICACHE",
    649 			   "\n");
    650     print_function_name(file,
    651 			function->fields[insn_name],
    652 			NULL,
    653 			function_name_prefix_icache);
    654     lf_printf(file, "\n(%s)\n", ICACHE_FUNCTION_FORMAL);
    655     lf_printf(file, "{\n");
    656     lf_indent(file, +2);
    657     lf_printf(file, "/* semantic routine */\n");
    658     table_entry_print_cpp_line_nr(file, function);
    659     if ((code & generate_with_semantic_icache)) {
    660       lf_print__c_code(file, function->annex);
    661       lf_printf(file, "error(\"Internal function must longjump\\n\");\n");
    662       lf_printf(file, "return 0;\n");
    663     }
    664     else {
    665       lf_printf(file, "return ");
    666       print_function_name(file,
    667 			  function->fields[insn_name],
    668 			  NULL,
    669 			  function_name_prefix_semantics);
    670       lf_printf(file, ";\n");
    671     }
    672 
    673     lf_print__internal_ref(file);
    674     lf_indent(file, -2);
    675     lf_printf(file, "}\n");
    676   }
    677 }
    678