Signed-off-by: Jose E. Marchesi <jema...@gnu.org> gcc/ChangeLog
* algol68/a68-low.cc: New file. * algol68/a68-low-misc.cc: Likewise. --- gcc/algol68/a68-low-misc.cc | 213 +++++++ gcc/algol68/a68-low.cc | 1153 +++++++++++++++++++++++++++++++++++ 2 files changed, 1366 insertions(+) create mode 100644 gcc/algol68/a68-low-misc.cc create mode 100644 gcc/algol68/a68-low.cc diff --git a/gcc/algol68/a68-low-misc.cc b/gcc/algol68/a68-low-misc.cc new file mode 100644 index 00000000000..e2ad5cb412b --- /dev/null +++ b/gcc/algol68/a68-low-misc.cc @@ -0,0 +1,213 @@ +/* Lower miscellaneous tree nodes 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 + <http://www.gnu.org/licenses/>. */ + +#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" + +/* Lower an assertion. + + assertion : assert symbol, enclosed clause. +*/ + +tree +a68_lower_assertion (NODE_T *p, LOW_CTX_T ctx) +{ + if (!OPTION_ASSERT (&A68_JOB)) + return a68_get_empty(); + + /* Build the call to the assert run-time function. */ + 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_ASSERT, + void_type_node, 2, + filename, + build_int_cst (unsigned_type_node, lineno)); + /* Check condition and call assert if required. */ + tree assertion = fold_build2_loc (a68_get_node_location (p), + COMPOUND_EXPR, + a68_void_type, + build2_loc (a68_get_node_location (p), + TRUTH_ORIF_EXPR, + a68_int_type, + a68_lower_tree (NEXT (SUB (p)), ctx), + fold_build2 (COMPOUND_EXPR, + a68_int_type, + call, + build_int_cst (a68_int_type, 0))), + a68_get_empty ()); + TREE_SIDE_EFFECTS (assertion) = 1; + return assertion; +} + +/* Lower a jump to a label. + + jump : goto symbol, identifier; + identifier. + + A jump lowers into a ({ GOTO_EXPR; EMPTY }). */ + +tree +a68_lower_jump (NODE_T *p, LOW_CTX_T ctx) +{ + NODE_T *label_identifier = SUB (p); + MOID_T *jump_mode = MOID (p); + if (!IS (label_identifier, IDENTIFIER)) + FORWARD (label_identifier); + + /* Create LABEL_DECL if necessary and chain it in both current block and bind + expression. */ + if (TAX_TREE_DECL (TAX (label_identifier)) == NULL_TREE) + { + tree label_decl = build_decl (a68_get_node_location (label_identifier), + LABEL_DECL, + a68_get_mangled_identifier (NSYMBOL (label_identifier)), + void_type_node); + TAX_TREE_DECL (TAX (label_identifier)) = label_decl; + } + + MOID (label_identifier) = M_VOID; + return fold_build2_loc (a68_get_node_location (p), + COMPOUND_EXPR, + CTYPE (jump_mode), + fold_build1_loc (a68_get_node_location (p), + GOTO_EXPR, + void_type_node, + a68_lower_tree (label_identifier, ctx)), + a68_get_skip_tree (jump_mode)); +} + +/* Lower a parameter into a chain of PARAM_DECLs. + + parameter : declarer, identifier; + parameter, comma symbol, identifier. +*/ + +tree +a68_lower_parameter (NODE_T *p, LOW_CTX_T ctx) +{ + tree prev_parm_decls = NULL_TREE; + NODE_T *identifier = NO_NODE; + if (IS (SUB (p), PARAMETER)) + { + prev_parm_decls = a68_lower_tree (SUB (p), ctx); + identifier = NEXT (NEXT (SUB (p))); + } + else + identifier = NEXT (SUB (p)); + + /* Create the PARM_DECL. */ + tree parm_decl = build_decl (a68_get_node_location (p), + PARM_DECL, + a68_get_mangled_identifier (NSYMBOL (identifier)), + CTYPE (MOID (identifier))); + DECL_CONTEXT (parm_decl) = current_function_decl; + DECL_ARG_TYPE (parm_decl) = TREE_TYPE (parm_decl); + TAX_TREE_DECL (TAX (identifier)) = parm_decl; + + layout_decl (parm_decl, 0); + + if (prev_parm_decls != NULL) + return chainon (prev_parm_decls, parm_decl); + else + return parm_decl; +} + +/* Lower a list of parameters into a chain of PARAM_DECLs. + + parameter list : parameter; + parameter list; comma symbol; parameter. +*/ + +tree +a68_lower_parameter_list (NODE_T *p, LOW_CTX_T ctx) +{ + tree parm_decl = NULL_TREE; + tree prev_parm_decls = NULL_TREE; + if (IS (SUB (p), PARAMETER_LIST)) + { + prev_parm_decls = a68_lower_tree (SUB (p), ctx); + parm_decl = a68_lower_tree (NEXT (NEXT (SUB (p))), ctx); + } + else + parm_decl = a68_lower_tree (SUB (p), ctx); + + gcc_assert (parm_decl != NULL_TREE); + if (prev_parm_decls != NULL) + return chainon (prev_parm_decls, parm_decl); + else + return parm_decl; +} + +/* Lower a parameter pack into a chain of PARAM_DECLs. + + parameter pack : open symbol, parameter list, close symbol. +*/ + +tree +a68_lower_parameter_pack (NODE_T *p, LOW_CTX_T ctx) +{ + /* Lower the contained PARAMETER_LIST. */ + return a68_lower_tree (NEXT (SUB (p)), ctx); +} + +/* Lower an applied operator. + + Applied operators lower into a function object that gets one argument in + case of monadic operators, or two arguments in case of dyadic operators. */ + +tree +a68_lower_operator (NODE_T *p, LOW_CTX_T ctx ATTRIBUTE_UNUSED) +{ + /* This is an user defined operator. Handle it in a similar way than applied + identifiers. */ + tree func_decl = TAX_TREE_DECL (TAX (p)); + if (func_decl == NULL_TREE) + { + if (IN_PROC (TAX (p))) + func_decl = a68_make_proc_identity_declaration_decl (p); + else + func_decl = a68_make_identity_declaration_decl (p); + TAX_TREE_DECL (TAX (p)) = func_decl; + } + return func_decl; +} diff --git a/gcc/algol68/a68-low.cc b/gcc/algol68/a68-low.cc new file mode 100644 index 00000000000..69376081b03 --- /dev/null +++ b/gcc/algol68/a68-low.cc @@ -0,0 +1,1153 @@ +/* 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 + <http://www.gnu.org/licenses/>. */ + +#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. */ + +tree +a68_get_mangled_identifier (const char *name) +{ + char *mangled_name = (char *) alloca (strlen (name) + 1); + memcpy (mangled_name, name, strlen (name) + 1); + + /* Avoid MONADS and NOMADS. */ + for (char *p = mangled_name; *p != '\0'; ++p) + { + if (strchr (MONADS, *p) != NULL || strchr (NOMADS, *p) != NULL + || strchr (":=", *p)) + *p = '_'; + } + + return get_identifier (mangled_name); +} + +/* 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) +{ + /* 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))); + tree func_decl = build_decl (UNKNOWN_LOCATION, + FUNCTION_DECL, + a68_get_mangled_identifier (NSYMBOL (identifier)), + func_type); + 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 an identity declaration. */ + +tree +a68_make_identity_declaration_decl (NODE_T *identifier) +{ + tree type = CTYPE (MOID (identifier)); + + tree decl = build_decl (a68_get_node_location (identifier), + VAR_DECL, + a68_get_mangled_identifier (NSYMBOL (identifier)), + type); + TREE_PUBLIC (decl) = 0; +#if 0 + if (!IS_REF (MOID (identifier))) + TREE_CONSTANT (decl) = 1; +#endif + 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) +{ + gcc_assert (IS_REF (MOID (identifier))); + + MOID_T *mode = MOID (identifier); + bool use_pointer = ((HEAP (TAX (identifier)) == HEAP_SYMBOL) + || HAS_ROWS (SUB (MOID (identifier)))); + tree type = use_pointer ? CTYPE (mode) : CTYPE (SUB (mode)); + tree decl = build_decl (a68_get_node_location (identifier), + VAR_DECL, + a68_get_mangled_identifier (NSYMBOL (identifier)), + type); + TREE_PUBLIC (decl) = 0; + 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 <constructor_elt, va_gc> *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 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 ATTRIBUTE_UNUSED) +{ + /* 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 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 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 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.level = 0; + return a68_lower_tree (p, top_ctx); +} -- 2.30.2