tree-cfg.c (gimplify_val): Move from tree-complex.c.
2004-07-22 Paolo Bonzini <bonzini@gnu.org> * tree-cfg.c (gimplify_val): Move from tree-complex.c. (gimplify_build1): Move from tree-complex.c do_unop. (gimplify_build2): Move from tree-complex.c do_binop. (gimplify_build3): New. * tree-complex.c (gimplify_val, do_unop, do_binop): Remove. Adjust throughout to call the functions above. * tree-flow.h: Declare the functions above. * tree-nested.c (gimplify_val): Rename to... (tsi_gimplify_val): ... this. * Makefile.in (tree_complex.o): Update dependencies. (stor-layout.o): Depend on regs.h. * c-common.c (handle_vector_size_attribute): Update for vector types without corresponding vector modes. * expr.c (expand_expr): Treat VECTOR_CST's like CONSTRUCTORS if a corresponding vector mode is not available. * print-tree.c (print_node): Print nunits for vector types * regclass.c (have_regs_of_mode): New. (init_reg_sets_1): Initialize it and use it instead of allocatable_regs_of_mode. * regs.h (have_regs_of_mode): Declare it. * stor-layout.c (layout_type): Pick a mode for vector types. * tree-complex.c (build_word_mode_vector_type, tree_vec_extract, build_replicated_const, do_unop, do_binop, do_plus_minus, do_negate, expand_vector_piecewise, expand_vector_parallel, expand_vector_addition, expand_vector_operations_1, expand_vector_operations, tree_lower_operations, pass_lower_vector_ssa, pass_pre_expand): New. (expand_complex_operations, pass_lower_complex): Remove. * tree-optimize.c (init_tree_optimization_passes): Adjust pass ordering for changes in tree-complex.c. * tree-pass.h: Declare new passes. * tree.c (finish_vector_type): Remove. (make_vector_type): New. (build_vector_type_for_mode, build_vector_type): Rewritten. * tree.def (VECTOR_TYPE): Document where the number of subparts is stored. * tree.h (TYPE_VECTOR_SUBPARTS): Use TYPE_PRECISION field. (make_vector): Remove declaration. From-SVN: r85039
This commit is contained in:
parent
727a31fab8
commit
26277d4179
|
@ -1,3 +1,45 @@
|
|||
2004-07-22 Paolo Bonzini <bonzini@gnu.org>
|
||||
|
||||
* tree-cfg.c (gimplify_val): Move from tree-complex.c.
|
||||
(gimplify_build1): Move from tree-complex.c do_unop.
|
||||
(gimplify_build2): Move from tree-complex.c do_binop.
|
||||
(gimplify_build3): New.
|
||||
* tree-complex.c (gimplify_val, do_unop, do_binop): Remove.
|
||||
Adjust throughout to call the functions above.
|
||||
* tree-flow.h: Declare the functions above.
|
||||
* tree-nested.c (gimplify_val): Rename to...
|
||||
(tsi_gimplify_val): ... this.
|
||||
|
||||
* Makefile.in (tree_complex.o): Update dependencies.
|
||||
(stor-layout.o): Depend on regs.h.
|
||||
* c-common.c (handle_vector_size_attribute): Update for
|
||||
vector types without corresponding vector modes.
|
||||
* expr.c (expand_expr): Treat VECTOR_CST's like CONSTRUCTORS if
|
||||
a corresponding vector mode is not available.
|
||||
* print-tree.c (print_node): Print nunits for vector types
|
||||
* regclass.c (have_regs_of_mode): New.
|
||||
(init_reg_sets_1): Initialize it and use it instead
|
||||
of allocatable_regs_of_mode.
|
||||
* regs.h (have_regs_of_mode): Declare it.
|
||||
* stor-layout.c (layout_type): Pick a mode for vector types.
|
||||
* tree-complex.c (build_word_mode_vector_type, tree_vec_extract,
|
||||
build_replicated_const, do_unop, do_binop, do_plus_minus,
|
||||
do_negate, expand_vector_piecewise, expand_vector_parallel,
|
||||
expand_vector_addition, expand_vector_operations_1,
|
||||
expand_vector_operations, tree_lower_operations,
|
||||
pass_lower_vector_ssa, pass_pre_expand): New.
|
||||
(expand_complex_operations, pass_lower_complex): Remove.
|
||||
* tree-optimize.c (init_tree_optimization_passes): Adjust
|
||||
pass ordering for changes in tree-complex.c.
|
||||
* tree-pass.h: Declare new passes.
|
||||
* tree.c (finish_vector_type): Remove.
|
||||
(make_vector_type): New.
|
||||
(build_vector_type_for_mode, build_vector_type): Rewritten.
|
||||
* tree.def (VECTOR_TYPE): Document where the number of
|
||||
subparts is stored.
|
||||
* tree.h (TYPE_VECTOR_SUBPARTS): Use TYPE_PRECISION field.
|
||||
(make_vector): Remove declaration.
|
||||
|
||||
2004-07-21 Richard Henderson <rth@redhat.com>
|
||||
|
||||
* gimple-low.c (expand_var_p): Don't look at TREE_ADDRESSABLE,
|
||||
|
|
|
@ -1585,7 +1585,7 @@ print-tree.o : print-tree.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H
|
|||
$(GGC_H) langhooks.h real.h
|
||||
stor-layout.o : stor-layout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
|
||||
$(FLAGS_H) function.h $(EXPR_H) $(RTL_H) toplev.h $(GGC_H) $(TM_P_H) $(TARGET_H) \
|
||||
langhooks.h
|
||||
langhooks.h $(REGS_H)
|
||||
tree-alias-type.o: tree-alias-type.c tree-alias-type.h $(SYSTEM_H) $(CONFIG_H) \
|
||||
$(GGC_H) $(TM_H) coretypes.h $(VARRAY_H)
|
||||
tree-alias-ander.o: tree-alias-ander.c tree-alias-ander.h $(SYSTEM_H) \
|
||||
|
@ -1922,7 +1922,8 @@ tree-sra.o : tree-sra.c $(CONFIG_H) system.h errors.h $(TREE_H) $(RTL_H) \
|
|||
langhooks.h tree-pass.h $(FLAGS_H) $(EXPR_H)
|
||||
tree-complex.o : tree-complex.c $(CONFIG_H) system.h $(TREE_H) \
|
||||
$(TM_H) $(TREE_FLOW_H) $(TREE_GIMPLE_H) tree-iterator.h tree-pass.h \
|
||||
$(FLAGS_H)
|
||||
$(FLAGS_H) $(OPTABS_H) $(RTL_H) $(MACHMODE_H) $(EXPR_H) \
|
||||
langhooks.h $(FLAGS_H) $(DIAGNOSTIC_H)
|
||||
df.o : df.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
||||
insn-config.h $(RECOG_H) function.h $(REGS_H) alloc-pool.h hard-reg-set.h \
|
||||
$(BASIC_BLOCK_H) $(DF_H)
|
||||
|
|
|
@ -4783,7 +4783,7 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
|
|||
bool *no_add_attrs)
|
||||
{
|
||||
unsigned HOST_WIDE_INT vecsize, nunits;
|
||||
enum machine_mode mode, orig_mode, new_mode;
|
||||
enum machine_mode orig_mode;
|
||||
tree type = *node, new_type, size;
|
||||
|
||||
*no_add_attrs = true;
|
||||
|
@ -4832,28 +4832,13 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
|
|||
|
||||
/* Calculate how many units fit in the vector. */
|
||||
nunits = vecsize / tree_low_cst (TYPE_SIZE_UNIT (type), 1);
|
||||
|
||||
/* Find a suitably sized vector. */
|
||||
new_mode = VOIDmode;
|
||||
for (mode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (orig_mode) == MODE_INT
|
||||
? MODE_VECTOR_INT
|
||||
: MODE_VECTOR_FLOAT);
|
||||
mode != VOIDmode;
|
||||
mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (vecsize == GET_MODE_SIZE (mode)
|
||||
&& nunits == (unsigned HOST_WIDE_INT) GET_MODE_NUNITS (mode))
|
||||
{
|
||||
new_mode = mode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_mode == VOIDmode)
|
||||
if (nunits & (nunits - 1))
|
||||
{
|
||||
error ("no vector mode with the size and type specified could be found");
|
||||
error ("number of components of the vector not a power of two");
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
new_type = build_vector_type_for_mode (type, new_mode);
|
||||
new_type = build_vector_type (type, nunits);
|
||||
|
||||
/* Build back pointers if needed. */
|
||||
*node = reconstruct_complex_type (*node, new_type);
|
||||
|
|
86
gcc/optabs.c
86
gcc/optabs.c
|
@ -627,6 +627,89 @@ expand_cmplxdiv_wide (rtx real0, rtx real1, rtx imag0, rtx imag1, rtx realr,
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return the optab used for computing the operation given by
|
||||
the tree code, CODE. This function is not always usable (for
|
||||
example, it cannot give complete results for multiplication
|
||||
or division) but probably ought to be relied on more widely
|
||||
throughout the expander. */
|
||||
optab
|
||||
optab_for_tree_code (enum tree_code code, tree type)
|
||||
{
|
||||
bool trapv;
|
||||
switch (code)
|
||||
{
|
||||
case BIT_AND_EXPR:
|
||||
return and_optab;
|
||||
|
||||
case BIT_IOR_EXPR:
|
||||
return ior_optab;
|
||||
|
||||
case BIT_NOT_EXPR:
|
||||
return one_cmpl_optab;
|
||||
|
||||
case BIT_XOR_EXPR:
|
||||
return xor_optab;
|
||||
|
||||
case TRUNC_MOD_EXPR:
|
||||
case CEIL_MOD_EXPR:
|
||||
case FLOOR_MOD_EXPR:
|
||||
case ROUND_MOD_EXPR:
|
||||
return TYPE_UNSIGNED (type) ? umod_optab : smod_optab;
|
||||
|
||||
case RDIV_EXPR:
|
||||
case TRUNC_DIV_EXPR:
|
||||
case CEIL_DIV_EXPR:
|
||||
case FLOOR_DIV_EXPR:
|
||||
case ROUND_DIV_EXPR:
|
||||
case EXACT_DIV_EXPR:
|
||||
return TYPE_UNSIGNED (type) ? udiv_optab : sdiv_optab;
|
||||
|
||||
case LSHIFT_EXPR:
|
||||
return ashl_optab;
|
||||
|
||||
case RSHIFT_EXPR:
|
||||
return TYPE_UNSIGNED (type) ? lshr_optab : ashr_optab;
|
||||
|
||||
case LROTATE_EXPR:
|
||||
return rotl_optab;
|
||||
|
||||
case RROTATE_EXPR:
|
||||
return rotr_optab;
|
||||
|
||||
case MAX_EXPR:
|
||||
return TYPE_UNSIGNED (type) ? umax_optab : smax_optab;
|
||||
|
||||
case MIN_EXPR:
|
||||
return TYPE_UNSIGNED (type) ? umin_optab : smin_optab;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
trapv = flag_trapv && INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type);
|
||||
switch (code)
|
||||
{
|
||||
case PLUS_EXPR:
|
||||
return trapv ? addv_optab : add_optab;
|
||||
|
||||
case MINUS_EXPR:
|
||||
return trapv ? subv_optab : sub_optab;
|
||||
|
||||
case MULT_EXPR:
|
||||
return trapv ? smulv_optab : smul_optab;
|
||||
|
||||
case NEGATE_EXPR:
|
||||
return trapv ? negv_optab : neg_optab;
|
||||
|
||||
case ABS_EXPR:
|
||||
return trapv ? absv_optab : abs_optab;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Wrapper around expand_binop which takes an rtx code to specify
|
||||
the operation to perform, not an optab pointer. All other
|
||||
|
@ -2804,7 +2887,8 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
|
|||
}
|
||||
|
||||
/* If there is no negate operation, try doing a subtract from zero.
|
||||
The US Software GOFAST library needs this. */
|
||||
The US Software GOFAST library needs this. FIXME: This is *wrong*
|
||||
for floating-point operations due to negative zeros! */
|
||||
if (unoptab->code == NEG)
|
||||
{
|
||||
rtx temp;
|
||||
|
|
|
@ -454,6 +454,10 @@ enum can_compare_purpose
|
|||
ccp_store_flag
|
||||
};
|
||||
|
||||
/* Return the optab used for computing the given operation on the type
|
||||
given by the second argument. */
|
||||
extern optab optab_for_tree_code (enum tree_code, tree);
|
||||
|
||||
/* Nonzero if a compare of mode MODE can be done straightforwardly
|
||||
(without splitting it into pieces). */
|
||||
extern int can_compare_p (enum rtx_code, enum machine_mode,
|
||||
|
|
|
@ -537,6 +537,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
|
|||
print_node (file, "values", TYPE_VALUES (node), indent + 4);
|
||||
else if (TREE_CODE (node) == ARRAY_TYPE || TREE_CODE (node) == SET_TYPE)
|
||||
print_node (file, "domain", TYPE_DOMAIN (node), indent + 4);
|
||||
else if (TREE_CODE (node) == VECTOR_TYPE)
|
||||
fprintf (file, " nunits %d", (int) TYPE_VECTOR_SUBPARTS (node));
|
||||
else if (TREE_CODE (node) == RECORD_TYPE
|
||||
|| TREE_CODE (node) == UNION_TYPE
|
||||
|| TREE_CODE (node) == QUAL_UNION_TYPE)
|
||||
|
|
|
@ -191,6 +191,10 @@ const char * reg_names[] = REGISTER_NAMES;
|
|||
|
||||
enum machine_mode reg_raw_mode[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
/* 1 if there is a register of given mode. */
|
||||
|
||||
bool have_regs_of_mode [MAX_MACHINE_MODE];
|
||||
|
||||
/* 1 if class does contain register of given mode. */
|
||||
|
||||
static char contains_reg_of_mode [N_REG_CLASSES] [MAX_MACHINE_MODE];
|
||||
|
@ -305,7 +309,6 @@ init_reg_sets_1 (void)
|
|||
{
|
||||
unsigned int i, j;
|
||||
unsigned int /* enum machine_mode */ m;
|
||||
char allocatable_regs_of_mode [MAX_MACHINE_MODE];
|
||||
|
||||
/* This macro allows the fixed or call-used registers
|
||||
and the register classes to depend on target flags. */
|
||||
|
@ -469,8 +472,8 @@ init_reg_sets_1 (void)
|
|||
SET_HARD_REG_BIT (regs_invalidated_by_call, i);
|
||||
}
|
||||
|
||||
memset (have_regs_of_mode, 0, sizeof (have_regs_of_mode));
|
||||
memset (contains_reg_of_mode, 0, sizeof (contains_reg_of_mode));
|
||||
memset (allocatable_regs_of_mode, 0, sizeof (allocatable_regs_of_mode));
|
||||
for (m = 0; m < (unsigned int) MAX_MACHINE_MODE; m++)
|
||||
for (i = 0; i < N_REG_CLASSES; i++)
|
||||
if ((unsigned) CLASS_MAX_NREGS (i, m) <= reg_class_size[i])
|
||||
|
@ -479,7 +482,7 @@ init_reg_sets_1 (void)
|
|||
&& HARD_REGNO_MODE_OK (j, m))
|
||||
{
|
||||
contains_reg_of_mode [i][m] = 1;
|
||||
allocatable_regs_of_mode [m] = 1;
|
||||
have_regs_of_mode [m] = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -487,7 +490,7 @@ init_reg_sets_1 (void)
|
|||
and take the maximum cost of moving any subset to any other. */
|
||||
|
||||
for (m = 0; m < (unsigned int) MAX_MACHINE_MODE; m++)
|
||||
if (allocatable_regs_of_mode [m])
|
||||
if (have_regs_of_mode [m])
|
||||
{
|
||||
for (i = 0; i < N_REG_CLASSES; i++)
|
||||
if (contains_reg_of_mode [i][m])
|
||||
|
|
|
@ -163,6 +163,10 @@ extern char regs_ever_live[FIRST_PSEUDO_REGISTER];
|
|||
|
||||
extern char regs_asm_clobbered[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
/* Vector indexed by machine mode saying whether there are regs of that mode. */
|
||||
|
||||
extern bool have_regs_of_mode [MAX_MACHINE_MODE];
|
||||
|
||||
/* For each hard register, the widest mode object that it can contain.
|
||||
This will be a MODE_INT mode if the register can hold integers. Otherwise
|
||||
it will be a MODE_FLOAT or a MODE_CC mode, whichever is valid for the
|
||||
|
|
|
@ -34,6 +34,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|||
#include "ggc.h"
|
||||
#include "target.h"
|
||||
#include "langhooks.h"
|
||||
#include "regs.h"
|
||||
|
||||
/* Set to one when set_sizetype has been called. */
|
||||
static int sizetype_set;
|
||||
|
@ -1582,10 +1583,52 @@ layout_type (tree type)
|
|||
break;
|
||||
|
||||
case VECTOR_TYPE:
|
||||
TYPE_UNSIGNED (type) = TYPE_UNSIGNED (TREE_TYPE (type));
|
||||
TYPE_SIZE (type) = bitsize_int (GET_MODE_BITSIZE (TYPE_MODE (type)));
|
||||
TYPE_SIZE_UNIT (type) = size_int (GET_MODE_SIZE (TYPE_MODE (type)));
|
||||
break;
|
||||
{
|
||||
int nunits = TYPE_VECTOR_SUBPARTS (type);
|
||||
tree nunits_tree = build_int_2 (nunits, 0);
|
||||
tree innertype = TREE_TYPE (type);
|
||||
|
||||
if (nunits & (nunits - 1))
|
||||
abort ();
|
||||
|
||||
/* Find an appropriate mode for the vector type. */
|
||||
if (TYPE_MODE (type) == VOIDmode)
|
||||
{
|
||||
enum machine_mode innermode = TYPE_MODE (innertype);
|
||||
enum machine_mode mode;
|
||||
|
||||
/* First, look for a supported vector type. */
|
||||
if (GET_MODE_CLASS (innermode) == MODE_FLOAT)
|
||||
mode = MIN_MODE_VECTOR_FLOAT;
|
||||
else
|
||||
mode = MIN_MODE_VECTOR_INT;
|
||||
|
||||
for (; mode != VOIDmode ; mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (GET_MODE_NUNITS (mode) == nunits
|
||||
&& GET_MODE_INNER (mode) == innermode
|
||||
&& VECTOR_MODE_SUPPORTED_P (mode))
|
||||
break;
|
||||
|
||||
/* For integers, try mapping it to a same-sized scalar mode. */
|
||||
if (mode == VOIDmode
|
||||
&& GET_MODE_CLASS (innermode) == MODE_INT)
|
||||
mode = mode_for_size (nunits * GET_MODE_BITSIZE (innermode),
|
||||
MODE_INT, 0);
|
||||
|
||||
if (mode == VOIDmode || !have_regs_of_mode[mode])
|
||||
TYPE_MODE (type) = BLKmode;
|
||||
else
|
||||
TYPE_MODE (type) = mode;
|
||||
}
|
||||
|
||||
TYPE_UNSIGNED (type) = TYPE_UNSIGNED (TREE_TYPE (type));
|
||||
TYPE_SIZE_UNIT (type) = int_const_binop (MULT_EXPR,
|
||||
TYPE_SIZE_UNIT (innertype),
|
||||
nunits_tree, 0);
|
||||
TYPE_SIZE (type) = int_const_binop (MULT_EXPR, TYPE_SIZE (innertype),
|
||||
nunits_tree, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case VOID_TYPE:
|
||||
/* This is an incomplete type and so doesn't have a size. */
|
||||
|
|
|
@ -4793,6 +4793,79 @@ struct tree_opt_pass pass_split_crit_edges =
|
|||
0, /* todo_flags_start */
|
||||
TODO_dump_func, /* todo_flags_finish */
|
||||
};
|
||||
|
||||
|
||||
/* Return EXP if it is a valid GIMPLE rvalue, else gimplify it into
|
||||
a temporary, make sure and register it to be renamed if necessary,
|
||||
and finally return the temporary. Put the statements to compute
|
||||
EXP before the current statement in BSI. */
|
||||
|
||||
tree
|
||||
gimplify_val (block_stmt_iterator *bsi, tree type, tree exp)
|
||||
{
|
||||
tree t, new_stmt, orig_stmt;
|
||||
|
||||
if (is_gimple_val (exp))
|
||||
return exp;
|
||||
|
||||
t = make_rename_temp (type, NULL);
|
||||
new_stmt = build (MODIFY_EXPR, type, t, exp);
|
||||
|
||||
orig_stmt = bsi_stmt (*bsi);
|
||||
SET_EXPR_LOCUS (new_stmt, EXPR_LOCUS (orig_stmt));
|
||||
TREE_BLOCK (new_stmt) = TREE_BLOCK (orig_stmt);
|
||||
|
||||
bsi_insert_before (bsi, new_stmt, BSI_SAME_STMT);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Build a ternary operation and gimplify it. Emit code before BSI.
|
||||
Return the gimple_val holding the result. */
|
||||
|
||||
tree
|
||||
gimplify_build3 (block_stmt_iterator *bsi, enum tree_code code,
|
||||
tree type, tree a, tree b, tree c)
|
||||
{
|
||||
tree ret;
|
||||
|
||||
ret = fold (build3 (code, type, a, b, c));
|
||||
STRIP_NOPS (ret);
|
||||
|
||||
return gimplify_val (bsi, type, ret);
|
||||
}
|
||||
|
||||
/* Build a binary operation and gimplify it. Emit code before BSI.
|
||||
Return the gimple_val holding the result. */
|
||||
|
||||
tree
|
||||
gimplify_build2 (block_stmt_iterator *bsi, enum tree_code code,
|
||||
tree type, tree a, tree b)
|
||||
{
|
||||
tree ret;
|
||||
|
||||
ret = fold (build2 (code, type, a, b));
|
||||
STRIP_NOPS (ret);
|
||||
|
||||
return gimplify_val (bsi, type, ret);
|
||||
}
|
||||
|
||||
/* Build a unary operation and gimplify it. Emit code before BSI.
|
||||
Return the gimple_val holding the result. */
|
||||
|
||||
tree
|
||||
gimplify_build1 (block_stmt_iterator *bsi, enum tree_code code, tree type,
|
||||
tree a)
|
||||
{
|
||||
tree ret;
|
||||
|
||||
ret = fold (build1 (code, type, a));
|
||||
STRIP_NOPS (ret);
|
||||
|
||||
return gimplify_val (bsi, type, ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Emit return warnings. */
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Lower complex operations to scalar operations.
|
||||
/* Lower complex number and vector operations to scalar operations.
|
||||
Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
@ -23,6 +23,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
#include "tm.h"
|
||||
#include "rtl.h"
|
||||
#include "expr.h"
|
||||
#include "insn-codes.h"
|
||||
#include "diagnostic.h"
|
||||
#include "optabs.h"
|
||||
#include "machmode.h"
|
||||
#include "langhooks.h"
|
||||
#include "tree-flow.h"
|
||||
#include "tree-gimple.h"
|
||||
#include "tree-iterator.h"
|
||||
|
@ -30,28 +37,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|||
#include "flags.h"
|
||||
|
||||
|
||||
/* Force EXP to be a gimple_val. */
|
||||
|
||||
static tree
|
||||
gimplify_val (block_stmt_iterator *bsi, tree type, tree exp)
|
||||
{
|
||||
tree t, new_stmt, orig_stmt;
|
||||
|
||||
if (is_gimple_val (exp))
|
||||
return exp;
|
||||
|
||||
t = make_rename_temp (type, NULL);
|
||||
new_stmt = build (MODIFY_EXPR, type, t, exp);
|
||||
|
||||
orig_stmt = bsi_stmt (*bsi);
|
||||
SET_EXPR_LOCUS (new_stmt, EXPR_LOCUS (orig_stmt));
|
||||
TREE_BLOCK (new_stmt) = TREE_BLOCK (orig_stmt);
|
||||
|
||||
bsi_insert_before (bsi, new_stmt, BSI_SAME_STMT);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Extract the real or imaginary part of a complex variable or constant.
|
||||
Make sure that it's a proper gimple_val and gimplify it if not.
|
||||
Emit any new code before BSI. */
|
||||
|
@ -85,35 +70,6 @@ extract_component (block_stmt_iterator *bsi, tree t, bool imagpart_p)
|
|||
return gimplify_val (bsi, inner_type, ret);
|
||||
}
|
||||
|
||||
/* Build a binary operation and gimplify it. Emit code before BSI.
|
||||
Return the gimple_val holding the result. */
|
||||
|
||||
static tree
|
||||
do_binop (block_stmt_iterator *bsi, enum tree_code code,
|
||||
tree type, tree a, tree b)
|
||||
{
|
||||
tree ret;
|
||||
|
||||
ret = fold (build (code, type, a, b));
|
||||
STRIP_NOPS (ret);
|
||||
|
||||
return gimplify_val (bsi, type, ret);
|
||||
}
|
||||
|
||||
/* Build a unary operation and gimplify it. Emit code before BSI.
|
||||
Return the gimple_val holding the result. */
|
||||
|
||||
static tree
|
||||
do_unop (block_stmt_iterator *bsi, enum tree_code code, tree type, tree a)
|
||||
{
|
||||
tree ret;
|
||||
|
||||
ret = fold (build1 (code, type, a));
|
||||
STRIP_NOPS (ret);
|
||||
|
||||
return gimplify_val (bsi, type, ret);
|
||||
}
|
||||
|
||||
/* Update an assignment to a complex variable in place. */
|
||||
|
||||
static void
|
||||
|
@ -142,8 +98,8 @@ expand_complex_addition (block_stmt_iterator *bsi, tree inner_type,
|
|||
{
|
||||
tree rr, ri;
|
||||
|
||||
rr = do_binop (bsi, code, inner_type, ar, br);
|
||||
ri = do_binop (bsi, code, inner_type, ai, bi);
|
||||
rr = gimplify_build2 (bsi, code, inner_type, ar, br);
|
||||
ri = gimplify_build2 (bsi, code, inner_type, ai, bi);
|
||||
|
||||
update_complex_assignment (bsi, rr, ri);
|
||||
}
|
||||
|
@ -158,19 +114,19 @@ expand_complex_multiplication (block_stmt_iterator *bsi, tree inner_type,
|
|||
{
|
||||
tree t1, t2, t3, t4, rr, ri;
|
||||
|
||||
t1 = do_binop (bsi, MULT_EXPR, inner_type, ar, br);
|
||||
t2 = do_binop (bsi, MULT_EXPR, inner_type, ai, bi);
|
||||
t3 = do_binop (bsi, MULT_EXPR, inner_type, ar, bi);
|
||||
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ar, br);
|
||||
t2 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ai, bi);
|
||||
t3 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ar, bi);
|
||||
|
||||
/* Avoid expanding redundant multiplication for the common
|
||||
case of squaring a complex number. */
|
||||
if (ar == br && ai == bi)
|
||||
t4 = t3;
|
||||
else
|
||||
t4 = do_binop (bsi, MULT_EXPR, inner_type, ai, br);
|
||||
t4 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ai, br);
|
||||
|
||||
rr = do_binop (bsi, MINUS_EXPR, inner_type, t1, t2);
|
||||
ri = do_binop (bsi, PLUS_EXPR, inner_type, t3, t4);
|
||||
rr = gimplify_build2 (bsi, MINUS_EXPR, inner_type, t1, t2);
|
||||
ri = gimplify_build2 (bsi, PLUS_EXPR, inner_type, t3, t4);
|
||||
|
||||
update_complex_assignment (bsi, rr, ri);
|
||||
}
|
||||
|
@ -187,19 +143,19 @@ expand_complex_div_straight (block_stmt_iterator *bsi, tree inner_type,
|
|||
{
|
||||
tree rr, ri, div, t1, t2, t3;
|
||||
|
||||
t1 = do_binop (bsi, MULT_EXPR, inner_type, br, br);
|
||||
t2 = do_binop (bsi, MULT_EXPR, inner_type, bi, bi);
|
||||
div = do_binop (bsi, PLUS_EXPR, inner_type, t1, t2);
|
||||
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, br, br);
|
||||
t2 = gimplify_build2 (bsi, MULT_EXPR, inner_type, bi, bi);
|
||||
div = gimplify_build2 (bsi, PLUS_EXPR, inner_type, t1, t2);
|
||||
|
||||
t1 = do_binop (bsi, MULT_EXPR, inner_type, ar, br);
|
||||
t2 = do_binop (bsi, MULT_EXPR, inner_type, ai, bi);
|
||||
t3 = do_binop (bsi, PLUS_EXPR, inner_type, t1, t2);
|
||||
rr = do_binop (bsi, code, inner_type, t3, div);
|
||||
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ar, br);
|
||||
t2 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ai, bi);
|
||||
t3 = gimplify_build2 (bsi, PLUS_EXPR, inner_type, t1, t2);
|
||||
rr = gimplify_build2 (bsi, code, inner_type, t3, div);
|
||||
|
||||
t1 = do_binop (bsi, MULT_EXPR, inner_type, ai, br);
|
||||
t2 = do_binop (bsi, MULT_EXPR, inner_type, ar, bi);
|
||||
t3 = do_binop (bsi, MINUS_EXPR, inner_type, t1, t2);
|
||||
ri = do_binop (bsi, code, inner_type, t3, div);
|
||||
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ai, br);
|
||||
t2 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ar, bi);
|
||||
t3 = gimplify_build2 (bsi, MINUS_EXPR, inner_type, t1, t2);
|
||||
ri = gimplify_build2 (bsi, code, inner_type, t3, div);
|
||||
|
||||
update_complex_assignment (bsi, rr, ri);
|
||||
}
|
||||
|
@ -215,8 +171,8 @@ expand_complex_div_wide (block_stmt_iterator *bsi, tree inner_type,
|
|||
tree rr, ri, ratio, div, t1, t2, min, max, cond;
|
||||
|
||||
/* Examine |br| < |bi|, and branch. */
|
||||
t1 = do_unop (bsi, ABS_EXPR, inner_type, br);
|
||||
t2 = do_unop (bsi, ABS_EXPR, inner_type, bi);
|
||||
t1 = gimplify_build1 (bsi, ABS_EXPR, inner_type, br);
|
||||
t2 = gimplify_build1 (bsi, ABS_EXPR, inner_type, bi);
|
||||
cond = fold (build (LT_EXPR, boolean_type_node, t1, t2));
|
||||
STRIP_NOPS (cond);
|
||||
|
||||
|
@ -292,20 +248,20 @@ expand_complex_div_wide (block_stmt_iterator *bsi, tree inner_type,
|
|||
|
||||
/* Now we have MIN(|br|, |bi|) and MAX(|br|, |bi|). We now use the
|
||||
ratio min/max to scale both the dividend and divisor. */
|
||||
ratio = do_binop (bsi, code, inner_type, min, max);
|
||||
ratio = gimplify_build2 (bsi, code, inner_type, min, max);
|
||||
|
||||
/* Calculate the divisor: min*ratio + max. */
|
||||
t1 = do_binop (bsi, MULT_EXPR, inner_type, min, ratio);
|
||||
div = do_binop (bsi, PLUS_EXPR, inner_type, t1, max);
|
||||
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, min, ratio);
|
||||
div = gimplify_build2 (bsi, PLUS_EXPR, inner_type, t1, max);
|
||||
|
||||
/* Result is now ((ar + ai*ratio)/div) + i((ai - ar*ratio)/div). */
|
||||
t1 = do_binop (bsi, MULT_EXPR, inner_type, ai, ratio);
|
||||
t2 = do_binop (bsi, PLUS_EXPR, inner_type, ar, t1);
|
||||
rr = do_binop (bsi, code, inner_type, t2, div);
|
||||
/* Result is now ((ar + ai*ratio)/div) + i((ai - ar*ratio)/div). */
|
||||
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ai, ratio);
|
||||
t2 = gimplify_build2 (bsi, PLUS_EXPR, inner_type, ar, t1);
|
||||
rr = gimplify_build2 (bsi, code, inner_type, t2, div);
|
||||
|
||||
t1 = do_binop (bsi, MULT_EXPR, inner_type, ar, ratio);
|
||||
t2 = do_binop (bsi, MINUS_EXPR, inner_type, ai, t1);
|
||||
ri = do_binop (bsi, code, inner_type, t2, div);
|
||||
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ar, ratio);
|
||||
t2 = gimplify_build2 (bsi, MINUS_EXPR, inner_type, ai, t1);
|
||||
ri = gimplify_build2 (bsi, code, inner_type, t2, div);
|
||||
|
||||
update_complex_assignment (bsi, rr, ri);
|
||||
}
|
||||
|
@ -343,8 +299,8 @@ expand_complex_negation (block_stmt_iterator *bsi, tree inner_type,
|
|||
{
|
||||
tree rr, ri;
|
||||
|
||||
rr = do_unop (bsi, NEGATE_EXPR, inner_type, ar);
|
||||
ri = do_unop (bsi, NEGATE_EXPR, inner_type, ai);
|
||||
rr = gimplify_build1 (bsi, NEGATE_EXPR, inner_type, ar);
|
||||
ri = gimplify_build1 (bsi, NEGATE_EXPR, inner_type, ai);
|
||||
|
||||
update_complex_assignment (bsi, rr, ri);
|
||||
}
|
||||
|
@ -359,7 +315,7 @@ expand_complex_conjugate (block_stmt_iterator *bsi, tree inner_type,
|
|||
{
|
||||
tree ri;
|
||||
|
||||
ri = do_unop (bsi, NEGATE_EXPR, inner_type, ai);
|
||||
ri = gimplify_build1 (bsi, NEGATE_EXPR, inner_type, ai);
|
||||
|
||||
update_complex_assignment (bsi, ar, ri);
|
||||
}
|
||||
|
@ -372,10 +328,11 @@ expand_complex_comparison (block_stmt_iterator *bsi, tree ar, tree ai,
|
|||
{
|
||||
tree cr, ci, cc, stmt, type;
|
||||
|
||||
cr = do_binop (bsi, code, boolean_type_node, ar, br);
|
||||
ci = do_binop (bsi, code, boolean_type_node, ai, bi);
|
||||
cc = do_binop (bsi, (code == EQ_EXPR ? TRUTH_AND_EXPR : TRUTH_OR_EXPR),
|
||||
boolean_type_node, cr, ci);
|
||||
cr = gimplify_build2 (bsi, code, boolean_type_node, ar, br);
|
||||
ci = gimplify_build2 (bsi, code, boolean_type_node, ai, bi);
|
||||
cc = gimplify_build2 (bsi,
|
||||
(code == EQ_EXPR ? TRUTH_AND_EXPR : TRUTH_OR_EXPR),
|
||||
boolean_type_node, cr, ci);
|
||||
|
||||
stmt = bsi_stmt (*bsi);
|
||||
modify_stmt (stmt);
|
||||
|
@ -517,17 +474,434 @@ expand_complex_operations_1 (block_stmt_iterator *bsi)
|
|||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a constant of type TYPE, made of VALUE's bits replicated
|
||||
every TYPE_SIZE (INNER_TYPE) bits to fit TYPE's precision. */
|
||||
static tree
|
||||
build_replicated_const (tree type, tree inner_type, HOST_WIDE_INT value)
|
||||
{
|
||||
int width = tree_low_cst (TYPE_SIZE (inner_type), 1);
|
||||
int n = HOST_BITS_PER_WIDE_INT / width;
|
||||
unsigned HOST_WIDE_INT low, high, mask;
|
||||
tree ret;
|
||||
|
||||
/* Main loop to process each statement. */
|
||||
/* ??? Could use dominator bits to propagate from complex_expr at the
|
||||
same time. This might reveal more constants, particularly in cases
|
||||
such as (complex = complex op scalar). This may not be relevant
|
||||
after SRA and subsequent cleanups. Proof of this would be if we
|
||||
verify that the code generated by expand_complex_div_wide is
|
||||
simplified properly to straight-line code. */
|
||||
if (n == 0)
|
||||
abort ();
|
||||
|
||||
if (width == HOST_BITS_PER_WIDE_INT)
|
||||
low = value;
|
||||
else
|
||||
{
|
||||
mask = ((HOST_WIDE_INT)1 << width) - 1;
|
||||
low = (unsigned HOST_WIDE_INT) ~0 / mask * (value & mask);
|
||||
}
|
||||
|
||||
if (TYPE_PRECISION (type) < HOST_BITS_PER_WIDE_INT)
|
||||
low &= ((HOST_WIDE_INT)1 << TYPE_PRECISION (type)) - 1, high = 0;
|
||||
else if (TYPE_PRECISION (type) == HOST_BITS_PER_WIDE_INT)
|
||||
high = 0;
|
||||
else if (TYPE_PRECISION (type) == 2 * HOST_BITS_PER_WIDE_INT)
|
||||
high = low;
|
||||
else
|
||||
abort ();
|
||||
|
||||
ret = build_int_2 (low, high);
|
||||
TREE_TYPE (ret) = type;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return a suitable vector types made of SUBPARTS units each of mode
|
||||
"word_mode" (the global variable). */
|
||||
static tree
|
||||
build_word_mode_vector_type (int nunits)
|
||||
{
|
||||
static tree innertype;
|
||||
static tree last;
|
||||
static int last_nunits;
|
||||
|
||||
if (!innertype)
|
||||
innertype = lang_hooks.types.type_for_mode (word_mode, 1);
|
||||
else if (last_nunits == nunits)
|
||||
return last;
|
||||
|
||||
/* We build a new type, but we canonicalize it nevertheless,
|
||||
because it still saves some memory. */
|
||||
last_nunits = nunits;
|
||||
last = type_hash_canon (nunits, build_vector_type (innertype, nunits));
|
||||
return last;
|
||||
}
|
||||
|
||||
typedef tree (*elem_op_func) (block_stmt_iterator *,
|
||||
tree, tree, tree, tree, tree, enum tree_code);
|
||||
|
||||
static inline tree
|
||||
tree_vec_extract (block_stmt_iterator *bsi, tree type,
|
||||
tree t, tree bitsize, tree bitpos)
|
||||
{
|
||||
if (bitpos)
|
||||
return gimplify_build3 (bsi, BIT_FIELD_REF, type, t, bitsize, bitpos);
|
||||
else
|
||||
return gimplify_build1 (bsi, VIEW_CONVERT_EXPR, type, t);
|
||||
}
|
||||
|
||||
static tree
|
||||
do_unop (block_stmt_iterator *bsi, tree inner_type, tree a,
|
||||
tree b ATTRIBUTE_UNUSED, tree bitpos, tree bitsize,
|
||||
enum tree_code code)
|
||||
{
|
||||
a = tree_vec_extract (bsi, inner_type, a, bitsize, bitpos);
|
||||
return gimplify_build1 (bsi, code, inner_type, a);
|
||||
}
|
||||
|
||||
static tree
|
||||
do_binop (block_stmt_iterator *bsi, tree inner_type, tree a, tree b,
|
||||
tree bitpos, tree bitsize, enum tree_code code)
|
||||
{
|
||||
a = tree_vec_extract (bsi, inner_type, a, bitsize, bitpos);
|
||||
b = tree_vec_extract (bsi, inner_type, b, bitsize, bitpos);
|
||||
return gimplify_build2 (bsi, code, inner_type, a, b);
|
||||
}
|
||||
|
||||
/* Expand vector addition to scalars. This does bit twiddling
|
||||
in order to increase parallelism:
|
||||
|
||||
a + b = (((int) a & 0x7f7f7f7f) + ((int) b & 0x7f7f7f7f)) ^
|
||||
(a ^ b) & 0x80808080
|
||||
|
||||
a - b = (((int) a | 0x80808080) - ((int) b & 0x7f7f7f7f)) ^
|
||||
(a ^ ~b) & 0x80808080
|
||||
|
||||
-b = (0x80808080 - ((int) b & 0x7f7f7f7f)) ^ (~b & 0x80808080)
|
||||
|
||||
This optimization should be done only if 4 vector items or more
|
||||
fit into a word. */
|
||||
static tree
|
||||
do_plus_minus (block_stmt_iterator *bsi, tree word_type, tree a, tree b,
|
||||
tree bitpos ATTRIBUTE_UNUSED, tree bitsize ATTRIBUTE_UNUSED,
|
||||
enum tree_code code)
|
||||
{
|
||||
tree inner_type = TREE_TYPE (TREE_TYPE (a));
|
||||
unsigned HOST_WIDE_INT max;
|
||||
tree low_bits, high_bits, a_low, b_low, result_low, signs;
|
||||
|
||||
max = GET_MODE_MASK (TYPE_MODE (inner_type));
|
||||
low_bits = build_replicated_const (word_type, inner_type, max >> 1);
|
||||
high_bits = build_replicated_const (word_type, inner_type, max & ~(max >> 1));
|
||||
|
||||
a = tree_vec_extract (bsi, word_type, a, bitsize, bitpos);
|
||||
b = tree_vec_extract (bsi, word_type, b, bitsize, bitpos);
|
||||
|
||||
signs = gimplify_build2 (bsi, BIT_XOR_EXPR, word_type, a, b);
|
||||
b_low = gimplify_build2 (bsi, BIT_AND_EXPR, word_type, b, low_bits);
|
||||
if (code == PLUS_EXPR)
|
||||
a_low = gimplify_build2 (bsi, BIT_AND_EXPR, word_type, a, low_bits);
|
||||
else
|
||||
{
|
||||
a_low = gimplify_build2 (bsi, BIT_IOR_EXPR, word_type, a, high_bits);
|
||||
signs = gimplify_build1 (bsi, BIT_NOT_EXPR, word_type, signs);
|
||||
}
|
||||
|
||||
signs = gimplify_build2 (bsi, BIT_AND_EXPR, word_type, signs, high_bits);
|
||||
result_low = gimplify_build2 (bsi, code, word_type, a_low, b_low);
|
||||
return gimplify_build2 (bsi, BIT_XOR_EXPR, word_type, result_low, signs);
|
||||
}
|
||||
|
||||
static tree
|
||||
do_negate (block_stmt_iterator *bsi, tree word_type, tree b,
|
||||
tree unused ATTRIBUTE_UNUSED, tree bitpos ATTRIBUTE_UNUSED,
|
||||
tree bitsize ATTRIBUTE_UNUSED,
|
||||
enum tree_code code ATTRIBUTE_UNUSED)
|
||||
{
|
||||
tree inner_type = TREE_TYPE (TREE_TYPE (b));
|
||||
HOST_WIDE_INT max;
|
||||
tree low_bits, high_bits, b_low, result_low, signs;
|
||||
|
||||
max = GET_MODE_MASK (TYPE_MODE (inner_type));
|
||||
low_bits = build_replicated_const (word_type, inner_type, max >> 1);
|
||||
high_bits = build_replicated_const (word_type, inner_type, max & ~(max >> 1));
|
||||
|
||||
b = tree_vec_extract (bsi, word_type, b, bitsize, bitpos);
|
||||
|
||||
b_low = gimplify_build2 (bsi, BIT_AND_EXPR, word_type, b, low_bits);
|
||||
signs = gimplify_build1 (bsi, BIT_NOT_EXPR, word_type, b);
|
||||
signs = gimplify_build2 (bsi, BIT_AND_EXPR, word_type, signs, high_bits);
|
||||
result_low = gimplify_build2 (bsi, MINUS_EXPR, word_type, high_bits, b_low);
|
||||
return gimplify_build2 (bsi, BIT_XOR_EXPR, word_type, result_low, signs);
|
||||
}
|
||||
|
||||
/* Expand a vector operation to scalars, by using many operations
|
||||
whose type is the vector type's inner type. */
|
||||
static tree
|
||||
expand_vector_piecewise (block_stmt_iterator *bsi, elem_op_func f,
|
||||
tree type, tree inner_type,
|
||||
tree a, tree b, enum tree_code code)
|
||||
{
|
||||
tree head, *chain = &head;
|
||||
tree part_width = TYPE_SIZE (inner_type);
|
||||
tree index = bitsize_int (0);
|
||||
int nunits = TYPE_VECTOR_SUBPARTS (type);
|
||||
int delta = tree_low_cst (part_width, 1)
|
||||
/ tree_low_cst (TYPE_SIZE (TREE_TYPE (type)), 1);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nunits;
|
||||
i += delta, index = int_const_binop (PLUS_EXPR, index, part_width, 0))
|
||||
{
|
||||
tree result = f (bsi, inner_type, a, b, index, part_width, code);
|
||||
*chain = tree_cons (NULL_TREE, result, NULL_TREE);
|
||||
chain = &TREE_CHAIN (*chain);
|
||||
}
|
||||
|
||||
return build1 (CONSTRUCTOR, type, head);
|
||||
}
|
||||
|
||||
/* Expand a vector operation to scalars with the freedom to use
|
||||
a scalar integer type, or to use a different size for the items
|
||||
in the vector type. */
|
||||
static tree
|
||||
expand_vector_parallel (block_stmt_iterator *bsi, elem_op_func f, tree type,
|
||||
tree a, tree b,
|
||||
enum tree_code code)
|
||||
{
|
||||
tree result, compute_type;
|
||||
enum machine_mode mode;
|
||||
int n_words = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
|
||||
|
||||
/* We have three strategies. If the type is already correct, just do
|
||||
the operation an element at a time. Else, if the vector is wider than
|
||||
one word, do it a word at a time; finally, if the vector is smaller
|
||||
than one word, do it as a scalar. */
|
||||
if (TYPE_MODE (TREE_TYPE (type)) == word_mode)
|
||||
return expand_vector_piecewise (bsi, f,
|
||||
type, TREE_TYPE (type),
|
||||
a, b, code);
|
||||
else if (n_words > 1)
|
||||
{
|
||||
tree word_type = build_word_mode_vector_type (n_words);
|
||||
result = expand_vector_piecewise (bsi, f,
|
||||
word_type, TREE_TYPE (word_type),
|
||||
a, b, code);
|
||||
result = gimplify_val (bsi, word_type, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use a single scalar operation with a mode no wider than word_mode. */
|
||||
mode = mode_for_size (tree_low_cst (TYPE_SIZE (type), 1), MODE_INT, 0);
|
||||
compute_type = lang_hooks.types.type_for_mode (mode, 1);
|
||||
result = f (bsi, compute_type, a, b, NULL_TREE, NULL_TREE, code);
|
||||
}
|
||||
|
||||
return build1 (VIEW_CONVERT_EXPR, type, result);
|
||||
}
|
||||
|
||||
/* Expand a vector operation to scalars; for integer types we can use
|
||||
special bit twiddling tricks to do the sums a word at a time, using
|
||||
function F_PARALLEL instead of F. These tricks are done only if
|
||||
they can process at least four items, that is, only if the vector
|
||||
holds at least four items and if a word can hold four items. */
|
||||
static tree
|
||||
expand_vector_addition (block_stmt_iterator *bsi,
|
||||
elem_op_func f, elem_op_func f_parallel,
|
||||
tree type, tree a, tree b, enum tree_code code)
|
||||
{
|
||||
int parts_per_word = UNITS_PER_WORD
|
||||
/ tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (type)), 1);
|
||||
|
||||
if (INTEGRAL_TYPE_P (TREE_TYPE (type))
|
||||
&& parts_per_word >= 4
|
||||
&& TYPE_VECTOR_SUBPARTS (type) >= 4)
|
||||
return expand_vector_parallel (bsi, f_parallel,
|
||||
type, a, b, code);
|
||||
else
|
||||
return expand_vector_piecewise (bsi, f,
|
||||
type, TREE_TYPE (type),
|
||||
a, b, code);
|
||||
}
|
||||
|
||||
/* Return a type for the widest vector mode whose components are of mode
|
||||
INNER_MODE, or NULL_TREE if none is found. */
|
||||
static tree
|
||||
type_for_widest_vector_mode (enum machine_mode inner_mode, optab op)
|
||||
{
|
||||
enum machine_mode best_mode = VOIDmode, mode;
|
||||
int best_nunits = 0;
|
||||
|
||||
if (GET_MODE_CLASS (inner_mode) == MODE_FLOAT)
|
||||
mode = MIN_MODE_VECTOR_FLOAT;
|
||||
else
|
||||
mode = MIN_MODE_VECTOR_INT;
|
||||
|
||||
for (; mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (GET_MODE_INNER (mode) == inner_mode
|
||||
&& GET_MODE_NUNITS (mode) > best_nunits
|
||||
&& op->handlers[mode].insn_code != CODE_FOR_nothing)
|
||||
best_mode = mode, best_nunits = GET_MODE_NUNITS (mode);
|
||||
|
||||
if (best_mode == VOIDmode)
|
||||
return NULL_TREE;
|
||||
else
|
||||
return lang_hooks.types.type_for_mode (best_mode, 1);
|
||||
}
|
||||
|
||||
/* Process one statement. If we identify a vector operation, expand it. */
|
||||
|
||||
static void
|
||||
expand_complex_operations (void)
|
||||
expand_vector_operations_1 (block_stmt_iterator *bsi)
|
||||
{
|
||||
tree stmt = bsi_stmt (*bsi);
|
||||
tree *p_rhs, rhs, type, compute_type;
|
||||
enum tree_code code;
|
||||
enum machine_mode compute_mode;
|
||||
optab op;
|
||||
|
||||
switch (TREE_CODE (stmt))
|
||||
{
|
||||
case RETURN_EXPR:
|
||||
stmt = TREE_OPERAND (stmt, 0);
|
||||
if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR)
|
||||
return;
|
||||
|
||||
/* FALLTHRU */
|
||||
|
||||
case MODIFY_EXPR:
|
||||
p_rhs = &TREE_OPERAND (stmt, 1);
|
||||
rhs = *p_rhs;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
type = TREE_TYPE (rhs);
|
||||
if (TREE_CODE (type) != VECTOR_TYPE)
|
||||
return;
|
||||
|
||||
code = TREE_CODE (rhs);
|
||||
if (TREE_CODE_CLASS (code) != '1'
|
||||
&& TREE_CODE_CLASS (code) != '2')
|
||||
return;
|
||||
|
||||
if (code == NOP_EXPR || code == VIEW_CONVERT_EXPR)
|
||||
return;
|
||||
|
||||
if (code == CONVERT_EXPR)
|
||||
abort ();
|
||||
|
||||
op = optab_for_tree_code (code, type);
|
||||
|
||||
/* Optabs will try converting a negation into a subtraction, so
|
||||
look for it as well. TODO: negation of floating-point vectors
|
||||
might be turned into an exclusive OR toggling the sign bit. */
|
||||
if (op == NULL
|
||||
&& code == NEGATE_EXPR
|
||||
&& INTEGRAL_TYPE_P (TREE_TYPE (type)))
|
||||
op = optab_for_tree_code (MINUS_EXPR, type);
|
||||
|
||||
/* For very wide vectors, try using a smaller vector mode. */
|
||||
compute_type = type;
|
||||
if (TYPE_MODE (type) == BLKmode && op)
|
||||
{
|
||||
tree vector_compute_type
|
||||
= type_for_widest_vector_mode (TYPE_MODE (TREE_TYPE (type)), op);
|
||||
if (vector_compute_type != NULL_TREE)
|
||||
compute_type = vector_compute_type;
|
||||
}
|
||||
|
||||
compute_mode = TYPE_MODE (compute_type);
|
||||
|
||||
/* If we are breaking a BLKmode vector into smaller pieces,
|
||||
type_for_widest_vector_mode has already looked into the optab,
|
||||
so skip these checks. */
|
||||
if (compute_type == type)
|
||||
{
|
||||
if ((GET_MODE_CLASS (compute_mode) == MODE_VECTOR_INT
|
||||
|| GET_MODE_CLASS (compute_mode) == MODE_VECTOR_FLOAT)
|
||||
&& op != NULL
|
||||
&& op->handlers[compute_mode].insn_code != CODE_FOR_nothing)
|
||||
return;
|
||||
else
|
||||
{
|
||||
/* There is no operation in hardware, so fall back to scalars. */
|
||||
compute_type = TREE_TYPE (type);
|
||||
compute_mode = TYPE_MODE (compute_type);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the compute mode is not a vector mode (hence we are decomposing
|
||||
a BLKmode vector to smaller, hardware-supported vectors), we may
|
||||
want to expand the operations in parallel. */
|
||||
if (GET_MODE_CLASS (compute_mode) != MODE_VECTOR_INT
|
||||
&& GET_MODE_CLASS (compute_mode) != MODE_VECTOR_FLOAT)
|
||||
switch (code)
|
||||
{
|
||||
case PLUS_EXPR:
|
||||
case MINUS_EXPR:
|
||||
if (TYPE_TRAP_SIGNED (type))
|
||||
break;
|
||||
|
||||
*p_rhs = expand_vector_addition (bsi, do_binop, do_plus_minus, type,
|
||||
TREE_OPERAND (rhs, 0),
|
||||
TREE_OPERAND (rhs, 1), code);
|
||||
modify_stmt (bsi_stmt (*bsi));
|
||||
return;
|
||||
|
||||
case NEGATE_EXPR:
|
||||
if (TYPE_TRAP_SIGNED (type))
|
||||
break;
|
||||
|
||||
*p_rhs = expand_vector_addition (bsi, do_unop, do_negate, type,
|
||||
TREE_OPERAND (rhs, 0),
|
||||
NULL_TREE, code);
|
||||
modify_stmt (bsi_stmt (*bsi));
|
||||
return;
|
||||
|
||||
case BIT_AND_EXPR:
|
||||
case BIT_IOR_EXPR:
|
||||
case BIT_XOR_EXPR:
|
||||
*p_rhs = expand_vector_parallel (bsi, do_binop, type,
|
||||
TREE_OPERAND (rhs, 0),
|
||||
TREE_OPERAND (rhs, 1), code);
|
||||
modify_stmt (bsi_stmt (*bsi));
|
||||
return;
|
||||
|
||||
case BIT_NOT_EXPR:
|
||||
*p_rhs = expand_vector_parallel (bsi, do_unop, type,
|
||||
TREE_OPERAND (rhs, 0),
|
||||
NULL_TREE, code);
|
||||
modify_stmt (bsi_stmt (*bsi));
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (TREE_CODE_CLASS (code) == '1')
|
||||
*p_rhs = expand_vector_piecewise (bsi, do_unop, type, compute_type,
|
||||
TREE_OPERAND (rhs, 0),
|
||||
NULL_TREE, code);
|
||||
else
|
||||
*p_rhs = expand_vector_piecewise (bsi, do_binop, type, compute_type,
|
||||
TREE_OPERAND (rhs, 0),
|
||||
TREE_OPERAND (rhs, 1), code);
|
||||
|
||||
modify_stmt (bsi_stmt (*bsi));
|
||||
}
|
||||
|
||||
static void
|
||||
expand_vector_operations (void)
|
||||
{
|
||||
block_stmt_iterator bsi;
|
||||
basic_block bb;
|
||||
|
||||
FOR_EACH_BB (bb)
|
||||
{
|
||||
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
|
||||
expand_vector_operations_1 (&bsi);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tree_lower_operations (void)
|
||||
{
|
||||
int old_last_basic_block = last_basic_block;
|
||||
block_stmt_iterator bsi;
|
||||
|
@ -538,15 +912,19 @@ expand_complex_operations (void)
|
|||
if (bb->index >= old_last_basic_block)
|
||||
continue;
|
||||
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
|
||||
expand_complex_operations_1 (&bsi);
|
||||
{
|
||||
expand_complex_operations_1 (&bsi);
|
||||
expand_vector_operations_1 (&bsi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct tree_opt_pass pass_lower_complex =
|
||||
|
||||
struct tree_opt_pass pass_lower_vector_ssa =
|
||||
{
|
||||
"complex", /* name */
|
||||
"vector", /* name */
|
||||
NULL, /* gate */
|
||||
expand_complex_operations, /* execute */
|
||||
expand_vector_operations, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
0, /* static_pass_number */
|
||||
|
@ -555,7 +933,24 @@ struct tree_opt_pass pass_lower_complex =
|
|||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
TODO_dump_func | TODO_rename_vars
|
||||
TODO_dump_func | TODO_rename_vars /* todo_flags_finish */
|
||||
| TODO_ggc_collect | TODO_verify_ssa
|
||||
| TODO_verify_stmts | TODO_verify_flow /* todo_flags_finish */
|
||||
| TODO_verify_stmts | TODO_verify_flow
|
||||
};
|
||||
|
||||
struct tree_opt_pass pass_pre_expand =
|
||||
{
|
||||
"oplower", /* name */
|
||||
0, /* gate */
|
||||
tree_lower_operations, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
0, /* static_pass_number */
|
||||
0, /* tv_id */
|
||||
PROP_cfg, /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
TODO_dump_func | TODO_ggc_collect
|
||||
| TODO_verify_stmts /* todo_flags_finish */
|
||||
};
|
||||
|
|
|
@ -496,6 +496,13 @@ extern tree tree_block_label (basic_block bb);
|
|||
extern void extract_true_false_edges_from_block (basic_block, edge *, edge *);
|
||||
extern bool tree_purge_dead_eh_edges (basic_block);
|
||||
extern bool tree_purge_all_dead_eh_edges (bitmap);
|
||||
extern tree gimplify_val (block_stmt_iterator *, tree, tree);
|
||||
extern tree gimplify_build1 (block_stmt_iterator *, enum tree_code,
|
||||
tree, tree);
|
||||
extern tree gimplify_build2 (block_stmt_iterator *, enum tree_code,
|
||||
tree, tree, tree);
|
||||
extern tree gimplify_build3 (block_stmt_iterator *, enum tree_code,
|
||||
tree, tree, tree, tree);
|
||||
|
||||
/* In tree-pretty-print.c. */
|
||||
extern void dump_generic_bb (FILE *, basic_block, int, int);
|
||||
|
|
|
@ -373,7 +373,7 @@ init_tmp_var (struct nesting_info *info, tree exp, tree_stmt_iterator *tsi)
|
|||
/* Similarly, but only do so to force EXP to satisfy is_gimple_val. */
|
||||
|
||||
static tree
|
||||
gimplify_val (struct nesting_info *info, tree exp, tree_stmt_iterator *tsi)
|
||||
tsi_gimplify_val (struct nesting_info *info, tree exp, tree_stmt_iterator *tsi)
|
||||
{
|
||||
if (is_gimple_val (exp))
|
||||
return exp;
|
||||
|
@ -790,7 +790,7 @@ convert_nonlocal_reference (tree *tp, int *walk_subtrees, void *data)
|
|||
where we only accept variables (and min_invariant, presumably),
|
||||
then compute the address into a temporary. */
|
||||
if (save_val_only)
|
||||
*tp = gimplify_val (wi->info, t, &wi->tsi);
|
||||
*tp = tsi_gimplify_val (wi->info, t, &wi->tsi);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -904,7 +904,7 @@ convert_local_reference (tree *tp, int *walk_subtrees, void *data)
|
|||
/* If we are in a context where we only accept values, then
|
||||
compute the address into a temporary. */
|
||||
if (save_val_only)
|
||||
*tp = gimplify_val (wi->info, t, &wi->tsi);
|
||||
*tp = tsi_gimplify_val (wi->info, t, &wi->tsi);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1041,7 +1041,7 @@ convert_nl_goto_reference (tree *tp, int *walk_subtrees, void *data)
|
|||
field = get_nl_goto_field (i);
|
||||
x = get_frame_field (info, target_context, field, &wi->tsi);
|
||||
x = build_addr (x);
|
||||
x = gimplify_val (info, x, &wi->tsi);
|
||||
x = tsi_gimplify_val (info, x, &wi->tsi);
|
||||
arg = tree_cons (NULL, x, NULL);
|
||||
x = build_addr (new_label);
|
||||
arg = tree_cons (NULL, x, arg);
|
||||
|
@ -1139,7 +1139,7 @@ convert_tramp_reference (tree *tp, int *walk_subtrees, void *data)
|
|||
/* Compute the address of the field holding the trampoline. */
|
||||
x = get_frame_field (info, target_context, x, &wi->tsi);
|
||||
x = build_addr (x);
|
||||
x = gimplify_val (info, x, &wi->tsi);
|
||||
x = tsi_gimplify_val (info, x, &wi->tsi);
|
||||
arg = tree_cons (NULL, x, NULL);
|
||||
|
||||
/* Do machine-specific ugliness. Normally this will involve
|
||||
|
|
|
@ -299,6 +299,7 @@ init_tree_optimization_passes (void)
|
|||
NEXT_PASS (pass_lower_cf);
|
||||
NEXT_PASS (pass_lower_eh);
|
||||
NEXT_PASS (pass_build_cfg);
|
||||
NEXT_PASS (pass_pre_expand);
|
||||
NEXT_PASS (pass_tree_profile);
|
||||
NEXT_PASS (pass_init_datastructures);
|
||||
NEXT_PASS (pass_all_optimizations);
|
||||
|
@ -325,7 +326,6 @@ init_tree_optimization_passes (void)
|
|||
NEXT_PASS (pass_tail_recursion);
|
||||
NEXT_PASS (pass_ch);
|
||||
NEXT_PASS (pass_profile);
|
||||
NEXT_PASS (pass_lower_complex);
|
||||
NEXT_PASS (pass_sra);
|
||||
NEXT_PASS (DUP_PASS (pass_rename_ssa_copies));
|
||||
NEXT_PASS (DUP_PASS (pass_dominator));
|
||||
|
|
|
@ -120,7 +120,8 @@ extern struct tree_opt_pass pass_may_alias;
|
|||
extern struct tree_opt_pass pass_split_crit_edges;
|
||||
extern struct tree_opt_pass pass_pre;
|
||||
extern struct tree_opt_pass pass_profile;
|
||||
extern struct tree_opt_pass pass_lower_complex;
|
||||
extern struct tree_opt_pass pass_pre_expand;
|
||||
extern struct tree_opt_pass pass_lower_vector_ssa;
|
||||
extern struct tree_opt_pass pass_fold_builtins;
|
||||
extern struct tree_opt_pass pass_early_warn_uninitialized;
|
||||
extern struct tree_opt_pass pass_late_warn_uninitialized;
|
||||
|
|
68
gcc/tree.c
68
gcc/tree.c
|
@ -111,7 +111,7 @@ static void set_type_quals (tree, int);
|
|||
static int type_hash_eq (const void *, const void *);
|
||||
static hashval_t type_hash_hash (const void *);
|
||||
static void print_type_hash_statistics (void);
|
||||
static void finish_vector_type (tree);
|
||||
static tree make_vector_type (tree, int, enum machine_mode);
|
||||
static int type_hash_marked_p (const void *);
|
||||
static unsigned int type_hash_list (tree, hashval_t);
|
||||
static unsigned int attribute_hash_list (tree, hashval_t);
|
||||
|
@ -5279,18 +5279,23 @@ tree_operand_check_failed (int idx, enum tree_code code, const char *file,
|
|||
}
|
||||
#endif /* ENABLE_TREE_CHECKING */
|
||||
|
||||
/* For a new vector type node T, build the information necessary for
|
||||
debugging output. */
|
||||
/* Create a new vector type node holding SUBPARTS units of type INNERTYPE,
|
||||
and mapped to the machine mode MODE. Initialize its fields and build
|
||||
the information necessary for debugging output. */
|
||||
|
||||
static void
|
||||
finish_vector_type (tree t)
|
||||
static tree
|
||||
make_vector_type (tree innertype, int nunits, enum machine_mode mode)
|
||||
{
|
||||
tree t = make_node (VECTOR_TYPE);
|
||||
|
||||
TREE_TYPE (t) = innertype;
|
||||
TYPE_VECTOR_SUBPARTS (t) = nunits;
|
||||
TYPE_MODE (t) = mode;
|
||||
layout_type (t);
|
||||
|
||||
{
|
||||
tree index = build_int_2 (TYPE_VECTOR_SUBPARTS (t) - 1, 0);
|
||||
tree array = build_array_type (TREE_TYPE (t),
|
||||
build_index_type (index));
|
||||
tree index = build_int_2 (nunits - 1, 0);
|
||||
tree array = build_array_type (innertype, build_index_type (index));
|
||||
tree rt = make_node (RECORD_TYPE);
|
||||
|
||||
TYPE_FIELDS (rt) = build_decl (FIELD_DECL, get_identifier ("f"), array);
|
||||
|
@ -5303,6 +5308,8 @@ finish_vector_type (tree t)
|
|||
numbers equal. */
|
||||
TYPE_UID (rt) = TYPE_UID (t);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static tree
|
||||
|
@ -5521,36 +5528,39 @@ reconstruct_complex_type (tree type, tree bottom)
|
|||
return outer;
|
||||
}
|
||||
|
||||
/* Returns a vector tree node given a vector mode and inner type. */
|
||||
/* Returns a vector tree node given a mode (integer, vector, or BLKmode) and
|
||||
the inner type. */
|
||||
tree
|
||||
build_vector_type_for_mode (tree innertype, enum machine_mode mode)
|
||||
{
|
||||
tree t;
|
||||
t = make_node (VECTOR_TYPE);
|
||||
TREE_TYPE (t) = innertype;
|
||||
TYPE_MODE (t) = mode;
|
||||
finish_vector_type (t);
|
||||
return t;
|
||||
int nunits;
|
||||
|
||||
if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
|
||||
|| GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
|
||||
nunits = GET_MODE_NUNITS (mode);
|
||||
|
||||
else if (GET_MODE_CLASS (mode) == MODE_INT)
|
||||
{
|
||||
/* Check that there are no leftover bits. */
|
||||
if (GET_MODE_BITSIZE (mode) % TREE_INT_CST_LOW (TYPE_SIZE (innertype)))
|
||||
abort ();
|
||||
|
||||
nunits = GET_MODE_BITSIZE (mode)
|
||||
/ TREE_INT_CST_LOW (TYPE_SIZE (innertype));
|
||||
}
|
||||
else
|
||||
abort ();
|
||||
|
||||
return make_vector_type (innertype, nunits, mode);
|
||||
}
|
||||
|
||||
/* Similarly, but takes inner type and units. */
|
||||
/* Similarly, but takes the inner type and number of units, which must be
|
||||
a power of two. */
|
||||
|
||||
tree
|
||||
build_vector_type (tree innertype, int nunits)
|
||||
{
|
||||
enum machine_mode innermode = TYPE_MODE (innertype);
|
||||
enum machine_mode mode;
|
||||
|
||||
if (GET_MODE_CLASS (innermode) == MODE_FLOAT)
|
||||
mode = MIN_MODE_VECTOR_FLOAT;
|
||||
else
|
||||
mode = MIN_MODE_VECTOR_INT;
|
||||
|
||||
for (; mode != VOIDmode ; mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (GET_MODE_NUNITS (mode) == nunits && GET_MODE_INNER (mode) == innermode)
|
||||
return build_vector_type_for_mode (innertype, mode);
|
||||
|
||||
return NULL_TREE;
|
||||
return make_vector_type (innertype, nunits, VOIDmode);
|
||||
}
|
||||
|
||||
/* Given an initializer INIT, return TRUE if INIT is zero or some
|
||||
|
|
|
@ -151,7 +151,8 @@ DEFTREECODE (REAL_TYPE, "real_type", 't', 0)
|
|||
DEFTREECODE (COMPLEX_TYPE, "complex_type", 't', 0)
|
||||
|
||||
/* Vector types. The TREE_TYPE field is the data type of the vector
|
||||
elements. */
|
||||
elements. The TYPE_PRECISION field is the number of subparts of
|
||||
the vector. */
|
||||
DEFTREECODE (VECTOR_TYPE, "vector_type", 't', 0)
|
||||
|
||||
/* C enums. The type node looks just like an INTEGER_TYPE node.
|
||||
|
|
|
@ -1516,7 +1516,7 @@ struct tree_block GTY(())
|
|||
|
||||
/* For a VECTOR_TYPE, this is the number of sub-parts of the vector. */
|
||||
#define TYPE_VECTOR_SUBPARTS(VECTOR_TYPE) \
|
||||
GET_MODE_NUNITS (VECTOR_TYPE_CHECK (VECTOR_TYPE)->type.mode)
|
||||
(VECTOR_TYPE_CHECK (VECTOR_TYPE)->type.precision)
|
||||
|
||||
/* Indicates that objects of this type must be initialized by calling a
|
||||
function when they are created. */
|
||||
|
@ -3480,7 +3480,6 @@ extern void expand_function_start (tree);
|
|||
extern void expand_pending_sizes (tree);
|
||||
extern void recompute_tree_invarant_for_addr_expr (tree);
|
||||
extern bool needs_to_live_in_memory (tree);
|
||||
extern tree make_vector (enum machine_mode, tree, int);
|
||||
extern tree reconstruct_complex_type (tree, tree);
|
||||
|
||||
extern int real_onep (tree);
|
||||
|
|
Loading…
Reference in New Issue