| /* Lowering routines for all things related to BITS values. |
| 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 yielind of SKIP for the given BITS mode. */ |
| |
| tree |
| a68_get_bits_skip_tree (MOID_T *m) |
| { |
| tree type; |
| |
| if (m == M_BITS) |
| type = a68_bits_type; |
| else if (m == M_LONG_BITS) |
| type = a68_long_bits_type; |
| else if (m == M_LONG_LONG_BITS) |
| type = a68_long_long_bits_type; |
| else if (m == M_SHORT_BITS) |
| type = a68_short_bits_type; |
| else if (m == M_SHORT_SHORT_BITS) |
| type = a68_short_short_bits_type; |
| else |
| gcc_unreachable (); |
| |
| return build_int_cst (type, 0); |
| } |
| |
| /* Given a BITS type, compute the number of bits that fit in a value of that |
| type. The result is an INT. */ |
| |
| tree |
| a68_bits_width (tree type) |
| { |
| return fold_convert (a68_int_type, TYPE_SIZE (type)); |
| } |
| |
| /* Given a BITS type, compute the maximum value that can be expressed with that |
| type. */ |
| |
| tree |
| a68_bits_maxbits (tree type) |
| { |
| return fold_convert (type, TYPE_MAX_VALUE (type)); |
| } |
| |
| /* Given a SIZETY INT value VAL, compute and return a SIZETY BITS reflecting |
| its constituent bits. |
| |
| In strict Algol 68 the BIN of a negative value is BITS (SKIP). |
| |
| In GNU 68 the BIN of a negative value is the constituent bits of the two's |
| complement of the value. */ |
| |
| tree |
| a68_bits_bin (MOID_T *m, tree val) |
| { |
| tree type = CTYPE (m); |
| |
| if (OPTION_STRICT (&A68_JOB)) |
| return a68_get_bits_skip_tree (m); |
| else |
| return fold_convert (type, val); |
| } |
| |
| /* Given a SIZETY BITS value BITS, compute and return the corresponding SIZETY |
| INT. |
| |
| In strict Algol 68 the ABS of a BITS value reflecting a bit pattern that |
| would correspond a negative integral value is INT (SKIP). |
| |
| In GNU 68 the ABS of a BITS value reflecting a bit pattern that would |
| correspond a negative integral value is that negative integral value. */ |
| |
| tree |
| a68_bits_abs (MOID_T *m, tree bits) |
| { |
| tree type = CTYPE (m); |
| |
| if (OPTION_STRICT (&A68_JOB)) |
| { |
| tree integral_val = save_expr (fold_convert (type, bits)); |
| return fold_build3 (COND_EXPR, |
| type, |
| fold_build2 (LT_EXPR, type, integral_val, |
| build_int_cst (type, 0)), |
| a68_get_int_skip_tree (m), |
| integral_val); |
| } |
| else |
| return fold_convert (type, bits); |
| } |
| |
| /* Given a SIZETY BITS value BITS, shorten it into a SIZETY BITS whose tree |
| type is TYPE. */ |
| |
| tree |
| a68_bits_shorten (tree type, tree bits) |
| { |
| /* This will truncate at the left, which is what is intended. */ |
| return fold_convert (type, bits); |
| } |
| |
| /* Given a SIZETY BITS value BITS, length it into a SIZETY BITS whose tree type |
| is TYPE. */ |
| |
| tree |
| a68_bits_leng (tree type, tree bits) |
| { |
| /* This will add zeroes to the left, which is what is intended. */ |
| return fold_convert (type, bits); |
| } |
| |
| /* Given a SIZETY BITS value BITS, compute and return a new SIZETY BITS whose |
| bits are the logical negation of the bits of BITS. */ |
| |
| tree |
| a68_bits_not (tree bits) |
| { |
| return fold_build1 (BIT_NOT_EXPR, TREE_TYPE (bits), bits); |
| } |
| |
| /* Given two SIZETY BITS values BITS1 and BITS2, compute and return a new |
| SIZETY BITS whose bits are the `and' of the bits of BITS1 and |
| BITS2. */ |
| |
| tree |
| a68_bits_and (tree bits1, tree bits2) |
| { |
| return fold_build2 (BIT_AND_EXPR, TREE_TYPE (bits1), bits1, bits2); |
| } |
| |
| /* Given two SIZETY BITS values BITS1 and BITS2, compute and return a new |
| SIZETY BITS whose bits are the inclusive-or of the bits of BITS1 and |
| BITS2. */ |
| |
| tree |
| a68_bits_ior (tree bits1, tree bits2) |
| { |
| return fold_build2 (BIT_IOR_EXPR, TREE_TYPE (bits1), bits1, bits2); |
| } |
| |
| /* Given two SIZETY BITS values BITS1 and BITS2, compute and return a new |
| SIZETY BITS whose bits are the exclusive-or of the bits of BITS1 and |
| BITS2. */ |
| |
| tree |
| a68_bits_xor (tree bits1, tree bits2) |
| { |
| return fold_build2 (BIT_XOR_EXPR, TREE_TYPE (bits1), bits1, bits2); |
| } |
| |
| /* Given a position POS of mode INT and a BITS of mode SIZETY BITS, return a |
| BOOL reflecting the state of the bit occupying the position POS in BITS. |
| |
| If POS is out of range a run-time error is emitted. */ |
| |
| tree |
| a68_bits_elem (NODE_T *p, tree pos, tree bits) |
| { |
| pos = save_expr (pos); |
| tree one = build_int_cst (TREE_TYPE (bits), 1); |
| |
| tree shift = fold_build2 (MINUS_EXPR, bitsizetype, |
| TYPE_SIZE (TREE_TYPE (bits)), |
| fold_convert (bitsizetype, pos)); |
| tree elem = fold_build2 (EQ_EXPR, |
| a68_bool_type, |
| fold_build2 (BIT_AND_EXPR, |
| TREE_TYPE (bits), |
| fold_build2 (RSHIFT_EXPR, |
| TREE_TYPE (bits), |
| bits, shift), |
| one), |
| one); |
| |
| /* Do bounds checking if requested. */ |
| if (OPTION_BOUNDS_CHECKING (&A68_JOB)) |
| { |
| 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_BITSBOUNDSERROR, |
| void_type_node, 3, |
| filename, |
| build_int_cst (unsigned_type_node, lineno), |
| fold_convert (ssizetype, pos)); |
| tree check = fold_build2 (TRUTH_AND_EXPR, integer_type_node, |
| fold_build2 (GT_EXPR, integer_type_node, |
| pos, fold_convert (TREE_TYPE (pos), integer_zero_node)), |
| fold_build2 (LE_EXPR, integer_type_node, |
| fold_convert (bitsizetype, pos), |
| TYPE_SIZE (TREE_TYPE (bits)))); |
| |
| check = fold_build2_loc (a68_get_node_location (p), |
| TRUTH_ORIF_EXPR, |
| ssizetype, |
| check, |
| fold_build2 (COMPOUND_EXPR, a68_bool_type, |
| call, boolean_false_node)); |
| elem = fold_build2 (COMPOUND_EXPR, a68_bool_type, |
| check, elem); |
| } |
| |
| return elem; |
| } |
| |
| /* Given two SIZETY BITS values BITS1 and BITS2, return a BOOL value indicating |
| whether all the bits set in BITS1 are also set in BITS2. */ |
| |
| tree |
| a68_bits_subset (tree bits1, tree bits2) |
| { |
| /* We compute this operation with `A | B == B' as specified by the Report */ |
| bits2 = save_expr (bits2); |
| return fold_build2 (EQ_EXPR, a68_bool_type, |
| fold_build2 (BIT_IOR_EXPR, TREE_TYPE (bits1), bits1, bits2), |
| bits2); |
| } |
| |
| /* Rotate the bits in BITS SHIFT bits to the left if SHIFT is positive, or ABS |
| (SHIFT) bits to the right if SHIFT is negative. |
| |
| A run-time error is raised if the count overflows the BITS value. */ |
| |
| tree |
| a68_bits_shift (tree shift, tree bits) |
| { |
| shift = save_expr (shift); |
| bits = save_expr (bits); |
| return fold_build3 (COND_EXPR, |
| TREE_TYPE (bits), |
| fold_build2 (GE_EXPR, TREE_TYPE (shift), |
| shift, build_int_cst (TREE_TYPE (shift), 0)), |
| fold_build2 (LSHIFT_EXPR, TREE_TYPE (bits), |
| bits, shift), |
| fold_build2 (RSHIFT_EXPR, TREE_TYPE (bits), |
| bits, |
| fold_build1 (ABS_EXPR, TREE_TYPE (shift), shift))); |
| } |
| |
| /* Given two bits values, build an expression that calculates whether A = B. */ |
| |
| tree |
| a68_bits_eq (tree a, tree b, location_t loc) |
| { |
| return fold_build2_loc (loc, EQ_EXPR, boolean_type_node, a, b); |
| } |
| |
| /* Given two bits values, build an expression that calculates whether A /= |
| B. */ |
| |
| tree |
| a68_bits_ne (tree a, tree b, location_t loc) |
| { |
| return fold_build2_loc (loc, NE_EXPR, boolean_type_node, a, b); |
| } |