Signed-off-by: Jose E. Marchesi <[email protected]>

gcc/ChangeLog

        * algol68/a68-low-multiples.cc: New file.
        * algol68/a68-low-structs.cc: Likewise.
        * algol68/a68-low-unions.cc: Likewise.
---
 gcc/algol68/a68-low-multiples.cc | 1097 ++++++++++++++++++++++++++++++
 gcc/algol68/a68-low-structs.cc   |   63 ++
 gcc/algol68/a68-low-unions.cc    |  279 ++++++++
 3 files changed, 1439 insertions(+)
 create mode 100644 gcc/algol68/a68-low-multiples.cc
 create mode 100644 gcc/algol68/a68-low-structs.cc
 create mode 100644 gcc/algol68/a68-low-unions.cc

diff --git a/gcc/algol68/a68-low-multiples.cc b/gcc/algol68/a68-low-multiples.cc
new file mode 100644
index 00000000000..ce5996c9249
--- /dev/null
+++ b/gcc/algol68/a68-low-multiples.cc
@@ -0,0 +1,1097 @@
+/* Lowering routines for all things related to multiples.
+   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"
+
+/* Algol 68 multiples are multi-dimensional and dynamically sized. They have a
+   static part and a dynamic part.  The static part is conformed by a
+   "descriptor", which contains information about each of the dimensions, and a
+   pointer to the actual elements stored in the multiple.  The dynamic part are
+   the elements, which are stored in column order.  Both the descriptor and the
+   elements may reside on the stack, data section, or the heap.  The mode of a
+   multiple is a "row".
+
+   Schematically, the descriptor contains:
+
+      triplets%
+        lb% ub% stride%
+       ...
+      elements%
+      elements_size%
+
+   Where elements_size% is the size of the buffer pointed by elements%, in
+   bytes.
+
+   There is a triplet per dimension in the multiple.  The number of dimensions
+   in a row mode is static and is determined at compile-time.
+
+   The infomation stored for each triplet is:
+
+     lb%     is the lower bound of the dimension.
+     ub%     is the upper bound of the dimension.
+     stride% is the stride of the dimension.
+
+   The stride of each dimension is the number of bytes to skip in order to
+   access the next element in that dimension.  They express the layout of the
+   multiple in memory.
+
+   Algol 68 multi-dimensional multiples are stored in row-major (generalized,
+   lexicographical) order:
+
+     [1:3,1:2]AMODE = ((e1, e2, e3),
+                       (e4, e5, e6))
+
+   is stored as:
+
+          1  2  3
+       1  e1 e2 e3      | stride 2S ->  stride 1S
+       2  e4 e5 e6      v
+
+   Where S is the size in bytes of a single element.  That means that for two
+   dimensional multiples, the column stride is always 1S and the row stride is
+   the column size.
+
+   In general, given a mode with number of elements N1, N2, N3, ...:
+
+     [N1,N2,N3...,Nn]AMODE
+
+   the strides of the dimensions are:
+
+     S1 = N2 * S2
+     S2 = N3 * S3
+     S3 = N4 * S4
+     ...
+     Si = N1 * N2 * ... * Ni-1
+
+   Indexing is then performed by a dot-product of an element coordinate and the
+   strides:
+
+     (i1,i2,i3) . (S1,S2,S3) = offset + i1*S1 + i2*S2 + i3*S3 = index in 
elements array.
+
+   Note that the number of elements in each dimension can be easily derived
+   from the bounds and there is no need to store them explicitly, save for
+   performance reasons.  Descriptors are bulky enough and often they they are
+   stored on the stack, so we prefer to pay in performance and save in
+   storage.  */
+
+/* Return a tree with the yielding of SKIP for the given row mode, a
+   multiple.  */
+
+tree
+a68_get_multiple_skip_tree (MOID_T *m)
+{
+  tree res = NULL_TREE;
+  int dim = DIM (m);
+  tree *lower_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+  tree *upper_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+  tree ssize_one_node = fold_convert (ssizetype, size_one_node);
+  tree ssize_zero_node = fold_convert (ssizetype, size_zero_node);
+  for (int i = 0; i < dim; ++i)
+    {
+      lower_bounds[i] = ssize_one_node;
+      upper_bounds[i] = ssize_zero_node;
+    }
+  res = a68_row_value (CTYPE (m), dim,
+                      build_int_cst (build_pointer_type (void_type_node), 0),
+                      size_zero_node, /* elements_size */
+                      lower_bounds, upper_bounds);
+  free (lower_bounds);
+  free (upper_bounds);
+  return res;
+}
+
+/* Return the number of dimensions of the multiple EXP as an integer
+   constant.  */
+
+tree
+a68_multiple_dimensions (tree exp)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+
+  /* triplets% is the first field in the descriptor.  */
+  tree triplets_field = TYPE_FIELDS (TREE_TYPE (exp));
+  return array_type_nelts_top (TREE_TYPE (triplets_field));
+}
+
+/* Return an expression that evaluates to the total number of elements stored
+   in a multiple as a sizetype.  */
+
+tree
+a68_multiple_num_elems (tree exp)
+{
+  /* We have to calculate the number of elements based on the dimension
+     triplets in the array type.  The number of dimensions is known at compile
+     time, so we don't really need a loop.  */
+
+  tree num_dimensions_tree = a68_multiple_dimensions (exp);
+  gcc_assert (TREE_CODE (num_dimensions_tree) == INTEGER_CST);
+  int num_dimensions = tree_to_shwi (num_dimensions_tree);
+
+  tree size = NULL_TREE;
+  for (int dim = 0; dim < num_dimensions; ++dim)
+    {
+      tree size_dim = size_int (dim);
+      tree lower_bound = a68_multiple_lower_bound (exp, size_dim);
+      tree upper_bound = a68_multiple_upper_bound (exp, size_dim);
+      tree dim_size = fold_build2 (PLUS_EXPR, sizetype,
+                                  fold_convert (sizetype, fold_build2 
(MINUS_EXPR,
+                                                                       
ssizetype,
+                                                                       
upper_bound,
+                                                                       
lower_bound)),
+                                  size_one_node);
+
+      if (size == NULL_TREE)
+       size = dim_size;
+      else
+       size = fold_build2 (MULT_EXPR, sizetype, size, dim_size);
+    }
+
+  return size;
+}
+
+/* Return a size expression that evaluates to the total size, in bytes, of the
+   elements stored in the multiple.  */
+
+tree
+a68_multiple_elements_size (tree exp)
+{
+  tree type = TREE_TYPE (exp);
+  gcc_assert (A68_ROW_TYPE_P (type));
+
+  /* elements_size% is the third field in the descriptor.  */
+  tree elements_size_field = TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (type)));
+  return fold_build3 (COMPONENT_REF, TREE_TYPE (elements_size_field),
+                     exp, elements_size_field, NULL_TREE);
+}
+
+/* Return the triplet for dimension DIM in the multiple EXP.  */
+
+static tree
+multiple_triplet (tree exp, tree dim)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+
+  /* triplets% is the first field in the descriptor.  */
+  tree triplets_field = TYPE_FIELDS (TREE_TYPE (exp));
+  tree triplets = fold_build3 (COMPONENT_REF,
+                              TREE_TYPE (triplets_field),
+                              exp,
+                              triplets_field,
+                              NULL_TREE);
+
+  /* Get the triplet for the given dimension.  */
+  return build4 (ARRAY_REF,
+                TREE_TYPE (TREE_TYPE (triplets)),
+                triplets,
+                dim,
+                NULL_TREE,
+                NULL_TREE);
+}
+
+/* Return the lower bound of dimension DIM of the multiple EXP.  The returned
+   value is a ssizetype.  */
+
+tree
+a68_multiple_lower_bound (tree exp, tree dim)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+
+  /* lb% is the first field in the triplet.  */
+  tree triplet = multiple_triplet (exp, dim);
+  tree lower_bound_field = TYPE_FIELDS (TREE_TYPE (triplet));
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (lower_bound_field),
+                     triplet,
+                     lower_bound_field,
+                     NULL_TREE);
+}
+
+/* Return an expression that sets the lower bound of dimension DIM of the
+   multiple EXP to BOUND.  */
+
+tree
+a68_multiple_set_lower_bound (tree exp, tree dim, tree bound)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+  return fold_build2 (MODIFY_EXPR,
+                     TREE_TYPE (bound),
+                     a68_multiple_lower_bound (exp, dim),
+                     bound);
+}
+
+/* Return the upper bound of dimension DIM of the multiple EXP.  The returned
+   value is a ssizetype.  */
+
+tree
+a68_multiple_upper_bound (tree exp, tree dim)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+
+  /* ub% is the second field in the triplet.  */
+  tree triplet = multiple_triplet (exp, dim);
+  tree upper_bound_field = TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (triplet)));
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (upper_bound_field),
+                     triplet,
+                     upper_bound_field,
+                     NULL_TREE);
+}
+
+/* Return an expression that sets the upper bound of dimension DIM of the
+   multiple EXP to BOUND.  */
+
+tree
+a68_multiple_set_upper_bound (tree exp, tree dim, tree bound)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+  return fold_build2 (MODIFY_EXPR,
+                     TREE_TYPE (bound),
+                     a68_multiple_upper_bound (exp, dim),
+                     bound);
+}
+
+/* Return the stride of dimension DIM of the multiple EXP.  */
+
+tree
+a68_multiple_stride (tree exp, tree dim)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+
+  /* stride% is the third field in the triplet.  */
+  tree triplet = multiple_triplet (exp, dim);
+  tree stride_field = TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (TREE_TYPE 
(triplet))));
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (stride_field),
+                     triplet,
+                     stride_field,
+                     NULL_TREE);
+}
+
+/* Return an expression that sets the stride of dimension DIM of the multiple
+   EXP to STRIDE.
+
+   STRIDE must be a sizetype.  */
+
+tree
+a68_multiple_set_stride (tree exp, tree dim, tree stride)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+  return fold_build2 (MODIFY_EXPR,
+                     TREE_TYPE (stride),
+                     a68_multiple_stride (exp, dim),
+                     stride);
+}
+
+/* Return the triplets of the multiple EXP.  */
+
+tree
+a68_multiple_triplets (tree exp)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+
+  /* triplets% is the first field in the descriptor.  */
+  tree triplets_field = TYPE_FIELDS (TREE_TYPE (exp));
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (triplets_field),
+                     exp,
+                     triplets_field,
+                     NULL_TREE);
+}
+
+/* Return the pointer to the elements of the multiple EXP.  */
+
+tree
+a68_multiple_elements (tree exp)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (exp)));
+
+  /* elements% is the second field in the descriptor.  */
+  tree elements_field = TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (exp)));
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (elements_field),
+                     exp,
+                     elements_field,
+                     NULL_TREE);
+}
+
+/* Return an expression that sets the elements% field of EXP to ELEMENTS.  */
+
+tree
+a68_multiple_set_elements (tree exp, tree elements)
+{
+  /* elements% is the second field in the descriptor.  */
+  tree elements_field = TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (exp)));
+  return fold_build2 (MODIFY_EXPR,
+                     TREE_TYPE (elements_field),
+                     fold_build3 (COMPONENT_REF,
+                                  TREE_TYPE (elements_field),
+                                  exp,
+                                  elements_field,
+                                  NULL_TREE),
+                     elements);
+}
+
+/* Return an expression that sets the elements_size% field of EXP to
+   ELEMENTS_SIZE, which must be a sizetype.  */
+
+tree
+a68_multiple_set_elements_size (tree exp, tree elements_size)
+{
+  /* elements_size% is the third field in the descriptor.  */
+  tree elements_size_field = TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (TREE_TYPE 
(exp))));
+  return fold_build2 (MODIFY_EXPR,
+                     TREE_TYPE (elements_size_field),
+                     fold_build3 (COMPONENT_REF,
+                                  TREE_TYPE (elements_size_field),
+                                  exp,
+                                  elements_size_field,
+                                  NULL_TREE),
+                     elements_size);
+}
+
+/* Given two arrays of LOWER_BOUNDs and UPPER_BOUNDs corresponding to DIM
+   dimensions of a multiple of type TYPE, fill in the strides in STRIDES, which
+   is assumed to be a buffer big enough to hold DIM tree nodes.  The bounds
+   shall be of type ssizetype, and the calculated strides are of type sizetype,
+   i.e. unsigned.  */
+
+void
+a68_multiple_compute_strides (tree type, size_t dim,
+                             tree *lower_bounds, tree *upper_bounds,
+                             tree *strides)
+{
+  tree stride = size_in_bytes (a68_row_elements_type (type));
+  for (ssize_t i = dim - 1; i >= 0; --i)
+    {
+      strides[i] = stride;
+
+      /* Calculate the stride for the previous dimension.  */
+      tree dim_num_elems
+       = save_expr (fold_build2 (PLUS_EXPR,
+                                 sizetype,
+                                 fold_convert (sizetype,
+                                               fold_build2 (MINUS_EXPR, 
ssizetype,
+                                                            upper_bounds[i], 
lower_bounds[i])),
+                                 size_one_node));
+      stride = fold_build2 (MULT_EXPR, sizetype, stride, dim_num_elems);
+    }
+}
+
+/* Return a constructor for a multiple of row type TYPE, using TRIPLETS and
+   ELEMENTS.  ELEMENTS_SIZE is the size in bytes of the memory pointed by
+   ELEMENTS.  */
+
+tree
+a68_row_value_raw (tree type, tree triplets,
+                  tree elements, tree elements_size)
+{
+  tree triplets_field;
+  tree elements_field;
+  tree elements_size_field;
+  vec <constructor_elt, va_gc> *ce = NULL;
+
+  gcc_assert (A68_ROW_TYPE_P (type));
+  triplets_field = TYPE_FIELDS (type);
+  elements_field = TREE_CHAIN (triplets_field);
+  elements_size_field = TREE_CHAIN (elements_field);
+  CONSTRUCTOR_APPEND_ELT (ce, triplets_field, triplets);
+  CONSTRUCTOR_APPEND_ELT (ce, elements_field,
+                         fold_build1 (CONVERT_EXPR ,TREE_TYPE 
(elements_field), elements));
+  CONSTRUCTOR_APPEND_ELT (ce, elements_size_field, elements_size);
+  return build_constructor (type, ce);
+}
+
+/* Return a constructor for a multiple of row type TYPE, of DIM dimensions and
+   pointing to ELEMENTS.
+
+   ELEMENTS_SIZE contains the size in bytes of the memory pointed by ELEMENTS.
+
+   *LOWER_BOUND and *UPPER_BOUND are the bounds for the DIM dimensions.
+*/
+
+tree
+a68_row_value (tree type, size_t dim,
+              tree elements, tree elements_size,
+              tree *lower_bound, tree *upper_bound)
+{
+  tree triplets_field;
+  tree elements_field;
+  tree elements_size_field;
+  vec <constructor_elt, va_gc> *ce = NULL;
+
+  gcc_assert (A68_ROW_TYPE_P (type));
+  triplets_field = TYPE_FIELDS (type);
+  elements_field = TREE_CHAIN (triplets_field);
+  elements_size_field = TREE_CHAIN (elements_field);
+
+  tree triplet_type = TREE_TYPE (TREE_TYPE (triplets_field));
+  tree lower_bound_field = TYPE_FIELDS (triplet_type);
+  tree upper_bound_field = TREE_CHAIN (TYPE_FIELDS (triplet_type));
+  tree stride_field = TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (triplet_type)));
+
+  /* Calculate strides.  */
+  tree *strides = (tree *) xmalloc (sizeof (tree) * dim);
+  a68_multiple_compute_strides (type, dim, lower_bound, upper_bound, strides);
+
+  vec <constructor_elt, va_gc> *triplets_ce = NULL;
+  for (size_t i = 0; i < dim; ++i)
+    {
+      CONSTRUCTOR_APPEND_ELT (triplets_ce,
+                             size_int (i),
+                             build_constructor_va (triplet_type,
+                                                   3,
+                                                   lower_bound_field, 
lower_bound[i],
+                                                   upper_bound_field, 
upper_bound[i],
+                                                   stride_field, strides[i]));
+    }
+  free (strides);
+  CONSTRUCTOR_APPEND_ELT (ce, triplets_field,
+                         build_constructor (TREE_TYPE (triplets_field), 
triplets_ce));
+  CONSTRUCTOR_APPEND_ELT (ce, elements_field,
+                         fold_build1 (CONVERT_EXPR, TREE_TYPE 
(elements_field), elements));
+  CONSTRUCTOR_APPEND_ELT (ce, elements_size_field,
+                         elements_size ? elements_size : size_zero_node);
+  tree multiple = build_constructor (type, ce);
+  return multiple;
+}
+
+/* Build a tree to slice a multiple given a set of indexes.
+
+   P is the tree node corresponding to the slice.  It is used as the source of
+   location information.
+
+   MULTIPLE is the multiple value being sliced.  If SLICING_NAME is true, it
+   means the slicing operation is for a name and therefore it must yield a
+   name.
+
+   INDEXES is a list of NUM_INDEXES indexes, which are units.
+   NUM_INDEXES must match the dimension of the multiple.  */
+
+tree
+a68_multiple_slice (NODE_T *p,
+                   tree multiple, bool slicing_name,
+                   int num_indexes, tree *indexes)
+{
+  tree slice = NULL_TREE;
+  tree bounds_check = NULL_TREE;
+
+  multiple = save_expr (multiple);
+  tree index = NULL_TREE;
+  for (int idx = 0; idx < num_indexes; ++idx)
+    {
+      tree lower_bound = a68_multiple_lower_bound (multiple, size_int (idx));
+      tree index_expr = save_expr (indexes[idx]);
+
+      /* Do run-time bound checking if requested.  */
+      if (OPTION_BOUNDS_CHECKING (&A68_JOB))
+       {
+         tree upper_bound = a68_multiple_upper_bound (multiple, size_int 
(idx));
+         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_ARRAYBOUNDS,
+                                        void_type_node, 5,
+                                        filename,
+                                        build_int_cst (unsigned_type_node, 
lineno),
+                                        fold_convert (ssizetype, index_expr),
+                                        fold_convert (ssizetype, lower_bound),
+                                        fold_convert (ssizetype, upper_bound));
+         call = fold_build2 (COMPOUND_EXPR, a68_bool_type, call, 
boolean_false_node);
+
+         /* If LB > UB, the dimension contains no elements.
+            Otherwise, it must hold IDX >= LB && IDX <= UB */
+         tree dim_bounds_check = fold_build2 (TRUTH_AND_EXPR, sizetype,
+                                              fold_build2 (LE_EXPR, ssizetype,
+                                                           lower_bound, 
upper_bound),
+                                              fold_build2 (TRUTH_AND_EXPR,
+                                                           boolean_type_node,
+                                                           fold_build2 
(GE_EXPR, ssizetype,
+                                                                        
fold_convert (ssizetype,
+                                                                               
       index_expr),
+                                                                        
lower_bound),
+                                                           fold_build2 
(LE_EXPR, ssizetype,
+                                                                        
fold_convert (ssizetype,
+                                                                               
       index_expr),
+                                                                        
upper_bound)));
+         dim_bounds_check = fold_build2_loc (a68_get_node_location (p),
+                                             TRUTH_ORIF_EXPR,
+                                             ssizetype,
+                                             dim_bounds_check, call);
+
+         /* bounds_check_ok || call_runtime_error */
+         if (bounds_check == NULL_TREE)
+           bounds_check = dim_bounds_check;
+         else
+           bounds_check = fold_build2 (TRUTH_ANDIF_EXPR,
+                                       ssizetype,
+                                       bounds_check,
+                                       dim_bounds_check);
+       }
+
+      /* Now add the effect of this dimension's subscript in the index.  Note
+        that the stride is expressed in bytes.  */
+      tree stride = a68_multiple_stride (multiple, size_int (idx));
+      tree adjusted_index
+       = fold_convert (sizetype, fold_build2 (MINUS_EXPR, ssizetype,
+                                              fold_convert (ssizetype, 
index_expr),
+                                              lower_bound));
+      tree term = fold_build2 (MULT_EXPR, sizetype,
+                              adjusted_index, stride);
+      if (index == NULL_TREE)
+       index = term;
+      else
+       index = fold_build2 (PLUS_EXPR, sizetype,
+                            index, term);
+    }
+
+  tree elements = a68_multiple_elements (multiple);
+  tree element_pointer_type = TREE_TYPE (elements);
+  tree element_type = TREE_TYPE (element_pointer_type);
+
+  /* Now refer to the indexed element.  In case we are slicing a ref to a
+     multiple, return the address of the element and not the element
+     itself.  */
+  tree element_address = fold_build2 (POINTER_PLUS_EXPR,
+                                     element_pointer_type,
+                                     elements,
+                                     index);
+  if (slicing_name)
+    slice = element_address;
+  else
+    slice = fold_build2 (MEM_REF,
+                        element_type,
+                        fold_build2 (POINTER_PLUS_EXPR,
+                                     element_pointer_type,
+                                     elements,
+                                     index),
+                        fold_convert (element_pointer_type,
+                                      integer_zero_node));
+
+  /* Prepend bounds checking code if necessary.  */
+  if (bounds_check != NULL_TREE)
+    {
+      slice = fold_build2_loc (a68_get_node_location (p),
+                              COMPOUND_EXPR,
+                              TREE_TYPE (slice),
+                              bounds_check,
+                              slice);
+    }
+
+  return slice;
+}
+
+/* Auxiliary routine for a68_multiple_copy_elemens.  */
+
+static tree
+copy_multiple_dimension_elems (size_t dim, size_t num_dimensions,
+                              tree to, tree from,
+                              tree to_elements, tree from_elements,
+                              tree *to_offset, tree *from_offset,
+                              tree *indexes)
+{
+  tree element_pointer_type = TREE_TYPE (from_elements);
+  tree element_type = TREE_TYPE (element_pointer_type);
+  tree upb = a68_multiple_upper_bound (from, size_int (dim));
+
+  char *name = xasprintf ("r%ld%%", dim);
+  indexes[dim] = a68_lower_tmpvar (name, ssizetype,
+                                  a68_multiple_lower_bound (from,
+                                                            size_int (dim)));
+  free (name);
+
+  /* Loop body.  */
+  a68_push_range (NULL);
+  {
+    /* if (indexes[dim] > upb) break; */
+    a68_add_stmt (fold_build1 (EXIT_EXPR, void_type_node,
+                              fold_build2 (GT_EXPR, size_type_node,
+                                           indexes[dim], upb)));
+
+    /* Add this dimension's contribution to the offsets.  */
+    tree index = fold_convert (sizetype,
+                              fold_build2 (MINUS_EXPR, ssizetype,
+                                           upb, indexes[dim]));
+    *to_offset = fold_build2 (PLUS_EXPR, sizetype,
+                             *to_offset,
+                             fold_build2 (MULT_EXPR, sizetype,
+                                          index,
+                                          a68_multiple_stride (to, size_int 
(dim))));
+    *from_offset = fold_build2 (PLUS_EXPR, sizetype,
+                               *from_offset,
+                               fold_build2 (MULT_EXPR, sizetype,
+                                            index,
+                                            a68_multiple_stride (from, 
size_int (dim))));
+
+    if (dim == num_dimensions - 1)
+      {
+       /* Most inner loop, copy one element.  */
+
+       tree to_off = a68_lower_tmpvar ("to_offset%", sizetype, *to_offset);
+       tree from_off = a68_lower_tmpvar ("from_offset%", sizetype, 
*from_offset);
+
+       tree to_elem = fold_build2 (MEM_REF,
+                                   element_type,
+                                   fold_build2 (POINTER_PLUS_EXPR,
+                                                element_pointer_type,
+                                                to_elements,
+                                                to_off),
+                                   fold_convert (element_pointer_type,
+                                                 integer_zero_node));
+       tree from_elem = fold_build2 (MEM_REF,
+                                     element_type,
+                                     fold_build2 (POINTER_PLUS_EXPR,
+                                                  element_pointer_type,
+                                                  from_elements,
+                                                  from_off),
+                                     fold_convert (element_pointer_type,
+                                                   integer_zero_node));
+
+       /* XXX
+          if may_overlap then modify only if dst_offset < src_offset */
+       a68_add_stmt (fold_build2 (MODIFY_EXPR, element_type,
+                                  to_elem, from_elem));
+      }
+    else
+      {
+       a68_add_stmt (copy_multiple_dimension_elems (dim + 1, num_dimensions,
+                                                    to, from,
+                                                    to_elements, from_elements,
+                                                    to_offset, from_offset,
+                                                    indexes));
+      }
+
+    /* indexes[dim]++ */
+    a68_add_stmt (fold_build2 (POSTINCREMENT_EXPR, ssizetype,
+                              indexes[dim], ssize_int (1)));
+  }
+  tree loop_body = a68_pop_range ();
+
+  return fold_build1 (LOOP_EXPR, void_type_node, loop_body);
+}
+
+/* Copy the elements of a given multiple (string) FROM to the multiple (string)
+   TO.
+
+   The dimensions and bounds of both multiples are supposed to match, and they
+   are supposed to not be flat.
+
+   XXX simple cases  with same strides may be done with a memcpy.
+   XXX compile this into a support routine to reduce code size.  */
+
+tree
+a68_multiple_copy_elems (MOID_T *mode, tree to, tree from)
+{
+  gcc_assert (A68_ROW_TYPE_P (TREE_TYPE (to))
+             && A68_ROW_TYPE_P (TREE_TYPE (from)));
+
+  /* Deflex modes as needed and determine dimension.  */
+  if (IS_FLEX (mode))
+    mode = SUB (mode);
+  int num_dimensions = (mode == M_STRING ? 1 : DIM (mode));
+
+  a68_push_range (NULL);
+  to = a68_lower_tmpvar ("to%", TREE_TYPE (to), to);
+  from = a68_lower_tmpvar ("from%", TREE_TYPE (from), from);
+  tree from_elements = a68_multiple_elements (from);
+  tree element_pointer_type = TREE_TYPE (from_elements);
+  from_elements = a68_lower_tmpvar ("from_elements%", element_pointer_type,
+                                   from_elements);
+  tree to_elements = a68_lower_tmpvar ("to_elements%", element_pointer_type,
+                                      a68_multiple_elements (to));
+
+  tree *indexes = (tree *) xmalloc (num_dimensions * sizeof (tree));
+  tree to_offset = size_zero_node;
+  tree from_offset = size_zero_node;
+  a68_add_stmt (copy_multiple_dimension_elems (0 /* dim */, num_dimensions,
+                                              to, from,
+                                              to_elements, from_elements,
+                                              &to_offset, &from_offset,
+                                              indexes));
+  free (indexes);
+  return a68_pop_range ();
+}
+
+/* Given a rows type, return the number of dimensions.  */
+
+tree
+a68_rows_dim (tree exp)
+{
+  gcc_assert (A68_ROWS_TYPE_P (TREE_TYPE (exp)));
+
+  /* dim% is the first field in the rows struct.  */
+  tree dim_field = TYPE_FIELDS (TREE_TYPE (exp));
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (dim_field),
+                     exp,
+                     dim_field,
+                     NULL_TREE);
+}
+
+/* Given a multiple value, create a rows value reflecting the multiple's
+   dimensions and triplets.  */
+
+tree
+a68_rows_value (tree multiple)
+{
+  tree rows_type = CTYPE (M_ROWS);
+  tree dim_field = TYPE_FIELDS (rows_type);
+  tree triplets_field = TREE_CHAIN (dim_field);
+
+  tree dimensions = save_expr (a68_multiple_dimensions (multiple));
+  tree triplets = fold_build1 (ADDR_EXPR, TREE_TYPE (triplets_field),
+                              a68_multiple_triplets (multiple));
+  return build_constructor_va (rows_type, 2,
+                              dim_field, dimensions,
+                              triplets_field, triplets);
+}
+
+/* Given a rows value and a dimension number, return the upper bound or the
+   lower of the given dimension.  The returned bound is a ssizetype.
+
+   DIM must be a sizetype.  */
+
+static tree
+rows_lower_or_upper_bound (tree rows, tree dim, bool upper)
+{
+  tree rows_type = TREE_TYPE (rows);
+  tree triplet_type = a68_triplet_type ();
+  tree triplet_pointer_type = build_pointer_type (triplet_type);
+  tree triplet_lb_field = TYPE_FIELDS (triplet_type);
+  tree triplet_ub_field = TREE_CHAIN (TYPE_FIELDS (triplet_type));
+  tree triplets_field = TREE_CHAIN (TYPE_FIELDS (rows_type));
+  tree triplets = fold_build3 (COMPONENT_REF, triplet_pointer_type,
+                              rows, triplets_field, NULL_TREE);
+  tree triplet_offset = fold_build2 (MULT_EXPR, sizetype,
+                                    dim,
+                                    size_in_bytes (triplet_type));
+  tree bound = fold_build3 (COMPONENT_REF, ssizetype,
+                           fold_build1 (INDIRECT_REF, triplet_type,
+                                        fold_build2 (POINTER_PLUS_EXPR,
+                                                     triplet_pointer_type,
+                                                     triplets,
+                                                     triplet_offset)),
+                           upper ? triplet_ub_field : triplet_lb_field,
+                           NULL_TREE);
+
+  return bound;
+}
+
+/* Return the lower bound of dimension DIM of ROWS.  */
+
+tree
+a68_rows_lower_bound (tree rows, tree dim)
+{
+  return rows_lower_or_upper_bound (rows, dim, false);
+}
+
+/* Return the upper bound of dimension DIM of ROWS.  */
+
+tree
+a68_rows_upper_bound (tree rows, tree dim)
+{
+  return rows_lower_or_upper_bound (rows, dim, true);
+}
+
+/* Return a tree that checks that a given INDEX is correct given a multiple's
+   bounds in a given rank DIM.
+
+   If UPPER_BOUND is true then INDEX shall be less or equal than the multiple's
+   upper bound.  Otherwise INDEX shall be bigger or equal than the multiple's
+   lower bound.
+
+   If the condition above doesn't hold then a call to a run-time function is
+   performed: if UPPER_BOUND is true then ARRAYUPPERBOUND is called.  Otherwise
+   ARRAYLOWERBOUND is called.  */
+
+tree
+a68_multiple_single_bound_check (NODE_T *p, tree dim,
+                                tree multiple, tree index, bool upper_bound)
+{
+  index = save_expr (index);
+  multiple = save_expr (multiple);
+
+  tree bound = (upper_bound
+               ? a68_multiple_upper_bound (multiple, dim)
+               : a68_multiple_lower_bound (multiple, dim));
+  a68_libcall_fn libcall = (upper_bound
+                           ? A68_LIBCALL_ARRAYUPPERBOUND
+                           : A68_LIBCALL_ARRAYLOWERBOUND);
+
+  /* Build the call to ARRAY*BOUNDS. */
+  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 (libcall,
+                                void_type_node, 4,
+                                filename,
+                                build_int_cst (unsigned_type_node, lineno),
+                                fold_convert (ssizetype, index),
+                                fold_convert (ssizetype, bound));
+  call = fold_build2 (COMPOUND_EXPR, a68_bool_type, call, boolean_false_node);
+
+  tree bounds_check = fold_build2 (upper_bound ? LE_EXPR : GE_EXPR,
+                                  ssizetype,
+                                  fold_convert (ssizetype, index),
+                                  bound);
+  return fold_build2_loc (a68_get_node_location (p),
+                         TRUTH_ORIF_EXPR,
+                         ssizetype,
+                         bounds_check, call);
+}
+
+/* Return a tree that checks whether the given DIM is a valid dimension/rank of
+   a boundable object with dimension BOUNDABLE_DIM.  If the provided DIM is not
+   a valid dimention then a call to the run-time function ARRAYDIM is
+   performed.
+
+   BOUNDABLE_DIM and DIM must be of type sizetype.  They are both one-based.
+
+   The parse tree node P is used as the source for the filename and line number
+   passed to the run-time function.  */
+
+static tree
+a68_boundable_dim_check (NODE_T *p, tree boundable_dim, tree dim)
+{
+  boundable_dim = save_expr (boundable_dim);
+  dim = save_expr (dim);
+
+  /* Build the call to ARRAYDIM. */
+  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_ARRAYDIM,
+                                void_type_node, 4,
+                                filename,
+                                build_int_cst (unsigned_type_node, lineno),
+                                boundable_dim, dim);
+  call = fold_build2 (COMPOUND_EXPR, a68_bool_type, call, boolean_false_node);
+
+  tree dim_check = fold_build2 (TRUTH_AND_EXPR, boolean_type_node,
+                               fold_build2 (GT_EXPR, boolean_type_node, dim, 
size_zero_node),
+                               fold_build2 (LE_EXPR, boolean_type_node, dim, 
boundable_dim));
+  return fold_build2_loc (a68_get_node_location (p),
+                         TRUTH_ORIF_EXPR,
+                         ssizetype,
+                         dim_check, call);
+}
+
+/* Return a tree that checks whether the given DIM is a valid dimension/rank of
+   the given rows value ROWS.
+
+   DIM is a sizetype.
+   The parse tree node P is used as the source for the filename and line
+   number.  */
+
+tree
+a68_rows_dim_check (NODE_T *p, tree rows, tree dim)
+{
+  return a68_boundable_dim_check (p, a68_rows_dim (rows), dim);
+}
+
+/* Return a tree that checks whether the given DIM is a valid dimension/rank of
+   the given multiple value MULTIPLE.
+
+   DIM is a sizetype.
+   The parse tree node P is used as the source for the filename and line
+   number.  */
+
+tree
+a68_multiple_dim_check (NODE_T *p, tree multiple, tree dim)
+{
+  return a68_boundable_dim_check (p, a68_multiple_dimensions (multiple), dim);
+}
+
+/* Return a tree that checks whether the given INDEX falls within the bounds of
+   MULTIPLE in the rank DIM.  If the provided index is out of bounds then a
+   call to the run-time function ARRAYBOUNDS is performed.
+
+   DIM must be a sizetype.
+   MULTIPLE must be a multiple value.
+   INDEX must be a ssizetype.
+
+   The parse tree node P is used as the source for the filename and line number
+   passed to the run-time function.  */
+
+tree
+a68_multiple_bounds_check (NODE_T *p, tree dim,
+                          tree multiple, tree index)
+{
+  index = save_expr (index);
+  multiple = save_expr (multiple);
+
+  tree upper_bound = a68_multiple_upper_bound (multiple, dim);
+  tree lower_bound = a68_multiple_lower_bound (multiple, dim);
+
+  /* Build the call to ARRAYBOUNDS. */
+  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_ARRAYBOUNDS,
+                                void_type_node, 5,
+                                filename,
+                                build_int_cst (unsigned_type_node, lineno),
+                                fold_convert (ssizetype, index),
+                                fold_convert (ssizetype, lower_bound),
+                                fold_convert (ssizetype, upper_bound));
+  call = fold_build2 (COMPOUND_EXPR, a68_bool_type, call, boolean_false_node);
+
+  /* If LB > UB, the dimension contains no elements.
+     Otherwise, it must hold IDX >= LB && IDX <= UB */
+  tree bounds_check = fold_build2 (TRUTH_AND_EXPR, sizetype,
+                                  fold_build2 (LE_EXPR, ssizetype,
+                                               lower_bound, upper_bound),
+                                  fold_build2 (TRUTH_AND_EXPR,
+                                               boolean_type_node,
+                                               fold_build2 (GE_EXPR, ssizetype,
+                                                            fold_convert 
(ssizetype,
+                                                                          
index),
+                                                            lower_bound),
+                                               fold_build2 (LE_EXPR, ssizetype,
+                                                            fold_convert 
(ssizetype,
+                                                                          
index),
+                                                            upper_bound)));
+  return fold_build2_loc (a68_get_node_location (p),
+                         TRUTH_ORIF_EXPR,
+                         ssizetype,
+                         bounds_check, call);
+}
+
+/* Emit a run-time error if the bounds of M1 and M2 are not the same.  Both
+   multiples are assumed to have the same type and therefore feature the same
+   number of dimensions.  */
+
+tree
+a68_multiple_bounds_check_equal (NODE_T *p, tree m1, tree m2)
+{
+  m1 = save_expr (m1);
+  m2 = save_expr (m2);
+
+  /* First determine the rank of the multiples and check they match.  */
+  tree m1_dimensions = a68_multiple_dimensions (m1);
+  tree m2_dimensions = a68_multiple_dimensions (m2);
+  gcc_assert (TREE_CODE (m1_dimensions) == INTEGER_CST
+             && TREE_CODE (m2_dimensions) == INTEGER_CST);
+
+  int dim1 = tree_to_shwi (m1_dimensions);
+  int dim2 = tree_to_shwi (m2_dimensions);
+  gcc_assert (dim1 == dim2);
+
+  a68_push_range (NULL /* VOID */);
+
+  /* For each dimension, check that bounds are the same in both multiples.  */
+  int i;
+  for (i = 0; i < dim1; ++i)
+    {
+      tree dim_tree = build_int_cst (ssizetype, i);
+      tree dim_plus_one = fold_build2 (PLUS_EXPR, ssizetype,
+                                      dim_tree,
+                                      fold_convert (ssizetype, size_one_node));
+
+      tree lb1 = save_expr (a68_multiple_lower_bound (m1, dim_tree));
+      tree lb2 = save_expr (a68_multiple_lower_bound (m2, dim_tree));
+
+      tree ub1 = save_expr (a68_multiple_upper_bound (m1, dim_tree));
+      tree ub2 = save_expr (a68_multiple_upper_bound (m2, dim_tree));
+
+      tree bounds_equal = fold_build2 (TRUTH_AND_EXPR,
+                                      boolean_type_node,
+                                      fold_build2 (EQ_EXPR, boolean_type_node,
+                                                   lb1, lb2),
+                                      fold_build2 (EQ_EXPR, boolean_type_node,
+                                                   ub1, ub2));
+
+      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_ARRAYBOUNDSMISMATCH,
+                                    void_type_node, 7,
+                                    filename,
+                                    build_int_cst (unsigned_type_node, lineno),
+                                    dim_plus_one,
+                                    lb1, ub1, lb2, ub2);
+      call = fold_build2 (COMPOUND_EXPR, boolean_type_node, call, 
boolean_false_node);
+
+      tree check = fold_build2_loc (a68_get_node_location (p),
+                                   TRUTH_ORIF_EXPR, boolean_type_node,
+                                   bounds_equal,
+                                   call);
+      a68_add_stmt (check);
+    }
+
+  return a68_pop_range ();
+}
+
+/* Allocate a multiple on the heap.
+
+   M is the mode the multiple to allocate.
+   DIM is the number of dimensions of the multiple.
+   ELEMS is a pointer to the elements of the multiple.
+   ELEMS_SIZE is the size in bytes of ELEMS.
+   *LOWER_BOUND and *UPPER_BOUND are the bounds for the DIM dimensions.  */
+
+tree
+a68_row_malloc (tree type, int dim, tree elems, tree elems_size,
+               tree *lower_bound, tree *upper_bound)
+{
+  tree ptr_to_type = build_pointer_type (type);
+
+  a68_push_range (NULL);
+
+  /* Allocate space for the descriptor.  */
+  tree ptr_to_multiple = a68_lower_tmpvar ("ptr_to_multiple%", ptr_to_type,
+                                          a68_lower_malloc (type, 
size_in_bytes (type)));
+  tree multiple = a68_row_value (type, dim,
+                                elems, elems_size,
+                                lower_bound, upper_bound);
+  a68_add_stmt (fold_build2 (MODIFY_EXPR, void_type_node,
+                            fold_build1 (INDIRECT_REF, type, ptr_to_multiple),
+                            multiple));
+  a68_add_stmt (ptr_to_multiple);
+  tree res = a68_pop_range ();
+  TREE_TYPE (res) = ptr_to_type;
+  return res;
+}
diff --git a/gcc/algol68/a68-low-structs.cc b/gcc/algol68/a68-low-structs.cc
new file mode 100644
index 00000000000..12bb6192fb4
--- /dev/null
+++ b/gcc/algol68/a68-low-structs.cc
@@ -0,0 +1,63 @@
+/* Lowering routines for all things related to structs.
+   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 the yielding of SKIP for the given structured mode.  */
+
+tree
+a68_get_struct_skip_tree (MOID_T *m)
+{
+  /* Build a constructor that assigns SKIPs to each field in the struct
+     type.  */
+
+  vec <constructor_elt, va_gc> *ve = NULL;
+  tree field = TYPE_FIELDS (CTYPE (m));
+  for (PACK_T *elem = PACK (m); elem; FORWARD (elem))
+    {
+      gcc_assert (field != NULL_TREE);
+      CONSTRUCTOR_APPEND_ELT (ve, field, a68_get_skip_tree (MOID (elem)));
+      field = DECL_CHAIN (field);
+    }
+
+  return build_constructor (CTYPE (m), ve);
+}
diff --git a/gcc/algol68/a68-low-unions.cc b/gcc/algol68/a68-low-unions.cc
new file mode 100644
index 00000000000..f775877f327
--- /dev/null
+++ b/gcc/algol68/a68-low-unions.cc
@@ -0,0 +1,279 @@
+/* Lowering routines for all things related to unions.
+   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"
+
+/* Algol 68 unions are implemented in this front-end as a data structure
+   consisting of an overhead followed by a value:
+
+     overhead%
+     value%
+
+   Where overhead% is an index that identifies the kind of object currently
+   united, and value% is a GENERIC union.  The value currently united in the
+   union is the overhead%-th field in value%.
+
+   At the language level there are no values of union modes in Algol 68.  All
+   values are built from either SKIP (for uninitialized UNION values) or as the
+   result of an uniting coercion.  */
+
+/* Given an union mode P and a mode Q, return whether Q is a mode in P.  */
+
+bool
+a68_union_contains_mode (MOID_T *p, MOID_T *q)
+{
+  while (EQUIVALENT (p) != NO_MOID)
+    p = EQUIVALENT (p);
+
+  for (PACK_T *pack = PACK (p); pack != NO_PACK; FORWARD (pack))
+    {
+      MOID_T *m = MOID (pack);
+
+      if (a68_is_equal_modes (q, m, SAFE_DEFLEXING)
+         || (m == M_STRING && IS_ROW (q) && SUB (q) == M_CHAR)
+         || (q == M_STRING && IS_ROW (m) && SUB (m) == M_CHAR))
+       return true;
+    }
+
+  return false;
+}
+
+/* Given an union mode P and a mode Q, return an integer with the index of the
+   occurrence of Q in P.  */
+
+int
+a68_united_mode_index (MOID_T *p, MOID_T *q)
+{
+  int ret = 0;
+  while (EQUIVALENT (p) != NO_MOID)
+    p = EQUIVALENT (p);
+  for (PACK_T *pack = PACK (p); pack != NO_PACK; FORWARD (pack))
+    {
+      MOID_T *m = MOID (pack);
+
+      if (a68_is_equal_modes (q, m, SAFE_DEFLEXING)
+         || (m == M_STRING && IS_ROW (q) && SUB (q) == M_CHAR)
+         || (q == M_STRING && IS_ROW (m) && SUB (m) == M_CHAR))
+       return ret;
+      ret += 1;
+    }
+
+  /* Not found.  Shouldn't happen.  */
+  gcc_unreachable ();
+  return 0;
+}
+
+/* Given two united modes FROM and TO, and an overhead FROM_OVERHEAD in mode
+   FROM, return the corresponding overhead in mode TO.
+
+   This function assumes that the mode with FROM_OVERHEAD in mode FROM exists
+   in TO.  */
+
+tree
+a68_union_translate_overhead (MOID_T *from, tree from_overhead,
+                             MOID_T *to)
+{
+  /* Note that the initialization value for to_overhead should never be used.
+     XXX perhaps translate it to a run-time call to abort/compiler-error.  */
+  tree to_overhead = size_int (0);
+
+  from_overhead = save_expr (from_overhead);
+
+  int i = 0;
+  for (PACK_T *pack = PACK (from); pack != NO_PACK; FORWARD (pack), ++i)
+    {
+      MOID_T *mode = MOID (pack);
+
+      if (a68_union_contains_mode (to, mode))
+       {
+         to_overhead = fold_build3 (COND_EXPR, sizetype,
+                                    fold_build2 (EQ_EXPR, boolean_type_node,
+                                                 from_overhead,
+                                                 size_int (i)),
+                                    size_int (a68_united_mode_index (to, 
mode)),
+                                    to_overhead);
+       }
+    }
+
+  return to_overhead;
+}
+
+/* Get the overhead of a given united value EXP.  */
+
+tree
+a68_union_overhead (tree exp)
+{
+  tree type = TREE_TYPE (exp);
+  tree overhead_field = TYPE_FIELDS (type);
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (overhead_field),
+                     exp,
+                     overhead_field,
+                     NULL_TREE);
+}
+
+/* Set the overhead of a given united value EXP to OVERHEAD.  */
+
+tree
+a68_union_set_overhead (tree exp, tree overhead)
+{
+  tree type = TREE_TYPE (exp);
+  tree overhead_field = TYPE_FIELDS (type);
+  return fold_build2 (MODIFY_EXPR,
+                     TREE_TYPE (overhead),
+                     fold_build3 (COMPONENT_REF,
+                                  TREE_TYPE (overhead_field),
+                                  exp,
+                                  overhead_field,
+                                  NULL_TREE),
+                     overhead);
+}
+
+/* Get the cunion in the given union EXP.  */
+
+tree
+a68_union_cunion (tree exp)
+{
+  tree type = TREE_TYPE (exp);
+  tree value_field = TREE_CHAIN (TYPE_FIELDS (type));
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (value_field),
+                     exp,
+                     value_field,
+                     NULL_TREE);
+}
+
+/* Build a SKIP value for a given union mode M.
+
+   The SKIP value computed is:
+
+   overhead% refers to the first united mode in the union
+   value% is the SKIP for the first united mode in the union
+*/
+
+tree
+a68_get_union_skip_tree (MOID_T *m)
+{
+  tree type = CTYPE (m);
+  tree overhead_field = TYPE_FIELDS (type);
+  tree value_field = TREE_CHAIN (TYPE_FIELDS (type));
+
+  /* Overhead selects the first union alternative.  */
+  tree overhead = size_zero_node;
+  /* First union alternative.
+
+     Note that the first union alternative corresponds to the last alternative
+     in the mode as written in the source program.  */
+  tree value_type = TREE_TYPE (value_field);
+  tree first_alternative_field = TYPE_FIELDS (value_type);
+  tree value = build_constructor_va (TREE_TYPE (value_field),
+                                    1,
+                                    first_alternative_field,
+                                    a68_get_skip_tree (MOID (PACK (m))));
+  return build_constructor_va (CTYPE (m),
+                              2,
+                              overhead_field, overhead,
+                              value_field, value);
+}
+
+/* Return the alternative (value) at the index INDEX in the united value
+   EXP.  */
+
+tree
+a68_union_alternative (tree exp, int index)
+{
+  tree type = TREE_TYPE (exp);
+  tree value_field = TREE_CHAIN (TYPE_FIELDS (type));
+  tree value = fold_build3 (COMPONENT_REF,
+                           TREE_TYPE (value_field),
+                           exp,
+                           value_field,
+                           NULL_TREE);
+
+  /* Get the current alternative in the value union.  */
+  tree value_type = TREE_TYPE (value_field);
+  tree alternative_field = TYPE_FIELDS (value_type);
+  for (int i = 0; i < index; ++i)
+    {
+      gcc_assert (TREE_CHAIN (alternative_field));
+      alternative_field = TREE_CHAIN (alternative_field);
+    }
+
+  /* Get the current alternative from the value.  */
+  return fold_build3 (COMPONENT_REF,
+                     TREE_TYPE (alternative_field),
+                     value,
+                     alternative_field,
+                     NULL_TREE);
+}
+
+/* Return a constructor for an union of mode MODE, holding the value in EXP
+   which is of mode EXP_MODE.  */
+
+tree
+a68_union_value (MOID_T *mode, tree exp, MOID_T *exp_mode)
+{
+  tree type = CTYPE (mode);
+  tree overhead_field = TYPE_FIELDS (type);
+  tree value_field = TREE_CHAIN (TYPE_FIELDS (type));
+
+  int alternative_index = a68_united_mode_index (mode, exp_mode);
+  tree overhead = build_int_cst (sizetype, alternative_index);
+
+  /* Get the field for the alternative corresponding to alternative_index.  */
+  tree value_type = TREE_TYPE (value_field);
+  tree alternative_field = TYPE_FIELDS (value_type);
+  for (int i = 0; i < alternative_index; ++i)
+    {
+      gcc_assert (TREE_CHAIN (alternative_field));
+      alternative_field = TREE_CHAIN (alternative_field);
+    }
+
+  tree value = build_constructor_va (TREE_TYPE (value_field),
+                                    1,
+                                    alternative_field,
+                                    a68_consolidate_ref (exp_mode, exp));
+  return build_constructor_va (type,
+                              2,
+                              overhead_field, overhead,
+                              value_field, value);
+}
-- 
2.30.2

Reply via email to