Signed-off-by: Jose E. Marchesi <[email protected]>
gcc/ChangeLog
* algol68/a68-low-coercions.cc: New file.
* algol68/a68-low-generator.cc: Likewise.
* algol68/a68-low-units.cc: Likewise.
---
gcc/algol68/a68-low-coercions.cc | 471 ++++++++++++
gcc/algol68/a68-low-generator.cc | 533 +++++++++++++
gcc/algol68/a68-low-units.cc | 1191 ++++++++++++++++++++++++++++++
3 files changed, 2195 insertions(+)
create mode 100644 gcc/algol68/a68-low-coercions.cc
create mode 100644 gcc/algol68/a68-low-generator.cc
create mode 100644 gcc/algol68/a68-low-units.cc
diff --git a/gcc/algol68/a68-low-coercions.cc b/gcc/algol68/a68-low-coercions.cc
new file mode 100644
index 00000000000..b9e1acee9ce
--- /dev/null
+++ b/gcc/algol68/a68-low-coercions.cc
@@ -0,0 +1,471 @@
+/* Lower Algol 68 coercions 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 a dereferencing coercion. */
+tree
+a68_lower_dereferencing (NODE_T *p, LOW_CTX_T ctx)
+{
+ return a68_low_deref (a68_lower_tree (SUB (p), ctx), SUB (p));
+}
+
+/* Lower an uniting coercion. */
+
+tree
+a68_lower_uniting (NODE_T *p, LOW_CTX_T ctx)
+{
+ tree coercend_tree = a68_lower_tree (SUB (p), ctx);
+
+ if (MOID (p) == M_ROWS)
+ {
+ /* ROWS is a mode to which any ROW mode can be strongly coerced. It is
+ used as the mode of the second operand of the ELEMS, LWB and UPB
+ operators. The coercion is expressed in the parse tree via uniting.
+ This results in replacing the multiple with a "rows" value that
+ contains dimension and bounds information. */
+ if (A68_ROW_TYPE_P (TREE_TYPE (coercend_tree)))
+ return a68_rows_value (coercend_tree);
+ else if (A68_UNION_TYPE_P (TREE_TYPE (coercend_tree)))
+ {
+ /* coercend_tree is expanded more than once below. */
+ coercend_tree = save_expr (coercend_tree);
+
+ /* Union of row modes. We should create a rows value for the
currently
+ selected value. */
+ a68_push_range (M_ROWS);
+ tree done_label = build_decl (UNKNOWN_LOCATION,
+ LABEL_DECL,
+ get_identifier ("done_label%"),
+ void_type_node);
+ DECL_CONTEXT (done_label) = a68_range_context ();
+ a68_add_decl (done_label);
+ a68_add_decl_expr (fold_build1 (DECL_EXPR, TREE_TYPE (done_label),
done_label));
+ tree rows = a68_lower_tmpvar ("rows%", CTYPE (M_ROWS),
+ a68_get_skip_tree (M_ROWS));
+ tree coercend_overhead = a68_union_overhead (coercend_tree);
+ tree overhead = a68_lower_tmpvar ("overhead%", TREE_TYPE
(coercend_overhead),
+ coercend_overhead);
+ int field_index = 0;
+ for (tree field = TYPE_FIELDS (TREE_TYPE (a68_union_cunion
(coercend_tree)));
+ field;
+ field = DECL_CHAIN (field))
+ {
+ a68_push_range (M_VOID);
+ {
+ /* Set rows% to the rows value computed from
coercend_tree.FIELD,
+ which is of some multiple type. */
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, CTYPE (M_ROWS),
+ rows,
+ a68_rows_value
(a68_union_alternative (coercend_tree,
+
field_index))));
+ a68_add_stmt (fold_build1 (GOTO_EXPR, void_type_node,
done_label));
+ a68_add_stmt (a68_get_skip_tree (M_VOID));
+ }
+ tree process_entry = a68_pop_range ();
+
+ /* IF overhead = field_index THEN rows% = rows_from_multiple FI */
+ a68_add_stmt (fold_build3 (COND_EXPR,
+ a68_void_type,
+ fold_build2 (EQ_EXPR,
+ TREE_TYPE (overhead),
+ overhead,
+ build_int_cst (TREE_TYPE
(overhead), field_index)),
+ process_entry,
+ a68_get_skip_tree (M_VOID)));
+ field_index += 1;
+ }
+
+ /* This should not be reached. Emit run-time error. */
+ {
+ 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_UNREACHABLE,
+ void_type_node, 2,
+ filename,
+ build_int_cst (unsigned_type_node,
lineno));
+ a68_add_stmt (call);
+ }
+
+ a68_add_stmt (build1 (LABEL_EXPR, void_type_node, done_label));
+ a68_add_stmt (rows);
+ return a68_pop_range ();
+ }
+ else
+ {
+ debug_tree (TREE_TYPE (coercend_tree));
+ gcc_assert (A68_ROWS_TYPE_P (TREE_TYPE (coercend_tree)));
+ return coercend_tree;
+ }
+ }
+ else if (IS_UNION (MOID (SUB (p))))
+ {
+ /* We have to extract the value of the coercend union. */
+ a68_push_range (MOID (p));
+ {
+ MOID_T *coercend_mode = MOID (SUB (p));
+ MOID_T *coercee_mode = MOID (p);
+
+ /* Temporaries for the coercend's components. */
+ tree coercend = a68_lower_tmpvar ("coercend%", TREE_TYPE
(coercend_tree), coercend_tree);
+ tree cval = a68_union_cunion (coercend);
+ tree coverhead = a68_union_overhead (coercend);
+ tree coercend_value = a68_lower_tmpvar ("coercend_value%", TREE_TYPE
(cval), cval);
+ tree coercend_overhead = a68_lower_tmpvar ("coercend_overhead%",
sizetype, coverhead);
+
+ /* Create the coercee. */
+ tree coercee = a68_lower_tmpvar ("coercee%",
+ CTYPE (MOID (p)),
+ a68_get_skip_tree (MOID (p)));
+ tree coercee_value = a68_union_cunion (coercee);
+
+ /* First translate overhead. This is crude, but it works. */
+ int idx = 0;
+ tree coercee_overhead = size_zero_node;
+ while (EQUIVALENT (coercend_mode) != NO_MOID)
+ coercend_mode = EQUIVALENT (coercend_mode);
+ for (PACK_T *pack = PACK (coercend_mode); pack != NO_PACK; FORWARD
(pack))
+ {
+ coercee_overhead = fold_build3 (COND_EXPR,
+ sizetype,
+ fold_build2 (EQ_EXPR,
+ sizetype,
+ coercend_overhead,
+ size_int (idx)),
+ size_int (a68_united_mode_index
(coercee_mode, MOID (pack))),
+ coercee_overhead);
+ idx++;
+ }
+ a68_add_stmt (a68_union_set_overhead (coercee, coercee_overhead));
+
+ /* Now copy over the value. This of course relies on the fact the
+ value of the coercend is smaller or of the same size than the value
+ of the built union. */
+ a68_add_stmt (a68_lower_memcpy (fold_build1 (ADDR_EXPR,
+ build_pointer_type
(TREE_TYPE (coercee_value)),
+ coercee_value),
+ fold_build1 (ADDR_EXPR,
+ build_pointer_type
(TREE_TYPE (coercend_value)),
+ coercend_value),
+ size_in_bytes (TREE_TYPE
(coercend_value))));
+ a68_add_stmt (coercee);
+ }
+ return a68_pop_range ();
+ }
+ else
+ {
+ /* Produce a united mode one of whose component modes is the mode of the
+ coercend. */
+ return a68_union_value (MOID (p), coercend_tree, MOID (SUB (p)));
+ }
+}
+
+/* Lower a rowing coercion. */
+
+tree
+a68_lower_rowing (NODE_T *p, LOW_CTX_T ctx)
+{
+ MOID_T *mode = MOID (p);
+ bool did_deref = false;
+
+ /* If the primary is a REF, we need to dereference it to get the referred
+ value. */
+ tree primary = NULL_TREE;
+ tree orig_primary = NULL_TREE;
+ MOID_T *target_mode = NO_MOID;
+ if (IS_REF (mode))
+ {
+ gcc_assert (IS_REF (MOID (SUB (p))));
+ did_deref = true;
+ target_mode = SUB (mode);
+
+ a68_push_range (mode);
+ /* Note that we have to consolidate because we need a pointer to compare
+ to NIL below. */
+ orig_primary = a68_lower_tmpvar ("orig_primary%",
+ CTYPE (MOID (SUB (p))),
+ a68_consolidate_ref (MOID (SUB (p)),
+ a68_lower_tree (SUB
(p), ctx)));
+ primary = a68_low_deref (orig_primary, SUB (p));
+ }
+ else
+ {
+ target_mode = mode;
+ primary = a68_lower_tree (SUB (p), ctx);
+ /* The primary gets expanded more than once below. */
+ primary = save_expr (primary);
+ }
+
+ /* Perform the rowing in the primary. */
+ tree ssize_one_node = fold_convert (ssizetype, size_one_node);
+ tree rowed_primary = NULL_TREE;
+ if (DIM (DEFLEX (target_mode)) >= 2)
+ {
+ /* []A -> [,]A */
+
+ /* First determine the number of dimensions of the resulting
+ multiple. */
+ tree primary_dimensions = a68_multiple_dimensions (primary);
+ gcc_assert (TREE_CODE (primary_dimensions) == INTEGER_CST);
+ int dim = tree_to_shwi (primary_dimensions) + 1;
+
+ /* Compute bounds. */
+ tree *lower_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+ tree *upper_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+
+ lower_bounds[0] = ssize_one_node;
+ upper_bounds[0] = ssize_one_node;
+ for (int d = 1; d < dim; ++d)
+ {
+ lower_bounds[d] = a68_multiple_lower_bound (primary, ssize_int (d -
1));
+ upper_bounds[d] = a68_multiple_upper_bound (primary, ssize_int (d -
1));
+ }
+
+ rowed_primary = a68_row_value (CTYPE (target_mode), dim,
+ a68_multiple_elements (primary),
+ a68_multiple_elements_size (primary),
+ lower_bounds, upper_bounds);
+ free (lower_bounds);
+ free (upper_bounds);
+ }
+ else
+ {
+ /* A -> []A */
+ tree row_type = CTYPE (target_mode);
+ tree lower_bound = ssize_one_node;
+ tree upper_bound = ssize_one_node;
+ tree elements = (did_deref
+ ? orig_primary
+ : fold_build1 (ADDR_EXPR,
+ build_pointer_type (TREE_TYPE (primary)),
+ build_constructor_va (build_array_type
(TREE_TYPE (primary),
+
build_index_type (size_zero_node)),
+ 1, size_zero_node,
primary)));
+ tree elements_type = a68_row_elements_type (row_type);
+ tree elements_size = size_in_bytes (elements_type);
+ rowed_primary = a68_row_value (row_type, 1,
+ elements, elements_size,
+ &lower_bound, &upper_bound);
+ }
+
+ /* Build a ref if we rowed a ref. */
+ if (did_deref)
+ {
+ tree pointer_type = build_pointer_type (TREE_TYPE (rowed_primary));
+ rowed_primary = fold_build1 (ADDR_EXPR, pointer_type, rowed_primary);
+ /* Rowing NIL yields NIL. */
+ rowed_primary = fold_build3_loc (a68_get_node_location (p),
+ COND_EXPR,
+ pointer_type,
+ fold_build2 (EQ_EXPR,
+ pointer_type,
+ fold_convert (pointer_type,
orig_primary),
+ build_int_cst
(pointer_type, 0)),
+ build_int_cst (pointer_type, 0),
+ rowed_primary);
+ a68_add_stmt (rowed_primary);
+ rowed_primary = a68_pop_range ();
+ }
+
+ return rowed_primary;
+}
+
+/* Lower a widening coercion.
+
+ Widening allows the following conversions of mode:
+
+ LONGSETY INT to LONGSETY REAL
+ LONGSETY REAL to LONGSETY COMPL
+ LONGSETY BITS to []BOOL
+ LONGSETY BYTES to []CHAR */
+
+tree
+a68_lower_widening (NODE_T *p, LOW_CTX_T ctx)
+{
+ if (MOID (p) == M_REAL
+ || MOID (p) == M_LONG_REAL
+ || MOID (p) == M_LONG_LONG_REAL)
+ {
+ return convert_to_real (CTYPE (MOID (p)), a68_lower_tree (SUB (p), ctx));
+ }
+ if (MOID (p) == M_COMPLEX
+ || MOID (p) == M_LONG_COMPLEX
+ || MOID (p) == M_LONG_LONG_COMPLEX)
+ {
+ return a68_complex_widen_from_real (MOID (p),
+ a68_lower_tree (SUB (p), ctx));
+ }
+ else if (MOID (p) == M_ROW_BOOL)
+ {
+ /* Widen a LONGSETY BITS to a row of BOOLs. */
+ tree coercend = a68_lower_tree (SUB (p), ctx);
+ tree coercend_type = TREE_TYPE (coercend);
+ HOST_WIDE_INT bits_size = int_size_in_bytes (coercend_type);
+ gcc_assert (bits_size != -1);
+ bits_size = bits_size * 8;
+
+ tree pointer_to_bool_type = build_pointer_type (a68_bool_type);
+ a68_push_range (M_ROW_BOOL);
+ /* First allocate space for the elements. */
+ tree elements = a68_lower_tmpvar ("elements%",
+ pointer_to_bool_type,
+ a68_lower_alloca (a68_bool_type,
+ fold_build2
(MULT_EXPR,
+ sizetype,
+ size_int
(bits_size),
+
size_in_bytes (a68_bool_type))));
+
+ /* Set the elements, each element is a BOOL which is TRUE if the
+ corresponding bit in the coercend is set, FALSE otherwise. */
+ tree coercend_one_node = build_int_cst (coercend_type, 1);
+ coercend = save_expr (coercend);
+ for (HOST_WIDE_INT bit = 0; bit < bits_size; ++bit)
+ {
+ tree offset = fold_build2 (MULT_EXPR, sizetype,
+ size_int (bit), size_in_bytes
(a68_bool_type));
+ tree bit_set = fold_convert (a68_bool_type,
+ fold_build2 (BIT_AND_EXPR, coercend_type,
+ fold_build2 (RSHIFT_EXPR,
coercend_type,
+ coercend,
+ build_int_cst
(coercend_type,
+
bits_size - 1 - bit)),
+ coercend_one_node));
+
+ a68_add_stmt (fold_build2 (MODIFY_EXPR,
+ a68_bool_type,
+ fold_build2 (MEM_REF,
+ a68_bool_type,
+ fold_build2
(POINTER_PLUS_EXPR,
+
pointer_to_bool_type,
+ elements,
+ offset),
+ fold_convert
(pointer_to_bool_type,
+
integer_zero_node)),
+ bit_set));
+ }
+
+ /* Create multiple. */
+ tree lower_bound = ssize_int (1);
+ tree upper_bound = ssize_int (bits_size);
+ tree elements_size = fold_build2 (MULT_EXPR, sizetype,
+ size_int (bits_size),
+ size_in_bytes (a68_bool_type));
+ tree multiple = a68_row_value (CTYPE (M_ROW_BOOL), 1 /* dim */,
+ elements, elements_size,
+ &lower_bound, &upper_bound);
+ a68_add_stmt (multiple);
+ return a68_pop_range ();
+ }
+ else
+ {
+ fatal_error (a68_get_node_location (p),
+ "cannot do widening from %s to %s",
+ a68_moid_to_string (MOID (SUB (p)), MOID_ERROR_WIDTH, SUB
(p)),
+ a68_moid_to_string (MOID (p), MOID_ERROR_WIDTH, p));
+ gcc_unreachable ();
+ }
+}
+
+/* Lower a voiding coercion.
+
+ The voiding lowers into a compound expression with the voided expression
+ (for side-effects) and returns EMPTY. */
+
+tree
+a68_lower_voiding (NODE_T *p, LOW_CTX_T ctx)
+{
+ return fold_build2_loc (a68_get_node_location (p),
+ COMPOUND_EXPR,
+ a68_void_type,
+ a68_lower_tree (SUB (p), ctx),
+ a68_get_empty ());
+}
+
+/* Lower a proceduring coercion.
+
+ proceduring : jump.
+
+ In the Revised language only jump statements can be procedured. The
+ coercion results in a new function whose body is the jump instruction. */
+
+tree
+a68_lower_proceduring (NODE_T *p, LOW_CTX_T ctx)
+{
+ tree jump = a68_lower_tree (SUB (p), ctx);
+
+ tree procedured_goto = a68_make_anonymous_routine_decl (MOID (p));
+ a68_add_decl (procedured_goto);
+ a68_add_decl_expr (fold_build1_loc (a68_get_node_location (p),
+ DECL_EXPR,
+ TREE_TYPE (procedured_goto),
+ procedured_goto));
+ announce_function (procedured_goto);
+
+ a68_push_function_range (procedured_goto, CTYPE (SUB (MOID (p))));
+ a68_pop_function_range (jump);
+ return fold_build1 (ADDR_EXPR,
+ build_pointer_type (TREE_TYPE (procedured_goto)),
+ procedured_goto);
+}
+
+/* Lower a deproceduring coercion.
+ The deproceduring lowers into a call expression. */
+
+tree
+a68_lower_deproceduring (NODE_T *p, LOW_CTX_T ctx)
+{
+ tree func = a68_lower_tree (SUB (p), ctx);
+
+ if (POINTER_TYPE_P (TREE_TYPE (func)))
+ {
+ if (TREE_CODE (func) == ADDR_EXPR)
+ func = TREE_OPERAND (func, 0);
+ else
+ func = fold_build1 (INDIRECT_REF,
+ TREE_TYPE (TREE_TYPE (func)),
+ func);
+ }
+
+ return build_call_expr_loc (a68_get_node_location (p), func, 0);
+}
diff --git a/gcc/algol68/a68-low-generator.cc b/gcc/algol68/a68-low-generator.cc
new file mode 100644
index 00000000000..5c4d65569b3
--- /dev/null
+++ b/gcc/algol68/a68-low-generator.cc
@@ -0,0 +1,533 @@
+/* Lower generators.
+ 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"
+
+
+typedef tree (*allocator_t) (tree, tree);
+
+/* Lower to code that fill in BOUNDS and elements pointers in the given buffer
+ pointed by BUFFER at offset OFFSET according to the mode MODE, and evals to
+ BUFFER. */
+
+static tree
+fill_in_buffer (tree buffer, tree offset, tree_stmt_iterator *bounds, MOID_T
*m,
+ allocator_t allocator)
+{
+ tree filler = NULL_TREE;
+ tree type = CTYPE (m);
+ tree pointer_type = build_pointer_type (type);
+
+ a68_push_stmt_list (M_VOID);
+
+ if (m == M_INT || m == M_BOOL || m == M_CHAR || m == M_REAL || IS_REF (m))
+ {
+ tree val_address = fold_build2 (POINTER_PLUS_EXPR, pointer_type, buffer,
offset);
+ tree init_val = a68_get_skip_tree (m);
+ tree modify = fold_build2 (MODIFY_EXPR,
+ type,
+ fold_build1 (INDIRECT_REF, type, val_address),
+ init_val);
+ a68_add_stmt (modify);
+ }
+ else if (!HAS_ROWS (m))
+ {
+ /* This mode has no rows. We can just fill in with zeroes, which
+ translates into SKIP values for all possibly contained types. */
+ tree call = builtin_decl_explicit (BUILT_IN_MEMSET);
+ call = build_call_expr_loc (UNKNOWN_LOCATION, call, 3,
+ buffer,
+ integer_zero_node,
+ fold_convert (sizetype, size_in_bytes (CTYPE
(m))));
+ a68_add_stmt (call);
+ }
+ else if (m == M_STRING)
+ {
+ /* Strings are rows but handled especially as they are created empty and
+ don't feature bounds in the formal declarer. */
+
+ /* First the descriptor. */
+ tree pointer_byte_size = size_int (POINTER_SIZE / BITS_PER_UNIT);
+ tree lb_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer),
buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, void_type_node,
+ fold_build1 (INDIRECT_REF, ssizetype,
lb_address),
+ ssize_int (1)));
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ tree ub_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer),
buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, void_type_node,
+ fold_build1 (INDIRECT_REF, ssizetype,
ub_address),
+ ssize_int (0)));
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ tree stride_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE
(buffer), buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, void_type_node,
+ fold_build1 (INDIRECT_REF, sizetype,
stride_address),
+ size_in_bytes (a68_char_type)));
+
+ /* The data is an empty string, i.e NULL. */
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ tree elems_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer),
buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, void_type_node,
+ fold_build1 (INDIRECT_REF, build_pointer_type
(a68_char_type),
+ elems_address),
+ build_int_cst (build_pointer_type
(a68_char_type), 0)));
+
+ /* The size of the elements is zero. */
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ tree elems_size_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE
(buffer), buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, void_type_node,
+ fold_build1 (INDIRECT_REF, build_pointer_type
(a68_char_type),
+ elems_size_address),
+ size_zero_node));
+ }
+ else if (A68_ROW_TYPE_P (type))
+ {
+ /* If the row mode is flexible we can deflex it now: these also must have
+ bounds specified for them, with the only exception of strings/flexible
+ rows of chars, which are handled above. Note we cannot use DEFLEXED
+ here because that contains the fully deflexed mode. For example,
+ DEFLEXED returns [][]INT for FLEX[]FLEX[]INT, and we want []FLEX[]INT
+ instead. */
+ if (IS_FLEX (m))
+ m = SUB (m);
+
+ /* Consume two bounds from BOUNDS for each dimension and patch them at
+ their right offsets. Note that we have to process from upper
+ dimension to lower dimension so we can calculate the stride as we
+ go. */
+ size_t dim = DIM (m);
+
+ /* Collect lower and upper bounds and calculate the number of elements of
+ the multiple. */
+ tree *lower_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+ tree *upper_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+ tree num_elems = NULL_TREE;
+ for (size_t i = 0; i < dim; ++i)
+ {
+ /* Note we have to convert the bounds from CTYPE(M_INT) to
+ ssizetype. */
+ lower_bounds[i] = fold_convert (ssizetype, save_expr (tsi_stmt
(*bounds)));
+ tsi_next (bounds);
+ upper_bounds[i] = fold_convert (ssizetype, save_expr (tsi_stmt
(*bounds)));
+ tsi_next (bounds);
+
+ tree dim_num_elems
+ = fold_build2 (PLUS_EXPR, sizetype,
+ fold_convert (sizetype,
+ fold_build2 (MINUS_EXPR, ssizetype,
+ upper_bounds[i],
lower_bounds[i])),
+ size_one_node);
+ dim_num_elems = fold_build3 (COND_EXPR,
+ sizetype,
+ fold_build2 (LT_EXPR, ssizetype,
+ upper_bounds[i],
lower_bounds[i]),
+ size_zero_node,
+ dim_num_elems);
+ if (num_elems == NULL_TREE)
+ num_elems = dim_num_elems;
+ else
+ num_elems = fold_build2 (MULT_EXPR, sizetype, num_elems,
dim_num_elems);
+ }
+
+ /* Calculate strides. */
+ tree *strides = (tree *) xmalloc (sizeof (tree) * dim);
+ a68_multiple_compute_strides (type, dim, lower_bounds, upper_bounds,
strides);
+
+ /* Now emit instructions to patch the bounds and strides. */
+ tree pointer_byte_size = size_int (POINTER_SIZE / BITS_PER_UNIT);
+ for (size_t i = 0; i < dim; ++i)
+ {
+ /* Lower bound. */
+ tree lb_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer),
buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR,
+ void_type_node,
+ fold_build1 (INDIRECT_REF, ssizetype,
lb_address),
+ lower_bounds[i]));
+ /* Upper bound. */
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ tree ub_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer),
buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR,
+ void_type_node,
+ fold_build1 (INDIRECT_REF, ssizetype,
ub_address),
+ upper_bounds[i]));
+ /* Stride. */
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ tree stride_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE
(buffer), buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR,
+ void_type_node,
+ fold_build1 (INDIRECT_REF, sizetype,
stride_address),
+ strides[i]));
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ }
+ free (lower_bounds);
+ free (upper_bounds);
+ free (strides);
+
+ /* Now allocate space for the elements. */
+ MOID_T *elem_mode = SUB (m);
+ tree elem_size = fold_convert (sizetype, size_in_bytes (CTYPE
(elem_mode)));
+ tree elems_size = save_expr (fold_build2 (MULT_EXPR, sizetype,
elem_size, num_elems));
+ tree elemsptr = (*allocator) (CTYPE (elem_mode), elems_size);
+ elemsptr = save_expr (elemsptr);
+
+ /* And initialize them. */
+ if (elem_mode == M_INT || elem_mode == M_BOOL || elem_mode == M_CHAR
+ || elem_mode == M_REAL || IS_REF (elem_mode))
+ {
+ /* Memsetting the buffer with either zeroes or ones satisfies the
+ SKIP value for these modes. */
+ tree call = builtin_decl_explicit (BUILT_IN_MEMSET);
+ call = build_call_expr_loc (UNKNOWN_LOCATION, call, 3,
+ elemsptr,
+ integer_zero_node,
+ elems_size);
+ a68_add_stmt (call);
+ }
+ else
+ {
+ /* Recurse in a loop to fill in elements. */
+ a68_push_range (NULL);
+ tree num_elems_var = a68_lower_tmpvar ("numelems%", size_type_node,
+ num_elems);
+ tree index = a68_lower_tmpvar ("index%", size_type_node,
size_zero_node);
+ tree elems_var = a68_lower_tmpvar ("elems%", TREE_TYPE (elemsptr),
+ elemsptr);
+ tree elem_offset = a68_lower_tmpvar ("elem_offset%", size_type_node,
+ size_zero_node);
+
+ /* Begin of loop body. */
+ a68_push_range (NULL);
+ a68_add_stmt (fold_build1 (EXIT_EXPR,
+ void_type_node,
+ fold_build2 (EQ_EXPR,
+ size_type_node,
+ index, num_elems_var)));
+ a68_add_stmt (fill_in_buffer (elems_var, elem_offset, bounds,
elem_mode,
+ allocator));
+ /* Increase elem_offset */
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, sizetype,
+ elem_offset,
+ fold_build2 (PLUS_EXPR, sizetype,
+ elem_offset, elem_size)));
+ /* 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 (a68_pop_range ());
+ }
+
+ /* Patch the elements% field. */
+ tree elems_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer),
buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR,
+ void_type_node,
+ fold_build1 (INDIRECT_REF,
+ build_pointer_type (CTYPE
(elem_mode)), elems_address),
+ elemsptr));
+ /* Patch the elements_size% field. */
+ offset = fold_build2 (PLUS_EXPR, sizetype, offset, pointer_byte_size);
+ tree elems_size_address = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE
(buffer), buffer, offset);
+ a68_add_stmt (fold_build2 (MODIFY_EXPR,
+ void_type_node,
+ fold_build1 (INDIRECT_REF,
+ sizetype,
+ elems_size_address),
+ elems_size));
+ }
+ else if (A68_STRUCT_TYPE_P (type))
+ {
+ /* Initialize the struct's fields in the allocated buffer. */
+ tree base = a68_lower_tmpvar ("base%", TREE_TYPE (buffer),
+ fold_build2 (POINTER_PLUS_EXPR,
+ TREE_TYPE (buffer),
+ buffer, offset));
+ PACK_T *field_pack = PACK (m);
+ for (tree field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ {
+ gcc_assert (COMPLETE_TYPE_P (TREE_TYPE (field)));
+ // printf ("BYTE_POSITION\n");
+ // debug_tree (byte_position (field));
+ a68_add_stmt (fill_in_buffer (base, byte_position (field),
+ bounds, MOID (field_pack), allocator));
+ FORWARD (field_pack);
+ }
+ }
+ else if (A68_UNION_TYPE_P (type))
+ {
+ /* Union values are initialized with an overhead of (sizetype) -1, which
+ means it is not initialized. Note that row declarers in united modes
+ are formal declarers, so they never contribute bounds. */
+ tree overhead_address
+ = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer), buffer, offset);
+ tree uninitialized = fold_convert (sizetype, build_minus_one_cst
(ssizetype));
+ a68_add_stmt (fold_build2 (MODIFY_EXPR, void_type_node,
+ fold_build1 (INDIRECT_REF, sizetype,
overhead_address),
+ uninitialized));
+#if 0
+ /* Set the rest of the union with zeroes. */
+ tree value_address
+ = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (buffer),
+ buffer,
+ fold_build2 (PLUS_EXPR, sizetype, offset, size_in_bytes
(sizetype)));
+
+ tree value_field = TREE_CHAIN (TYPE_FIELDS (type));
+ tree call = builtin_decl_explicit (BUILT_IN_MEMSET);
+ call = build_call_expr_loc (UNKNOWN_LOCATION, call, 3,
+ value_address,
+ integer_zero_node,
+ size_in_bytes (TREE_TYPE (value_field)));
+ a68_add_stmt (call);
+#endif
+ }
+ else
+ gcc_unreachable ();
+
+ a68_add_stmt (buffer);
+ filler = a68_pop_stmt_list ();
+ TREE_TYPE (filler) = pointer_type;
+ return filler;
+}
+
+/* Lower to code that generates storage for a value of mode M, using bounds
+ from BOUNDS. */
+
+static tree
+gen_mode (MOID_T *m, tree_stmt_iterator *bounds, allocator_t allocator)
+{
+ /* Allocate space for the value and fill it. */
+ tree buffer = (*allocator) (CTYPE (m), size_in_bytes (CTYPE (m)));
+ buffer = save_expr (buffer);
+ return fill_in_buffer (buffer, size_zero_node, bounds, m, allocator);
+}
+
+/* Collect row bounds from BOUNDS.
+ Lower bounds are optional, and if not found they default to 1. */
+
+static void
+collect_bounds (NODE_T *p, LOW_CTX_T ctx)
+{
+ for (; p != NO_NODE; FORWARD (p))
+ {
+ if (IS (p, BOUNDS_LIST))
+ collect_bounds (SUB (p), ctx);
+ else if (IS (p, BOUND))
+ collect_bounds (SUB (p), ctx);
+ else if (IS (p, UNIT))
+ {
+ /* First the lower bound. */
+ tree lower_bound;
+ if (NEXT (p) != NO_NODE && IS (NEXT (p), COLON_SYMBOL))
+ {
+ lower_bound = a68_lower_tree (p, ctx);
+ p = NEXT_NEXT (p);
+ }
+ else
+ /* Default lower bound. */
+ lower_bound = integer_one_node;
+
+ /* Now the upper bound. */
+ tree upper_bound = a68_lower_tree (p, ctx);
+
+ /* See the comment for collect_declarer_bounds for an explanation for
+ the usage of save_expr here. */
+ a68_add_stmt (save_expr (lower_bound));
+ a68_add_stmt (save_expr (upper_bound));
+ }
+ }
+}
+
+/* Append all the bounds found in the given declarer in the current statements
+ list. */
+
+static void
+collect_declarer_bounds_1 (NODE_T *p, LOW_CTX_T ctx)
+{
+ for (; p != NO_NODE; FORWARD (p))
+ {
+ if (IS (p, BOUNDS))
+ collect_bounds (SUB (p), ctx);
+ else if (IS (p, INDICANT) && IS_LITERALLY (p, "STRING"))
+ return;
+ else if (IS (p, INDICANT))
+ {
+ if (TAX (p) != NO_TAG && HAS_ROWS (MOID (TAX (p))))
+ /* Continue from definition at MODE A = .... */
+ collect_declarer_bounds_1 (NEXT_NEXT (NODE (TAX (p))), ctx);
+ }
+ else if (IS (p, DECLARER)
+ && (IS_UNION (MOID (p)) || !HAS_ROWS (MOID (p))))
+ return;
+ else
+ collect_declarer_bounds_1 (SUB (p), ctx);
+ }
+}
+
+/* Given a declarer node, return a statements list with all the expressions of
+ the bounds within it.
+
+ Note that the language rules mandates that the bounds expression shall be
+ evaluated just once even when they are used by several generators, such as
+ in
+
+ [n +:= 1]real a, b;
+
+ Therefore the expressions are saved in save_exprs and the statements list
+ is cached in the CDECL field of the parse tree node. */
+
+static tree
+collect_declarer_bounds (NODE_T *p, LOW_CTX_T ctx)
+{
+ if (CDECL (p) == NULL_TREE)
+ {
+ a68_push_stmt_list (M_VOID);
+ collect_declarer_bounds_1 (SUB (p), ctx);
+ CDECL (p) = a68_pop_stmt_list ();
+ }
+
+ return CDECL (p);
+}
+
+/* Low the elaboration of a generator.
+
+ The lowered code evaluates to a pointer.
+
+ DECLARER is the actual declarer passed to the generator.
+
+ MODE is the mode of the value to generate.
+
+ HEAP is true if we are lowering a heap generator, false if we are lowering a
+ LOC generator. */
+
+tree
+a68_low_generator (NODE_T *declarer,
+ MOID_T *mode,
+ bool heap, LOW_CTX_T ctx)
+{
+ /* If the declarer is a mode indicant which has a recursive definition then
+ we need to lower to a function which gets immediately called rather than
+ an expression, to handle the recursivity. In that case, though, we need
+ to always heap allocated memory for obvious reasons, which sucks, but such
+ is life. */
+
+ if (IS (SUB (declarer), INDICANT) && TAX (SUB (declarer)) != NO_TAG
+ && IS_RECURSIVE (TAX (SUB (declarer))))
+ {
+ if (TAX_TREE_DECL (TAX (SUB (declarer))) != NULL_TREE)
+ {
+ /* This is a recursive mode indicant. Just call the function. */
+ return save_expr (build_call_expr_loc (a68_get_node_location (SUB
(declarer)),
+ TAX_TREE_DECL (TAX (SUB
(declarer))),
+ 0));
+ }
+
+ tree ret_type = build_pointer_type (CTYPE (mode));
+ tree func_decl = build_decl (a68_get_node_location (declarer),
+ FUNCTION_DECL,
+ NULL_TREE /* name, set below */,
+ build_function_type (ret_type,
void_list_node));
+ char *name = xasprintf ("genroutine%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 ();
+ TREE_ADDRESSABLE (func_decl) = 1;
+ TREE_PUBLIC (func_decl) = a68_in_global_range ();
+ TREE_STATIC (func_decl) = 1;
+ TAX_TREE_DECL (TAX (SUB (declarer))) = func_decl;
+
+ a68_add_decl (func_decl);
+ a68_add_decl_expr (fold_build1_loc (a68_get_node_location (declarer),
+ DECL_EXPR,
+ TREE_TYPE (func_decl),
+ func_decl));
+ announce_function (func_decl);
+ a68_push_function_range (func_decl, ret_type);
+
+ /* Collect bounds from declarer. */
+ tree bounds = collect_declarer_bounds (declarer, ctx);
+
+ /* Allocate and initialize a memory buffer for a value of mode MODE with
+ bounds in BOUNDS. */
+ tree_stmt_iterator bounds_iter = tsi_start (bounds);
+ tree gen = gen_mode (mode, &bounds_iter, a68_lower_malloc);
+ a68_pop_function_range (gen);
+ /* Avoid this generator function, which uses the global lexical
+ environment, to be reused in other contexts. */
+ TAX_TREE_DECL (TAX (SUB (declarer))) = NULL_TREE;
+ return save_expr (build_call_expr_loc (a68_get_node_location (declarer),
+ func_decl, 0));
+ }
+ else
+ {
+ /* Collect bounds from declarer. */
+ tree bounds = collect_declarer_bounds (declarer, ctx);
+
+ /* Allocate and initialize a memory buffer for a value of mode MODE with
+ bounds in BOUNDS. */
+ tree_stmt_iterator bounds_iter = tsi_start (bounds);
+ tree gen = gen_mode (mode, &bounds_iter,
+ heap ? a68_lower_malloc : a68_lower_alloca);
+ return gen;
+ }
+}
+
+/* Allocate storage for a value of mode M.
+ NBOUNDS is the number of bounds in BOUNDS. */
+
+tree
+a68_low_gen (MOID_T *m, size_t nbounds, tree *bounds, bool use_heap)
+{
+ /* First collect bounds from BOUNDS into a statements list, which is what
+ gen_mode expects. */
+ tree bounds_list = alloc_stmt_list ();
+ for (size_t i = 0; i < nbounds; ++i)
+ append_to_statement_list_force (bounds[i], &bounds_list);
+ allocator_t allocator = use_heap ? a68_lower_malloc : a68_lower_alloca;
+
+ tree_stmt_iterator q = tsi_start (bounds_list);
+ tree ret = gen_mode (m, &q, allocator);
+ free_stmt_list (bounds_list);
+ return ret;
+}
diff --git a/gcc/algol68/a68-low-units.cc b/gcc/algol68/a68-low-units.cc
new file mode 100644
index 00000000000..85a94a8b0ac
--- /dev/null
+++ b/gcc/algol68/a68-low-units.cc
@@ -0,0 +1,1191 @@
+/* Lower units 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"
+
+/* Note that enclosed clauses, which are units, are handled in
+ a68-low-clauses. */
+
+/* Lower an applied identifier.
+
+ This lowers into the declaration of the referred identifier. The
+ declaration of the identifier should now be available in the symbol table
+ entry for the identifier. */
+
+tree
+a68_lower_identifier (NODE_T *p, LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+ if (TAG_TABLE (TAX (p)) == A68_STANDENV)
+ {
+ /* This identifier is defined in the standard prelude. Use its lowering
+ handler. */
+ LOWERER_T lowerer = LOWERER (TAX (p));
+ return (*lowerer) (p, ctx);
+ }
+ else
+ {
+ tree id_decl = TAX_TREE_DECL (TAX (p));
+
+ if (id_decl == NULL_TREE)
+ {
+ /* This is an applied identifier used before the corresponding
defining
+ identifier gets defined in either an identity declaration or a
+ variable declaration. Create the declaration and install it in the
+ symbol table. The declaration itself, declaration expr and
+ initialization assignment for the declaration will be emitted by
the
+ corresponding declaration lowering handler. Note that the defining
+ identifier (and therefore the declaration associated with this
applied
+ identifier) may be in an outer lexical block. */
+
+ if (IS (MOID (p), PROC_SYMBOL))
+ {
+ if (VARIABLE (TAX (p)))
+ id_decl = a68_make_variable_declaration_decl (p);
+ else if (IN_PROC (TAX (p)))
+ id_decl = a68_make_proc_identity_declaration_decl (p);
+ else
+ id_decl = a68_make_identity_declaration_decl (p);
+ }
+ else
+ {
+ if (VARIABLE (TAX (p)))
+ id_decl = a68_make_variable_declaration_decl (p);
+ else
+ id_decl = a68_make_identity_declaration_decl (p);
+ }
+
+ TAX_TREE_DECL (TAX (p)) = id_decl;
+ }
+
+ /* If the identifier refers to a FUNCTION_DECL, this means the
declaration
+ was made by a procecure-identity-dclaration. The applied identifier in
+ that case refers to the address of the corresponding function. */
+ if (TREE_CODE (id_decl) == FUNCTION_DECL)
+ return fold_build1 (ADDR_EXPR,
+ build_pointer_type (TREE_TYPE (id_decl)),
+ id_decl);
+ else
+ return id_decl;
+ }
+}
+
+/* Lower a string denotation.
+
+ String denotations are of mode []CHAR, and lower into a multiple with a
+ single dimension, and with the following characteristics:
+
+ - The lower bound of dimension 0 is 1.
+ - The upper bound of dimension 0 is strlen (NSYMBOL (p)).
+ - The stride of dimension 0 is 0.
+ - The pointed elements are a buffer of CHARs allocated in the stack. */
+
+tree
+a68_lower_string_denotation (NODE_T *p, LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+ /* First process string breaks. */
+ char *str = a68_string_process_breaks (NSYMBOL (p));
+
+ /* Build a multiple of UCS-4 CHARs from the resulting UTF-8 string. */
+ size_t ucslen;
+ uint32_t *ucsbuf = a68_u8_to_u32 ((const uint8_t *) str, strlen (str),
+ NULL, &ucslen);
+ free (str);
+ tree string_literal = build_string_literal (ucslen * sizeof (uint32_t),
+ (char *) ucsbuf, a68_char_type);
+ tree elements = string_literal;
+ tree lower_bound = fold_convert (ssizetype, size_one_node);
+ tree upper_bound = ssize_int (ucslen);
+ tree elements_size = fold_build2 (MULT_EXPR, sizetype,
+ size_int (ucslen),
+ size_in_bytes (a68_char_type));
+ tree multiple = a68_row_value (CTYPE (M_ROW_CHAR), 1,
+ elements, elements_size,
+ &lower_bound, &upper_bound);
+ TREE_CONSTANT (multiple) = true;
+ free (ucsbuf);
+ return multiple;
+}
+
+/* Lower denotation.
+
+ denotation : int denotation; real denotation; bits denotation;
+ row char denotation;
+ true symbol; false symbol;
+ empty symbol;
+ longety, int denotation;
+ longety, real denotation;
+ longety, bits denotation;
+ shortety, int denotation;
+ shortety, real denotation;
+ shortety, bits denotation.
+
+ Denotations lower into GENERIC cst expressions. */
+
+tree
+a68_lower_denotation (NODE_T *p, LOW_CTX_T ctx)
+{
+ MOID_T *moid = MOID (p);
+
+ if (moid == M_VOID)
+ /* EMPTY */
+ return a68_lower_empty (p, ctx);
+ else if (moid == M_BOOL)
+ /* TRUE or FALSE. */
+ return (NSYMBOL (p)[0] == 'T') ? boolean_true_node : boolean_false_node;
+ else if (moid == M_CHAR)
+ {
+ char *s = a68_string_process_breaks (NSYMBOL (p));
+ uint32_t ucs;
+ int length = a68_u8_mbtouc (&ucs, (const uint8_t *) s, 1);
+ gcc_assert (length == 1);
+ free (s);
+ return build_int_cst (a68_char_type, ucs);
+ }
+ else if (moid == M_ROW_CHAR)
+ return a68_lower_string_denotation (p, ctx);
+ else if (moid == M_INT
+ || moid == M_LONG_INT
+ || moid == M_LONG_LONG_INT
+ || moid == M_SHORT_INT
+ || moid == M_SHORT_SHORT_INT)
+ {
+ /* SIZETY INT */
+ tree type;
+ char *end;
+ NODE_T *s = NO_NODE;
+ if (IS (SUB (p), LONGETY) || IS (SUB (p), SHORTETY))
+ s = NEXT (SUB (p));
+ else
+ s = SUB (p);
+
+ type = CTYPE (moid);
+ int64_t val = strtol (NSYMBOL (s), &end, 10);
+ gcc_assert (end[0] == '\0');
+ return build_int_cst (type, val);
+ }
+ if (moid == M_BITS
+ || moid == M_LONG_BITS
+ || moid == M_LONG_LONG_BITS
+ || moid == M_SHORT_BITS
+ || moid == M_SHORT_SHORT_BITS)
+ {
+ /* SIZETY BITS */
+
+ tree type;
+ char *end;
+ NODE_T *s = NO_NODE;
+ if (IS (SUB (p), LONGETY) || IS (SUB (p), SHORTETY))
+ s = NEXT (SUB (p));
+ else
+ s = SUB (p);
+
+ type = CTYPE (moid);
+ int64_t radix = strtol (NSYMBOL (s), &end, 10);
+ gcc_assert (end != NSYMBOL (s) && *end == 'r');
+ end++;
+ int64_t val = strtol (end, &end, radix);
+ gcc_assert (end[0] == '\0');
+ return build_int_cst (type, val);
+ }
+ else if (moid == M_REAL
+ || moid == M_LONG_REAL
+ || moid == M_LONG_LONG_REAL)
+ {
+ /* SIZETY INT */
+ tree type;
+ NODE_T *s = NO_NODE;
+ if (IS (SUB (p), LONGETY) || IS (SUB (p), SHORTETY))
+ s = NEXT (SUB (p));
+ else
+ s = SUB (p);
+
+ if (moid == M_REAL)
+ type = float_type_node;
+ else if (moid == M_LONG_REAL)
+ type = double_type_node;
+ else if (moid == M_LONG_LONG_REAL)
+ type = long_double_type_node;
+ else
+ gcc_unreachable ();
+
+ REAL_VALUE_TYPE val;
+ real_from_string (&val, NSYMBOL (s));
+ return build_real (type, val);
+ }
+
+ gcc_unreachable ();
+ return NULL_TREE;
+}
+
+/* Lower SKIP.
+
+ skip
+*/
+
+tree
+a68_lower_skip (NODE_T *p, LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+ return a68_get_skip_tree (MOID (p));
+}
+
+/* Lower NIHIL.
+
+ nihil : nil.
+
+ NIL stands for a name referring to no value and which must be
+ distinguishable from any other name. It is of mode REF AMODE. NIL is never
+ subject to coercion and it may only occur where the context is strong,
+ i.e. where AMODE is known at compile-time.
+
+ It lowers to a pointer to AMODE with value 0. */
+
+tree
+a68_lower_nihil (NODE_T *p, LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+ tree type = CTYPE (MOID (p));
+
+ gcc_assert (type == a68_void_type || POINTER_TYPE_P (type));
+ if (type == a68_void_type)
+ return a68_lower_empty (p, ctx);
+ else
+ return build_int_cst (type, 0);
+}
+
+/* Lower EMPTY. */
+
+tree
+a68_lower_empty (NODE_T *p ATTRIBUTE_UNUSED, LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+ return a68_get_empty ();
+}
+
+/* Lower an identity relation.
+
+ identity relation : tertiary, is symbol, tertiary;
+ tertiary, isnt symbol, tertiary.
+
+ An identity relation determines whether two name values are the same. It
+ lowers into EQ_EXPR in case of IS and into NE_EXPR in case of ISNT. */
+
+tree
+a68_lower_identity_relation (NODE_T *p, LOW_CTX_T ctx)
+{
+ NODE_T *lhs = SUB (p);
+ NODE_T *oper = NEXT (lhs);
+ NODE_T *rhs = NEXT (oper);
+
+ /* Consolidate arguments to make sure we are comparing pointers in the
+ r-value context of the EQ_EXPR or NE_EXPR operation below. */
+ tree op1 = a68_consolidate_ref (MOID (lhs), a68_lower_tree (lhs, ctx));
+ tree op2 = a68_consolidate_ref (MOID (rhs), a68_lower_tree (rhs, ctx));
+
+ tree_code code;
+ if (IS (oper, IS_SYMBOL))
+ code = EQ_EXPR;
+ else if (IS (oper, ISNT_SYMBOL))
+ code = NE_EXPR;
+ else
+ gcc_unreachable ();
+
+ return fold_build2_loc (a68_get_node_location (p),
+ code, boolean_type_node, op1, op2);
+}
+
+/* Lower AND_FUNCTION and OR_FUNCTION.
+
+ and function : tertiary, andf symbol, tertiary.
+ or function : tertiary, orf_symbol, tertiary.
+
+ These are pseudo-operators that are used to implement short-circuits
+ evaluation of logical expressions.
+
+ These pseudo-operators lower into TRUTH_ANDIF_EXPR or TRUTH_ORIF_EXPR,
+ respectively. */
+
+tree
+a68_lower_logic_function (NODE_T *p, LOW_CTX_T ctx)
+{
+ NODE_T *lhs = SUB (p);
+ NODE_T *oper = NEXT (lhs);
+ NODE_T *rhs = NEXT (oper);
+
+ tree op1 = a68_lower_tree (lhs, ctx);
+ tree op2 = a68_lower_tree (rhs, ctx);
+
+ tree_code code;
+ if (IS (oper, ANDF_SYMBOL))
+ code = TRUTH_ANDIF_EXPR;
+ else if (IS (oper, ORF_SYMBOL))
+ code = TRUTH_ORIF_EXPR;
+ else
+ gcc_unreachable ();
+
+ return fold_build2_loc (a68_get_node_location (p),
+ code, boolean_type_node, op1, op2);
+}
+
+/* Lower a primary.
+
+ primary : identifier; denotation; cast; enclosed clause; format text.
+
+ The primary lowers into some GENERIC expression. */
+
+tree
+a68_lower_primary (NODE_T *p, LOW_CTX_T ctx)
+{
+ return a68_lower_tree (SUB (p), ctx);
+}
+
+/* Lower a cast.
+
+ cast : declarer, enclosed clause;
+ void symbol, enclosed clause.
+
+ A cast establishes a strong context with some required mode. This context
+ allows coercions to be applied, and these coercions have been inserted in
+ the parse tree by the parser. */
+
+tree
+a68_lower_cast (NODE_T *p, LOW_CTX_T ctx)
+{
+ return a68_lower_tree (NEXT (SUB (p)), ctx);
+}
+
+/* Lower a slice.
+
+ slice : MULTIPLE INDEXER
+
+ Slicing a multiple may result in either an element of the multiple, if the
+ operation is indexing, or another multiple, if the operation is a
+ trimming. */
+
+static void
+lower_subscript_for_indexes (NODE_T *p, LOW_CTX_T ctx)
+{
+ for (; p != NO_NODE; FORWARD (p))
+ {
+ switch (ATTRIBUTE (p))
+ {
+ case TRIMMER:
+ /* Because of ANNOTATION (indexer) == SLICE */
+ gcc_unreachable ();
+ break;
+ case UNIT:
+ a68_add_stmt (a68_lower_tree (p, ctx));
+ break;
+ case GENERIC_ARGUMENT:
+ case GENERIC_ARGUMENT_LIST:
+ lower_subscript_for_indexes (SUB (p), ctx);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+lower_subscript_for_trimmers (NODE_T *p, LOW_CTX_T ctx,
+ tree multiple, tree new_multiple,
+ int *dim, int *new_dim,
+ tree elements_pointer_type)
+{
+ /* new.elements := multiple.elements;
+ FOR dim TO num dimensions
+ DO CO t[dim] is either a subscript i or a trimmer i : j @ k CO
+ new.elements +:= i * multiple.strides[dim];
+ IF t[dim] is a trimmer
+ THEN INT d := ( k is absent | 1 | multiple.lb[dim] - k );
+ new.lb[dim] := multiple.lb[dim] - d;
+ new.ub[dim] := multiple.ub[dim] - d;
+ new.strides[dim] := multiple.strides[dim]
+ FI
+ OD
+ */
+
+ for (; p != NO_NODE; FORWARD (p))
+ {
+ switch (ATTRIBUTE (p))
+ {
+ case UNIT:
+ {
+ tree unit = save_expr (fold_convert (ssizetype, a68_lower_tree (p,
ctx)));
+ tree new_elements = a68_multiple_elements (new_multiple);
+ tree size_dim = size_int (*dim);
+ tree dim_lower_bound = save_expr (a68_multiple_lower_bound
(multiple, size_dim));
+ tree stride = save_expr (a68_multiple_stride (multiple, size_dim));
+
+ /* Validate bounds. */
+ if (OPTION_BOUNDS_CHECKING (&A68_JOB))
+ a68_add_stmt (a68_multiple_bounds_check (p, size_dim, multiple,
unit));
+
+ /* new_elements += i * strides[dim] */
+ tree offset = fold_build2 (MULT_EXPR, sizetype,
+ fold_convert (sizetype, fold_build2
(MINUS_EXPR, ssizetype,
+
unit, dim_lower_bound)),
+ stride);
+
+ offset = save_expr (offset);
+ new_elements = fold_build2 (POINTER_PLUS_EXPR,
+ elements_pointer_type,
+ new_elements,
+ offset);
+ a68_add_stmt (a68_multiple_set_elements (new_multiple,
new_elements));
+
+ /* elements_size -= i * strides[dim] */
+ tree elements_size = a68_multiple_elements_size (new_multiple);
+ elements_size = fold_build2 (MINUS_EXPR, sizetype,
+ elements_size, offset);
+ a68_add_stmt (a68_multiple_set_elements_size (new_multiple,
elements_size));
+
+ *dim += 1;
+ break;
+ }
+ case TRIMMER:
+ {
+ /* First collect components from the trimmer. */
+ tree size_dim = size_int (*dim);
+ tree dim_lower_bound = save_expr (a68_multiple_lower_bound
(multiple, size_dim));
+ tree lower_bound = dim_lower_bound;
+ tree upper_bound = save_expr (a68_multiple_upper_bound (multiple,
size_dim));
+ tree at = ssize_int (1);
+
+ NODE_T *q = SUB (p);
+ if (q != NO_NODE)
+ {
+ if (IS (q, AT_SYMBOL))
+ {
+ /* Both bounds are implicit. */
+ at = save_expr (fold_convert (ssizetype, a68_lower_tree
(NEXT (q), ctx)));
+ }
+ else if (IS (q, COLON_SYMBOL))
+ {
+ /* Lower bound is implicit. */
+ FORWARD (q);
+ if (IS (q, AT_SYMBOL))
+ {
+ /* Upper bound is implicit, AT specified. */
+ gcc_assert (IS (q, AT_SYMBOL));
+ at = save_expr (fold_convert (ssizetype, a68_lower_tree
(NEXT (q), ctx)));
+ }
+ else
+ {
+ upper_bound
+ = save_expr (fold_convert (ssizetype, a68_lower_tree
(q, ctx)));
+ FORWARD (q);
+ if (q != NO_NODE)
+ {
+ gcc_assert (IS (q, AT_SYMBOL));
+ at = save_expr (fold_convert (ssizetype,
a68_lower_tree (NEXT (q), ctx)));
+ }
+ }
+ }
+ else
+ {
+ /* Lower bound is explicit. */
+ lower_bound = fold_convert (ssizetype, a68_lower_tree (q,
ctx));
+ FORWARD (q);
+ gcc_assert (IS (q, COLON_SYMBOL));
+ FORWARD (q);
+ if (q != NO_NODE)
+ {
+ if (IS (q, AT_SYMBOL))
+ at = save_expr (fold_convert (ssizetype,
a68_lower_tree (NEXT (q), ctx)));
+ else
+ {
+ upper_bound
+ = save_expr (fold_convert (ssizetype,
a68_lower_tree (q, ctx)));
+ FORWARD (q);
+ if (q != NO_NODE && IS (q, AT_SYMBOL))
+ at =
+ save_expr (fold_convert (ssizetype,
a68_lower_tree (NEXT (q), ctx)));
+ }
+ }
+ }
+ }
+
+ /* Time for some bounds checking.
+
+ Note that in trimmers, given the current dimension's bounds
+ (L,U), we cannot simply do the check:
+
+ L <= lower_bound <= U
+ L <= upper_bound <= U
+
+ This is because the multiple may be flat, and the dimension may
+ have bounds such like U < L. In that case, the expressions
+ above would always eval to false for any lower_bound and
+ upper_bound.
+
+ So we check for this instead:
+
+ L <= lower_bound AND upper_bound <= U
+
+ This allows to trim a "flat dimension" using a trimmer where
+ upper_bound < lower_bound. The result is, of course, another
+ "flat dimension" in the multiple result of the trimming. */
+
+ if (OPTION_BOUNDS_CHECKING (&A68_JOB))
+ {
+ a68_add_stmt (a68_multiple_single_bound_check (p, size_dim,
multiple,
+ lower_bound,
+ false /*
upper_bound */));
+ a68_add_stmt (a68_multiple_single_bound_check (p, size_dim,
multiple,
+ upper_bound,
+ true /*
upper_bound */));
+ }
+
+ /* new_elements += i * strides[dim] */
+ tree stride = save_expr (a68_multiple_stride (multiple, size_dim));
+ tree new_elements = a68_multiple_elements (new_multiple);
+ tree offset = fold_build2 (MULT_EXPR, sizetype,
+ fold_convert (sizetype, fold_build2
(MINUS_EXPR, ssizetype,
+
lower_bound, dim_lower_bound)),
+ stride);
+
+ offset = save_expr (offset);
+ new_elements = fold_build2 (POINTER_PLUS_EXPR,
+ elements_pointer_type,
+ new_elements,
+ offset);
+ a68_add_stmt (a68_multiple_set_elements (new_multiple,
new_elements));
+
+ /* elements_size -= i * strides[dim] */
+ tree elements_size = a68_multiple_elements_size (new_multiple);
+ elements_size = fold_build2 (MINUS_EXPR, sizetype,
+ elements_size, offset);
+ a68_add_stmt (a68_multiple_set_elements_size (new_multiple,
+ elements_size));
+
+ /* Fill the triplet for this dimension in new_multiple. */
+ tree size_new_dim = size_int (*new_dim);
+ tree d = fold_build2 (MINUS_EXPR, ssizetype, lower_bound, at);
+
+ a68_add_stmt (a68_multiple_set_lower_bound (new_multiple,
size_new_dim,
+ fold_build2
(MINUS_EXPR, ssizetype,
+
lower_bound, d)));
+ a68_add_stmt (a68_multiple_set_upper_bound (new_multiple,
size_new_dim,
+ fold_build2
(MINUS_EXPR, ssizetype,
+
upper_bound, d)));
+ a68_add_stmt (a68_multiple_set_stride (new_multiple, size_new_dim,
stride));
+
+ *new_dim += 1;
+ *dim += 1;
+ break;
+ }
+ default:
+ lower_subscript_for_trimmers (SUB (p), ctx,
+ multiple, new_multiple,
+ dim, new_dim,
+ elements_pointer_type);
+ }
+ }
+}
+
+tree
+a68_lower_slice (NODE_T *p, LOW_CTX_T ctx)
+{
+ NODE_T *indexer = NEXT_SUB (p);
+ MOID_T *orig_multiple_mode = MOID (SUB (p));
+ MOID_T *multiple_mode = orig_multiple_mode;
+ bool slicing_name = false;
+
+ /* First of all, lower the multiple being sliced. If it is a name to a
+ multiple, set a flag and dereference. */
+ tree multiple = a68_lower_tree (SUB (p), ctx);
+ MOID_T *orig_sliced_multiple_mode = MOID (p);
+ MOID_T *sliced_multiple_mode = MOID (p);
+ size_t slice_num_dimensions = 0;
+ if (IS_REF (MOID (SUB (p))))
+ {
+ slicing_name = true;
+ multiple = a68_low_deref (multiple, SUB (p));
+ multiple_mode = SUB (multiple_mode);
+ slice_num_dimensions = DIM (SUB (MOID (p)));
+ sliced_multiple_mode = SUB (sliced_multiple_mode);
+ }
+ else
+ slice_num_dimensions = DIM (MOID (p));
+
+ tree slice = NULL_TREE;
+ if (ANNOTATION (indexer) == SLICE)
+ {
+ /* The slice has only indexers and no trimmers. Collect units and slice
+ an element of the multiple using a68_multiple_slice. This operation
+ results in an element of the multiple. */
+
+ /* Collect units */
+ a68_push_range (NULL);
+ lower_subscript_for_indexes (SUB (indexer), ctx);
+ tree units = a68_pop_range ();
+
+ /* We need to allocate space for as many indexes as dimensions of the
+ multiple. */
+ tree num_dimensions_tree = a68_multiple_dimensions (multiple);
+ gcc_assert (TREE_CODE (num_dimensions_tree) == INTEGER_CST);
+ int num_dimensions = tree_to_shwi (num_dimensions_tree);
+
+ int num_indexes = 0;
+ tree *indexes = (tree *) xmalloc (sizeof (tree) * num_dimensions);
+ for (tree_stmt_iterator si = tsi_start (units);
+ !tsi_end_p (si);
+ tsi_next (&si))
+ {
+ /* Add the unit to the list of indexes. */
+ indexes[num_indexes] = tsi_stmt (si);
+ num_indexes++;
+ }
+ gcc_assert (num_indexes == num_dimensions);
+
+ /* Slice. */
+ slice = a68_multiple_slice (p, multiple, slicing_name,
+ num_indexes, indexes);
+ free (indexes);
+ }
+ else if (ANNOTATION (indexer) == TRIMMER)
+ {
+ /* The slice has both indexers and trimmers. Traverse the indexer
+ subtree to obtain the descriptor of the trimmed multiple (which is
+ another multiple) and the pointer to the elements, which points to
+ some position within the elements of the trimmed multiple. This
+ operation results in a new multiple of the same mode than the trimmed
+ multiple with shared elements. */
+
+ a68_push_range (sliced_multiple_mode);
+
+ tree sliced_multiple = a68_lower_tmpvar ("multiple%", TREE_TYPE
(multiple),
+ multiple);
+ tree *lower_bounds = (tree *) xmalloc (sizeof (tree) *
slice_num_dimensions);
+ tree *upper_bounds = (tree *) xmalloc (sizeof (tree) *
slice_num_dimensions);
+ tree ssize_one_node = fold_convert (ssizetype, size_one_node);
+ tree ssize_zero_node = fold_convert (ssizetype, size_zero_node);
+ for (size_t d = 0; d < slice_num_dimensions; ++d)
+ {
+ /* Note that these dummy bounds and the implied strides will be
+ overwritten by lower_subscript_for_trimmers below. */
+ lower_bounds[d] = ssize_one_node;
+ upper_bounds[d] = ssize_zero_node;
+ }
+ tree new_multiple = a68_row_value (CTYPE (sliced_multiple_mode),
+ slice_num_dimensions,
+ a68_multiple_elements
(sliced_multiple),
+ a68_multiple_elements_size
(sliced_multiple),
+ lower_bounds, upper_bounds);
+ new_multiple = save_expr (new_multiple);
+ new_multiple = a68_lower_tmpvar ("new_multiple%", TREE_TYPE
(new_multiple),
+ new_multiple);
+
+ int dim = 0;
+ int new_dim = 0;
+ lower_subscript_for_trimmers (SUB (indexer), ctx,
+ sliced_multiple, new_multiple,
+ &dim, &new_dim,
+ a68_row_elements_pointer_type (TREE_TYPE
(multiple)));
+ a68_add_stmt (new_multiple);
+ slice = a68_pop_range ();
+
+ /* In case we are slicing a ref to a multiple, return the address of the
+ resulting multiple and not the multiple itself. But in this case we
+ need an address in the heap, because the trimmed multiple may be in
+ the heap and the result shall have the same scope. */
+ if (slicing_name)
+ {
+ tree ptrtype = CTYPE (orig_sliced_multiple_mode);
+ tree slice_addr = fold_build1 (ADDR_EXPR, ptrtype, slice);
+ tree alloc = a68_lower_malloc (ptrtype, size_in_bytes (TREE_TYPE
(slice)));
+ alloc = save_expr (alloc);
+ tree copy = a68_lower_memcpy (alloc, slice_addr, size_in_bytes
(TREE_TYPE (slice)));
+
+ slice = fold_build2 (COMPOUND_EXPR, ptrtype, copy, alloc);
+ }
+ }
+ else
+ gcc_unreachable ();
+
+ return slice;
+}
+
+/* Lower a selection.
+
+ selection : selector, secondary.
+ selector : field identifier, of symbol.
+
+ The selection lowers into a COMPONENT_REF of the field corresponding to the
+ field identifier. */
+
+tree
+a68_lower_selection (NODE_T *p, LOW_CTX_T ctx)
+{
+ NODE_T *secondary = NEXT (SUB (p));
+ NODE_T *field_identifier = SUB (SUB (p));
+
+ MOID_T *secondary_mode = MOID (secondary);
+ tree secondary_expr = a68_lower_tree (secondary, ctx);
+
+ tree res = NULL_TREE;
+
+ /* If the secondary is an address, we need to indirect. */
+ if (IS_REF (secondary_mode))
+ {
+ secondary_expr = a68_low_deref (secondary_expr, secondary);
+ secondary_mode = SUB (secondary_mode);
+ }
+
+ if (IS_FLEX (secondary_mode) || IS_ROW (secondary_mode))
+ {
+ /* This is the selection of a multiple of structs.
+
+ The result is a multiple with same dimensions, dimension bounds and
+ strides than the indexed multiple. The elements pointer is made to
+ point to the selected field of the first struct. */
+
+ MOID_T *result_mode = MOID (p);
+ if (IS_REF (result_mode))
+ result_mode = SUB (result_mode);
+ MOID_T *struct_mode = SUB (secondary_mode);
+ tree field_id = a68_get_mangled_identifier (SYMBOL (INFO
(field_identifier)));
+ tree struct_type = CTYPE (struct_mode);
+ a68_push_range (result_mode);
+ tree selection = a68_lower_tmpvar ("selection%", CTYPE (result_mode),
+ a68_get_skip_tree (result_mode));
+ tree multiple = a68_lower_tmpvar ("multiple%", TREE_TYPE
(secondary_expr),
+ secondary_expr);
+
+ /* First set the bounds of the selection, which are exactly the same
+ bounds than the selected multiple. */
+ for (int dim = 0; dim < DIM (DEFLEX (secondary_mode)); ++dim)
+ {
+ tree size_dim = size_int (dim);
+ tree lower_bound = a68_multiple_lower_bound (multiple, size_dim);
+ tree upper_bound = a68_multiple_upper_bound (multiple, size_dim);
+ tree stride = a68_multiple_stride (multiple, size_dim);
+ a68_add_stmt (a68_multiple_set_lower_bound (selection, size_dim,
+ lower_bound));
+ a68_add_stmt (a68_multiple_set_upper_bound (selection, size_dim,
+ upper_bound));
+ a68_add_stmt (a68_multiple_set_stride (selection, size_dim,
+ stride));
+ }
+
+ /* Now set the elements pointer, which is the elements pointer of the
+ selected multiple offset the offset of the selected field in its
+ struct type. */
+ tree elements = a68_multiple_elements (selection);
+ tree multiple_elements = a68_multiple_elements (multiple);
+ tree multiple_elements_size = a68_multiple_elements_size (multiple);
+ tree element_pointer_type = TREE_TYPE (elements);
+ tree field_offset = NULL_TREE;
+ for (tree f = TYPE_FIELDS (struct_type); f; f = DECL_CHAIN (f))
+ {
+ if (field_id == DECL_NAME (f))
+ {
+ field_offset = byte_position (f);
+ break;
+ }
+ }
+ gcc_assert (field_offset != NULL_TREE);
+ a68_add_stmt (a68_multiple_set_elements (selection,
+ fold_build2 (POINTER_PLUS_EXPR,
+
element_pointer_type,
+ multiple_elements,
+ field_offset)));
+
+ /* The size of the buffer pointed by the elements pointer has to be
+ adjusted accordingly. */
+ a68_add_stmt (a68_multiple_set_elements_size (selection,
+ fold_build2 (MINUS_EXPR,
sizetype,
+
multiple_elements_size,
+
field_offset)));
+
+ a68_add_stmt (selection);
+ res = a68_pop_range ();
+ }
+ else
+ {
+ /* This is the selection of a struct field. */
+ gcc_assert (A68_STRUCT_TYPE_P (TREE_TYPE (secondary_expr)));
+
+ /* Search for the selected field in the struct type. */
+ tree struct_type = TREE_TYPE (secondary_expr);
+ tree field_id = a68_get_mangled_identifier (SYMBOL (INFO
(field_identifier)));
+ tree field = NULL_TREE;
+ for (tree f = TYPE_FIELDS (struct_type); f; f = DECL_CHAIN (f))
+ {
+ if (field_id == DECL_NAME (f))
+ {
+ field = f;
+ break;
+ }
+ }
+ gcc_assert (field != NULL_TREE);
+
+ /* Emit the COMPONENT_REF. */
+ res = fold_build3_loc (a68_get_node_location (p),
+ COMPONENT_REF,
+ TREE_TYPE (field),
+ secondary_expr,
+ field,
+ NULL_TREE);
+ }
+
+ /* The selection of a name yields a name. */
+ if (IS_REF (MOID (secondary)))
+ /* XXX This may require copying. */
+ return fold_build1 (ADDR_EXPR, CTYPE (MOID (p)), res);
+ else
+ return res;
+}
+
+/* Lower a secondary.
+
+ secondary : primary; generator; selection.
+
+ The secondary lowers into some GENERIC expression. */
+
+tree
+a68_lower_secondary (NODE_T *p, LOW_CTX_T ctx)
+{
+ return a68_lower_tree (SUB (p), ctx);
+}
+
+/* Lower a formula.
+
+ formula : secondary, operator, secondary;
+ secondary, operator, monadic formula;
+ secondary, operator, formula;
+ monadic formula;
+ monadic formula, operator, secondary;
+ monadic formula, operator, monadic formula;
+ monadic formula, operator, formula;
+ formula, operator, secondary;
+ formula, operator, monadic formula;
+ formula, operator, formula.
+
+ The formula lowers into some GENERIC expression. */
+
+tree
+a68_lower_formula (NODE_T *p, LOW_CTX_T ctx)
+{
+ if (IS (SUB (p), MONADIC_FORMULA) && NEXT (SUB (p)) == NO_NODE)
+ return a68_lower_tree (SUB (p), ctx);
+ else
+ {
+ /* If the operator is defined in the standard prelude, then use its
lowering
+ code. */
+ if (TAG_TABLE (TAX (NEXT (SUB (p)))) == A68_STANDENV)
+ {
+ LOWERER_T lowerer = LOWERER (TAX (NEXT (SUB (p))));
+ return (*lowerer) (p, ctx);
+ }
+ else
+ {
+ tree arg1 = a68_lower_tree (SUB (p), ctx);
+ tree op = a68_lower_tree (NEXT (SUB (p)), ctx);
+ tree arg2 = a68_lower_tree (NEXT (NEXT (SUB (p))), ctx);
+
+ if (POINTER_TYPE_P (TREE_TYPE (op)))
+ op = fold_build1 (INDIRECT_REF,
+ TREE_TYPE (TREE_TYPE (op)),
+ op);
+ return build_call_expr_loc (a68_get_node_location (p), op, 2, arg1,
arg2);
+ }
+ }
+}
+
+/* Lower a monadic formula.
+
+ monadic formula : operator, secondary;
+ operator, monadic formula.
+
+ The monadic formula lowers into some GENERIC expression. */
+
+tree
+a68_lower_monadic_formula (NODE_T *p, LOW_CTX_T ctx)
+{
+ /* If the operator is defined in the standard prelude, then use its lowering
+ code. */
+ if (TAG_TABLE (TAX (SUB (p))) == A68_STANDENV)
+ {
+ LOWERER_T lowerer = LOWERER (TAX (SUB (p)));
+ return (*lowerer) (p, ctx);
+ }
+ else
+ {
+ tree op = a68_lower_tree (SUB (p), ctx);
+ tree secondary = a68_lower_tree (NEXT (SUB (p)), ctx);
+
+ if (POINTER_TYPE_P (TREE_TYPE (op)))
+ op = fold_build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (op)), op);
+ return build_call_expr_loc (a68_get_node_location (p), op, 1, secondary);
+ }
+}
+
+/* Lower a tertiary.
+
+ tertiary : nihil; monadic formula; formula; secondary.
+
+ The tertiary lowers to some GENERIC expression. */
+
+tree
+a68_lower_tertiary (NODE_T *p, LOW_CTX_T ctx)
+{
+ return a68_lower_tree (SUB (p), ctx);
+}
+
+/* Lower an assignation.
+
+ assignation : tertiary, assign symbol, tertiary;
+ tertiary, assign symbol, identity relation;
+ tertiary, assign symbol, and function;
+ tertiary, assign symbol, or function;
+ tertiary, assign symbol, routine text;
+ tertiary, assign symbol, jump;
+ tertiary, assign symbol, skip;
+ tertiary, assign symbol, assignation;
+ tertiary, assign symbol, code clause.
+
+ An assignation lowers into appending a MODIFY_EXPR to the statements list,
+ and the result of the expression is the left hand side. A compound
+ expression fits perfectly */
+
+tree
+a68_lower_assignation (NODE_T *p, LOW_CTX_T ctx)
+{
+ NODE_T *lhs_node = SUB (p);
+ NODE_T *rhs_node = NEXT (NEXT (SUB (p)));
+ tree lhs = a68_lower_tree (lhs_node, ctx);
+ tree rhs = a68_lower_tree (rhs_node, ctx);
+
+ return a68_low_assignation (p,
+ lhs, MOID (lhs_node),
+ rhs, MOID (rhs_node));
+}
+
+/* Lower a generator.
+
+ generator : loc symbol, declarer;
+ heap symbol, declarer;
+ new symbol, declarer.
+
+ LOC generators lower into calls to BUILT_IN_ALLOCA.
+ HEAP generators lower into calls to malloc. */
+
+tree
+a68_lower_generator (NODE_T *p, LOW_CTX_T ctx)
+{
+ NODE_T *declarer = NEXT (SUB (p));
+ return a68_low_generator (declarer,
+ MOID (declarer),
+ !IS (SUB (p), LOC_SYMBOL),
+ ctx);
+}
+
+/* Lower a procedure call.
+
+ */
+
+static void
+collect_call_arguments (NODE_T *p, vec<tree, va_gc> *args, LOW_CTX_T ctx)
+{
+ for (; p != NO_NODE; FORWARD (p))
+ {
+ if (IS (p, UNIT))
+ {
+ /* In Algol 68 parameters are passed via an identity declaration, so
+ this must implement same semantics. */
+ tree arg = a68_lower_tree (p, ctx);
+ if (HAS_ROWS (MOID (p)))
+ arg = a68_low_dup (arg);
+ arg = a68_consolidate_ref (MOID (p), arg);
+ args->quick_push (arg);
+ }
+ else
+ collect_call_arguments (SUB (p), args, ctx);
+ }
+}
+
+tree
+a68_lower_call (NODE_T *p, LOW_CTX_T ctx)
+{
+ MOID_T *proc_mode = MOID (SUB (p));
+ MOID_T *ret_mode = SUB (proc_mode);
+ unsigned int nargs = DIM (proc_mode);
+
+ /* Collect arguments. */
+ vec<tree, va_gc> *args;
+ vec_alloc (args, nargs);
+ collect_call_arguments (NEXT (SUB (p)), args, ctx);
+
+ /* Lower the primary to call. */
+ tree primary = a68_lower_tree (SUB (p), ctx);
+
+ /* We need a pointer to a function type. */
+ if (!POINTER_TYPE_P (TREE_TYPE (primary)))
+ primary = fold_build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (primary)),
+ primary);
+
+ /* Build a function call. */
+ tree call = build_call_vec (CTYPE (ret_mode), primary, args);
+ SET_EXPR_LOCATION (call, a68_get_node_location (p));
+ return call;
+}
+
+/* Lower a routine text.
+
+ routine text : parameter pack, (declarer ; void symbol), colon symbol,
assignation;
+ parameter pack, (declarer ; void symbol), colon symbol,
identity relation;
+ parameter pack, (declarer ; void symbol), colon symbol, and
function;
+ parameter pack, (declarer ; void symbol), colon symbol, or
runction;
+ parameter pack, (declarer ; void symbol), colon symbol,
jump;
+ parameter pack, (declarer ; void symbol), colon symbol,
skip;
+ parameter pack, (declarer ; void symbol), colon symbol,
tertiary;
+ parameter pack, (declarer ; void symbol), colon symbol,
routine text;
+ parameter pack, (declarer ; void symboL), colon symbol,
code clause;
+ (declarer ; void symbol), colon symbol, assignation;
+ (declarer ; void symbol), colon symbol, identity relation;
+ (declarer ; void symbol), colon symbol, and function;
+ (declarer ; void symbol), colon symbol, or runction;
+ (declarer ; void symbol), colon symbol, jump;
+ (declarer ; void symbol), colon symbol, skip;
+ (declarer ; void symbol), colon symbol, tertiary;
+ (declarer ; void symbol), colon symbol, routine text;
+ (declarer ; void symbol), colon symbol, code clause.
+
+ Routine texts are used to create routines. They can stand as the actual
+ parameter of an identity declaration, as the actual parameter of a call, or
+ as the right-hand side of an assignation.
+
+ This lowering function is called in two different contexts:
+
+ 1) As part of a routine-identity-declaration, in which case the routine
+ resulting from this routine-text is beign ascribed to an identifier given
+ in ctx.proc_decl_identifier. In that case, we lower to a FUNC_DECL
+ initialized with the body of the routine-text.
+
+ 2) As a free standing routine-text. In that case ctx.proc_decl_identifier is
+ NO_NODE. We lower to the address of a FUNC_DECL that features some unique
+ name. This pointer will then likely be assigned or ascribed to some
+ variable or identifier in non-contracted identity declaration, but we
+ cannot assume that so we have to opt for the indirection. */
+
+tree
+a68_lower_routine_text (NODE_T *p, LOW_CTX_T ctx)
+{
+ NODE_T *s = SUB (p);
+
+ tree func_decl = NULL_TREE;
+ NODE_T *defining_identifier = ctx.proc_decl_identifier;
+ if (defining_identifier != NO_NODE)
+ {
+ /* The routine-text is part of a routine-identity-declaration. */
+ func_decl = TAX_TREE_DECL (TAX (defining_identifier));
+ if (func_decl == NULL_TREE)
+ {
+ func_decl = a68_make_proc_identity_declaration_decl
(defining_identifier);
+ TAX_TREE_DECL (TAX (defining_identifier)) = func_decl;
+ }
+ }
+ else
+ /* The routine-text is free standing. */
+ func_decl = a68_make_anonymous_routine_decl (MOID (p));
+
+ a68_add_decl (func_decl);
+ a68_add_decl_expr (fold_build1_loc (a68_get_node_location (p),
+ DECL_EXPR,
+ TREE_TYPE (func_decl),
+ func_decl));
+ announce_function (func_decl);
+
+ /* PARAMETER_PACK. */
+ NODE_T *parameter_pack_node = NO_NODE;
+ tree parameter_pack = NULL_TREE; /* This is computed below. */
+ if (IS (s, PARAMETER_PACK))
+ {
+ parameter_pack_node = s;
+ FORWARD (s);
+ }
+
+ /* DECLARER or VOID_SYMBOL */
+ if (IS (s, DECLARER) || IS (s, VOID_SYMBOL))
+ /* This is not used, as this formal declarer is also available in the
+ procedure mode. So just skip it. */
+ FORWARD (s);
+
+ /* Skip the COLON_SYMBOL. */
+ gcc_assert (IS (s, COLON_SYMBOL));
+ FORWARD (s);
+
+ /* Lower the function body.
+
+ This should be done in a new range in which the formal parameters of the
+ routine-text have been declared. */
+ a68_push_function_range (func_decl, CTYPE (SUB (MOID (p))) /* result_type
*/);
+ if (parameter_pack_node != NO_NODE)
+ parameter_pack = a68_lower_tree (parameter_pack_node, ctx);
+ DECL_ARGUMENTS (func_decl) = parameter_pack;
+ ctx.proc_decl_identifier = NO_NODE;
+ tree func_body = a68_lower_tree (s, ctx);
+ a68_pop_function_range (func_body);
+
+ if (defining_identifier != NO_NODE)
+ /* Routine-text immediately ascribed to some identifier in a
+ proc-identity-declaration. Return the FUNC_DECL. */
+ return func_decl;
+ else
+ /* Free standing routine-text. Return its address. */
+ return fold_build1 (ADDR_EXPR,
+ build_pointer_type (TREE_TYPE (func_decl)),
+ func_decl);
+}
+
+/* Lower an unit.
+
+ unit : assignation; identity relation;
+ and function; or function; routine text;
+ jump; skip; tertiary; assertion; code clause.
+
+ The unit lowers to an expression. */
+
+tree
+a68_lower_unit (NODE_T *p, LOW_CTX_T ctx)
+{
+ return a68_lower_tree (SUB (p), ctx);
+}
--
2.30.2