/* Lower the Algol 68 parse tree to GENERIC. Copyright (C) 2025 Jose E. Marchesi. 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 "tree.h" #include "fold-const.h" #include "diagnostic.h" #include "langhooks.h" #include "tm.h" #include "function.h" #include "cgraph.h" #include "toplev.h" #include "varasm.h" #include "predict.h" #include "stor-layout.h" #include "tree-iterator.h" #include "stringpool.h" #include "print-tree.h" #include "gimplify.h" #include "dumpfile.h" #include "convert.h" #include "a68.h" /* Return a tree with an identifier for the mangled version of a given name. Publicized symbols feature the module name publishing them. Internal symbols don't. Bold words, i.e. mode indicants, module indicants and operator indicants, are mangled to upper-case. Tags, i.e. identifiers are mangled to lower-case. Monads and nomads are mangled to letter codes symbolizing the symbols: % (p)ercentage ^ (c)aret & (a)mpersand + pl(u)s - (m)inus ~ (t)ilde ! (b)ang ? (q)uestion mark > bi(g)ger than < (l)ess than / (s)lash = (e)qual : c(o)lon * sta(r) Each letter code is followed by a single underscore character. */ static tree get_mangled_identifier_or_indicant (const char *name, bool indicant, const char *mname, bool internal, bool numbered) { /* First determine the size of the mangled symbol. */ size_t mangled_size = strlen (name) + 1; if (mname) { /* Add size for MNAME_ */ mangled_size += strlen (mname) + 1; if (internal) /* Another _ */ mangled_size += 1; } for (const char *p = name; *p; ++p) { mangled_size += 1; if (strchr (MONADS, *p) != NULL || strchr (NOMADS, *p) != NULL || *p == ':') /* Each monad or nomad requires two chars to encode. */ mangled_size += 1; } char *number_buf = NULL; if (numbered) { static unsigned int cnt; number_buf = xasprintf ("%d", cnt++); mangled_size += strlen (number_buf); } /* Now fill-in the mangled symbol. */ char *mangled_name = (char *) alloca (mangled_size); size_t pos = 0; if (mname) { for (const char *p = mname; *p; ++p) /* Module names are bold words. Make sure to emit them in upper-case. */ mangled_name[pos++] = TOUPPER (*p); mangled_name[pos++] = '_'; if (internal) mangled_name[pos++] = '_'; } for (const char *p = name; *p; ++p) { if (strchr (MONADS, *p) != NULL || strchr (NOMADS, *p) != NULL || *p == ':') { char c; switch (*p) { case '%': c = 'p'; break; case '^': c = 'c'; break; case '&': c = 'a'; break; case '+': c = 'u'; break; case '-': c = 'm'; break; case '~': c = 't'; break; case '!': c = 'b'; break; case '?': c = 'q'; break; case '>': c = 'g'; break; case '<': c = 'l'; break; case '/': c = 's'; break; case '=': c = 'e'; break; case ':': c = 'o'; break; case '*': c = 'r'; break; default: /* Should not happen. */ gcc_unreachable (); } mangled_name[pos++] = c; mangled_name[pos++] = '_'; } else { if (indicant) mangled_name[pos++] = TOUPPER (*p); else mangled_name[pos++] = TOLOWER (*p); } } if (numbered) { for (char *p = number_buf; *p; ++p) mangled_name[pos++] = *p; free (number_buf); } mangled_name[pos++] = '\0'; return get_identifier (mangled_name); } tree a68_get_mangled_identifier (const char *name, const char *mname, bool internal, bool numbered) { return get_mangled_identifier_or_indicant (name, false /* indicant */, mname, internal, numbered); } tree a68_get_mangled_indicant (const char *name, const char *mname, bool internal, bool numbered) { return get_mangled_identifier_or_indicant (name, true /* indicant */, mname, internal, numbered); } /* Demangle a given SYMBOL. This function does the reverse operation than get_mangled_identifier_or_indicant. */ char * a68_demangle_symbol (const char *mname, const char *symbol, bool is_operator) { gcc_assert (strlen (symbol) >= strlen (mname) + 1); /* First get rid of the module name and underscore. */ symbol += strlen (mname) + 1; /* Now demangle the rest. */ size_t size = strlen (symbol) + 1; char *demangled = (char *) xmalloc (size + 1); size_t o = 0; for (size_t i = 0; i < size; ++i) { if (symbol[i+1] == '_') { switch (symbol[i]) { case 'p': demangled[o++] = '+'; break; case 'c': demangled[o++] = '^'; break; case 'a': demangled[o++] = '&'; break; case 'u': demangled[o++] = '+'; break; case 'm': demangled[o++] = '-'; break; case 't': demangled[o++] = '~'; break; case 'b': demangled[o++] = '!'; break; case 'q': demangled[o++] = '?'; break; case 'g': demangled[o++] = '>'; break; case 'l': demangled[o++] = '<'; break; case 's': demangled[o++] = '/'; break; case 'e': demangled[o++] = '='; break; case 'o': demangled[o++] = ':'; break; case 'r': demangled[o++] = '*'; break; default: /* Invalid mangling. */ // XXX this should be checked at import time in extract. gcc_unreachable (); } i += 1; } else demangled[o++] = symbol[i]; } demangled[o] = '\0'; if (is_operator) { /* Remove trailing digits. */ for (size_t i = strlen (demangled) - 1; i > 0; --i) { if (ISDIGIT (demangled[i])) demangled[i] = '\0'; else break; } } return demangled; } /* Return a tree with the EMPTY value. EMPTY is the only denotation of the VOID mode. It is used in unions to denote "no value". It must have size zero, so it lowers into an empty constructor with zero elements of type void. This is what GNU C uses to implement the empty struct extension. */ tree a68_get_empty (void) { return build_constructor (a68_void_type, NULL); } /* Return a tree with the yielding of SKIP of a given mode. SKIP stands for some value of some given mode. It shall be used only in a context where the compiler can determine the mode. The particular value to which it elaborates is non-important, but this compiler always uses the same values. See the a68_get_ref_*_tree functions for details on what values are these. */ tree a68_get_skip_tree (MOID_T *m) { tree expr = NULL_TREE; while (EQUIVALENT (m) != NO_MOID) m = EQUIVALENT (m); if (IS_INTEGRAL (m)) expr = a68_get_int_skip_tree (m); else if (m == M_CHAR) expr = a68_get_char_skip_tree (); else if (m == M_BOOL) expr = a68_get_bool_skip_tree (); else if (IS_REAL (m)) expr = a68_get_real_skip_tree (m); else if (IS_BITS (m)) expr = a68_get_bits_skip_tree (m); else if (IS_REF (m)) expr = a68_get_ref_skip_tree (m); else if (IS (m, PROC_SYMBOL)) expr = a68_get_proc_skip_tree (m); else if (IS_STRUCT (m)) expr = a68_get_struct_skip_tree (m); else if (IS_UNION (m)) expr = a68_get_union_skip_tree (m); else if (IS_FLEXETY_ROW (m)) expr = a68_get_multiple_skip_tree (m); else if (m == M_STRING) expr = a68_get_string_skip_tree (); else if (m == M_ROWS || IS (m, SERIES_MODE)) { /* XXX assert that all modes in the series are rows? */ tree rows_type = CTYPE (M_ROWS); tree dim_field = TYPE_FIELDS (rows_type); tree triplets_field = TREE_CHAIN (dim_field); tree null_pointer = build_int_cst (TREE_TYPE (triplets_field), 0); expr = build_constructor_va (rows_type, 2, dim_field, size_zero_node, triplets_field, null_pointer); } else if (m == M_VOID || m == M_HIP) expr = a68_get_empty (); else { fatal_error (UNKNOWN_LOCATION, "get skip tree: cannot compute SKIP for mode %s", a68_moid_to_string (m, MOID_ERROR_WIDTH, NODE (m), true)); gcc_unreachable (); } return expr; } /* Given a tree node EXP holding a value of mode M: *NUM_REFS is set to the number of REFs in M. *NUM_POINTERS is set to the number of pointers in the type of EXP that correspond to the REFs in M. */ void a68_ref_counts (tree exp, MOID_T *m, int *num_refs, int *num_pointers) { /* Count REFs in M and pointers in the type of EXP. Note that VAR_DECLs corresponding to REF PROC are of type pointer, so these should not count for the count! */ /* Make sure we are accessing the real mode definition. */ while (EQUIVALENT (m) != NO_MOID) m = EQUIVALENT (m); *num_refs = 0; *num_pointers = 0; for (MOID_T *s = m; s != NO_MOID && IS_REF (s); s = SUB (s)) *num_refs += 1; for (tree p = TREE_TYPE (exp); p != NULL_TREE && POINTER_TYPE_P (p) && TREE_CODE (TREE_TYPE (p)) != FUNCTION_TYPE; p = TREE_TYPE (p)) *num_pointers += 1; gcc_assert (*num_refs >= *num_pointers); } /* The Algol 68 variable declaration [LOC|HEAP] AMODE foo; Is in principle equivalent to the identity declaration REF AMODE foo = [LOC|HEAP] AMODE; In both cases the object ascribed to the defining identifier `foo' is of mode REF AMODE. The ascribed object is a name which is created by a generator implied in the actual declarer in the first case, and an explicit generator in the initialization expression in the second case. However, this front-end implements these two cases differently in order to reduce the amount of both indirect addressing and of storage: - The variable declaration `[LOC|HEAP] AMODE foo;' lowers into a VAR_DECL with type ATYPE provided that the generator is LOC and that it contains no rows. Accessing it requires direct addressing. When its address is required, an ADDR_EXPR shall be used. - The identity declaration `REF AMODE foo = LOC AMODE;' lowers into a VAR_DECL with type *ATYPE. Accessing it requires indirect addressing. It is effectively a pointer. This introduces the complication that an expression (the VAR_DECL) whose type is TYPE can appear in a place where *TYPE is expected. This function, given the required mode and an expression, adds as many ADDR_EXPR to EXPR as necessary so the resulting value is of the required type. Other than this nuisance, the parser guarantees that the entities have the right type at the location they appear, so a call to a68_consolidate_ref is all must be needed at any point in the lowering process to guarantee a valid value for the context. This function expects: - That the type of EXPR is zero or more pointers to a base type BTYPE. - That the mode M is zero or more REFs to a base non-ref mode AMODE. - That the number of pointers in the type of EXPR is less or equal than the number of REFs in the mode M. - That BTYPE and AMODE are equivalent. */ tree a68_consolidate_ref (MOID_T *m, tree expr) { int num_refs, num_pointers; a68_ref_counts (expr, m, &num_refs, &num_pointers); /* Address EXPR as many times as necessary to match the number of REFs in the desired mode. */ while (num_pointers < num_refs) { if (TREE_CODE (expr) == COMPOUND_EXPR) { /* (..., x) -> (..., &x) */ // gcc_assert (TREE_CODE (TREE_OPERAND (expr, 0)) == MODIFY_EXPR); // gcc_assert (VAR_P (TREE_OPERAND (expr, 1))); TREE_OPERAND (expr, 1) = a68_consolidate_ref (m, TREE_OPERAND (expr, 1)); TREE_TYPE (expr) = TREE_TYPE (TREE_OPERAND (expr, 1)); } else { /* x -> &x */ if (TREE_CODE (expr) == INDIRECT_REF) /* expr is an indirection. Remove the pointer rather than adding an addr. This avoids &* situations and marking stuff as addressable unnecessarily. */ expr = TREE_OPERAND (expr,0); else { TREE_ADDRESSABLE (expr) = true; expr = fold_build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (expr)), expr); } } num_pointers += 1; } return expr; } /* Make a declaration for an anonymous routine of mode MODE. */ tree a68_make_anonymous_routine_decl (MOID_T *mode) { /* The CTYPE of MODE is a pointer to a function. We need the pointed function type for the FUNCTION_DECL. */ tree func_type = TREE_TYPE (CTYPE (mode)); tree func_decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, NULL_TREE /* name, set below. */, func_type); char *name = xasprintf ("routine%d", DECL_UID (func_decl)); DECL_NAME (func_decl) = a68_get_mangled_identifier (name); free (name); DECL_EXTERNAL (func_decl) = 0; DECL_STATIC_CHAIN (func_decl) = !a68_in_global_range (); /* Nested functions should be addressable. XXX this should be propagated to their containing functions, so for now we mark them all as addressable. */ TREE_ADDRESSABLE (func_decl) = 1; /* A nested function is not global. */ TREE_PUBLIC (func_decl) = a68_in_global_range (); TREE_STATIC (func_decl) = 1; return func_decl; } /* Make a declaration for a constant procedure or operator. */ tree a68_make_proc_identity_declaration_decl (NODE_T *identifier, const char *module_name, bool indicant, bool external, const char *extern_symbol) { /* The CTYPE of MODE is a pointer to a function. We need the pointed function type for the FUNCTION_DECL. */ tree func_type = TREE_TYPE (CTYPE (MOID (identifier))); bool public_range = PUBLIC_RANGE (TABLE (TAX (identifier))); tree func_decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, NULL_TREE, /* name, set below. */ func_type); if (public_range) { bool publicized = PUBLICIZED (TAX (identifier)); DECL_EXTERNAL (func_decl) = 0; if (publicized) TREE_PUBLIC (func_decl) = 1; else TREE_PUBLIC (func_decl) = 0; if (indicant) DECL_NAME (func_decl) = a68_get_mangled_indicant (NSYMBOL (identifier), module_name, false /* internal */, (IS (identifier, DEFINING_OPERATOR) || IS (identifier, OPERATOR))); else DECL_NAME (func_decl) = a68_get_mangled_identifier (NSYMBOL (identifier), module_name, false /* internal */, (IS (identifier, DEFINING_OPERATOR) || IS (identifier, OPERATOR))); } else if (external) { DECL_EXTERNAL (func_decl) = 1; TREE_PUBLIC (func_decl) = 1; DECL_NAME (func_decl) = get_identifier (extern_symbol); } else { DECL_EXTERNAL (func_decl) = 0; DECL_STATIC_CHAIN (func_decl) = !a68_in_global_range (); /* Nested functions should be addressable. XXX this should be propagated to their containing functions, so for now we mark them all as addressable. */ TREE_ADDRESSABLE (func_decl) = 1; /* A nested function is not global. */ TREE_PUBLIC (func_decl) = a68_in_global_range (); if (indicant) DECL_NAME (func_decl) = a68_get_mangled_indicant (NSYMBOL (identifier)); else DECL_NAME (func_decl) = a68_get_mangled_identifier (NSYMBOL (identifier)); } TREE_STATIC (func_decl) = 1; return func_decl; } /* Make a declaration for an identity declaration. */ tree a68_make_identity_declaration_decl (NODE_T *identifier, const char *module_name, bool indicant, bool external, const char *extern_symbol) { tree type = CTYPE (MOID (identifier)); bool public_range = PUBLIC_RANGE (TABLE (TAX (identifier))); tree decl = build_decl (a68_get_node_location (identifier), VAR_DECL, NULL_TREE, /* name, set below. */ type); if (public_range) { bool publicized = PUBLICIZED (TAX (identifier)); DECL_EXTERNAL (decl) = 0; TREE_STATIC (decl) = 1; if (publicized) TREE_PUBLIC (decl) = 1; else TREE_PUBLIC (decl) = 0; if (indicant) DECL_NAME (decl) = a68_get_mangled_indicant (NSYMBOL (identifier), module_name, false /* internal */, (IS (identifier, DEFINING_OPERATOR) || IS (identifier, OPERATOR))); else DECL_NAME (decl) = a68_get_mangled_identifier (NSYMBOL (identifier), module_name, false /* internal */, (IS (identifier, DEFINING_OPERATOR) || IS (identifier, OPERATOR))); } else if (external) { DECL_EXTERNAL (decl) = 1; TREE_PUBLIC (decl) = 1; DECL_NAME (decl) = get_identifier (extern_symbol); } else { DECL_EXTERNAL (decl) = 0; TREE_PUBLIC (decl) = 0; DECL_NAME (decl) = a68_get_mangled_identifier (NSYMBOL (identifier)); } DECL_INITIAL (decl) = a68_get_skip_tree (MOID (identifier)); return decl; } /* Make a declaration for a variable declaration. The mode of the given identifier is expected to be a REF AMODE. */ tree a68_make_variable_declaration_decl (NODE_T *identifier, const char *module_name, bool external, const char *extern_symbol) { gcc_assert (IS_REF (MOID (identifier))); MOID_T *mode = MOID (identifier); bool use_pointer = (HEAP (TAX (identifier)) != STATIC_SYMBOL && ((HEAP (TAX (identifier)) == HEAP_SYMBOL) || HAS_ROWS (SUB (MOID (identifier))))); bool public_range = PUBLIC_RANGE (TABLE (TAX (identifier))); tree type = use_pointer ? CTYPE (mode) : CTYPE (SUB (mode)); tree decl = build_decl (a68_get_node_location (identifier), VAR_DECL, NULL_TREE, /* name, set below. */ type); if (public_range) { bool publicized = PUBLICIZED (TAX (identifier)); DECL_EXTERNAL (decl) = 0; TREE_STATIC (decl) = 1; if (publicized) TREE_PUBLIC (decl) = 1; else TREE_PUBLIC (decl) = 0; DECL_NAME (decl) = a68_get_mangled_identifier (NSYMBOL (identifier), module_name, false /* internal */, (IS (identifier, DEFINING_OPERATOR) || IS (identifier, OPERATOR))); } else if (external) { DECL_EXTERNAL (decl) = 1; TREE_PUBLIC (decl) = 1; DECL_NAME (decl) = get_identifier (extern_symbol); } else { TREE_PUBLIC (decl) = 0; DECL_NAME (decl) = a68_get_mangled_identifier (NSYMBOL (identifier)); } DECL_INITIAL (decl) = a68_get_skip_tree (use_pointer ? mode : SUB (mode)); return decl; } /* Do a checked indirection. P is a tree node used for its location information. EXP is an expression that gets indirected. EXP_MODE is the mode of exp. */ tree a68_checked_indirect_ref (NODE_T *p, tree exp, MOID_T *exp_mode) { tree exp_type = TREE_TYPE (exp); tree nil_check = NULL_TREE; if (OPTION_NIL_CHECKING (&A68_JOB)) { exp = save_expr (exp); tree consolidated_exp = a68_consolidate_ref (exp_mode, exp); /* Check whether we are dereferencing NIL. */ unsigned int lineno = NUMBER (LINE (INFO (p))); const char *filename_str = FILENAME (LINE (INFO (p))); tree filename = build_string_literal (strlen (filename_str) + 1, filename_str); tree call = a68_build_libcall (A68_LIBCALL_DEREFNIL, void_type_node, 2, filename, build_int_cst (unsigned_type_node, lineno)); call = fold_build2 (COMPOUND_EXPR, a68_bool_type, call, boolean_false_node); nil_check = fold_build2 (NE_EXPR, exp_type, consolidated_exp, build_int_cst (exp_type, 0)); nil_check = fold_build2 (TRUTH_ORIF_EXPR, exp_type, nil_check, call); } tree deref = fold_build1 (INDIRECT_REF, TREE_TYPE (exp_type), exp); if (nil_check == NULL_TREE) return deref; else return fold_build2 (COMPOUND_EXPR, TREE_TYPE (deref), nil_check, deref); } /* Deref a given expression EXP whose mode is MOID (P). The value to dereference always corresponds to a name, but it may consist of: - Not a pointer, in which case corresponds to a name lowered to a VAR_DECL. - A pointer to a function, in which case corresponds to a name of mode REF PROC, lowered to a VAR_DECL. - Any other pointer corresponds to a name lowered to a VAR_DECL that is a pointer. In the first two cases, in both r-value and l-value situations the expected result is achieved by just returning the value: in r-value the decl denotes the value, in l-value the decl denotes the (direct) address of the value. */ tree a68_low_deref (tree exp, NODE_T *p) { int num_refs, num_pointers; a68_ref_counts (exp, MOID (p), &num_refs, &num_pointers); if (num_refs > num_pointers) return exp; else { gcc_assert (num_refs == num_pointers); return a68_checked_indirect_ref (p, exp, MOID (p)); } } /* Get a deep-copy of a given Algol 68 value EXP. */ tree a68_low_dup (tree expr, bool use_heap) { tree dup = NULL_TREE; tree type = TREE_TYPE (expr); /* XXX */ use_heap = true; /* Determine the mode corresponding to the type of EXPR. */ MOID_T *m = a68_type_moid (type); gcc_assert (m != NO_MOID); while (EQUIVALENT (m) != NO_MOID) m = EQUIVALENT (m); if (A68_ROW_TYPE_P (type)) { /* We need to copy the elements as well as the descriptor. There is no need to check bounds. */ /* Deflexe the mode as appropriate. */ while (IS_FLEX (m)) m = SUB (m); gcc_assert (IS_ROW (m) || m == M_STRING); a68_push_range (NULL); /* First allocate space for the dupped elements. */ expr = save_expr (expr); tree elements = a68_multiple_elements (expr); tree element_pointer_type = TREE_TYPE (elements); tree element_type = TREE_TYPE (element_pointer_type); tree new_elements_size = save_expr (a68_multiple_elements_size (expr)); tree new_elements = a68_lower_tmpvar ("new_elements%", TREE_TYPE (elements), (use_heap ? a68_lower_malloc (TREE_TYPE (TREE_TYPE (elements)), new_elements_size) : a68_lower_alloca (TREE_TYPE (TREE_TYPE (elements)), new_elements_size))); /* Then copy the elements. If the mode of the elements stored in the multiple dont have rows, then we can just use memcpy. Otherwise, we have to loop and recurse to dup all the elements in the multiple one by one. The above applies to multiples of any number of dimensions. */ if (m == M_STRING || !HAS_ROWS (SUB (m))) { a68_add_stmt (a68_lower_memcpy (new_elements, elements, new_elements_size)); a68_add_stmt (new_elements); } else { /* Note that num_elems includes elements that are not accessible due to trimming. */ tree num_elems = a68_lower_tmpvar ("numelems%", size_type_node, fold_build2 (TRUNC_DIV_EXPR, sizetype, new_elements_size, size_in_bytes (element_type))); tree orig_elements = a68_lower_tmpvar ("orig_elements%", element_pointer_type, elements); tree index = a68_lower_tmpvar ("index%", size_type_node, size_zero_node); /* Begin of loop body. */ a68_push_range (NULL); /* if (index == num_elems) break; */ a68_add_stmt (fold_build1 (EXIT_EXPR, void_type_node, fold_build2 (EQ_EXPR, size_type_node, index, num_elems))); /* new_elements[index] = elements[index] */ tree offset = fold_build2 (MULT_EXPR, sizetype, index, size_in_bytes (element_type)); tree new_elem_lvalue = fold_build2 (MEM_REF, element_type, fold_build2 (POINTER_PLUS_EXPR, element_pointer_type, new_elements, offset), fold_convert (element_pointer_type, integer_zero_node)); tree elem = fold_build2 (MEM_REF, element_type, fold_build2 (POINTER_PLUS_EXPR, element_pointer_type, orig_elements, offset), fold_convert (element_pointer_type, integer_zero_node)); a68_add_stmt (fold_build2 (MODIFY_EXPR, element_type, new_elem_lvalue, a68_low_dup (elem, use_heap))); /* index++ */ a68_add_stmt (fold_build2 (POSTINCREMENT_EXPR, size_type_node, index, size_one_node)); tree loop_body = a68_pop_range (); /* End of loop body. */ a68_add_stmt (fold_build1 (LOOP_EXPR, void_type_node, loop_body)); a68_add_stmt (new_elements); } new_elements = a68_pop_range (); TREE_TYPE (new_elements) = element_pointer_type; /* Now build a descriptor pointing to the dupped elements and return it. Note that the descriptor is always allocated on the stack. */ dup = a68_row_value_raw (type, a68_multiple_triplets (expr), new_elements, new_elements_size); } else if (!HAS_ROWS (m)) { /* Non-multiple values that do not contain rows do not need to be dupped, since they can be just moved around using the semantics of MODIFY_EXPR. */ dup = expr; } else if (A68_STRUCT_TYPE_P (type)) { /* Since struct value can contain multiples and unions and other values that require deep copy, we cannot simply rely on the C semantics of a MODIFY_EXPR. */ tree struct_type = type; vec *ce = NULL; expr = save_expr (expr); for (tree field = TYPE_FIELDS (struct_type); field; field = TREE_CHAIN (field)) { CONSTRUCTOR_APPEND_ELT (ce, field, a68_low_dup (fold_build3 (COMPONENT_REF, TREE_TYPE (field), expr, field, NULL_TREE), use_heap)); } dup = build_constructor (struct_type, ce); } else if (A68_UNION_TYPE_P (type)) { /* We need to recurse in whatever type corresponding to the active mode in the united value. This shall be done at run-time by using a series of IF overhead IS index of mode blah in union THEN dup = dup_type (CTYPE (mode blah in union)) FI */ MOID_T *union_mode = a68_type_moid (type); a68_push_range (union_mode); dup = a68_lower_tmpvar ("dup%", type, expr); tree cunion_type = TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (type))); tree field_decl = TYPE_FIELDS (cunion_type); while (EQUIVALENT (union_mode) != NO_MOID) union_mode = EQUIVALENT (union_mode); for (PACK_T *pack = PACK (union_mode); pack != NO_PACK; FORWARD (pack)) { tree continue_label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL, NULL, /* Set below. */ void_type_node); char *label_name = xasprintf ("continue%d%%", DECL_UID (continue_label_decl)); DECL_NAME (continue_label_decl) = get_identifier (label_name); free (label_name); a68_add_decl (continue_label_decl); a68_add_stmt (fold_build2 (TRUTH_ORIF_EXPR, integer_type_node, fold_build2 (EQ_EXPR, integer_type_node, a68_union_overhead (dup), size_int (a68_united_mode_index (union_mode, MOID (pack)))), fold_build2 (COMPOUND_EXPR, integer_type_node, build1 (GOTO_EXPR, void_type_node, continue_label_decl), integer_zero_node))); a68_add_stmt (fold_build2 (MODIFY_EXPR, type, fold_build3 (COMPONENT_REF, TREE_TYPE (field_decl), a68_union_cunion (dup), field_decl, NULL_TREE), a68_low_dup (fold_build3 (COMPONENT_REF, TREE_TYPE (field_decl), a68_union_cunion (dup), field_decl, NULL_TREE), use_heap))); a68_add_stmt (build1 (LABEL_EXPR, void_type_node, continue_label_decl)); field_decl = TREE_CHAIN (field_decl); } a68_add_stmt (dup); dup = a68_pop_range (); } else /* Not an Algol 68 value. */ gcc_unreachable (); return dup; } /* Lower code to ascribe the value yielded by the expression in RHS to the defining identifier implied by the LHS, which is a VAR_DECL tree. MODE is the mode of the value to be ascribed. */ tree a68_low_ascription (MOID_T *mode, tree lhs, tree rhs) { gcc_assert (VAR_P (lhs)); tree type = CTYPE (mode); if (IS (mode, PROC_SYMBOL)) { /* A pointer to a function, or a function, is expected at the right hand side. We need a pointer for the left hand side.. */ if (TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE) { type = build_pointer_type (type); rhs = fold_build1 (ADDR_EXPR, type, rhs); } } if (HAS_ROWS (mode)) rhs = a68_low_dup (rhs); return fold_build2 (MODIFY_EXPR, type, lhs, rhs); } /* Perform an assignation of RHS to LHS. MODE_RHS is the mode of the rhs. MODE_LHS is the mode of the lhs. MODE_LHS shall be REF [FLEX] MODE_LHS. */ tree a68_low_assignation (NODE_T *p, tree lhs, MOID_T *mode_lhs, tree rhs, MOID_T *mode_rhs) { NODE_T *lhs_node = SUB (p); tree assignation = NULL_TREE; tree orig_rhs = rhs; if (IS_FLEXETY_ROW (mode_rhs)) { /* Make a deep copy of the rhs. Note that we have to use the heap because the scope of the lhs may be older than the scope of the rhs. XXX this can be ommitted if a68_multiple_copy_elems below supports overlapping multiples. */ if (HAS_ROWS (mode_rhs)) rhs = a68_low_dup (rhs, true /* use_heap */); rhs = save_expr (rhs); /* Determine whether the REF [FLEX] MODE_LHS is flexible. */ if (SUB (mode_lhs) == M_STRING || IS_FLEX (SUB (mode_lhs))) { /* Assigning to a flexible name updates descriptor with new bounds and also sets the elements to the dup of the rhs. No boundscheck is peformed. XXX but bound checking in contained values may be necessary, ghost elements. */ if (POINTER_TYPE_P (TREE_TYPE (lhs)) && TREE_TYPE (TREE_TYPE (lhs)) == TREE_TYPE (rhs)) { /* Make sure to not evaluate the expression yielding the pointer more than once. */ lhs = save_expr (lhs); tree deref_lhs = a68_checked_indirect_ref (lhs_node, lhs, mode_lhs); assignation = fold_build2 (COMPOUND_EXPR, TREE_TYPE (lhs), fold_build2 (MODIFY_EXPR, TREE_TYPE (lhs), deref_lhs, rhs), lhs); } else { /* The lhs is either a variable or a component ref as a l-value. It is ok to evaluate it as an r-value as well as doing so inroduces no side-effects. */ assignation = fold_build2 (COMPOUND_EXPR, TREE_TYPE (lhs), fold_build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs), lhs); } } else { /* Dereference the multiple at the left-hand side. This may require indirection. */ tree effective_lhs; if (POINTER_TYPE_P (TREE_TYPE (lhs))) { /* The name at the lhs is a pointer. */ gcc_assert (TREE_TYPE (TREE_TYPE (lhs)) == TREE_TYPE (rhs)); lhs = save_expr (lhs); effective_lhs = a68_checked_indirect_ref (lhs_node, lhs, mode_lhs); } else { /* The name at the lhs is either a variable or a component ref as a l-value. It is ok to evaluate it as an r-value as well as doing so introduces no side-effects. */ effective_lhs = lhs; } /* Copy over the elements in a loop. The space occupied by the previous elements stored in the lhs multiple will be recovered by either stack shrinkage or garbage collected. */ tree copy_elements = a68_multiple_copy_elems (mode_rhs, effective_lhs, rhs); assignation = fold_build2 (COMPOUND_EXPR, TREE_TYPE (lhs), copy_elements, lhs); /* Check the bounds of the multiple at the rhs to make sure they are the same than the bounds of the multiple already referred by the lhs. If the bounds don't match then emit a run-time error. */ if (OPTION_BOUNDS_CHECKING (&A68_JOB)) assignation = fold_build2 (COMPOUND_EXPR, TREE_TYPE (assignation), a68_multiple_bounds_check_equal (p, effective_lhs, rhs), assignation); } } else { /* First make sure we got a pointer in the RHS in case it is a name. */ rhs = a68_consolidate_ref (mode_rhs, rhs); /* The assignation implies copying the entire value being assigned, so make sure we do a deep copy whenever needed. Note that we have to use the heap because the scope of the lhs may be older than the scope of the rhs. */ if (HAS_ROWS (mode_rhs)) rhs = a68_low_dup (rhs, true /* use_heap */); if (POINTER_TYPE_P (TREE_TYPE (lhs)) && TREE_TYPE (TREE_TYPE (lhs)) == TREE_TYPE (rhs)) { /* If the left hand side is a pointer, deref it, but return the pointer. Make sure to not evaluate the expression yielding the pointer more than once. */ lhs = save_expr (lhs); tree deref_lhs = a68_checked_indirect_ref (lhs_node, lhs, mode_lhs); assignation = fold_build2 (COMPOUND_EXPR, TREE_TYPE (lhs), fold_build2 (MODIFY_EXPR, TREE_TYPE (lhs), deref_lhs, rhs), lhs); } else { /* Otherwise the lhs is either a variable or a component ref as an l-value. It is ok to evaluate it as an r-value as well as doing so introduces no side-effects. */ assignation = fold_build2 (COMPOUND_EXPR, TREE_TYPE (lhs), fold_build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs), lhs); } } /* Since it is been assigned to a name, the rhs is no longer constant. */ if (A68_ROW_TYPE_P (TREE_TYPE (orig_rhs)) || A68_STRUCT_TYPE_P (TREE_TYPE (orig_rhs))) TREE_CONSTANT (orig_rhs) = 0; return assignation; } /* Build a tree that copies SIZE bytes from SRC into DST. */ tree a68_lower_memcpy (tree dst, tree src, tree size) { return build_call_expr (builtin_decl_explicit (BUILT_IN_MEMCPY), 3, dst, src, size); } /* Build a tree that allocates SIZE bytes on the stack and returns a *TYPE pointer to it. */ tree a68_lower_alloca (tree type, tree size) { tree call = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN); call = build_call_expr_loc (UNKNOWN_LOCATION, call, 2, size, size_int (TYPE_ALIGN (type))); call = fold_convert (build_pointer_type (type), call); return call; } /* Build a tree that allocates SIZE bytes on the heap and returns a *TYPE pointer to it. */ tree a68_lower_malloc (tree type, tree size) { return fold_convert (build_pointer_type (type), a68_build_libcall (A68_LIBCALL_MALLOC, ptr_type_node, 1, size)); } /* Build code for a temporary variable named NAME, of type TYPE and initialized to INIT. Returns the decl node for the temporary. */ tree a68_lower_tmpvar (const char *name, tree type, tree init) { tree tmpvar = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (name), type); DECL_ARTIFICIAL (tmpvar) = 1; DECL_IGNORED_P (tmpvar) = 1; a68_add_decl (tmpvar); a68_add_decl_expr (fold_build1 (DECL_EXPR, type, tmpvar)); a68_add_stmt (fold_build2 (INIT_EXPR, type, tmpvar, init)); return tmpvar; } /* Build a FUNC_DECL for a top-level non-public function and return it. */ tree a68_low_toplevel_func_decl (const char *name, tree fntype) { tree fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, NULL /* set below */, fntype); char *_name = xasprintf ("__ga68_%s%d", name, DECL_UID (fndecl)); DECL_NAME (fndecl) = get_identifier (_name); free (_name); DECL_EXTERNAL (fndecl) = 0; TREE_PUBLIC (fndecl) = 0; TREE_STATIC (fndecl) = 1; return fndecl; } /* Build a PARM_DECL whose context is TYPE with the given NAME. */ tree a68_low_func_param (tree fndecl, const char *name, tree type) { tree param = build_decl (UNKNOWN_LOCATION, PARM_DECL, get_identifier (name), type); DECL_CONTEXT (param) = fndecl; DECL_ARG_TYPE (param) = TREE_TYPE (param); layout_decl (param, 0); return param; } /* Lower revelations, to calls to either its prelude or poslude. This function always returns NULL_TREE. */ static tree lower_revelations (NODE_T *p, LOW_CTX_T ctx, bool prelude) { for (; p != NO_NODE; FORWARD (p)) { lower_revelations (SUB (p), ctx, prelude); if (IS (p, MODULE_INDICANT)) { tree decl = build_decl (a68_get_node_location (p), FUNCTION_DECL, NULL_TREE, /* name, set below. */ build_function_type (void_type_node, void_list_node)); DECL_NAME (decl) = a68_get_mangled_identifier (prelude ? "_prelude" : "_postlude", NSYMBOL (p)); DECL_EXTERNAL (decl) = 1; TREE_PUBLIC (decl) = 1; a68_add_decl (decl); a68_add_stmt (build_call_expr_loc (a68_get_node_location (p), decl, 0)); } } return NULL_TREE; } /* Lower a module text. module text : revelation part, def part, postlude part, fed symbol ; revelation part, def part, fed symbol ; def part, postlude part, fed symbol ; def part, postlude part, fed symbol. def part : def symbol, enquiry clause. postlude part : postlude symbol, serial clause. Each module text lowers to two functions which are callable from outside the current compilation unit: MODULENAME__prelude MODULENAME__postlude Declarations inside prelude and postlude are lowered into global decl trees. The non-PUBlicized ones are marked as static. This handler always returns NULL_TREE. */ static tree lower_module_text (NODE_T *p, LOW_CTX_T ctx) { NODE_T *def_part = (IS (SUB (p), REVELATION_PART) ? NEXT_SUB (p) : SUB (p)); NODE_T *revelation_part = (IS (SUB (p), REVELATION_PART) ? SUB (p) : NO_NODE); NODE_T *postlude_part = (IS (NEXT (def_part), FED_SYMBOL) ? NO_NODE : NEXT (def_part)); NODE_T *prelude_enquiry = NEXT_SUB (def_part); /* The global sentinel of the module, initialized to 0. */ tree sentinel_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, NULL /* name */, sizetype); char *sentinel_name = xasprintf ("%s__sentinel", ctx.module_definition_name); DECL_NAME (sentinel_decl) = get_identifier (sentinel_name); free (sentinel_name); TREE_PUBLIC (sentinel_decl) = 0; TREE_STATIC (sentinel_decl) = 1; DECL_CONTEXT (sentinel_decl) = NULL_TREE; /* File scope. */ make_decl_rtl (sentinel_decl); varpool_node::finalize_decl (sentinel_decl); /* Create the prelude function. */ tree prelude_decl = build_decl (a68_get_node_location (def_part), FUNCTION_DECL, NULL_TREE, /* name, set below. */ build_function_type (void_type_node, void_list_node)); DECL_NAME (prelude_decl) = a68_get_mangled_identifier ("_prelude", ctx.module_definition_name); DECL_EXTERNAL (prelude_decl) = 0; TREE_PUBLIC (prelude_decl) = 1; TREE_STATIC (prelude_decl) = 1; a68_push_function_range (prelude_decl, void_type_node /* result_type */, true /* top_level */); { /* Increase sentinel. */ a68_add_stmt (fold_build2 (POSTINCREMENT_EXPR, sizetype, sentinel_decl, size_one_node)); a68_push_stmt_list (NULL); { a68_push_stmt_list (NULL); { /* Add calls to preludes of modules in REVELATION_PART. */ lower_revelations (revelation_part, ctx, true /* prelude */); a68_add_stmt (a68_lower_tree (prelude_enquiry, ctx)); } tree do_prelude = a68_pop_stmt_list (); a68_push_stmt_list (M_VOID); tree do_nothing = a68_pop_stmt_list (); /* Do the prelude work only if sentinel is 1. */ a68_add_stmt (fold_build3 (COND_EXPR, void_type_node, fold_build2 (EQ_EXPR, sizetype, sentinel_decl, size_one_node), do_prelude, do_nothing)); } tree prelude_body = a68_pop_stmt_list (); a68_pop_function_range (prelude_body); } /* Create the postlude function. This is done even if the module definition has no postlude in the source code. */ location_t postlude_loc = UNKNOWN_LOCATION; if (postlude_part != NO_NODE) postlude_loc = a68_get_node_location (postlude_part); tree postlude_decl = build_decl (postlude_loc, FUNCTION_DECL, NULL_TREE, /* name, set below. */ build_function_type (void_type_node, void_list_node)); DECL_NAME (postlude_decl) = a68_get_mangled_identifier ("_postlude", ctx.module_definition_name); DECL_EXTERNAL (postlude_decl) = 0; TREE_PUBLIC (postlude_decl) = 1; TREE_STATIC (postlude_decl) = 1; a68_push_function_range (postlude_decl, void_type_node /* result_type */, true /* top_level */); { /* Decrease sentinel. */ a68_add_stmt (fold_build2 (POSTINCREMENT_EXPR, sizetype, sentinel_decl, size_one_node)); a68_push_stmt_list (NULL); { a68_push_stmt_list (NULL); { /* Add calls to postludes of modules in REVELATION_PART. */ lower_revelations (revelation_part, ctx, false /* prelude */); /* Perhaps the postlude code, if there is one. */ NODE_T *postlude_serial = NO_NODE; if (postlude_part != NO_NODE) postlude_serial = NEXT_SUB (postlude_part); if (postlude_serial != NO_NODE) a68_add_stmt (a68_lower_tree (postlude_serial, ctx)); } tree do_postlude = a68_pop_stmt_list (); a68_push_stmt_list (M_VOID); tree do_nothing = a68_pop_stmt_list (); /* Do the postlude work only if sentinel is 0. */ a68_add_stmt (fold_build3 (COND_EXPR, void_type_node, fold_build2 (EQ_EXPR, sizetype, sentinel_decl, size_zero_node), do_postlude, do_nothing)); } tree postlude_body = a68_pop_stmt_list (); a68_pop_function_range (postlude_body); } return NULL_TREE; } /* Lower a set of module declarations. module declaration : module symbol, defining module, equals symbol, module text ; module_declaration, comma symbol, defining module, equals symbol, module text. Each module declaration lowers into the side-effects of emitting prelude and postlude global functions, and emitting global declarations for the declarations in the module definition prelude. This handler always returns NULL_TREE. */ static tree lower_module_declaration (NODE_T *p, LOW_CTX_T ctx) { for (; p != NO_NODE; FORWARD (p)) { if (IS (p, DEFINING_MODULE_INDICANT)) { ctx.module_definition_name = NSYMBOL (p); A68_MODULE_DEFINITION_DECLS->truncate (0); vec_alloc (A68_MODULE_DEFINITION_DECLS, 16); lower_module_text (NEXT (NEXT (p)), ctx); for (tree d : A68_MODULE_DEFINITION_DECLS) { if (TREE_CODE (d) == FUNCTION_DECL) cgraph_node::finalize_function (d, true); else { rest_of_decl_compilation (d, 1, 0); make_decl_rtl (d); } } } else lower_module_declaration (SUB (p), ctx); } return NULL_TREE; } /* Lower a prelude packet. prelude packet : module declaration. This handler always returns NULL_TREE. */ static tree lower_prelude_packet (NODE_T *p, LOW_CTX_T ctx) { a68_lower_tree (SUB (p), ctx); return NULL_TREE; } /* Lower a particular program. particular program : label, enclosed clause; enclosed clause. This handler always returns NULL_TREE. */ static tree lower_particular_program (NODE_T *p, LOW_CTX_T ctx) { /* Create the main function that conforms the particular program. */ tree main_decl = build_decl (a68_get_node_location (p), FUNCTION_DECL, get_identifier ("__algol68_main"), build_function_type (void_type_node, void_list_node)); DECL_EXTERNAL (main_decl) = 0; TREE_PUBLIC (main_decl) = 1; TREE_STATIC (main_decl) = 1; a68_push_function_range (main_decl, void_type_node /* result_type */); /* Lower the body of the function. */ NODE_T *enclosed_clause = (IS (SUB (p), ENCLOSED_CLAUSE) ? SUB (p) : NEXT (SUB (p))); tree body_expr = a68_lower_tree (enclosed_clause, ctx); a68_pop_function_range (body_expr); return NULL_TREE; } /* Lower a packet. packet : particular program ; prelude packet. */ static tree lower_packet (NODE_T *p, LOW_CTX_T ctx) { return a68_lower_tree (SUB (p), ctx); } /* Lower the given tree P using the given context CTX. */ tree a68_lower_tree (NODE_T *p, LOW_CTX_T ctx) { #if 0 for (int i = 0; i < ctx.level; ++i) printf (" "); printf ("LOWER TREE: %d::%s\n", NUMBER (p), a68_attribute_name (ATTRIBUTE (p))); #endif ctx.level++; tree res = NULL_TREE; if (p == NO_NODE) gcc_unreachable (); switch (ATTRIBUTE (p)) { case PACKET: res = lower_packet (p, ctx); break; case PRELUDE_PACKET: res = lower_prelude_packet (p, ctx); break; case MODULE_DECLARATION: res = lower_module_declaration (p, ctx); break; case MODULE_TEXT: res = lower_module_text (p, ctx); break; case PARTICULAR_PROGRAM: res = lower_particular_program (p, ctx); break; /* Clauses */ case ENCLOSED_CLAUSE: res = a68_lower_enclosed_clause (p, ctx); break; case CLOSED_CLAUSE: res = a68_lower_closed_clause (p, ctx); break; case ACCESS_CLAUSE: res = a68_lower_access_clause (p, ctx); break; case PARALLEL_CLAUSE: res = a68_lower_parallel_clause (p, ctx); break; case COLLATERAL_CLAUSE: res = a68_lower_collateral_clause (p, ctx); break; case UNIT_LIST: res = a68_lower_unit_list (p, ctx); break; case CONDITIONAL_CLAUSE: res = a68_lower_conditional_clause (p, ctx); break; case ENQUIRY_CLAUSE: res = a68_lower_enquiry_clause (p, ctx); break; case CASE_CLAUSE: res = a68_lower_case_clause (p, ctx); break; case CONFORMITY_CLAUSE: res = a68_lower_conformity_clause (p, ctx); break; case LOOP_CLAUSE: res = a68_lower_loop_clause (p, ctx); break; case SERIAL_CLAUSE: res = a68_lower_serial_clause (p, ctx); break; case INITIALISER_SERIES: res = a68_lower_initialiser_series (p, ctx); break; case EXIT_SYMBOL: res = a68_lower_completer (p, ctx); break; case LABELED_UNIT: res = a68_lower_labeled_unit (p, ctx); break; case LABEL: res = a68_lower_label (p, ctx); break; /* Declarations. */ case DECLARATION_LIST: res = a68_lower_declaration_list (p, ctx); break; case DECLARER: res = a68_lower_declarer (p, ctx); break; case IDENTITY_DECLARATION: res = a68_lower_identity_declaration (p, ctx); break; case VARIABLE_DECLARATION: res = a68_lower_variable_declaration (p, ctx); break; case PROCEDURE_DECLARATION: res = a68_lower_procedure_declaration (p, ctx); break; case PROCEDURE_VARIABLE_DECLARATION: res = a68_lower_procedure_variable_declaration (p, ctx); break; case PRIORITY_DECLARATION: res = a68_lower_priority_declaration (p, ctx); break; case BRIEF_OPERATOR_DECLARATION: res = a68_lower_brief_operator_declaration (p, ctx); break; case OPERATOR_DECLARATION: res = a68_lower_operator_declaration (p, ctx); break; case MODE_DECLARATION: res = a68_lower_mode_declaration (p, ctx); break; /* Units. */ case UNIT: res = a68_lower_unit (p, ctx); break; case ROUTINE_TEXT: res = a68_lower_routine_text (p, ctx); break; case ASSIGNATION: res = a68_lower_assignation (p, ctx); break; case TERTIARY: res = a68_lower_tertiary (p, ctx); break; case MONADIC_FORMULA: res = a68_lower_monadic_formula (p, ctx); break; case FORMULA: res = a68_lower_formula (p, ctx); break; case SECONDARY: res = a68_lower_secondary (p, ctx); break; case SLICE: res = a68_lower_slice (p, ctx); break; case SELECTION: res = a68_lower_selection (p, ctx); break; case PRIMARY: res = a68_lower_primary (p, ctx); break; case GENERATOR: res = a68_lower_generator (p, ctx); break; case CALL: res = a68_lower_call (p, ctx); break; case CAST: res = a68_lower_cast (p, ctx); break; case AND_FUNCTION: case OR_FUNCTION: res = a68_lower_logic_function (p, ctx); break; case IDENTITY_RELATION: res = a68_lower_identity_relation (p, ctx); break; case EMPTY_SYMBOL: res = a68_lower_empty (p, ctx); break; case NIHIL: res = a68_lower_nihil (p, ctx); break; case SKIP: res = a68_lower_skip (p, ctx); break; case DENOTATION: res = a68_lower_denotation (p, ctx); break; case IDENTIFIER: res = a68_lower_identifier (p, ctx); break; /* Coercions. */ case ROWING: res = a68_lower_rowing (p, ctx); break; case WIDENING: res = a68_lower_widening (p, ctx); break; case DEPROCEDURING: res = a68_lower_deproceduring (p, ctx); break; case PROCEDURING: res = a68_lower_proceduring (p, ctx); break; case VOIDING: res = a68_lower_voiding (p, ctx); break; case DEREFERENCING: res = a68_lower_dereferencing (p, ctx); break; /* Others. */ case UNITING: res = a68_lower_uniting (p, ctx); break; case JUMP: res = a68_lower_jump (p, ctx); break; case PARAMETER: res = a68_lower_parameter (p, ctx); break; case PARAMETER_LIST: res = a68_lower_parameter_list (p, ctx); break; case PARAMETER_PACK: res = a68_lower_parameter_pack (p, ctx); break; case OPERATOR: res = a68_lower_operator (p, ctx); break; case ASSERTION: res = a68_lower_assertion (p, ctx); break; case STOP: res = NULL_TREE; break; default: fatal_error (a68_get_node_location (p), "cannot lower node %s", a68_attribute_name (ATTRIBUTE (p))); gcc_unreachable (); break; } return res; } /* Lower an Algol 68 complete parse tree to a GENERIC tree. */ tree a68_lower_top_tree (NODE_T *p) { LOW_CTX_T top_ctx; top_ctx.declarer = NULL; top_ctx.proc_decl_identifier = NO_NODE; top_ctx.proc_decl_operator = false; top_ctx.level = 0; top_ctx.module_definition_name = NULL; vec_alloc (A68_MODULE_DEFINITION_DECLS, 16); return a68_lower_tree (p, top_ctx); }