123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821 |
- /* The IGEN simulator generator for GDB, the GNU Debugger.
- Copyright 2002-2022 Free Software Foundation, Inc.
- Contributed by Andrew Cagney.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include <stdlib.h>
- #include "misc.h"
- #include "lf.h"
- #include "table.h"
- #include "filter.h"
- #include "igen.h"
- #include "ld-insn.h"
- #include "ld-decode.h"
- #include "gen.h"
- #include "gen-semantics.h"
- #include "gen-idecode.h"
- #include "gen-icache.h"
- static void
- print_icache_function_header (lf *file,
- const char *basename,
- const char *format_name,
- opcode_bits *expanded_bits,
- int is_function_definition,
- int nr_prefetched_words)
- {
- lf_printf (file, "\n");
- lf_print__function_type_function (file, print_icache_function_type,
- "EXTERN_ICACHE", " ");
- print_function_name (file,
- basename, format_name, NULL,
- expanded_bits, function_name_prefix_icache);
- lf_printf (file, "\n(");
- print_icache_function_formal (file, nr_prefetched_words);
- lf_printf (file, ")");
- if (!is_function_definition)
- lf_printf (file, ";");
- lf_printf (file, "\n");
- }
- void
- print_icache_declaration (lf *file,
- insn_entry * insn,
- opcode_bits *expanded_bits,
- insn_opcodes *opcodes, int nr_prefetched_words)
- {
- print_icache_function_header (file,
- insn->name,
- insn->format_name,
- expanded_bits,
- 0 /* is not function definition */ ,
- nr_prefetched_words);
- }
- static void
- print_icache_extraction (lf *file,
- const char *format_name,
- cache_entry_type cache_type,
- const char *entry_name,
- const char *entry_type,
- const char *entry_expression,
- char *single_insn_field,
- line_ref *line,
- insn_field_entry *cur_field,
- opcode_bits *expanded_bits,
- icache_decl_type what_to_declare,
- icache_body_type what_to_do)
- {
- const char *expression;
- opcode_bits *bits;
- char *reason;
- ASSERT (format_name != NULL);
- ASSERT (entry_name != NULL);
- /* figure out exactly what should be going on here */
- switch (cache_type)
- {
- case scratch_value:
- if ((what_to_do & put_values_in_icache)
- || what_to_do == do_not_use_icache)
- {
- reason = "scratch";
- what_to_do = do_not_use_icache;
- }
- else
- return;
- break;
- case compute_value:
- if ((what_to_do & get_values_from_icache)
- || what_to_do == do_not_use_icache)
- {
- reason = "compute";
- what_to_do = do_not_use_icache;
- }
- else
- return;
- break;
- case cache_value:
- if ((what_to_declare != undef_variables)
- || !(what_to_do & put_values_in_icache))
- {
- reason = "cache";
- what_to_declare = ((what_to_do & put_values_in_icache)
- ? declare_variables : what_to_declare);
- }
- else
- return;
- break;
- default:
- abort (); /* Bad switch. */
- }
- /* For the type, default to a simple unsigned */
- if (entry_type == NULL || strlen (entry_type) == 0)
- entry_type = "unsigned";
- /* look through the set of expanded sub fields to see if this field
- has been given a constant value */
- for (bits = expanded_bits; bits != NULL; bits = bits->next)
- {
- if (bits->field == cur_field)
- break;
- }
- /* Define a storage area for the cache element */
- switch (what_to_declare)
- {
- case undef_variables:
- /* We've finished with the #define value - destory it */
- lf_indent_suppress (file);
- lf_printf (file, "#undef %s\n", entry_name);
- return;
- case define_variables:
- /* Using direct access for this entry, clear any prior
- definition, then define it */
- lf_indent_suppress (file);
- lf_printf (file, "#undef %s\n", entry_name);
- /* Don't type cast pointer types! */
- lf_indent_suppress (file);
- if (strchr (entry_type, '*') != NULL)
- lf_printf (file, "#define %s (", entry_name);
- else
- lf_printf (file, "#define %s ((%s) ", entry_name, entry_type);
- break;
- case declare_variables:
- /* using variables to define the value */
- if (line != NULL)
- lf_print__line_ref (file, line);
- lf_printf (file, "%s const %s UNUSED = ", entry_type, entry_name);
- break;
- }
- /* define a value for that storage area as determined by what is in
- the cache */
- if (bits != NULL
- && single_insn_field != NULL
- && strcmp (entry_name, single_insn_field) == 0
- && strcmp (entry_name, cur_field->val_string) == 0
- && ((bits->opcode->is_boolean && bits->value == 0)
- || (!bits->opcode->is_boolean)))
- {
- /* The cache rule is specifying what to do with a simple
- instruction field.
- Because of instruction expansion, the field is either a
- constant value or equal to the specified constant (boolean
- comparison). (The latter indicated by bits->value == 0).
- The case of a field not being equal to the specified boolean
- value is handled later. */
- expression = "constant field";
- ASSERT (bits->field == cur_field);
- if (bits->opcode->is_boolean)
- {
- ASSERT (bits->value == 0);
- lf_printf (file, "%d", bits->opcode->boolean_constant);
- }
- else if (bits->opcode->last < bits->field->last)
- {
- lf_printf (file, "%d",
- bits->value << (bits->field->last - bits->opcode->last));
- }
- else
- {
- lf_printf (file, "%d", bits->value);
- }
- }
- else if (bits != NULL
- && single_insn_field != NULL
- && strncmp (entry_name,
- single_insn_field,
- strlen (single_insn_field)) == 0
- && strncmp (entry_name + strlen (single_insn_field),
- "_is_",
- strlen ("_is_")) == 0
- && ((bits->opcode->is_boolean
- && ((unsigned)
- atol (entry_name + strlen (single_insn_field) +
- strlen ("_is_")) == bits->opcode->boolean_constant))
- || (!bits->opcode->is_boolean)))
- {
- /* The cache rule defines an entry for the comparison between a
- single instruction field and a constant. The value of the
- comparison in someway matches that of the opcode field that
- was made constant through expansion. */
- expression = "constant compare";
- if (bits->opcode->is_boolean)
- {
- lf_printf (file, "%d /* %s == %d */",
- bits->value == 0,
- single_insn_field, bits->opcode->boolean_constant);
- }
- else if (bits->opcode->last < bits->field->last)
- {
- lf_printf (file, "%d /* %s == %d */",
- (atol
- (entry_name + strlen (single_insn_field) +
- strlen ("_is_")) ==
- (bits->
- value << (bits->field->last - bits->opcode->last))),
- single_insn_field,
- (bits->
- value << (bits->field->last - bits->opcode->last)));
- }
- else
- {
- lf_printf (file, "%d /* %s == %d */",
- (atol
- (entry_name + strlen (single_insn_field) +
- strlen ("_is_")) == bits->value), single_insn_field,
- bits->value);
- }
- }
- else
- {
- /* put the field in the local variable, possibly also enter it
- into the cache */
- expression = "extraction";
- /* handle the cache */
- if ((what_to_do & get_values_from_icache)
- || (what_to_do & put_values_in_icache))
- {
- lf_printf (file, "cache_entry->crack.%s.%s",
- format_name, entry_name);
- if (what_to_do & put_values_in_icache) /* also put it in the cache? */
- {
- lf_printf (file, " = ");
- }
- }
- if ((what_to_do & put_values_in_icache)
- || what_to_do == do_not_use_icache)
- {
- if (cur_field != NULL)
- {
- if (entry_expression != NULL && strlen (entry_expression) > 0)
- error (line,
- "Instruction field entry with nonempty expression\n");
- if (cur_field->first == 0
- && cur_field->last == options.insn_bit_size - 1)
- lf_printf (file, "(instruction_%d)", cur_field->word_nr);
- else if (cur_field->last == options.insn_bit_size - 1)
- lf_printf (file, "MASKED%d (instruction_%d, %d, %d)",
- options.insn_bit_size,
- cur_field->word_nr,
- i2target (options.hi_bit_nr, cur_field->first),
- i2target (options.hi_bit_nr, cur_field->last));
- else
- lf_printf (file, "EXTRACTED%d (instruction_%d, %d, %d)",
- options.insn_bit_size,
- cur_field->word_nr,
- i2target (options.hi_bit_nr, cur_field->first),
- i2target (options.hi_bit_nr, cur_field->last));
- }
- else
- {
- lf_printf (file, "%s", entry_expression);
- }
- }
- }
- switch (what_to_declare)
- {
- case define_variables:
- lf_printf (file, ")");
- break;
- case undef_variables:
- break;
- case declare_variables:
- lf_printf (file, ";");
- break;
- }
- ASSERT (reason != NULL && expression != NULL);
- lf_printf (file, " /* %s - %s */\n", reason, expression);
- }
- void
- print_icache_body (lf *file,
- insn_entry * instruction,
- opcode_bits *expanded_bits,
- cache_entry *cache_rules,
- icache_decl_type what_to_declare,
- icache_body_type what_to_do, int nr_prefetched_words)
- {
- /* extract instruction fields */
- lf_printf (file, "/* Extraction: %s\n", instruction->name);
- lf_printf (file, " ");
- switch (what_to_declare)
- {
- case define_variables:
- lf_printf (file, "#define");
- break;
- case declare_variables:
- lf_printf (file, "declare");
- break;
- case undef_variables:
- lf_printf (file, "#undef");
- break;
- }
- lf_printf (file, " ");
- switch (what_to_do)
- {
- case get_values_from_icache:
- lf_printf (file, "get-values-from-icache");
- break;
- case put_values_in_icache:
- lf_printf (file, "put-values-in-icache");
- break;
- case both_values_and_icache:
- lf_printf (file, "get-values-from-icache|put-values-in-icache");
- break;
- case do_not_use_icache:
- lf_printf (file, "do-not-use-icache");
- break;
- }
- lf_printf (file, "\n ");
- print_insn_words (file, instruction);
- lf_printf (file, " */\n");
- /* pass zero - fetch from memory any missing instructions.
- Some of the instructions will have already been fetched (in the
- instruction array), others will still need fetching. */
- switch (what_to_do)
- {
- case get_values_from_icache:
- break;
- case put_values_in_icache:
- case both_values_and_icache:
- case do_not_use_icache:
- {
- int word_nr;
- switch (what_to_declare)
- {
- case undef_variables:
- break;
- case define_variables:
- case declare_variables:
- for (word_nr = nr_prefetched_words;
- word_nr < instruction->nr_words; word_nr++)
- {
- /* FIXME - should be using print_icache_extraction? */
- lf_printf (file,
- "%sinstruction_word instruction_%d UNUSED = ",
- options.module.global.prefix.l, word_nr);
- lf_printf (file, "IMEM%d_IMMED (cia, %d)",
- options.insn_bit_size, word_nr);
- lf_printf (file, ";\n");
- }
- }
- }
- }
- /* if putting the instruction words in the cache, define references
- for them */
- if (options.gen.insn_in_icache)
- {
- /* FIXME: is the instruction_word type correct? */
- print_icache_extraction (file, instruction->format_name, cache_value, "insn", /* name */
- "instruction_word", /* type */
- "instruction", /* expression */
- NULL, /* origin */
- NULL, /* line */
- NULL, NULL, what_to_declare, what_to_do);
- }
- lf_printf (file, "\n");
- /* pass one - process instruction fields.
- If there is no cache rule, the default is to enter the field into
- the cache */
- {
- insn_word_entry *word;
- for (word = instruction->words; word != NULL; word = word->next)
- {
- insn_field_entry *cur_field;
- for (cur_field = word->first;
- cur_field->first < options.insn_bit_size;
- cur_field = cur_field->next)
- {
- /* Always expand named fields (even if constant), so
- references are valid. */
- if (cur_field->type == insn_field_string)
- {
- cache_entry *cache_rule;
- cache_entry_type value_type = cache_value;
- line_ref *value_line = instruction->line;
- /* check the cache table to see if it contains a rule
- overriding the default cache action for an
- instruction field */
- for (cache_rule = cache_rules;
- cache_rule != NULL; cache_rule = cache_rule->next)
- {
- if (filter_is_subset (instruction->field_names,
- cache_rule->original_fields)
- && strcmp (cache_rule->name,
- cur_field->val_string) == 0)
- {
- value_type = cache_rule->entry_type;
- value_line = cache_rule->line;
- if (value_type == compute_value)
- {
- options.warning (cache_rule->line,
- "instruction field of type `compute' changed to `cache'\n");
- cache_rule->entry_type = cache_value;
- }
- break;
- }
- }
- /* Define an entry for the field within the
- instruction */
- print_icache_extraction (file, instruction->format_name, value_type, cur_field->val_string, /* name */
- NULL, /* type */
- NULL, /* expression */
- cur_field->val_string, /* insn field */
- value_line,
- cur_field,
- expanded_bits,
- what_to_declare, what_to_do);
- }
- }
- }
- }
- /* pass two - any cache fields not processed above */
- {
- cache_entry *cache_rule;
- for (cache_rule = cache_rules;
- cache_rule != NULL; cache_rule = cache_rule->next)
- {
- if (filter_is_subset (instruction->field_names,
- cache_rule->original_fields)
- && !filter_is_member (instruction->field_names, cache_rule->name))
- {
- char *single_field =
- filter_next (cache_rule->original_fields, "");
- if (filter_next (cache_rule->original_fields, single_field) !=
- NULL)
- single_field = NULL;
- print_icache_extraction (file, instruction->format_name, cache_rule->entry_type, cache_rule->name, cache_rule->type, cache_rule->expression, single_field, cache_rule->line, NULL, /* cur_field */
- expanded_bits,
- what_to_declare, what_to_do);
- }
- }
- }
- lf_print__internal_ref (file);
- }
- typedef struct _form_fields form_fields;
- struct _form_fields
- {
- char *name;
- filter *fields;
- form_fields *next;
- };
- static form_fields *
- insn_table_cache_fields (insn_table *isa)
- {
- form_fields *forms = NULL;
- insn_entry *insn;
- for (insn = isa->insns; insn != NULL; insn = insn->next)
- {
- form_fields **form = &forms;
- while (1)
- {
- if (*form == NULL)
- {
- /* new format name, add it */
- form_fields *new_form = ZALLOC (form_fields);
- new_form->name = insn->format_name;
- filter_add (&new_form->fields, insn->field_names);
- *form = new_form;
- break;
- }
- else if (strcmp ((*form)->name, insn->format_name) == 0)
- {
- /* already present, add field names to the existing list */
- filter_add (&(*form)->fields, insn->field_names);
- break;
- }
- form = &(*form)->next;
- }
- }
- return forms;
- }
- extern void
- print_icache_struct (lf *file, insn_table *isa, cache_entry *cache_rules)
- {
- /* Create a list of all the different instruction formats with their
- corresponding field names. */
- form_fields *formats = insn_table_cache_fields (isa);
- lf_printf (file, "\n");
- lf_printf (file, "#define WITH_%sIDECODE_CACHE_SIZE %d\n",
- options.module.global.prefix.u,
- (options.gen.icache ? options.gen.icache_size : 0));
- lf_printf (file, "\n");
- /* create an instruction cache if being used */
- if (options.gen.icache)
- {
- lf_printf (file, "typedef struct _%sidecode_cache {\n",
- options.module.global.prefix.l);
- lf_indent (file, +2);
- {
- form_fields *format;
- lf_printf (file, "unsigned_word address;\n");
- lf_printf (file, "void *semantic;\n");
- lf_printf (file, "union {\n");
- lf_indent (file, +2);
- for (format = formats; format != NULL; format = format->next)
- {
- lf_printf (file, "struct {\n");
- lf_indent (file, +2);
- {
- cache_entry *cache_rule;
- char *field;
- /* space for any instruction words */
- if (options.gen.insn_in_icache)
- lf_printf (file, "instruction_word insn[%d];\n",
- isa->max_nr_words);
- /* define an entry for any applicable cache rules */
- for (cache_rule = cache_rules;
- cache_rule != NULL; cache_rule = cache_rule->next)
- {
- /* nb - sort of correct - should really check against
- individual instructions */
- if (filter_is_subset
- (format->fields, cache_rule->original_fields))
- {
- char *memb;
- lf_printf (file, "%s %s;",
- (cache_rule->type == NULL
- ? "unsigned"
- : cache_rule->type), cache_rule->name);
- lf_printf (file, " /*");
- for (memb =
- filter_next (cache_rule->original_fields, "");
- memb != NULL;
- memb =
- filter_next (cache_rule->original_fields, memb))
- {
- lf_printf (file, " %s", memb);
- }
- lf_printf (file, " */\n");
- }
- }
- /* define an entry for any fields not covered by a cache rule */
- for (field = filter_next (format->fields, "");
- field != NULL; field = filter_next (format->fields, field))
- {
- cache_entry *cache_rule;
- int found_rule = 0;
- for (cache_rule = cache_rules;
- cache_rule != NULL; cache_rule = cache_rule->next)
- {
- if (strcmp (cache_rule->name, field) == 0)
- {
- found_rule = 1;
- break;
- }
- }
- if (!found_rule)
- lf_printf (file, "unsigned %s; /* default */\n", field);
- }
- }
- lf_indent (file, -2);
- lf_printf (file, "} %s;\n", format->name);
- }
- lf_indent (file, -2);
- lf_printf (file, "} crack;\n");
- }
- lf_indent (file, -2);
- lf_printf (file, "} %sidecode_cache;\n",
- options.module.global.prefix.l);
- }
- else
- {
- /* alernativly, since no cache, emit a dummy definition for
- idecode_cache so that code refering to the type can still compile */
- lf_printf (file, "typedef void %sidecode_cache;\n",
- options.module.global.prefix.l);
- }
- lf_printf (file, "\n");
- }
- static void
- print_icache_function (lf *file,
- insn_entry * instruction,
- opcode_bits *expanded_bits,
- insn_opcodes *opcodes,
- cache_entry *cache_rules, int nr_prefetched_words)
- {
- int indent;
- /* generate code to enter decoded instruction into the icache */
- lf_printf (file, "\n");
- lf_print__function_type_function (file, print_icache_function_type,
- "EXTERN_ICACHE", "\n");
- indent = print_function_name (file,
- instruction->name,
- instruction->format_name,
- NULL,
- expanded_bits, function_name_prefix_icache);
- indent += lf_printf (file, " ");
- lf_indent (file, +indent);
- lf_printf (file, "(");
- print_icache_function_formal (file, nr_prefetched_words);
- lf_printf (file, ")\n");
- lf_indent (file, -indent);
- /* function header */
- lf_printf (file, "{\n");
- lf_indent (file, +2);
- print_my_defines (file,
- instruction->name,
- instruction->format_name, expanded_bits);
- print_itrace (file, instruction, 1 /*putting-value-in-cache */ );
- print_idecode_validate (file, instruction, opcodes);
- lf_printf (file, "\n");
- lf_printf (file, "{\n");
- lf_indent (file, +2);
- if (options.gen.semantic_icache)
- lf_printf (file, "unsigned_word nia;\n");
- print_icache_body (file,
- instruction,
- expanded_bits,
- cache_rules,
- (options.gen.direct_access
- ? define_variables
- : declare_variables),
- (options.gen.semantic_icache
- ? both_values_and_icache
- : put_values_in_icache), nr_prefetched_words);
- lf_printf (file, "\n");
- lf_printf (file, "cache_entry->address = cia;\n");
- lf_printf (file, "cache_entry->semantic = ");
- print_function_name (file,
- instruction->name,
- instruction->format_name,
- NULL, expanded_bits, function_name_prefix_semantics);
- lf_printf (file, ";\n");
- lf_printf (file, "\n");
- if (options.gen.semantic_icache)
- {
- lf_printf (file, "/* semantic routine */\n");
- print_semantic_body (file, instruction, expanded_bits, opcodes);
- lf_printf (file, "return nia;\n");
- }
- if (!options.gen.semantic_icache)
- {
- lf_printf (file, "/* return the function proper */\n");
- lf_printf (file, "return ");
- print_function_name (file,
- instruction->name,
- instruction->format_name,
- NULL,
- expanded_bits, function_name_prefix_semantics);
- lf_printf (file, ";\n");
- }
- if (options.gen.direct_access)
- {
- print_icache_body (file,
- instruction,
- expanded_bits,
- cache_rules,
- undef_variables,
- (options.gen.semantic_icache
- ? both_values_and_icache
- : put_values_in_icache), nr_prefetched_words);
- }
- lf_indent (file, -2);
- lf_printf (file, "}\n");
- lf_indent (file, -2);
- lf_printf (file, "}\n");
- }
- void
- print_icache_definition (lf *file,
- insn_entry * insn,
- opcode_bits *expanded_bits,
- insn_opcodes *opcodes,
- cache_entry *cache_rules, int nr_prefetched_words)
- {
- print_icache_function (file,
- insn,
- expanded_bits,
- opcodes, cache_rules, nr_prefetched_words);
- }
- void
- print_icache_internal_function_declaration (lf *file,
- function_entry * function,
- void *data)
- {
- ASSERT (options.gen.icache);
- if (function->is_internal)
- {
- lf_printf (file, "\n");
- lf_print__function_type_function (file, print_icache_function_type,
- "INLINE_ICACHE", "\n");
- print_function_name (file,
- function->name,
- NULL, NULL, NULL, function_name_prefix_icache);
- lf_printf (file, "\n(");
- print_icache_function_formal (file, 0);
- lf_printf (file, ");\n");
- }
- }
- void
- print_icache_internal_function_definition (lf *file,
- function_entry * function,
- void *data)
- {
- ASSERT (options.gen.icache);
- if (function->is_internal)
- {
- lf_printf (file, "\n");
- lf_print__function_type_function (file, print_icache_function_type,
- "INLINE_ICACHE", "\n");
- print_function_name (file,
- function->name,
- NULL, NULL, NULL, function_name_prefix_icache);
- lf_printf (file, "\n(");
- print_icache_function_formal (file, 0);
- lf_printf (file, ")\n");
- lf_printf (file, "{\n");
- lf_indent (file, +2);
- lf_printf (file, "/* semantic routine */\n");
- if (options.gen.semantic_icache)
- {
- lf_print__line_ref (file, function->code->line);
- table_print_code (file, function->code);
- lf_printf (file,
- "error (\"Internal function must longjump\\n\");\n");
- lf_printf (file, "return 0;\n");
- }
- else
- {
- lf_printf (file, "return ");
- print_function_name (file,
- function->name,
- NULL,
- NULL, NULL, function_name_prefix_semantics);
- lf_printf (file, ";\n");
- }
- lf_print__internal_ref (file);
- lf_indent (file, -2);
- lf_printf (file, "}\n");
- }
- }
|