diff options
author | Brendan Kehoe <brendan@gcc.gnu.org> | 1993-01-08 20:58:44 -0500 |
---|---|---|
committer | Brendan Kehoe <brendan@gcc.gnu.org> | 1993-01-08 20:58:44 -0500 |
commit | 76e616dbb8637ec8b69727b670ce22ab71bf6bd8 (patch) | |
tree | a86f0e1e02541c776986d9cae017b23929e60f9e /gcc | |
parent | c6dc70d681151224404b31dc86e2071e2b20b04c (diff) | |
download | gcc-76e616dbb8637ec8b69727b670ce22ab71bf6bd8.zip gcc-76e616dbb8637ec8b69727b670ce22ab71bf6bd8.tar.gz gcc-76e616dbb8637ec8b69727b670ce22ab71bf6bd8.tar.bz2 |
Initial revision
From-SVN: r3161
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/convert.c | 370 | ||||
-rw-r--r-- | gcc/convert.h | 3 |
2 files changed, 373 insertions, 0 deletions
diff --git a/gcc/convert.c b/gcc/convert.c new file mode 100644 index 0000000..c67f510 --- /dev/null +++ b/gcc/convert.c @@ -0,0 +1,370 @@ +/* Utility routines for data type conversion for GNU C. + Copyright (C) 1987, 1988, 1991, 1992 Free Software Foundation, Inc. + +This file is part of GNU C. + +GNU CC 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 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* These routines are somewhat language-independent utility function + intended to be called by the language-specific convert () functions. */ + +#include "config.h" +#include "tree.h" +#include "flags.h" +#include "convert.h" + +/* Convert EXPR to some pointer type TYPE. + + EXPR must be pointer, integer, enumeral, or literal zero; + in other cases error is called. */ + +tree +convert_to_pointer (type, expr) + tree type, expr; +{ + register tree intype = TREE_TYPE (expr); + register enum tree_code form = TREE_CODE (intype); + + if (integer_zerop (expr)) + { + if (type == TREE_TYPE (null_pointer_node)) + return null_pointer_node; + expr = build_int_2 (0, 0); + TREE_TYPE (expr) = type; + return expr; + } + + if (form == POINTER_TYPE) + return build1 (NOP_EXPR, type, expr); + + + if (form == INTEGER_TYPE || form == ENUMERAL_TYPE) + { + if (type_precision (intype) == POINTER_SIZE) + return build1 (CONVERT_EXPR, type, expr); + expr = convert (type_for_size (POINTER_SIZE, 0), expr); + if (TYPE_MODE (TREE_TYPE (expr)) != TYPE_MODE (type)) + /* There is supposed to be some integral type + that is the same width as a pointer. */ + abort (); + return convert_to_pointer (type, expr); + } + + error ("cannot convert to a pointer type"); + + return null_pointer_node; +} + +/* Convert EXPR to some floating-point type TYPE. + + EXPR must be float, integer, or enumeral; + in other cases error is called. */ + +tree +convert_to_real (type, expr) + tree type, expr; +{ + register enum tree_code form = TREE_CODE (TREE_TYPE (expr)); + + if (form == REAL_TYPE) + return build1 (flag_float_store ? CONVERT_EXPR : NOP_EXPR, + type, expr); + + if (form == INTEGER_TYPE || form == ENUMERAL_TYPE) + return build1 (FLOAT_EXPR, type, expr); + + if (form == POINTER_TYPE) + error ("pointer value used where a floating point value was expected"); + else + error ("aggregate value used where a float was expected"); + + { + register tree tem = make_node (REAL_CST); + TREE_TYPE (tem) = type; + TREE_REAL_CST (tem) = REAL_VALUE_ATOF ("0.0"); + return tem; + } +} + +/* Convert EXPR to some integer (or enum) type TYPE. + + EXPR must be pointer, integer, discrete (enum, char, or bool), or float; + in other cases error is called. + + The result of this is always supposed to be a newly created tree node + not in use in any existing structure. */ + +tree +convert_to_integer (type, expr) + tree type, expr; +{ + register tree intype = TREE_TYPE (expr); + register enum tree_code form = TREE_CODE (intype); + + if (form == POINTER_TYPE) + { + if (integer_zerop (expr)) + expr = integer_zero_node; + else + expr = fold (build1 (CONVERT_EXPR, + type_for_size (POINTER_SIZE, 0), expr)); + intype = TREE_TYPE (expr); + form = TREE_CODE (intype); + if (intype == type) + return expr; + } + + if (form == INTEGER_TYPE || form == ENUMERAL_TYPE + || form == BOOLEAN_TYPE || form == CHAR_TYPE) + { + register unsigned outprec = TYPE_PRECISION (type); + register unsigned inprec = TYPE_PRECISION (intype); + register enum tree_code ex_form = TREE_CODE (expr); + + /* If we are widening the type, put in an explicit conversion. + Similarly if we are not changing the width. However, if this is + a logical operation that just returns 0 or 1, we can change the + type of the expression (see below). */ + + if (TREE_CODE_CLASS (ex_form) == '<' + || ex_form == TRUTH_AND_EXPR || ex_form == TRUTH_ANDIF_EXPR + || ex_form == TRUTH_OR_EXPR || ex_form == TRUTH_ORIF_EXPR + || ex_form == TRUTH_XOR_EXPR || ex_form == TRUTH_NOT_EXPR) + { + TREE_TYPE (expr) = type; + return expr; + } + else if (outprec >= inprec) + return build1 (NOP_EXPR, type, expr); + +/* Here detect when we can distribute the truncation down past some arithmetic. + For example, if adding two longs and converting to an int, + we can equally well convert both to ints and then add. + For the operations handled here, such truncation distribution + is always safe. + It is desirable in these cases: + 1) when truncating down to full-word from a larger size + 2) when truncating takes no work. + 3) when at least one operand of the arithmetic has been extended + (as by C's default conversions). In this case we need two conversions + if we do the arithmetic as already requested, so we might as well + truncate both and then combine. Perhaps that way we need only one. + + Note that in general we cannot do the arithmetic in a type + shorter than the desired result of conversion, even if the operands + are both extended from a shorter type, because they might overflow + if combined in that type. The exceptions to this--the times when + two narrow values can be combined in their narrow type even to + make a wider result--are handled by "shorten" in build_binary_op. */ + + switch (ex_form) + { + case RSHIFT_EXPR: + /* We can pass truncation down through right shifting + when the shift count is a nonpositive constant. */ + if (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST + && tree_int_cst_lt (TREE_OPERAND (expr, 1), integer_one_node)) + goto trunc1; + break; + + case LSHIFT_EXPR: + /* We can pass truncation down through left shifting + when the shift count is a nonnegative constant. */ + if (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST + && ! tree_int_cst_lt (TREE_OPERAND (expr, 1), integer_zero_node) + && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) + { + /* If shift count is less than the width of the truncated type, + really shift. */ + if (tree_int_cst_lt (TREE_OPERAND (expr, 1), TYPE_SIZE (type))) + /* In this case, shifting is like multiplication. */ + goto trunc1; + else + /* If it is >= that width, result is zero. + Handling this with trunc1 would give the wrong result: + (int) ((long long) a << 32) is well defined (as 0) + but (int) a << 32 is undefined and would get a warning. */ + return convert_to_integer (type, integer_zero_node); + } + break; + + case MAX_EXPR: + case MIN_EXPR: + case MULT_EXPR: + { + tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type); + tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type); + + /* Don't distribute unless the output precision is at least as big + as the actual inputs. Otherwise, the comparison of the + truncated values will be wrong. */ + if (outprec >= TYPE_PRECISION (TREE_TYPE (arg0)) + && outprec >= TYPE_PRECISION (TREE_TYPE (arg1)) + /* If signedness of arg0 and arg1 don't match, + we can't necessarily find a type to compare them in. */ + && (TREE_UNSIGNED (TREE_TYPE (arg0)) + == TREE_UNSIGNED (TREE_TYPE (arg1)))) + goto trunc1; + break; + } + + case PLUS_EXPR: + case MINUS_EXPR: + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_ANDTC_EXPR: + trunc1: + { + tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type); + tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type); + + if (outprec >= BITS_PER_WORD + || TRULY_NOOP_TRUNCATION (outprec, inprec) + || inprec > TYPE_PRECISION (TREE_TYPE (arg0)) + || inprec > TYPE_PRECISION (TREE_TYPE (arg1))) + { + /* Do the arithmetic in type TYPEX, + then convert result to TYPE. */ + register tree typex = type; + + /* Can't do arithmetic in enumeral types + so use an integer type that will hold the values. */ + if (TREE_CODE (typex) == ENUMERAL_TYPE) + typex = type_for_size (TYPE_PRECISION (typex), + TREE_UNSIGNED (typex)); + + /* But now perhaps TYPEX is as wide as INPREC. + In that case, do nothing special here. + (Otherwise would recurse infinitely in convert. */ + if (TYPE_PRECISION (typex) != inprec) + { + /* Don't do unsigned arithmetic where signed was wanted, + or vice versa. + Exception: if either of the original operands were + unsigned then can safely do the work as unsigned. + And we may need to do it as unsigned + if we truncate to the original size. */ + typex = ((TREE_UNSIGNED (TREE_TYPE (expr)) + || TREE_UNSIGNED (TREE_TYPE (arg0)) + || TREE_UNSIGNED (TREE_TYPE (arg1))) + ? unsigned_type (typex) : signed_type (typex)); + return convert (type, + build_binary_op (ex_form, + convert (typex, arg0), + convert (typex, arg1), + 0)); + } + } + } + break; + + case NEGATE_EXPR: + case BIT_NOT_EXPR: + case ABS_EXPR: + { + register tree typex = type; + + /* Can't do arithmetic in enumeral types + so use an integer type that will hold the values. */ + if (TREE_CODE (typex) == ENUMERAL_TYPE) + typex = type_for_size (TYPE_PRECISION (typex), + TREE_UNSIGNED (typex)); + + /* But now perhaps TYPEX is as wide as INPREC. + In that case, do nothing special here. + (Otherwise would recurse infinitely in convert. */ + if (TYPE_PRECISION (typex) != inprec) + { + /* Don't do unsigned arithmetic where signed was wanted, + or vice versa. */ + typex = (TREE_UNSIGNED (TREE_TYPE (expr)) + ? unsigned_type (typex) : signed_type (typex)); + return convert (type, + build_unary_op (ex_form, + convert (typex, TREE_OPERAND (expr, 0)), + 1)); + } + } + + case NOP_EXPR: + /* If truncating after truncating, might as well do all at once. + If truncating after extending, we may get rid of wasted work. */ + return convert (type, get_unwidened (TREE_OPERAND (expr, 0), type)); + + case COND_EXPR: + /* Can treat the two alternative values like the operands + of an arithmetic expression. */ + { + tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type); + tree arg2 = get_unwidened (TREE_OPERAND (expr, 2), type); + + if (outprec >= BITS_PER_WORD + || TRULY_NOOP_TRUNCATION (outprec, inprec) + || inprec > TYPE_PRECISION (TREE_TYPE (arg1)) + || inprec > TYPE_PRECISION (TREE_TYPE (arg2))) + { + /* Do the arithmetic in type TYPEX, + then convert result to TYPE. */ + register tree typex = type; + + /* Can't do arithmetic in enumeral types + so use an integer type that will hold the values. */ + if (TREE_CODE (typex) == ENUMERAL_TYPE) + typex = type_for_size (TYPE_PRECISION (typex), + TREE_UNSIGNED (typex)); + + /* But now perhaps TYPEX is as wide as INPREC. + In that case, do nothing special here. + (Otherwise would recurse infinitely in convert. */ + if (TYPE_PRECISION (typex) != inprec) + { + /* Don't do unsigned arithmetic where signed was wanted, + or vice versa. */ + typex = (TREE_UNSIGNED (TREE_TYPE (expr)) + ? unsigned_type (typex) : signed_type (typex)); + return convert (type, + fold (build (COND_EXPR, typex, + TREE_OPERAND (expr, 0), + convert (typex, arg1), + convert (typex, arg2)))); + } + else + /* It is sometimes worthwhile + to push the narrowing down through the conditional. */ + return fold (build (COND_EXPR, type, + TREE_OPERAND (expr, 0), + convert (type, TREE_OPERAND (expr, 1)), + convert (type, TREE_OPERAND (expr, 2)))); + } + } + + } + + return build1 (NOP_EXPR, type, expr); + } + + if (form == REAL_TYPE) + return build1 (FIX_TRUNC_EXPR, type, expr); + + error ("aggregate value used where an integer was expected"); + + { + register tree tem = build_int_2 (0, 0); + TREE_TYPE (tem) = type; + return tem; + } +} diff --git a/gcc/convert.h b/gcc/convert.h new file mode 100644 index 0000000..5e54da3 --- /dev/null +++ b/gcc/convert.h @@ -0,0 +1,3 @@ +extern tree convert_to_integer PROTO ((tree, tree)); +extern tree convert_to_pointer PROTO ((tree, tree)); +extern tree convert_to_real PROTO ((tree, tree)); |