diff options
Diffstat (limited to 'gcc/ada/utils2.c')
-rw-r--r-- | gcc/ada/utils2.c | 2049 |
1 files changed, 2049 insertions, 0 deletions
diff --git a/gcc/ada/utils2.c b/gcc/ada/utils2.c new file mode 100644 index 0000000..424673b --- /dev/null +++ b/gcc/ada/utils2.c @@ -0,0 +1,2049 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * U T I L S 2 * + * * + * C Implementation File * + * * + * $Revision: 1.1 $ + * * + * Copyright (C) 1992-2001, Free Software Foundation, Inc. * + * * + * GNAT is free software; you can redistribute it and/or modify it under * + * terms of the GNU General Public License as published by the Free Soft- * + * ware Foundation; either version 2, or (at your option) any later ver- * + * sion. GNAT is distributed in the hope that it will be useful, but WITH- * + * OUT 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 distributed with GNAT; see file COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "tree.h" +#include "flags.h" +#include "ada.h" +#include "types.h" +#include "atree.h" +#include "stringt.h" +#include "uintp.h" +#include "fe.h" +#include "elists.h" +#include "nlists.h" +#include "sinfo.h" +#include "einfo.h" +#include "ada-tree.h" +#include "gigi.h" + +static tree find_common_type PARAMS ((tree, tree)); +static int contains_save_expr_p PARAMS ((tree)); +static tree contains_null_expr PARAMS ((tree)); +static tree compare_arrays PARAMS ((tree, tree, tree)); +static tree nonbinary_modular_operation PARAMS ((enum tree_code, tree, + tree, tree)); +static tree build_simple_component_ref PARAMS ((tree, tree, tree)); + +/* Prepare expr to be an argument of a TRUTH_NOT_EXPR or other logical + operation. + + This preparation consists of taking the ordinary representation of + an expression expr and producing a valid tree boolean expression + describing whether expr is nonzero. We could simply always do + + build_binary_op (NE_EXPR, expr, integer_zero_node, 1), + + but we optimize comparisons, &&, ||, and !. + + The resulting type should always be the same as the input type. + This function is simpler than the corresponding C version since + the only possible operands will be things of Boolean type. */ + +tree +truthvalue_conversion (expr) + tree expr; +{ + tree type = TREE_TYPE (expr); + + switch (TREE_CODE (expr)) + { + case EQ_EXPR: case NE_EXPR: case LE_EXPR: case GE_EXPR: + case LT_EXPR: case GT_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case ERROR_MARK: + return expr; + + case COND_EXPR: + /* Distribute the conversion into the arms of a COND_EXPR. */ + return fold (build (COND_EXPR, type, TREE_OPERAND (expr, 0), + truthvalue_conversion (TREE_OPERAND (expr, 1)), + truthvalue_conversion (TREE_OPERAND (expr, 2)))); + + case WITH_RECORD_EXPR: + return build (WITH_RECORD_EXPR, type, + truthvalue_conversion (TREE_OPERAND (expr, 0)), + TREE_OPERAND (expr, 1)); + + default: + return build_binary_op (NE_EXPR, type, expr, + convert (type, integer_zero_node)); + } +} + +/* Return the base type of TYPE. */ + +tree +get_base_type (type) + tree type; +{ + if (TREE_CODE (type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (type)) + type = TREE_TYPE (TYPE_FIELDS (type)); + + while (TREE_TYPE (type) != 0 + && (TREE_CODE (type) == INTEGER_TYPE + || TREE_CODE (type) == REAL_TYPE)) + type = TREE_TYPE (type); + + return type; +} + +/* Likewise, but only return types known to the Ada source. */ +tree +get_ada_base_type (type) + tree type; +{ + while (TREE_TYPE (type) != 0 + && (TREE_CODE (type) == INTEGER_TYPE + || TREE_CODE (type) == REAL_TYPE) + && ! TYPE_EXTRA_SUBTYPE_P (type)) + type = TREE_TYPE (type); + + return type; +} + +/* EXP is a GCC tree representing an address. See if we can find how + strictly the object at that address is aligned. Return that alignment + in bits. If we don't know anything about the alignment, return 0. + We do not go merely by type information here since the check on + N_Validate_Unchecked_Alignment does that. */ + +unsigned int +known_alignment (exp) + tree exp; +{ + unsigned int lhs, rhs; + + switch (TREE_CODE (exp)) + { + case CONVERT_EXPR: + case NOP_EXPR: + case NON_LVALUE_EXPR: + /* Conversions between pointers and integers don't change the alignment + of the underlying object. */ + return known_alignment (TREE_OPERAND (exp, 0)); + + case PLUS_EXPR: + case MINUS_EXPR: + /* If two address are added, the alignment of the result is the + minimum of the two aligments. */ + lhs = known_alignment (TREE_OPERAND (exp, 0)); + rhs = known_alignment (TREE_OPERAND (exp, 1)); + return MIN (lhs, rhs); + + case INTEGER_CST: + /* The first part of this represents the lowest bit in the constant, + but is it in bytes, not bits. */ + return MIN (BITS_PER_UNIT + * (TREE_INT_CST_LOW (exp) & - TREE_INT_CST_LOW (exp)), + BIGGEST_ALIGNMENT); + + case MULT_EXPR: + /* If we know the alignment of just one side, use it. Otherwise, + use the product of the alignments. */ + lhs = known_alignment (TREE_OPERAND (exp, 0)); + rhs = known_alignment (TREE_OPERAND (exp, 1)); + if (lhs == 0 || rhs == 0) + return MIN (BIGGEST_ALIGNMENT, MAX (lhs, rhs)); + + return MIN (BIGGEST_ALIGNMENT, lhs * rhs); + + case ADDR_EXPR: + return expr_align (TREE_OPERAND (exp, 0)); + + default: + return 0; + } +} + +/* We have a comparison or assignment operation on two types, T1 and T2, + which are both either array types or both record types. + Return the type that both operands should be converted to, if any. + Otherwise return zero. */ + +static tree +find_common_type (t1, t2) + tree t1, t2; +{ + /* If either type is non-BLKmode, use it. Note that we know that we will + not have any alignment problems since if we did the non-BLKmode + type could not have been used. */ + if (TYPE_MODE (t1) != BLKmode) + return t1; + else if (TYPE_MODE (t2) != BLKmode) + return t2; + + /* Otherwise, return the type that has a constant size. */ + if (TREE_CONSTANT (TYPE_SIZE (t1))) + return t1; + else if (TREE_CONSTANT (TYPE_SIZE (t2))) + return t2; + + /* In this case, both types have variable size. It's probably + best to leave the "type mismatch" because changing it could + case a bad self-referential reference. */ + return 0; +} + +/* See if EXP contains a SAVE_EXPR in a position where we would + normally put it. + + ??? This is a real kludge, but is probably the best approach short + of some very general solution. */ + +static int +contains_save_expr_p (exp) + tree exp; +{ + switch (TREE_CODE (exp)) + { + case SAVE_EXPR: + return 1; + + case ADDR_EXPR: case INDIRECT_REF: + case COMPONENT_REF: + case NOP_EXPR: case CONVERT_EXPR: case UNCHECKED_CONVERT_EXPR: + return contains_save_expr_p (TREE_OPERAND (exp, 0)); + + case CONSTRUCTOR: + return (CONSTRUCTOR_ELTS (exp) != 0 + && contains_save_expr_p (CONSTRUCTOR_ELTS (exp))); + + case TREE_LIST: + return (contains_save_expr_p (TREE_VALUE (exp)) + || (TREE_CHAIN (exp) != 0 + && contains_save_expr_p (TREE_CHAIN (exp)))); + + default: + return 0; + } +} + +/* See if EXP contains a NULL_EXPR in an expression we use for sizes. Return + it if so. This is used to detect types whose sizes involve computations + that are known to raise Constraint_Error. */ + +static tree +contains_null_expr (exp) + tree exp; +{ + tree tem; + + if (TREE_CODE (exp) == NULL_EXPR) + return exp; + + switch (TREE_CODE_CLASS (TREE_CODE (exp))) + { + case '1': + return contains_null_expr (TREE_OPERAND (exp, 0)); + + case '<': case '2': + tem = contains_null_expr (TREE_OPERAND (exp, 0)); + if (tem != 0) + return tem; + + return contains_null_expr (TREE_OPERAND (exp, 1)); + + case 'e': + switch (TREE_CODE (exp)) + { + case SAVE_EXPR: + return contains_null_expr (TREE_OPERAND (exp, 0)); + + case COND_EXPR: + tem = contains_null_expr (TREE_OPERAND (exp, 0)); + if (tem != 0) + return tem; + + tem = contains_null_expr (TREE_OPERAND (exp, 1)); + if (tem != 0) + return tem; + + return contains_null_expr (TREE_OPERAND (exp, 2)); + + default: + return 0; + } + + default: + return 0; + } +} + +/* Return an expression tree representing an equality comparison of + A1 and A2, two objects of ARRAY_TYPE. The returned expression should + be of type RESULT_TYPE + + Two arrays are equal in one of two ways: (1) if both have zero length + in some dimension (not necessarily the same dimension) or (2) if the + lengths in each dimension are equal and the data is equal. We perform the + length tests in as efficient a manner as possible. */ + +static tree +compare_arrays (result_type, a1, a2) + tree a1, a2; + tree result_type; +{ + tree t1 = TREE_TYPE (a1); + tree t2 = TREE_TYPE (a2); + tree result = convert (result_type, integer_one_node); + tree a1_is_null = convert (result_type, integer_zero_node); + tree a2_is_null = convert (result_type, integer_zero_node); + int length_zero_p = 0; + + /* Process each dimension separately and compare the lengths. If any + dimension has a size known to be zero, set SIZE_ZERO_P to 1 to + suppress the comparison of the data. */ + while (TREE_CODE (t1) == ARRAY_TYPE && TREE_CODE (t2) == ARRAY_TYPE) + { + tree lb1 = TYPE_MIN_VALUE (TYPE_DOMAIN (t1)); + tree ub1 = TYPE_MAX_VALUE (TYPE_DOMAIN (t1)); + tree lb2 = TYPE_MIN_VALUE (TYPE_DOMAIN (t2)); + tree ub2 = TYPE_MAX_VALUE (TYPE_DOMAIN (t2)); + tree bt = get_base_type (TREE_TYPE (lb1)); + tree length1 = fold (build (MINUS_EXPR, bt, ub1, lb1)); + tree length2 = fold (build (MINUS_EXPR, bt, ub2, lb2)); + tree nbt; + tree tem; + tree comparison, this_a1_is_null, this_a2_is_null; + + /* If the length of the first array is a constant, swap our operands + unless the length of the second array is the constant zero. + Note that we have set the `length' values to the length - 1. */ + if (TREE_CODE (length1) == INTEGER_CST + && ! integer_zerop (fold (build (PLUS_EXPR, bt, length2, + convert (bt, integer_one_node))))) + { + tem = a1, a1 = a2, a2 = tem; + tem = t1, t1 = t2, t2 = tem; + tem = lb1, lb1 = lb2, lb2 = tem; + tem = ub1, ub1 = ub2, ub2 = tem; + tem = length1, length1 = length2, length2 = tem; + tem = a1_is_null, a1_is_null = a2_is_null, a2_is_null = tem; + } + + /* If the length of this dimension in the second array is the constant + zero, we can just go inside the original bounds for the first + array and see if last < first. */ + if (integer_zerop (fold (build (PLUS_EXPR, bt, length2, + convert (bt, integer_one_node))))) + { + tree ub = TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + tree lb = TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + + comparison = build_binary_op (LT_EXPR, result_type, ub, lb); + + if (contains_placeholder_p (comparison)) + comparison = build (WITH_RECORD_EXPR, result_type, + comparison, a1); + if (contains_placeholder_p (length1)) + length1 = build (WITH_RECORD_EXPR, bt, length1, a1); + + length_zero_p = 1; + + this_a1_is_null = comparison; + this_a2_is_null = convert (result_type, integer_one_node); + } + + /* If the length is some other constant value, we know that the + this dimension in the first array cannot be superflat, so we + can just use its length from the actual stored bounds. */ + else if (TREE_CODE (length2) == INTEGER_CST) + { + ub1 = TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + lb1 = TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + ub2 = TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t2))); + lb2 = TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t2))); + nbt = get_base_type (TREE_TYPE (ub1)); + + comparison + = build_binary_op (EQ_EXPR, result_type, + build_binary_op (MINUS_EXPR, nbt, ub1, lb1), + build_binary_op (MINUS_EXPR, nbt, ub2, lb2)); + + /* Note that we know that UB2 and LB2 are constant and hence + cannot contain a PLACEHOLDER_EXPR. */ + + if (contains_placeholder_p (comparison)) + comparison = build (WITH_RECORD_EXPR, result_type, comparison, a1); + if (contains_placeholder_p (length1)) + length1 = build (WITH_RECORD_EXPR, bt, length1, a1); + + this_a1_is_null = build_binary_op (LT_EXPR, result_type, ub1, lb1); + this_a2_is_null = convert (result_type, integer_zero_node); + } + + /* Otherwise compare the computed lengths. */ + else + { + if (contains_placeholder_p (length1)) + length1 = build (WITH_RECORD_EXPR, bt, length1, a1); + if (contains_placeholder_p (length2)) + length2 = build (WITH_RECORD_EXPR, bt, length2, a2); + + comparison + = build_binary_op (EQ_EXPR, result_type, length1, length2); + + this_a1_is_null + = build_binary_op (LT_EXPR, result_type, length1, + convert (bt, integer_zero_node)); + this_a2_is_null + = build_binary_op (LT_EXPR, result_type, length2, + convert (bt, integer_zero_node)); + } + + result = build_binary_op (TRUTH_ANDIF_EXPR, result_type, + result, comparison); + + a1_is_null = build_binary_op (TRUTH_ORIF_EXPR, result_type, + this_a1_is_null, a1_is_null); + a2_is_null = build_binary_op (TRUTH_ORIF_EXPR, result_type, + this_a2_is_null, a2_is_null); + + t1 = TREE_TYPE (t1); + t2 = TREE_TYPE (t2); + } + + /* Unless the size of some bound is known to be zero, compare the + data in the array. */ + if (! length_zero_p) + { + tree type = find_common_type (TREE_TYPE (a1), TREE_TYPE (a2)); + + if (type != 0) + a1 = convert (type, a1), a2 = convert (type, a2); + + + result = build_binary_op (TRUTH_ANDIF_EXPR, result_type, result, + build (EQ_EXPR, result_type, a1, a2)); + + } + + /* The result is also true if both sizes are zero. */ + result = build_binary_op (TRUTH_ORIF_EXPR, result_type, + build_binary_op (TRUTH_ANDIF_EXPR, result_type, + a1_is_null, a2_is_null), + result); + + /* If either operand contains SAVE_EXPRs, they have to be evaluated before + starting the comparison above since the place it would be otherwise + evaluated would be wrong. */ + + if (contains_save_expr_p (a1)) + result = build (COMPOUND_EXPR, result_type, a1, result); + + if (contains_save_expr_p (a2)) + result = build (COMPOUND_EXPR, result_type, a2, result); + + return result; +} + +/* Compute the result of applying OP_CODE to LHS and RHS, where both are of + type TYPE. We know that TYPE is a modular type with a nonbinary + modulus. */ + +static tree +nonbinary_modular_operation (op_code, type, lhs, rhs) + enum tree_code op_code; + tree type; + tree lhs, rhs; +{ + tree modulus = TYPE_MODULUS (type); + unsigned int needed_precision = tree_floor_log2 (modulus) + 1; + unsigned int precision; + int unsignedp = 1; + tree op_type = type; + tree result; + + /* If this is an addition of a constant, convert it to a subtraction + of a constant since we can do that faster. */ + if (op_code == PLUS_EXPR && TREE_CODE (rhs) == INTEGER_CST) + rhs = fold (build (MINUS_EXPR, type, modulus, rhs)), op_code = MINUS_EXPR; + + /* For the logical operations, we only need PRECISION bits. For + addition and subraction, we need one more and for multiplication we + need twice as many. But we never want to make a size smaller than + our size. */ + if (op_code == PLUS_EXPR || op_code == MINUS_EXPR) + needed_precision += 1; + else if (op_code == MULT_EXPR) + needed_precision *= 2; + + precision = MAX (needed_precision, TYPE_PRECISION (op_type)); + + /* Unsigned will do for everything but subtraction. */ + if (op_code == MINUS_EXPR) + unsignedp = 0; + + /* If our type is the wrong signedness or isn't wide enough, make a new + type and convert both our operands to it. */ + if (TYPE_PRECISION (op_type) < precision + || TREE_UNSIGNED (op_type) != unsignedp) + { + /* Copy the node so we ensure it can be modified to make it modular. */ + op_type = copy_node (type_for_size (precision, unsignedp)); + modulus = convert (op_type, modulus); + TYPE_MODULUS (op_type) = modulus; + TYPE_MODULAR_P (op_type) = 1; + lhs = convert (op_type, lhs); + rhs = convert (op_type, rhs); + } + + /* Do the operation, then we'll fix it up. */ + result = fold (build (op_code, op_type, lhs, rhs)); + + /* For multiplication, we have no choice but to do a full modulus + operation. However, we want to do this in the narrowest + possible size. */ + if (op_code == MULT_EXPR) + { + tree div_type = copy_node (type_for_size (needed_precision, 1)); + modulus = convert (div_type, modulus); + TYPE_MODULUS (div_type) = modulus; + TYPE_MODULAR_P (div_type) = 1; + result = convert (op_type, + fold (build (TRUNC_MOD_EXPR, div_type, + convert (div_type, result), modulus))); + } + + /* For subtraction, add the modulus back if we are negative. */ + else if (op_code == MINUS_EXPR) + { + result = save_expr (result); + result = fold (build (COND_EXPR, op_type, + build (LT_EXPR, integer_type_node, result, + convert (op_type, integer_zero_node)), + fold (build (PLUS_EXPR, op_type, + result, modulus)), + result)); + } + + /* For the other operations, subtract the modulus if we are >= it. */ + else + { + result = save_expr (result); + result = fold (build (COND_EXPR, op_type, + build (GE_EXPR, integer_type_node, + result, modulus), + fold (build (MINUS_EXPR, op_type, + result, modulus)), + result)); + } + + return convert (type, result); +} + +/* Make a binary operation of kind OP_CODE. RESULT_TYPE is the type + desired for the result. Usually the operation is to be performed + in that type. For MODIFY_EXPR and ARRAY_REF, RESULT_TYPE may be 0 + in which case the type to be used will be derived from the operands. + + This function is very much unlike the ones for C and C++ since we + have already done any type conversion and matching required. All we + have to do here is validate the work done by SEM and handle subtypes. */ + +tree +build_binary_op (op_code, result_type, left_operand, right_operand) + enum tree_code op_code; + tree result_type; + tree left_operand; + tree right_operand; +{ + tree left_type = TREE_TYPE (left_operand); + tree right_type = TREE_TYPE (right_operand); + tree left_base_type = get_base_type (left_type); + tree right_base_type = get_base_type (right_type); + tree operation_type = result_type; + tree best_type = 0; + tree modulus; + tree result; + int has_side_effects = 0; + + /* If one (but not both, unless they have the same object) operands are a + WITH_RECORD_EXPR, do the operation and then surround it with the + WITH_RECORD_EXPR. Don't do this for assignment, for an ARRAY_REF, or + for an ARRAY_RANGE_REF because we need to keep track of the + WITH_RECORD_EXPRs on both operands very carefully. */ + if (op_code != MODIFY_EXPR && op_code != ARRAY_REF + && op_code != ARRAY_RANGE_REF + && TREE_CODE (left_operand) == WITH_RECORD_EXPR + && (TREE_CODE (right_operand) != WITH_RECORD_EXPR + || operand_equal_p (TREE_OPERAND (left_operand, 1), + TREE_OPERAND (right_operand, 1), 0))) + { + tree right = right_operand; + + if (TREE_CODE (right) == WITH_RECORD_EXPR) + right = TREE_OPERAND (right, 0); + + result = build_binary_op (op_code, result_type, + TREE_OPERAND (left_operand, 0), right); + return build (WITH_RECORD_EXPR, TREE_TYPE (result), result, + TREE_OPERAND (left_operand, 1)); + } + else if (op_code != MODIFY_EXPR && op_code != ARRAY_REF + && op_code != ARRAY_RANGE_REF + && TREE_CODE (left_operand) != WITH_RECORD_EXPR + && TREE_CODE (right_operand) == WITH_RECORD_EXPR) + { + result = build_binary_op (op_code, result_type, left_operand, + TREE_OPERAND (right_operand, 0)); + return build (WITH_RECORD_EXPR, TREE_TYPE (result), result, + TREE_OPERAND (right_operand, 1)); + } + + if (operation_type != 0 + && TREE_CODE (operation_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (operation_type)) + operation_type = TREE_TYPE (TYPE_FIELDS (operation_type)); + + if (operation_type != 0 + && ! AGGREGATE_TYPE_P (operation_type) + && TYPE_EXTRA_SUBTYPE_P (operation_type)) + operation_type = get_base_type (operation_type); + + modulus = (operation_type != 0 && TREE_CODE (operation_type) == INTEGER_TYPE + && TYPE_MODULAR_P (operation_type) + ? TYPE_MODULUS (operation_type) : 0); + + switch (op_code) + { + case MODIFY_EXPR: + /* If there were any integral or pointer conversions on LHS, remove + them; we'll be putting them back below if needed. Likewise for + conversions between array and record types. But don't do this if + the right operand is not BLKmode (for packed arrays) + unless we are not changing the mode. */ + while ((TREE_CODE (left_operand) == CONVERT_EXPR + || TREE_CODE (left_operand) == NOP_EXPR + || TREE_CODE (left_operand) == UNCHECKED_CONVERT_EXPR) + && (((INTEGRAL_TYPE_P (left_type) + || POINTER_TYPE_P (left_type)) + && (INTEGRAL_TYPE_P (TREE_TYPE + (TREE_OPERAND (left_operand, 0))) + || POINTER_TYPE_P (TREE_TYPE + (TREE_OPERAND (left_operand, 0))))) + || (((TREE_CODE (left_type) == RECORD_TYPE + /* Don't remove conversions to left-justified modular + types. */ + && ! TYPE_LEFT_JUSTIFIED_MODULAR_P (left_type)) + || TREE_CODE (left_type) == ARRAY_TYPE) + && ((TREE_CODE (TREE_TYPE + (TREE_OPERAND (left_operand, 0))) + == RECORD_TYPE) + || (TREE_CODE (TREE_TYPE + (TREE_OPERAND (left_operand, 0))) + == ARRAY_TYPE)) + && (TYPE_MODE (right_type) == BLKmode + || (TYPE_MODE (left_type) + == TYPE_MODE (TREE_TYPE + (TREE_OPERAND + (left_operand, 0)))))))) + { + left_operand = TREE_OPERAND (left_operand, 0); + left_type = TREE_TYPE (left_operand); + } + + if (operation_type == 0) + operation_type = left_type; + + /* If the RHS has a conversion between record and array types and + an inner type is no worse, use it. Note we cannot do this for + modular types or types with TYPE_ALIGN_OK_P, since the latter + might indicate a conversion between a root type and a class-wide + type, which we must not remove. */ + while (TREE_CODE (right_operand) == UNCHECKED_CONVERT_EXPR + && ((TREE_CODE (right_type) == RECORD_TYPE + && ! TYPE_LEFT_JUSTIFIED_MODULAR_P (right_type) + && ! TYPE_ALIGN_OK_P (right_type) + && ! TYPE_IS_FAT_POINTER_P (right_type)) + || TREE_CODE (right_type) == ARRAY_TYPE) + && (((TREE_CODE (TREE_TYPE (TREE_OPERAND (right_operand, 0))) + == RECORD_TYPE) + && ! (TYPE_LEFT_JUSTIFIED_MODULAR_P + (TREE_TYPE (TREE_OPERAND (right_operand, 0)))) + && ! (TYPE_ALIGN_OK_P + (TREE_TYPE (TREE_OPERAND (right_operand, 0)))) + && ! (TYPE_IS_FAT_POINTER_P + (TREE_TYPE (TREE_OPERAND (right_operand, 0))))) + || (TREE_CODE (TREE_TYPE (TREE_OPERAND (right_operand, 0))) + == ARRAY_TYPE)) + && (0 == (best_type + == find_common_type (right_type, + TREE_TYPE (TREE_OPERAND + (right_operand, 0)))) + || right_type != best_type)) + { + right_operand = TREE_OPERAND (right_operand, 0); + right_type = TREE_TYPE (right_operand); + } + + /* If we are copying one array or record to another, find the best type + to use. */ + if (((TREE_CODE (left_type) == ARRAY_TYPE + && TREE_CODE (right_type) == ARRAY_TYPE) + || (TREE_CODE (left_type) == RECORD_TYPE + && TREE_CODE (right_type) == RECORD_TYPE)) + && (best_type = find_common_type (left_type, right_type)) != 0) + operation_type = best_type; + + /* If a class-wide type may be involved, force use of the RHS type. */ + if (TREE_CODE (right_type) == RECORD_TYPE + && TYPE_ALIGN_OK_P (right_type)) + operation_type = right_type; + + /* After we strip off any COMPONENT_REF, ARRAY_REF, or ARRAY_RANGE_REF + from the lhs, we must have either an INDIRECT_REF or a decl. Allow + UNCHECKED_CONVERT_EXPRs, but set TREE_ADDRESSABLE to show they are + in an LHS. Finally, allow NOP_EXPR if both types are the same tree + code and mode because we know these will be nops. */ + for (result = left_operand; + TREE_CODE (result) == COMPONENT_REF + || TREE_CODE (result) == ARRAY_REF + || TREE_CODE (result) == ARRAY_RANGE_REF + || TREE_CODE (result) == REALPART_EXPR + || TREE_CODE (result) == IMAGPART_EXPR + || TREE_CODE (result) == WITH_RECORD_EXPR + || TREE_CODE (result) == UNCHECKED_CONVERT_EXPR + || ((TREE_CODE (result) == NOP_EXPR + || TREE_CODE (result) == CONVERT_EXPR) + && (TREE_CODE (TREE_TYPE (result)) + == TREE_CODE (TREE_TYPE (TREE_OPERAND (result, 0)))) + && (TYPE_MODE (TREE_TYPE (TREE_OPERAND (result, 0))) + == TYPE_MODE (TREE_TYPE (result)))); + result = TREE_OPERAND (result, 0)) + if (TREE_CODE (result) == UNCHECKED_CONVERT_EXPR) + TREE_ADDRESSABLE (result) = 1; + + if (TREE_CODE (result) != INDIRECT_REF && TREE_CODE (result) != NULL_EXPR + && ! DECL_P (result)) + gigi_abort (516); + + /* Convert the right operand to the operation type unless + it is either already of the correct type or if the type + involves a placeholder, since the RHS may not have the same + record type. */ + if (operation_type != right_type + && (! (TREE_CODE (TYPE_SIZE (operation_type)) != INTEGER_CST + && contains_placeholder_p (TYPE_SIZE (operation_type))))) + { + /* For a variable-size type, with both BLKmode, convert using + CONVERT_EXPR instead of an unchecked conversion since we don't + need to make a temporary (and can't anyway). */ + if (TREE_CODE (TYPE_SIZE (operation_type)) != INTEGER_CST + && TYPE_MODE (TREE_TYPE (right_operand)) == BLKmode + && TREE_CODE (right_operand) != UNCONSTRAINED_ARRAY_REF) + right_operand = build1 (CONVERT_EXPR, operation_type, + right_operand); + else + right_operand = convert (operation_type, right_operand); + + right_type = operation_type; + } + + /* If the modes differ, make up a bogus type and convert the RHS to + it. This can happen with packed types. */ + if (TYPE_MODE (left_type) != TYPE_MODE (right_type)) + { + tree new_type = copy_node (left_type); + + TYPE_SIZE (new_type) = TYPE_SIZE (right_type); + TYPE_SIZE_UNIT (new_type) = TYPE_SIZE_UNIT (right_type); + TYPE_MAIN_VARIANT (new_type) = new_type; + right_operand = convert (new_type, right_operand); + } + + has_side_effects = 1; + modulus = 0; + break; + + case ARRAY_REF: + if (operation_type == 0) + operation_type = TREE_TYPE (left_type); + + /* ... fall through ... */ + + case ARRAY_RANGE_REF: + + /* First convert the right operand to its base type. This will + prevent unneed signedness conversions when sizetype is wider than + integer. */ + right_operand = convert (right_base_type, right_operand); + right_operand = convert (TYPE_DOMAIN (left_type), right_operand); + + if (! TREE_CONSTANT (right_operand) + || ! TREE_CONSTANT (TYPE_MIN_VALUE (right_type)) + || op_code == ARRAY_RANGE_REF) + mark_addressable (left_operand); + + /* If the array is an UNCHECKED_CONVERT_EXPR from and to BLKmode + types, convert it to a normal conversion since GCC can deal + with any mis-alignment as part of the handling of compponent + references. */ + + if (TREE_CODE (left_operand) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (TREE_TYPE (left_operand)) == BLKmode + && TYPE_MODE (TREE_TYPE (TREE_OPERAND (left_operand, 0))) == BLKmode) + left_operand = build1 (CONVERT_EXPR, TREE_TYPE (left_operand), + TREE_OPERAND (left_operand, 0)); + + modulus = 0; + break; + + case GE_EXPR: + case LE_EXPR: + case GT_EXPR: + case LT_EXPR: + if (POINTER_TYPE_P (left_type)) + gigi_abort (501); + + /* ... fall through ... */ + + case EQ_EXPR: + case NE_EXPR: + /* If either operand is a NULL_EXPR, just return a new one. */ + if (TREE_CODE (left_operand) == NULL_EXPR) + return build (op_code, result_type, + build1 (NULL_EXPR, integer_type_node, + TREE_OPERAND (left_operand, 0)), + integer_zero_node); + + else if (TREE_CODE (right_operand) == NULL_EXPR) + return build (op_code, result_type, + build1 (NULL_EXPR, integer_type_node, + TREE_OPERAND (right_operand, 0)), + integer_zero_node); + + /* If either object is a left-justified modular types, get the + fields from within. */ + if (TREE_CODE (left_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (left_type)) + { + left_operand = convert (TREE_TYPE (TYPE_FIELDS (left_type)), + left_operand); + left_type = TREE_TYPE (left_operand); + left_base_type = get_base_type (left_type); + } + + if (TREE_CODE (right_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (right_type)) + { + right_operand = convert (TREE_TYPE (TYPE_FIELDS (right_type)), + right_operand); + right_type = TREE_TYPE (right_operand); + right_base_type = get_base_type (right_type); + } + + /* If either object if an UNCHECKED_CONVERT_EXPR between two BLKmode + objects, change it to a CONVERT_EXPR. */ + if (TREE_CODE (left_operand) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (left_type) == BLKmode + && TYPE_MODE (TREE_TYPE (TREE_OPERAND (left_operand, 0))) == BLKmode) + left_operand = build1 (CONVERT_EXPR, left_type, + TREE_OPERAND (left_operand, 0)); + if (TREE_CODE (right_operand) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (right_type) == BLKmode + && (TYPE_MODE (TREE_TYPE (TREE_OPERAND (right_operand, 0))) + == BLKmode)) + right_operand = build1 (CONVERT_EXPR, right_type, + TREE_OPERAND (right_operand, 0)); + + /* If both objects are arrays, compare them specially. */ + if ((TREE_CODE (left_type) == ARRAY_TYPE + || (TREE_CODE (left_type) == INTEGER_TYPE + && TYPE_HAS_ACTUAL_BOUNDS_P (left_type))) + && (TREE_CODE (right_type) == ARRAY_TYPE + || (TREE_CODE (right_type) == INTEGER_TYPE + && TYPE_HAS_ACTUAL_BOUNDS_P (right_type)))) + { + result = compare_arrays (result_type, left_operand, right_operand); + + if (op_code == EQ_EXPR) + ; + else if (op_code == NE_EXPR) + result = invert_truthvalue (result); + else + gigi_abort (502); + + return result; + } + + /* Otherwise, the base types must be the same unless the objects are + records. If we have records, use the best type and convert both + operands to that type. */ + if (left_base_type != right_base_type) + { + if (TREE_CODE (left_base_type) == RECORD_TYPE + && TREE_CODE (right_base_type) == RECORD_TYPE) + { + /* The only way these are permitted to be the same is if both + types have the same name. In that case, one of them must + not be self-referential. Use that one as the best type. + Even better is if one is of fixed size. */ + best_type = 0; + + if (TYPE_NAME (left_base_type) == 0 + || TYPE_NAME (left_base_type) != TYPE_NAME (right_base_type)) + gigi_abort (503); + + if (TREE_CONSTANT (TYPE_SIZE (left_base_type))) + best_type = left_base_type; + else if (TREE_CONSTANT (TYPE_SIZE (right_base_type))) + best_type = right_base_type; + else if (! contains_placeholder_p (TYPE_SIZE (left_base_type))) + best_type = left_base_type; + else if (! contains_placeholder_p (TYPE_SIZE (right_base_type))) + best_type = right_base_type; + else + gigi_abort (504); + + left_operand = convert (best_type, left_operand); + right_operand = convert (best_type, right_operand); + } + else + gigi_abort (505); + } + + /* If we are comparing a fat pointer against zero, we need to + just compare the data pointer. */ + else if (TYPE_FAT_POINTER_P (left_base_type) + && TREE_CODE (right_operand) == CONSTRUCTOR + && integer_zerop (TREE_VALUE (TREE_OPERAND (right_operand, 1)))) + { + right_operand = build_component_ref (left_operand, NULL_TREE, + TYPE_FIELDS (left_base_type)); + left_operand = convert (TREE_TYPE (right_operand), + integer_zero_node); + } + else + { + left_operand = convert (left_base_type, left_operand); + right_operand = convert (right_base_type, right_operand); + } + + modulus = 0; + break; + + case PREINCREMENT_EXPR: + case PREDECREMENT_EXPR: + case POSTINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + /* In these, the result type and the left operand type should be the + same. Do the operation in the base type of those and convert the + right operand (which is an integer) to that type. + + Note that these operations are only used in loop control where + we guarantee that no overflow can occur. So nothing special need + be done for modular types. */ + + if (left_type != result_type) + gigi_abort (506); + + operation_type = get_base_type (result_type); + left_operand = convert (operation_type, left_operand); + right_operand = convert (operation_type, right_operand); + has_side_effects = 1; + modulus = 0; + break; + + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + /* The RHS of a shift can be any type. Also, ignore any modulus + (we used to abort, but this is needed for unchecked conversion + to modular types). Otherwise, processing is the same as normal. */ + if (operation_type != left_base_type) + gigi_abort (514); + + modulus = 0; + left_operand = convert (operation_type, left_operand); + break; + + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + left_operand = truthvalue_conversion (left_operand); + right_operand = truthvalue_conversion (right_operand); + goto common; + + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + /* For binary modulus, if the inputs are in range, so are the + outputs. */ + if (modulus != 0 && integer_pow2p (modulus)) + modulus = 0; + + goto common; + + case COMPLEX_EXPR: + if (TREE_TYPE (result_type) != left_base_type + || TREE_TYPE (result_type) != right_base_type) + gigi_abort (515); + + left_operand = convert (left_base_type, left_operand); + right_operand = convert (right_base_type, right_operand); + break; + + case TRUNC_DIV_EXPR: case TRUNC_MOD_EXPR: + case CEIL_DIV_EXPR: case CEIL_MOD_EXPR: + case FLOOR_DIV_EXPR: case FLOOR_MOD_EXPR: + case ROUND_DIV_EXPR: case ROUND_MOD_EXPR: + /* These always produce results lower than either operand. */ + modulus = 0; + goto common; + + default: + common: + /* The result type should be the same as the base types of the + both operands (and they should be the same). Convert + everything to the result type. */ + + if (operation_type != left_base_type + || left_base_type != right_base_type) + gigi_abort (507); + + left_operand = convert (operation_type, left_operand); + right_operand = convert (operation_type, right_operand); + } + + if (modulus != 0 && ! integer_pow2p (modulus)) + { + result = nonbinary_modular_operation (op_code, operation_type, + left_operand, right_operand); + modulus = 0; + } + /* If either operand is a NULL_EXPR, just return a new one. */ + else if (TREE_CODE (left_operand) == NULL_EXPR) + return build1 (NULL_EXPR, operation_type, TREE_OPERAND (left_operand, 0)); + else if (TREE_CODE (right_operand) == NULL_EXPR) + return build1 (NULL_EXPR, operation_type, TREE_OPERAND (right_operand, 0)); + else + result = fold (build (op_code, operation_type, + left_operand, right_operand)); + + TREE_SIDE_EFFECTS (result) |= has_side_effects; + TREE_CONSTANT (result) + = (TREE_CONSTANT (left_operand) & TREE_CONSTANT (right_operand) + && op_code != ARRAY_REF && op_code != ARRAY_RANGE_REF); + + if ((op_code == ARRAY_REF || op_code == ARRAY_RANGE_REF) + && TYPE_VOLATILE (operation_type)) + TREE_THIS_VOLATILE (result) = 1; + + /* If we are working with modular types, perform the MOD operation + if something above hasn't eliminated the need for it. */ + if (modulus != 0) + result = fold (build (FLOOR_MOD_EXPR, operation_type, result, + convert (operation_type, modulus))); + + if (result_type != 0 && result_type != operation_type) + result = convert (result_type, result); + + return result; +} + +/* Similar, but for unary operations. */ + +tree +build_unary_op (op_code, result_type, operand) + enum tree_code op_code; + tree result_type; + tree operand; +{ + tree type = TREE_TYPE (operand); + tree base_type = get_base_type (type); + tree operation_type = result_type; + tree result; + int side_effects = 0; + + /* If we have a WITH_RECORD_EXPR as our operand, do the operation first, + then surround it with the WITH_RECORD_EXPR. This allows GCC to do better + expression folding. */ + if (TREE_CODE (operand) == WITH_RECORD_EXPR) + { + result = build_unary_op (op_code, result_type, + TREE_OPERAND (operand, 0)); + return build (WITH_RECORD_EXPR, TREE_TYPE (result), result, + TREE_OPERAND (operand, 1)); + } + + if (operation_type != 0 + && TREE_CODE (operation_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (operation_type)) + operation_type = TREE_TYPE (TYPE_FIELDS (operation_type)); + + if (operation_type != 0 + && ! AGGREGATE_TYPE_P (operation_type) + && TYPE_EXTRA_SUBTYPE_P (operation_type)) + operation_type = get_base_type (operation_type); + + switch (op_code) + { + case REALPART_EXPR: + case IMAGPART_EXPR: + if (operation_type == 0) + result_type = operation_type = TREE_TYPE (type); + else if (result_type != TREE_TYPE (type)) + gigi_abort (513); + + result = fold (build1 (op_code, operation_type, operand)); + break; + + case TRUTH_NOT_EXPR: + if (result_type != base_type) + gigi_abort (508); + + result = invert_truthvalue (truthvalue_conversion (operand)); + break; + + case ATTR_ADDR_EXPR: + case ADDR_EXPR: + switch (TREE_CODE (operand)) + { + case INDIRECT_REF: + case UNCONSTRAINED_ARRAY_REF: + result = TREE_OPERAND (operand, 0); + + /* Make sure the type here is a pointer, not a reference. + GCC wants pointer types for function addresses. */ + if (result_type == 0) + result_type = build_pointer_type (type); + break; + + case NULL_EXPR: + result = operand; + TREE_TYPE (result) = type = build_pointer_type (type); + break; + + case ARRAY_REF: + case ARRAY_RANGE_REF: + case COMPONENT_REF: + case BIT_FIELD_REF: + /* If this is for 'Address, find the address of the prefix and + add the offset to the field. Otherwise, do this the normal + way. */ + if (op_code == ATTR_ADDR_EXPR) + { + HOST_WIDE_INT bitsize; + HOST_WIDE_INT bitpos; + tree offset, inner; + enum machine_mode mode; + int unsignedp, volatilep; + unsigned int alignment; + + inner = get_inner_reference (operand, &bitsize, &bitpos, &offset, + &mode, &unsignedp, &volatilep, + &alignment); + + /* If INNER is a padding type whose field has a self-referential + size, convert to that inner type. We know the offset is zero + and we need to have that type visible. */ + if (TREE_CODE (TREE_TYPE (inner)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (inner)) + && (contains_placeholder_p + (TYPE_SIZE (TREE_TYPE (TYPE_FIELDS + (TREE_TYPE (inner))))))) + inner = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (inner))), + inner); + + /* Compute the offset as a byte offset from INNER. */ + if (offset == 0) + offset = size_zero_node; + + offset = size_binop (PLUS_EXPR, offset, + size_int (bitpos / BITS_PER_UNIT)); + + /* Take the address of INNER, convert the offset to void *, and + add then. It will later be converted to the desired result + type, if any. */ + inner = build_unary_op (ADDR_EXPR, NULL_TREE, inner); + inner = convert (ptr_void_type_node, inner); + offset = convert (ptr_void_type_node, offset); + result = build_binary_op (PLUS_EXPR, ptr_void_type_node, + inner, offset); + result = convert (build_pointer_type (TREE_TYPE (operand)), + result); + break; + } + goto common; + + case CONSTRUCTOR: + /* If this is just a constructor for a padded record, we can + just take the address of the single field and convert it to + a pointer to our type. */ + if (TREE_CODE (type) == RECORD_TYPE && TYPE_IS_PADDING_P (type)) + { + result + = build_unary_op (ADDR_EXPR, NULL_TREE, + TREE_VALUE (CONSTRUCTOR_ELTS (operand))); + result = convert (build_pointer_type (TREE_TYPE (operand)), + result); + break; + } + + goto common; + + case NOP_EXPR: + if (AGGREGATE_TYPE_P (type) + && AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (operand, 0)))) + return build_unary_op (ADDR_EXPR, result_type, + TREE_OPERAND (operand, 0)); + + /* If this NOP_EXPR doesn't change the mode, get the result type + from this type and go down. We need to do this in case + this is a conversion of a CONST_DECL. */ + if (TYPE_MODE (type) != BLKmode + && (TYPE_MODE (type) + == TYPE_MODE (TREE_TYPE (TREE_OPERAND (operand, 0))))) + return build_unary_op (ADDR_EXPR, + (result_type == 0 + ? build_pointer_type (type) + : result_type), + TREE_OPERAND (operand, 0)); + goto common; + + case CONST_DECL: + operand = DECL_CONST_CORRESPONDING_VAR (operand); + + /* ... fall through ... */ + + default: + common: + + if (type != error_mark_node) + operation_type = build_pointer_type (type); + + mark_addressable (operand); + result = fold (build1 (ADDR_EXPR, operation_type, operand)); + } + + TREE_CONSTANT (result) = staticp (operand) || TREE_CONSTANT (operand); + break; + + case INDIRECT_REF: + /* If we want to refer to an entire unconstrained array, + make up an expression to do so. This will never survive to + the backend. If TYPE is a thin pointer, first convert the + operand to a fat pointer. */ + if (TYPE_THIN_POINTER_P (type) + && TYPE_UNCONSTRAINED_ARRAY (TREE_TYPE (type)) != 0) + { + operand + = convert (TREE_TYPE (TYPE_UNCONSTRAINED_ARRAY (TREE_TYPE (type))), + operand); + type = TREE_TYPE (operand); + } + + if (TYPE_FAT_POINTER_P (type)) + result = build1 (UNCONSTRAINED_ARRAY_REF, + TYPE_UNCONSTRAINED_ARRAY (type), operand); + + else if (TREE_CODE (operand) == ADDR_EXPR) + result = TREE_OPERAND (operand, 0); + + else + { + result = fold (build1 (op_code, TREE_TYPE (type), operand)); + TREE_READONLY (result) = TREE_STATIC (result) + = TREE_READONLY (TREE_TYPE (type)); + } + + side_effects = flag_volatile + || (! TYPE_FAT_POINTER_P (type) && TYPE_VOLATILE (TREE_TYPE (type))); + break; + + case NEGATE_EXPR: + case BIT_NOT_EXPR: + { + tree modulus = ((operation_type != 0 + && TREE_CODE (operation_type) == INTEGER_TYPE + && TYPE_MODULAR_P (operation_type)) + ? TYPE_MODULUS (operation_type) : 0); + int mod_pow2 = modulus != 0 && integer_pow2p (modulus); + + /* If this is a modular type, there are various possibilities + depending on the operation and whether the modulus is a + power of two or not. */ + + if (modulus != 0) + { + if (operation_type != base_type) + gigi_abort (509); + + operand = convert (operation_type, operand); + + /* The fastest in the negate case for binary modulus is + the straightforward code; the TRUNC_MOD_EXPR below + is an AND operation. */ + if (op_code == NEGATE_EXPR && mod_pow2) + result = fold (build (TRUNC_MOD_EXPR, operation_type, + fold (build1 (NEGATE_EXPR, operation_type, + operand)), + modulus)); + + /* For nonbinary negate case, return zero for zero operand, + else return the modulus minus the operand. If the modulus + is a power of two minus one, we can do the subtraction + as an XOR since it is equivalent and faster on most machines. */ + else if (op_code == NEGATE_EXPR && ! mod_pow2) + { + if (integer_pow2p (fold (build (PLUS_EXPR, operation_type, + modulus, + convert (operation_type, + integer_one_node))))) + result = fold (build (BIT_XOR_EXPR, operation_type, + operand, modulus)); + else + result = fold (build (MINUS_EXPR, operation_type, + modulus, operand)); + + result = fold (build (COND_EXPR, operation_type, + fold (build (NE_EXPR, integer_type_node, + operand, + convert (operation_type, + integer_zero_node))), + result, operand)); + } + else + { + /* For the NOT cases, we need a constant equal to + the modulus minus one. For a binary modulus, we + XOR against the constant and subtract the operand from + that constant for nonbinary modulus. */ + + tree cnst = fold (build (MINUS_EXPR, operation_type, modulus, + convert (operation_type, + integer_one_node))); + + if (mod_pow2) + result = fold (build (BIT_XOR_EXPR, operation_type, + operand, cnst)); + else + result = fold (build (MINUS_EXPR, operation_type, + cnst, operand)); + } + + break; + } + } + + /* ... fall through ... */ + + default: + if (operation_type != base_type) + gigi_abort (509); + + result = fold (build1 (op_code, operation_type, convert (operation_type, + operand))); + } + + if (side_effects) + { + TREE_SIDE_EFFECTS (result) = 1; + if (TREE_CODE (result) == INDIRECT_REF) + TREE_THIS_VOLATILE (result) = TYPE_VOLATILE (TREE_TYPE (result)); + } + + if (result_type != 0 && TREE_TYPE (result) != result_type) + result = convert (result_type, result); + + return result; +} + +/* Similar, but for COND_EXPR. */ + +tree +build_cond_expr (result_type, condition_operand, true_operand, false_operand) + tree result_type; + tree condition_operand; + tree true_operand; + tree false_operand; +{ + tree result; + int addr_p = 0; + + /* Front-end verifies that result, true and false operands have same base + type. Convert everything to the result type. */ + + true_operand = convert (result_type, true_operand); + false_operand = convert (result_type, false_operand); + + /* If the result type is unconstrained, take the address of + the operands and then dereference our result. */ + + if (TREE_CODE (result_type) == UNCONSTRAINED_ARRAY_TYPE + || (TREE_CODE (TYPE_SIZE (result_type)) != INTEGER_CST + && contains_placeholder_p (TYPE_SIZE (result_type)))) + { + addr_p = 1; + result_type = build_pointer_type (result_type); + true_operand = build_unary_op (ADDR_EXPR, result_type, true_operand); + false_operand = build_unary_op (ADDR_EXPR, result_type, false_operand); + } + + result = fold (build (COND_EXPR, result_type, condition_operand, + true_operand, false_operand)); + if (addr_p) + result = build_unary_op (INDIRECT_REF, NULL_TREE, result); + + return result; +} + + +/* Build a CALL_EXPR to call FUNDECL with one argument, ARG. Return + the CALL_EXPR. */ + +tree +build_call_1_expr (fundecl, arg) + tree fundecl; + tree arg; +{ + tree call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fundecl)), + build_unary_op (ADDR_EXPR, NULL_TREE, fundecl), + chainon (NULL_TREE, build_tree_list (NULL_TREE, arg)), + NULL_TREE); + + TREE_SIDE_EFFECTS (call) = 1; + + return call; +} + +/* Build a CALL_EXPR to call FUNDECL with two arguments, ARG1 & ARG2. Return + the CALL_EXPR. */ + +tree +build_call_2_expr (fundecl, arg1, arg2) + tree fundecl; + tree arg1, arg2; +{ + tree call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fundecl)), + build_unary_op (ADDR_EXPR, NULL_TREE, fundecl), + chainon (chainon (NULL_TREE, + build_tree_list (NULL_TREE, arg1)), + build_tree_list (NULL_TREE, arg2)), + NULL_TREE); + + TREE_SIDE_EFFECTS (call) = 1; + + return call; +} + +/* Likewise to call FUNDECL with no arguments. */ + +tree +build_call_0_expr (fundecl) + tree fundecl; +{ + tree call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fundecl)), + build_unary_op (ADDR_EXPR, NULL_TREE, fundecl), + NULL_TREE, NULL_TREE); + + TREE_SIDE_EFFECTS (call) = 1; + + return call; +} + +/* Call a function FCN that raises an exception and pass the line + number and file name, if requested. */ + +tree +build_call_raise (fndecl) + tree fndecl; +{ + const char *str = discard_file_names ? "" : ref_filename; + int len = strlen (str) + 1; + tree filename = build_string (len, str); + + TREE_TYPE (filename) + = build_array_type (char_type_node, + build_index_type (build_int_2 (len, 0))); + + return + build_call_2_expr (fndecl, + build1 (ADDR_EXPR, build_pointer_type (char_type_node), + filename), + build_int_2 (lineno, 0)); +} + +/* Return a CONSTRUCTOR of TYPE whose list is LIST. */ + +tree +build_constructor (type, list) + tree type; + tree list; +{ + tree elmt; + int allconstant = (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST); + int side_effects = 0; + tree result; + + for (elmt = list; elmt; elmt = TREE_CHAIN (elmt)) + { + if (! TREE_CONSTANT (TREE_VALUE (elmt)) + || (TREE_CODE (type) == RECORD_TYPE + && DECL_BIT_FIELD (TREE_PURPOSE (elmt)) + && TREE_CODE (TREE_VALUE (elmt)) != INTEGER_CST)) + allconstant = 0; + + if (TREE_SIDE_EFFECTS (TREE_VALUE (elmt))) + side_effects = 1; + + /* Propagate an NULL_EXPR from the size of the type. We won't ever + be executing the code we generate here in that case, but handle it + specially to avoid the cmpiler blowing up. */ + if (TREE_CODE (type) == RECORD_TYPE + && (0 != (result + = contains_null_expr (DECL_SIZE (TREE_PURPOSE (elmt)))))) + return build1 (NULL_EXPR, type, TREE_OPERAND (result, 0)); + } + + /* If TYPE is a RECORD_TYPE and the fields are not in the + same order as their bit position, don't treat this as constant + since varasm.c can't handle it. */ + if (allconstant && TREE_CODE (type) == RECORD_TYPE) + { + tree last_pos = bitsize_zero_node; + tree field; + + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + tree this_pos = bit_position (field); + + if (TREE_CODE (this_pos) != INTEGER_CST + || tree_int_cst_lt (this_pos, last_pos)) + { + allconstant = 0; + break; + } + + last_pos = this_pos; + } + } + + result = build (CONSTRUCTOR, type, NULL_TREE, list); + TREE_CONSTANT (result) = allconstant; + TREE_STATIC (result) = allconstant; + TREE_SIDE_EFFECTS (result) = side_effects; + TREE_READONLY (result) = TREE_READONLY (type); + + return result; +} + +/* Return a COMPONENT_REF to access a field that is given by COMPONENT, + an IDENTIFIER_NODE giving the name of the field, or FIELD, a FIELD_DECL, + for the field. + + We also handle the fact that we might have been passed a pointer to the + actual record and know how to look for fields in variant parts. */ + +static tree +build_simple_component_ref (record_variable, component, field) + tree record_variable; + tree component; + tree field; +{ + tree record_type = TYPE_MAIN_VARIANT (TREE_TYPE (record_variable)); + tree ref; + + if ((TREE_CODE (record_type) != RECORD_TYPE + && TREE_CODE (record_type) != UNION_TYPE + && TREE_CODE (record_type) != QUAL_UNION_TYPE) + || TYPE_SIZE (record_type) == 0) + gigi_abort (510); + + /* Either COMPONENT or FIELD must be specified, but not both. */ + if ((component != 0) == (field != 0)) + gigi_abort (511); + + /* If no field was specified, look for a field with the specified name + in the current record only. */ + if (field == 0) + for (field = TYPE_FIELDS (record_type); field; + field = TREE_CHAIN (field)) + if (DECL_NAME (field) == component) + break; + + if (field == 0) + return 0; + + /* If this field is not in the specified record, see if we can find + something in the record whose original field is the same as this one. */ + if (DECL_CONTEXT (field) != record_type) + /* Check if there is a field with name COMPONENT in the record. */ + { + tree new_field; + + /* First loop thru normal components. */ + + for (new_field = TYPE_FIELDS (record_type); new_field != 0; + new_field = TREE_CHAIN (new_field)) + if (DECL_ORIGINAL_FIELD (new_field) == field + || new_field == DECL_ORIGINAL_FIELD (field) + || (DECL_ORIGINAL_FIELD (field) != 0 + && (DECL_ORIGINAL_FIELD (field) + == DECL_ORIGINAL_FIELD (new_field)))) + break; + + /* Next, loop thru DECL_INTERNAL_P components if we haven't found + the component in the first search. Doing this search in 2 steps + is required to avoiding hidden homonymous fields in the + _Parent field. */ + + if (new_field == 0) + for (new_field = TYPE_FIELDS (record_type); new_field != 0; + new_field = TREE_CHAIN (new_field)) + if (DECL_INTERNAL_P (new_field)) + { + tree field_ref + = build_simple_component_ref (record_variable, + NULL_TREE, new_field); + ref = build_simple_component_ref (field_ref, NULL_TREE, field); + + if (ref != 0) + return ref; + } + + field = new_field; + } + + if (field == 0) + return 0; + + /* If the record variable is an UNCHECKED_CONVERT_EXPR from and to BLKmode + types, convert it to a normal conversion since GCC can deal with any + mis-alignment as part of the handling of compponent references. */ + if (TREE_CODE (record_variable) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (TREE_TYPE (record_variable)) == BLKmode + && TYPE_MODE (TREE_TYPE (TREE_OPERAND (record_variable, 0))) == BLKmode) + record_variable = build1 (CONVERT_EXPR, TREE_TYPE (record_variable), + TREE_OPERAND (record_variable, 0)); + + /* It would be nice to call "fold" here, but that can lose a type + we need to tag a PLACEHOLDER_EXPR with, so we can't do it. */ + ref = build (COMPONENT_REF, TREE_TYPE (field), record_variable, field); + + if (TREE_READONLY (record_variable) || TREE_READONLY (field)) + TREE_READONLY (ref) = 1; + if (TREE_THIS_VOLATILE (record_variable) || TREE_THIS_VOLATILE (field) + || TYPE_VOLATILE (record_type)) + TREE_THIS_VOLATILE (ref) = 1; + + return ref; +} + +/* Like build_simple_component_ref, except that we give an error if the + reference could not be found. */ + +tree +build_component_ref (record_variable, component, field) + tree record_variable; + tree component; + tree field; +{ + tree ref = build_simple_component_ref (record_variable, component, field); + + if (ref != 0) + return ref; + + /* If FIELD was specified, assume this is an invalid user field so + raise constraint error. Otherwise, we can't find the type to return, so + abort. */ + + else if (field != 0) + return build1 (NULL_EXPR, TREE_TYPE (field), + build_call_raise (raise_constraint_error_decl)); + else + gigi_abort (512); +} + +/* Build a GCC tree to call an allocation or deallocation function. + If GNU_OBJ is nonzero, it is an object to deallocate. Otherwise, + generate an allocator. + + GNU_SIZE is the size of the object in bytes and ALIGN is the alignment in + bits. GNAT_PROC, if present, is a procedure to call and GNAT_POOL is the + storage pool to use. If not preset, malloc and free will be used except + if GNAT_PROC is the "fake" value of -1, in which case we allocate the + object dynamically on the stack frame. */ + +tree +build_call_alloc_dealloc (gnu_obj, gnu_size, align, gnat_proc, gnat_pool) + tree gnu_obj; + tree gnu_size; + int align; + Entity_Id gnat_proc; + Entity_Id gnat_pool; +{ + tree gnu_align = size_int (align / BITS_PER_UNIT); + + if (TREE_CODE (gnu_size) != INTEGER_CST && contains_placeholder_p (gnu_size)) + gnu_size = build (WITH_RECORD_EXPR, sizetype, gnu_size, + build_unary_op (INDIRECT_REF, NULL_TREE, gnu_obj)); + + if (Present (gnat_proc)) + { + /* The storage pools are obviously always tagged types, but the + secondary stack uses the same mechanism and is not tagged */ + if (Is_Tagged_Type (Etype (gnat_pool))) + { + /* The size is the third parameter; the alignment is the + same type. */ + Entity_Id gnat_size_type + = Etype (Next_Formal (Next_Formal (First_Formal (gnat_proc)))); + tree gnu_size_type = gnat_to_gnu_type (gnat_size_type); + tree gnu_proc = gnat_to_gnu (gnat_proc); + tree gnu_proc_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_proc); + tree gnu_pool = gnat_to_gnu (gnat_pool); + tree gnu_pool_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_pool); + tree gnu_args = NULL_TREE; + tree gnu_call; + + /* The first arg is always the address of the storage pool; next + comes the address of the object, for a deallocator, then the + size and alignment. */ + gnu_args + = chainon (gnu_args, build_tree_list (NULL_TREE, gnu_pool_addr)); + + if (gnu_obj) + gnu_args + = chainon (gnu_args, build_tree_list (NULL_TREE, gnu_obj)); + + gnu_args + = chainon (gnu_args, + build_tree_list (NULL_TREE, + convert (gnu_size_type, gnu_size))); + gnu_args + = chainon (gnu_args, + build_tree_list (NULL_TREE, + convert (gnu_size_type, gnu_align))); + + gnu_call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (gnu_proc)), + gnu_proc_addr, gnu_args, NULL_TREE); + TREE_SIDE_EFFECTS (gnu_call) = 1; + return gnu_call; + } + + /* Secondary stack case. */ + else + { + /* The size is the second parameter */ + Entity_Id gnat_size_type + = Etype (Next_Formal (First_Formal (gnat_proc))); + tree gnu_size_type = gnat_to_gnu_type (gnat_size_type); + tree gnu_proc = gnat_to_gnu (gnat_proc); + tree gnu_proc_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_proc); + tree gnu_args = NULL_TREE; + tree gnu_call; + + /* The first arg is the address of the object, for a + deallocator, then the size */ + if (gnu_obj) + gnu_args + = chainon (gnu_args, build_tree_list (NULL_TREE, gnu_obj)); + + gnu_args + = chainon (gnu_args, + build_tree_list (NULL_TREE, + convert (gnu_size_type, gnu_size))); + + gnu_call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (gnu_proc)), + gnu_proc_addr, gnu_args, NULL_TREE); + TREE_SIDE_EFFECTS (gnu_call) = 1; + return gnu_call; + } + } + + else if (gnu_obj) + return build_call_1_expr (free_decl, gnu_obj); + else if (gnat_pool == -1) + { + /* If the size is a constant, we can put it in the fixed portion of + the stack frame to avoid the need to adjust the stack pointer. */ + if (TREE_CODE (gnu_size) == INTEGER_CST && ! flag_stack_check) + { + tree gnu_range + = build_range_type (NULL_TREE, size_one_node, gnu_size); + tree gnu_array_type = build_array_type (char_type_node, gnu_range); + tree gnu_decl = + create_var_decl (get_identifier ("RETVAL"), NULL_TREE, + gnu_array_type, NULL_TREE, 0, 0, 0, 0, 0); + + return convert (ptr_void_type_node, + build_unary_op (ADDR_EXPR, NULL_TREE, gnu_decl)); + } + else + return build (ALLOCATE_EXPR, ptr_void_type_node, gnu_size, gnu_align); + } + else + return build_call_1_expr (malloc_decl, gnu_size); +} + +/* Build a GCC tree to correspond to allocating an object of TYPE whose + initial value is INIT, if INIT is nonzero. Convert the expression to + RESULT_TYPE, which must be some type of pointer. Return the tree. + GNAT_PROC and GNAT_POOL optionally give the procedure to call and + the storage pool to use. */ + +tree +build_allocator (type, init, result_type, gnat_proc, gnat_pool) + tree type; + tree init; + tree result_type; + Entity_Id gnat_proc; + Entity_Id gnat_pool; +{ + tree size = TYPE_SIZE_UNIT (type); + tree result; + + /* If the initializer, if present, is a NULL_EXPR, just return a new one. */ + if (init != 0 && TREE_CODE (init) == NULL_EXPR) + return build1 (NULL_EXPR, result_type, TREE_OPERAND (init, 0)); + + /* If RESULT_TYPE is a fat or thin pointer, set SIZE to be the sum of the + sizes of the object and its template. Allocate the whole thing and + fill in the parts that are known. */ + else if (TYPE_FAT_OR_THIN_POINTER_P (result_type)) + { + tree template_type + = (TYPE_FAT_POINTER_P (result_type) + ? TREE_TYPE (TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (result_type)))) + : TREE_TYPE (TYPE_FIELDS (TREE_TYPE (result_type)))); + tree storage_type + = build_unc_object_type (template_type, type, + get_identifier ("ALLOC")); + tree storage_ptr_type = build_pointer_type (storage_type); + tree storage; + tree template_cons = NULL_TREE; + + size = TYPE_SIZE_UNIT (storage_type); + + if (TREE_CODE (size) != INTEGER_CST + && contains_placeholder_p (size)) + size = build (WITH_RECORD_EXPR, sizetype, size, init); + + storage = build_call_alloc_dealloc (NULL_TREE, size, + TYPE_ALIGN (storage_type), + gnat_proc, gnat_pool); + storage = convert (storage_ptr_type, make_save_expr (storage)); + + if (TREE_CODE (type) == RECORD_TYPE && TYPE_IS_PADDING_P (type)) + { + type = TREE_TYPE (TYPE_FIELDS (type)); + + if (init != 0) + init = convert (type, init); + } + + /* If there is an initializing expression, make a constructor for + the entire object including the bounds and copy it into the + object. If there is no initializing expression, just set the + bounds. */ + if (init != 0) + { + template_cons = tree_cons (TREE_CHAIN (TYPE_FIELDS (storage_type)), + init, NULL_TREE); + template_cons = tree_cons (TYPE_FIELDS (storage_type), + build_template (template_type, type, + init), + template_cons); + + return convert + (result_type, + build (COMPOUND_EXPR, storage_ptr_type, + build_binary_op + (MODIFY_EXPR, storage_type, + build_unary_op (INDIRECT_REF, NULL_TREE, + convert (storage_ptr_type, storage)), + build_constructor (storage_type, template_cons)), + convert (storage_ptr_type, storage))); + } + else + return build + (COMPOUND_EXPR, result_type, + build_binary_op + (MODIFY_EXPR, template_type, + build_component_ref + (build_unary_op (INDIRECT_REF, NULL_TREE, + convert (storage_ptr_type, storage)), + NULL_TREE, TYPE_FIELDS (storage_type)), + build_template (template_type, type, NULL_TREE)), + convert (result_type, convert (storage_ptr_type, storage))); + } + + /* If we have an initializing expression, see if its size is simpler + than the size from the type. */ + if (init != 0 && TYPE_SIZE_UNIT (TREE_TYPE (init)) != 0 + && (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (init))) == INTEGER_CST + || (TREE_CODE (size) != INTEGER_CST + && contains_placeholder_p (size)))) + size = TYPE_SIZE_UNIT (TREE_TYPE (init)); + + /* If the size is still self-referential, reference the initializing + expression, if it is present. If not, this must have been a + call to allocate a library-level object, in which case we use + the maximum size. */ + if (TREE_CODE (size) != INTEGER_CST && contains_placeholder_p (size)) + { + if (init == 0) + size = max_size (size, 1); + else + size = build (WITH_RECORD_EXPR, sizetype, size, init); + } + + /* If the size overflows, pass -1 so the allocator will raise + storage error. */ + if (TREE_CODE (size) == INTEGER_CST && TREE_OVERFLOW (size)) + size = ssize_int (-1); + + /* If this is a type whose alignment is larger than the + biggest we support in normal alignment and this is in + the default storage pool, make an "aligning type", allocate + it, point to the field we need, and return that. */ + if (TYPE_ALIGN (type) > BIGGEST_ALIGNMENT + && No (gnat_proc)) + { + tree new_type = make_aligning_type (type, TYPE_ALIGN (type), size); + + result = build_call_alloc_dealloc (NULL_TREE, TYPE_SIZE (new_type), + BIGGEST_ALIGNMENT, Empty, Empty); + result = save_expr (result); + result = convert (build_pointer_type (new_type), result); + result = build_unary_op (INDIRECT_REF, NULL_TREE, result); + result = build_component_ref (result, NULL_TREE, + TYPE_FIELDS (new_type)); + result = convert (result_type, + build_unary_op (ADDR_EXPR, NULL_TREE, result)); + } + else + result = convert (result_type, + build_call_alloc_dealloc (NULL_TREE, size, + TYPE_ALIGN (type), + gnat_proc, gnat_pool)); + + /* If we have an initial value, put the new address into a SAVE_EXPR, assign + the value, and return the address. Do this with a COMPOUND_EXPR. */ + + if (init) + { + result = save_expr (result); + result + = build (COMPOUND_EXPR, TREE_TYPE (result), + build_binary_op + (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (result)), + build_unary_op (INDIRECT_REF, TREE_TYPE (TREE_TYPE (result)), + result), + init), + result); + } + + return convert (result_type, result); +} + +/* Fill in a VMS descriptor for EXPR and return a constructor for it. + GNAT_FORMAL is how we find the descriptor record. */ + +tree +fill_vms_descriptor (expr, gnat_formal) + tree expr; + Entity_Id gnat_formal; +{ + tree record_type = TREE_TYPE (TREE_TYPE (get_gnu_tree (gnat_formal))); + tree field; + tree const_list = 0; + + expr = maybe_unconstrained_array (expr); + mark_addressable (expr); + + for (field = TYPE_FIELDS (record_type); field; field = TREE_CHAIN (field)) + { + tree init = DECL_INITIAL (field); + + if (TREE_CODE (init) != INTEGER_CST + && contains_placeholder_p (init)) + init = build (WITH_RECORD_EXPR, TREE_TYPE (init), init, expr); + + const_list = tree_cons (field, convert (TREE_TYPE (field), init), + const_list); + } + + return build_constructor (record_type, nreverse (const_list)); +} + +/* Indicate that we need to make the address of EXPR_NODE and it therefore + should not be allocated in a register. Return 1 if successful. */ + +int +mark_addressable (expr_node) + tree expr_node; +{ + while (1) + switch (TREE_CODE (expr_node)) + { + case ADDR_EXPR: + case COMPONENT_REF: + case ARRAY_REF: + case ARRAY_RANGE_REF: + case REALPART_EXPR: + case IMAGPART_EXPR: + case NOP_EXPR: + expr_node = TREE_OPERAND (expr_node, 0); + break; + + case CONSTRUCTOR: + TREE_ADDRESSABLE (expr_node) = 1; + return 1; + + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + put_var_into_stack (expr_node); + TREE_ADDRESSABLE (expr_node) = 1; + return 1; + + case FUNCTION_DECL: + TREE_ADDRESSABLE (expr_node) = 1; + return 1; + + case CONST_DECL: + return (DECL_CONST_CORRESPONDING_VAR (expr_node) != 0 + && (mark_addressable + (DECL_CONST_CORRESPONDING_VAR (expr_node)))); + default: + return 1; + } +} |