/* Exporting Algol 68 module interfaces. Copyright (C) 2025 Jose E. Marchesi. Copyright (C) 2010-2025 Free Software Foundation, Inc. Written by Jose E. Marchesi. GCC 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, or (at your option) any later version. GCC 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 GCC; see the file COPYING3. If not see . */ #define INCLUDE_MEMORY #include "config.h" #include "system.h" #include "coretypes.h" #include "memmodel.h" #include "tree.h" #include "target.h" #include "tm_p.h" #include "simple-object.h" #include "varasm.h" #include "intl.h" #include "output.h" /* for assemble_string */ #include "common/common-target.h" #include "dwarf2asm.h" #include #include "a68.h" #ifndef TARGET_AIX_OS #define TARGET_AIX_OS 0 #endif /* The size of the target's pointer type. */ #ifndef PTR_SIZE #define PTR_SIZE (POINTER_SIZE / BITS_PER_UNIT) #endif /* Create a new module interface, initially with no modes and no extracts. MODULE_NAME is the name of the module as it is accessed at the source level, which corresponds to a bold word. */ MOIF_T * a68_moif_new (const char *module_name) { MOIF_T *moif = ggc_cleared_alloc (); VERSION (moif) = GA68_EXPORTS_VERSION; NAME (moif) = (module_name == NULL ? NULL : ggc_strdup (module_name)); PRELUDE (moif) = NULL; POSTLUDE (moif) = NULL; vec_alloc (MODES (moif), 16); vec_alloc (MODULES (moif), 16); vec_alloc (IDENTIFIERS (moif), 16); vec_alloc (INDICANTS (moif), 16); vec_alloc (PRIOS (moif), 16); vec_alloc (OPERATORS (moif), 16); return moif; } /* Add a new mode to a module interface. */ static void a68_add_moid_to_moif (MOIF_T *moif, MOID_T *m) { if (! MODES(moif)->contains (m)) vec_safe_push (MODES (moif), m); } /* Add a new identifier extract to a module interface. */ void a68_add_identifier_to_moif (MOIF_T *moif, TAG_T *tag) { EXTRACT_T *e = ggc_alloc (); const char *tag_symbol = IDENTIFIER_POINTER (DECL_NAME (TAX_TREE_DECL (tag))); EXTRACT_KIND (e) = GA68_EXTRACT_IDEN; EXTRACT_SYMBOL (e) = ggc_strdup (tag_symbol); EXTRACT_MODE (e) = MOID (tag); EXTRACT_PRIO (e) = 0; EXTRACT_VARIABLE (e) = VARIABLE (tag); EXTRACT_IN_PROC (e) = IN_PROC (tag); if (! IDENTIFIERS (moif)->contains (e)) { a68_add_moid_to_moif (moif, MOID (tag)); vec_safe_push (IDENTIFIERS (moif), e); } } /* Add a new mode indicant extract to a module interface. */ static void a68_add_indicant_to_moif (MOIF_T *moif, TAG_T *tag) { EXTRACT_T *e = ggc_alloc (); /* Mode tags are not associated with declarations, so we have to do the mangling here. */ tree id = a68_get_mangled_indicant (NSYMBOL (NODE (tag)), NAME (moif)); const char *tag_symbol = IDENTIFIER_POINTER (id); EXTRACT_KIND (e) = GA68_EXTRACT_MODE; EXTRACT_SYMBOL (e) = ggc_strdup (tag_symbol); EXTRACT_MODE (e) = MOID (tag); EXTRACT_PRIO (e) = 0; EXTRACT_VARIABLE (e) = false; EXTRACT_IN_PROC (e) = false; if (! INDICANTS (moif)->contains (e)) { a68_add_moid_to_moif (moif, MOID (tag)); vec_safe_push (INDICANTS (moif), e); } } /* Add a new module extract to a module interface. */ static void a68_add_module_to_moif (MOIF_T *moif, TAG_T *tag) { EXTRACT_T *e = ggc_alloc (); /* Module tags are not associated with declarations, so we have to do the mangling here. */ tree id = a68_get_mangled_indicant (NSYMBOL (NODE (tag)), NAME (moif)); const char *tag_symbol = IDENTIFIER_POINTER (id); EXTRACT_KIND (e) = GA68_EXTRACT_MODU; EXTRACT_SYMBOL (e) = ggc_strdup (tag_symbol); EXTRACT_MODE (e) = NO_MOID; EXTRACT_PRIO (e) = 0; EXTRACT_VARIABLE (e) = false; EXTRACT_IN_PROC (e) = false; if (! MODULES (moif)->contains (e)) vec_safe_push (MODULES (moif), e); } /* Add a new priority extract to a module interface. */ static void a68_add_prio_to_moif (MOIF_T *moif, TAG_T *tag) { EXTRACT_T *e = ggc_alloc (); /* Priority tags are not associated with declarations, so we have to do the mangling here. */ tree id = a68_get_mangled_indicant (NSYMBOL (NODE (tag)), NAME (moif)); const char *tag_symbol = IDENTIFIER_POINTER (id); EXTRACT_KIND (e) = GA68_EXTRACT_PRIO; EXTRACT_SYMBOL (e) = ggc_strdup (tag_symbol); EXTRACT_MODE (e) = NO_MOID; EXTRACT_PRIO (e) = PRIO (tag); EXTRACT_VARIABLE (e) = false; EXTRACT_IN_PROC (e) = false; if (! PRIOS (moif)->contains (e)) vec_safe_push (PRIOS (moif), e); } /* Add a new operator extract to a module interface. */ static void a68_add_operator_to_moif (MOIF_T *moif, TAG_T *tag) { EXTRACT_T *e = ggc_alloc (); const char *tag_symbol = IDENTIFIER_POINTER (DECL_NAME (TAX_TREE_DECL (tag))); EXTRACT_KIND (e) = GA68_EXTRACT_OPER; EXTRACT_SYMBOL (e) = ggc_strdup (tag_symbol); EXTRACT_MODE (e) = MOID (tag); EXTRACT_PRIO (e) = 0; EXTRACT_VARIABLE (e) = EXTRACT_VARIABLE (tag); /* There are no operatorvariable-declarations */ gcc_assert (EXTRACT_VARIABLE (e) == false); EXTRACT_IN_PROC (e) = IN_PROC (tag); if (! OPERATORS (moif)->contains (e)) { a68_add_moid_to_moif (moif, MOID (tag)); vec_safe_push (OPERATORS (moif), e); } } /* Make the exports section the asm_out_file's new current section. */ static void a68_switch_to_export_section (void) { static section *exports_sec; if (exports_sec == NULL) { gcc_assert (targetm_common.have_named_sections); #ifdef OBJECT_FORMAT_MACHO exports_sec = get_section (A68_EXPORT_SEGMENT_NAME "," A68_EXPORT_SECTION_NAME, SECTION_DEBUG, NULL); #else exports_sec = get_section (A68_EXPORT_SECTION_NAME, TARGET_AIX_OS ? SECTION_EXCLUDE : SECTION_DEBUG, NULL); #endif } switch_to_section (exports_sec); } /* Output a sized string. */ static void a68_asm_output_string (const char *s, const char *comment) { dw2_asm_output_data (2, strlen (s) + 1, comment); assemble_string (s, strlen (s) + 1); } /* Output a mode to the exports section if it hasn't been emitted already. */ static void a68_asm_output_mode (MOID_T *m, const char *module_label) { /* Do nothing if the mode has been already emitted and therefore there is already a label to access it. */ if (ASM_LABEL (m) != NULL) return; /* Mode indicants are not emitted in the mode table, but as mode extracts in the extracts table. Still we have to emit the named mode. */ if (IS (m, INDICANT)) m = MOID (NODE (m)); /* Collection of modes. */ if (IS (m, SERIES_MODE) || IS (m, STOWED_MODE)) { for (PACK_T *p = PACK (m); p != NO_PACK; FORWARD (p)) a68_asm_output_mode (MOID (p), module_label); return; } /* Ok we got a mode to output. */ /* First emit referred modes and sub-modes. Note how we have to create a label for the mode and install it in the NODE_T in order to avoid infinite recursion in case of ref-induced recursive mode definitions. */ static long int cnt; static char label[100]; ASM_GENERATE_INTERNAL_LABEL (label, "LM", cnt++); ASM_LABEL (m) = ggc_strdup (label); if (IS_REF(m) || IS_FLEX (m)) a68_asm_output_mode (SUB (m), module_label); else if (m != M_STRING && IS_FLEXETY_ROW (m)) a68_asm_output_mode (SUB (m), module_label); else if (!IS_COMPLEX (m) && (IS_STRUCT (m) || IS_UNION (m))) { for (PACK_T *p = PACK (m); p != NO_PACK; FORWARD (p)) a68_asm_output_mode (MOID (p), module_label); } else if (IS (m, PROC_SYMBOL)) { a68_asm_output_mode (SUB (m), module_label); for (PACK_T *p = PACK (m); p != NO_PACK; FORWARD (p)) a68_asm_output_mode (MOID (p), module_label); } /* No recursion below this point pls. */ /* Emit a label for this mode. */ ASM_OUTPUT_LABEL (asm_out_file, ASM_LABEL (m)); /* Now emit assembly for the mode entry. */ if (m == M_VOID) dw2_asm_output_data (1, GA68_MODE_VOID, "void"); else if (m == M_CHAR) dw2_asm_output_data (1, GA68_MODE_CHAR, "char"); else if (m == M_BOOL) dw2_asm_output_data (1, GA68_MODE_BOOL, "bool"); else if (m == M_STRING) dw2_asm_output_data (1, GA68_MODE_STRING, "string"); else if (IS_INTEGRAL (m)) { dw2_asm_output_data (1, GA68_MODE_INT, "int"); dw2_asm_output_data (1, DIM (m), "sizety"); } else if (IS_REAL (m)) { dw2_asm_output_data (1, GA68_MODE_REAL, "real"); dw2_asm_output_data (1, DIM (m), "sizety"); } else if (IS_BITS (m)) { dw2_asm_output_data (1, GA68_MODE_BITS, "bits"); dw2_asm_output_data (1, DIM (m), "sizety"); } else if (IS_BYTES (m)) { dw2_asm_output_data (1, GA68_MODE_BYTES, "bytes"); dw2_asm_output_data (1, DIM (m), "sizety"); } else if (IS_COMPLEX (m)) { /* Complex is a struct of two reals of the right sizety. */ int dim = DIM (MOID (PACK (m))); dw2_asm_output_data (1, GA68_MODE_CMPL, "compl"); dw2_asm_output_data (1, dim, "sizety"); } else if (IS_REF (m)) { dw2_asm_output_data (1, GA68_MODE_NAME, "ref"); dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (SUB (m)), module_label, "referred mode"); } else if (IS_FLEX (m)) { dw2_asm_output_data (1, GA68_MODE_FLEX, "flex"); dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (SUB (m)), module_label, "flexible row mode"); } else if (IS_ROW (m)) { dw2_asm_output_data (1, GA68_MODE_ROW, "row"); dw2_asm_output_data (1, DIM (m), "dim"); /* XXX for now emit zeroes as triplets. */ for (int i = 0; i < DIM (m); ++i) { dw2_asm_output_data (PTR_SIZE, 0, "lb"); dw2_asm_output_data (PTR_SIZE, 0, "ub"); } dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (SUB (m)), module_label, "row of"); } else if (IS_STRUCT (m)) { dw2_asm_output_data (1, GA68_MODE_STRUCT, "struct"); dw2_asm_output_data (2, DIM (m), "nfields"); for (PACK_T *p = PACK (m); p != NO_PACK; FORWARD (p)) { dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (MOID (p)), module_label, "field mode"); if (TEXT (p) != NO_TEXT) a68_asm_output_string (TEXT (p), "field name"); else a68_asm_output_string ("", "field name"); } } else if (IS_UNION (m)) { dw2_asm_output_data (1, GA68_MODE_UNION, "union"); dw2_asm_output_data (2, DIM (m), "nmodes"); for (PACK_T *p = PACK (m); p != NO_PACK; FORWARD (p)) dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (MOID (p)), module_label, "united mode"); } else if (IS (m, PROC_SYMBOL)) { dw2_asm_output_data (1, GA68_MODE_PROC, "proc"); dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (SUB (m)), module_label, "ret mode"); dw2_asm_output_data (1, DIM (m), "nargs"); for (PACK_T *p = PACK (m); p != NO_PACK; FORWARD (p)) { dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (MOID (p)), module_label, "arg mode"); if (TEXT (p) != NO_TEXT) a68_asm_output_string (TEXT (p), "arg name"); else a68_asm_output_string ("", "arg name"); } } else dw2_asm_output_data (1, GA68_MODE_UNKNOWN, "unknown mode %s", a68_moid_to_string (m, 80, NO_NODE, false)); } /* Output an extract for a given tag to the extracts section. */ static void a68_asm_output_extract (const char *module_label, int kind, const char *symbol, MOID_T *mode, int prio, bool variable, bool in_proc) { static char begin_label[100]; static char end_label[100]; static long int cnt; ASM_GENERATE_INTERNAL_LABEL (begin_label, "LEBL", cnt); ASM_GENERATE_INTERNAL_LABEL (end_label, "LEEL", cnt); cnt++; dw2_asm_output_delta (PTR_SIZE, end_label, begin_label, "extract size"); ASM_OUTPUT_LABEL (asm_out_file, begin_label); bool encode_mdextra = false; switch (kind) { case GA68_EXTRACT_MODU: dw2_asm_output_data (1, GA68_EXTRACT_MODU, "module extract %s", symbol); a68_asm_output_string (symbol, "module indication"); break; case GA68_EXTRACT_MODE: dw2_asm_output_data (1, GA68_EXTRACT_MODE, "mode extract %s", symbol); a68_asm_output_string (symbol, "mode indication"); dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (mode), module_label, "mode"); break; case GA68_EXTRACT_IDEN: dw2_asm_output_data (1, GA68_EXTRACT_IDEN, "identifier extract %s", symbol); a68_asm_output_string (symbol, "name"); dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (mode), module_label, "mode"); encode_mdextra = true; break; case GA68_EXTRACT_PRIO: dw2_asm_output_data (1, GA68_EXTRACT_PRIO, "prio extract %s", symbol); a68_asm_output_string (symbol, "opname"); dw2_asm_output_data (1, prio, "priority"); break; case GA68_EXTRACT_OPER: dw2_asm_output_data (1, GA68_EXTRACT_OPER, "operator extract %s", symbol); a68_asm_output_string (symbol, "opname"); dw2_asm_output_delta (PTR_SIZE, ASM_LABEL (mode), module_label, "mode"); encode_mdextra = true; break; default: gcc_unreachable (); } if (encode_mdextra) { dw2_asm_output_data (PTR_SIZE, 2, "mdextra size"); dw2_asm_output_data (1, variable, "variable"); dw2_asm_output_data (1, in_proc, "in_proc"); } else dw2_asm_output_data (PTR_SIZE, 0, "mdextra size"); ASM_OUTPUT_LABEL (asm_out_file, end_label); } /* Output a module interface. */ static void a68_asm_output_moif (MOIF_T *moif) { a68_switch_to_export_section (); static char module_label[100]; static long int moifcnt; ASM_GENERATE_INTERNAL_LABEL (module_label, "LMOIF", moifcnt++); ASM_OUTPUT_LABEL (asm_out_file, module_label); if (flag_debug_asm) { fputs (ASM_COMMENT_START " MODIF START ", asm_out_file); fputs (NAME (moif), asm_out_file); fputc ('\n', asm_out_file); } dw2_asm_output_data (1, A68_EXPORT_MAGIC1, "magic1"); dw2_asm_output_data (1, A68_EXPORT_MAGIC2, "magic2"); dw2_asm_output_data (2, VERSION (moif), "exports version"); a68_asm_output_string (NAME (moif), "module name"); a68_asm_output_string (PRELUDE (moif) ? PRELUDE (moif) : "", "prelude symbol"); a68_asm_output_string (POSTLUDE (moif) ? POSTLUDE (moif) : "", "postlude symbol"); /* Modes table. */ static char modes_begin_label[100]; static char modes_end_label[100]; static long int modescnt; ASM_GENERATE_INTERNAL_LABEL (modes_begin_label, "LMTL", modescnt++); ASM_GENERATE_INTERNAL_LABEL (modes_end_label, "LMTL", modescnt++); if (flag_debug_asm) fputs ("\t" ASM_COMMENT_START " modes table\n", asm_out_file); dw2_asm_output_delta (PTR_SIZE, modes_end_label, modes_begin_label, "modes size"); ASM_OUTPUT_LABEL (asm_out_file, modes_begin_label); for (MOID_T *m : MODES (moif)) a68_asm_output_mode (m, module_label); ASM_OUTPUT_LABEL (asm_out_file, modes_end_label); /* Extracts table. */ static char extracts_begin_label[100]; static char extracts_end_label[100]; static long int extractscnt; ASM_GENERATE_INTERNAL_LABEL (extracts_begin_label, "LETL", extractscnt++); ASM_GENERATE_INTERNAL_LABEL (extracts_end_label, "LETL", extractscnt++); if (flag_debug_asm) fputs ("\t" ASM_COMMENT_START " extracts table\n", asm_out_file); dw2_asm_output_delta (PTR_SIZE, extracts_end_label, extracts_begin_label, "extracts size"); ASM_OUTPUT_LABEL (asm_out_file, extracts_begin_label); for (EXTRACT_T *e : MODULES (moif)) a68_asm_output_extract (module_label, GA68_EXTRACT_MODU, EXTRACT_SYMBOL (e), EXTRACT_MODE (e), EXTRACT_PRIO (e), EXTRACT_VARIABLE (e), EXTRACT_IN_PROC (e)); for (EXTRACT_T *e : INDICANTS (moif)) a68_asm_output_extract (module_label, GA68_EXTRACT_MODE, EXTRACT_SYMBOL (e), EXTRACT_MODE (e), EXTRACT_PRIO (e), EXTRACT_VARIABLE (e), EXTRACT_IN_PROC (e)); for (EXTRACT_T *e : IDENTIFIERS (moif)) a68_asm_output_extract (module_label, GA68_EXTRACT_IDEN, EXTRACT_SYMBOL (e), EXTRACT_MODE (e), EXTRACT_PRIO (e), EXTRACT_VARIABLE (e), EXTRACT_IN_PROC (e)); for (EXTRACT_T *e : PRIOS (moif)) a68_asm_output_extract (module_label, GA68_EXTRACT_PRIO, EXTRACT_SYMBOL (e), EXTRACT_MODE (e), EXTRACT_PRIO (e), EXTRACT_VARIABLE (e), EXTRACT_IN_PROC (e)); for (EXTRACT_T *e : OPERATORS (moif)) a68_asm_output_extract (module_label, GA68_EXTRACT_OPER, EXTRACT_SYMBOL (e), EXTRACT_MODE (e), EXTRACT_PRIO (e), EXTRACT_VARIABLE (e), EXTRACT_IN_PROC (e)); ASM_OUTPUT_LABEL (asm_out_file, extracts_end_label); if (flag_debug_asm) { fputs (ASM_COMMENT_START " MODIF END ", asm_out_file); fputs (NAME (moif), asm_out_file); fputc ('\n', asm_out_file); } } /* Emit export information for the module definition in the parse tree P. */ void a68_do_exports (NODE_T *p) { for (;p != NO_NODE; FORWARD (p)) { if (IS (p, DEFINING_MODULE_INDICANT)) { // XXX only do this if the defining module is to be // exported. Accessed modules without PUB are not exported. */ TAG_T *tag = a68_find_tag_global (TABLE (p), MODULE_SYMBOL, NSYMBOL (p)); gcc_assert (tag != NO_TAG); if (EXPORTED (tag)) { tree module_id = a68_get_mangled_indicant (NSYMBOL (p)); MOIF_T *moif = a68_moif_new (IDENTIFIER_POINTER (module_id)); char *prelude = xasprintf ("%s__prelude", IDENTIFIER_POINTER (module_id)); char *postlude = xasprintf ("%s__postlude", IDENTIFIER_POINTER (module_id)); PRELUDE (moif) = ggc_strdup (prelude); POSTLUDE (moif) = ggc_strdup (postlude); free (prelude); free (postlude); NODE_T *module_text = NEXT (NEXT (p)); gcc_assert (IS (module_text, MODULE_TEXT)); NODE_T *def_part = (IS (SUB (module_text), REVELATION_PART) ? NEXT_SUB (module_text) : SUB (module_text)); gcc_assert (IS (def_part, DEF_PART)); TABLE_T *table = TABLE (SUB (def_part)); gcc_assert (PUBLIC_RANGE (table)); for (TAG_T *t = MODULES (table); t != NO_TAG; FORWARD (t)) { if (PUBLICIZED (t)) a68_add_module_to_moif (moif, t); } for (TAG_T *t = INDICANTS (table); t != NO_TAG; FORWARD (t)) { if (PUBLICIZED (t)) a68_add_indicant_to_moif (moif, t); } for (TAG_T *t = IDENTIFIERS (table); t != NO_TAG; FORWARD (t)) { if (PUBLICIZED (t)) a68_add_identifier_to_moif (moif, t); } for (TAG_T *t = PRIO (table); t != NO_TAG; FORWARD (t)) { if (PUBLICIZED (t)) a68_add_prio_to_moif (moif, t); } for (TAG_T *t = OPERATORS (table); t != NO_TAG; FORWARD (t)) { if (PUBLICIZED (t)) a68_add_operator_to_moif (moif, t); } a68_asm_output_moif (moif); if (flag_a68_dump_moif) a68_dump_moif (moif); } } else a68_do_exports (SUB (p)); } }