diff options
Diffstat (limited to 'gcc/tree-complex.c')
-rw-r--r-- | gcc/tree-complex.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/gcc/tree-complex.c b/gcc/tree-complex.c new file mode 100644 index 00000000000..fe963041a05 --- /dev/null +++ b/gcc/tree-complex.c @@ -0,0 +1,572 @@ +/* Lower complex operations to scalar operations. + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "tm.h" +#include "tree-flow.h" +#include "tree-simple.h" +#include "tree-iterator.h" +#include "tree-pass.h" +#include "flags.h" + + +/* Build a temporary. Make sure and register it to be renamed. */ + +static tree +make_temp (tree type) +{ + tree t = create_tmp_var (type, NULL); + add_referenced_tmp_var (t); + bitmap_set_bit (vars_to_rename, var_ann (t)->uid); + return t; +} + +/* 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_temp (type); + 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. */ + +static tree +extract_component (block_stmt_iterator *bsi, tree t, bool imagpart_p) +{ + tree ret, inner_type; + + inner_type = TREE_TYPE (TREE_TYPE (t)); + switch (TREE_CODE (t)) + { + case COMPLEX_CST: + ret = (imagpart_p ? TREE_IMAGPART (t) : TREE_REALPART (t)); + break; + + case COMPLEX_EXPR: + ret = TREE_OPERAND (t, imagpart_p); + break; + + case VAR_DECL: + case PARM_DECL: + ret = build1 ((imagpart_p ? IMAGPART_EXPR : REALPART_EXPR), + inner_type, t); + break; + + default: + abort (); + } + + 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 +update_complex_assignment (block_stmt_iterator *bsi, tree r, tree i) +{ + tree stmt = bsi_stmt (*bsi); + tree type; + + modify_stmt (stmt); + if (TREE_CODE (stmt) == RETURN_EXPR) + stmt = TREE_OPERAND (stmt, 0); + + type = TREE_TYPE (TREE_OPERAND (stmt, 1)); + TREE_OPERAND (stmt, 1) = build (COMPLEX_EXPR, type, r, i); +} + +/* Expand complex addition to scalars: + a + b = (ar + br) + i(ai + bi) + a - b = (ar - br) + i(ai + bi) +*/ + +static void +expand_complex_addition (block_stmt_iterator *bsi, tree inner_type, + tree ar, tree ai, tree br, tree bi, + enum tree_code code) +{ + tree rr, ri; + + rr = do_binop (bsi, code, inner_type, ar, br); + ri = do_binop (bsi, code, inner_type, ai, bi); + + update_complex_assignment (bsi, rr, ri); +} + +/* Expand complex multiplication to scalars: + a * b = (ar*br - ai*bi) + i(ar*bi + br*ai) +*/ + +static void +expand_complex_multiplication (block_stmt_iterator *bsi, tree inner_type, + tree ar, tree ai, tree br, tree bi) +{ + 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); + + /* 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); + + rr = do_binop (bsi, MINUS_EXPR, inner_type, t1, t2); + ri = do_binop (bsi, PLUS_EXPR, inner_type, t3, t4); + + update_complex_assignment (bsi, rr, ri); +} + +/* Expand complex division to scalars, straightforward algorithm. + a / b = ((ar*br + ai*bi)/t) + i((ai*br - ar*bi)/t) + t = br*br + bi*bi +*/ + +static void +expand_complex_div_straight (block_stmt_iterator *bsi, tree inner_type, + tree ar, tree ai, tree br, tree bi, + enum tree_code code) +{ + 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 = 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 = 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); + + update_complex_assignment (bsi, rr, ri); +} + +/* Expand complex division to scalars, modified algorithm to minimize + overflow with wide input ranges. */ + +static void +expand_complex_div_wide (block_stmt_iterator *bsi, tree inner_type, + tree ar, tree ai, tree br, tree bi, + enum tree_code code) +{ + 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); + cond = fold (build (LT_EXPR, boolean_type_node, t1, t2)); + STRIP_NOPS (cond); + + if (TREE_CONSTANT (cond)) + { + if (integer_zerop (cond)) + min = bi, max = br; + else + min = br, max = bi; + } + else + { + basic_block bb_cond, bb_true, bb_false, bb_join; + tree l1, l2, l3; + edge e; + + l1 = create_artificial_label (); + t1 = build (GOTO_EXPR, void_type_node, l1); + l2 = create_artificial_label (); + t2 = build (GOTO_EXPR, void_type_node, l2); + cond = build (COND_EXPR, void_type_node, cond, t1, t2); + bsi_insert_before (bsi, cond, BSI_SAME_STMT); + + min = make_temp (inner_type); + max = make_temp (inner_type); + l3 = create_artificial_label (); + + /* Split the original block, and create the TRUE and FALSE blocks. */ + e = split_block (bsi->bb, cond); + bb_cond = e->src; + bb_join = e->dest; + bb_true = create_empty_bb (bb_cond); + bb_false = create_empty_bb (bb_true); + + /* Wire the blocks together. */ + e->flags = EDGE_TRUE_VALUE; + redirect_edge_succ (e, bb_true); + make_edge (bb_cond, bb_false, EDGE_FALSE_VALUE); + make_edge (bb_true, bb_join, 0); + make_edge (bb_false, bb_join, 0); + + /* Update dominance info. Note that bb_join's data was + updated by split_block. */ + if (dom_computed[CDI_DOMINATORS] >= DOM_CONS_OK) + { + set_immediate_dominator (CDI_DOMINATORS, bb_true, bb_cond); + set_immediate_dominator (CDI_DOMINATORS, bb_false, bb_cond); + } + + /* Compute min and max for TRUE block. */ + *bsi = bsi_start (bb_true); + t1 = build (LABEL_EXPR, void_type_node, l1); + bsi_insert_after (bsi, t1, BSI_NEW_STMT); + t1 = build (MODIFY_EXPR, inner_type, min, br); + bsi_insert_after (bsi, t1, BSI_NEW_STMT); + t1 = build (MODIFY_EXPR, inner_type, max, bi); + bsi_insert_after (bsi, t1, BSI_NEW_STMT); + + /* Compute min and max for FALSE block. */ + *bsi = bsi_start (bb_false); + t1 = build (LABEL_EXPR, void_type_node, l2); + bsi_insert_after (bsi, t1, BSI_NEW_STMT); + t1 = build (MODIFY_EXPR, inner_type, min, bi); + bsi_insert_after (bsi, t1, BSI_NEW_STMT); + t1 = build (MODIFY_EXPR, inner_type, max, br); + bsi_insert_after (bsi, t1, BSI_NEW_STMT); + + /* Insert the join label into the tail of the original block. */ + *bsi = bsi_start (bb_join); + t1 = build (LABEL_EXPR, void_type_node, l3); + bsi_insert_before (bsi, t1, BSI_SAME_STMT); + } + + /* 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); + + /* 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); + + /* 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); + + 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); + + update_complex_assignment (bsi, rr, ri); +} + +/* Expand complex division to scalars. */ + +static void +expand_complex_division (block_stmt_iterator *bsi, tree inner_type, + tree ar, tree ai, tree br, tree bi, + enum tree_code code) +{ + switch (flag_complex_divide_method) + { + case 0: + /* straightforward implementation of complex divide acceptable. */ + expand_complex_div_straight (bsi, inner_type, ar, ai, br, bi, code); + break; + case 1: + /* wide ranges of inputs must work for complex divide. */ + expand_complex_div_wide (bsi, inner_type, ar, ai, br, bi, code); + break; + default: + /* C99-like requirements for complex divide (not yet implemented). */ + abort (); + } +} + +/* Expand complex negation to scalars: + -a = (-ar) + i(-ai) +*/ + +static void +expand_complex_negation (block_stmt_iterator *bsi, tree inner_type, + tree ar, tree ai) +{ + tree rr, ri; + + rr = do_unop (bsi, NEGATE_EXPR, inner_type, ar); + ri = do_unop (bsi, NEGATE_EXPR, inner_type, ai); + + update_complex_assignment (bsi, rr, ri); +} + +/* Expand complex conjugate to scalars: + ~a = (ar) + i(-ai) +*/ + +static void +expand_complex_conjugate (block_stmt_iterator *bsi, tree inner_type, + tree ar, tree ai) +{ + tree ri; + + ri = do_unop (bsi, NEGATE_EXPR, inner_type, ai); + + update_complex_assignment (bsi, ar, ri); +} + +/* Expand complex comparison (EQ or NE only). */ + +static void +expand_complex_comparison (block_stmt_iterator *bsi, tree ar, tree ai, + tree br, tree bi, enum tree_code code) +{ + 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); + + stmt = bsi_stmt (*bsi); + modify_stmt (stmt); + + switch (TREE_CODE (stmt)) + { + case RETURN_EXPR: + stmt = TREE_OPERAND (stmt, 0); + /* FALLTHRU */ + case MODIFY_EXPR: + type = TREE_TYPE (TREE_OPERAND (stmt, 1)); + TREE_OPERAND (stmt, 1) = convert (type, cc); + break; + case COND_EXPR: + TREE_OPERAND (stmt, 0) = cc; + break; + default: + abort (); + } +} + +/* Process one statement. If we identify a complex operation, expand it. */ + +static void +expand_complex_operations_1 (block_stmt_iterator *bsi) +{ + tree stmt = bsi_stmt (*bsi); + tree rhs, type, inner_type; + tree ac, ar, ai, bc, br, bi; + enum tree_code code; + + switch (TREE_CODE (stmt)) + { + case RETURN_EXPR: + stmt = TREE_OPERAND (stmt, 0); + if (!stmt) + return; + if (TREE_CODE (stmt) != MODIFY_EXPR) + return; + /* FALLTHRU */ + + case MODIFY_EXPR: + rhs = TREE_OPERAND (stmt, 1); + break; + + case COND_EXPR: + rhs = TREE_OPERAND (stmt, 0); + break; + + default: + return; + } + + type = TREE_TYPE (rhs); + code = TREE_CODE (rhs); + + /* Initial filter for operations we handle. */ + switch (code) + { + case PLUS_EXPR: + case MINUS_EXPR: + case MULT_EXPR: + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case RDIV_EXPR: + case NEGATE_EXPR: + case CONJ_EXPR: + if (TREE_CODE (type) != COMPLEX_TYPE) + return; + inner_type = TREE_TYPE (type); + break; + + case EQ_EXPR: + case NE_EXPR: + inner_type = TREE_TYPE (TREE_OPERAND (rhs, 1)); + if (TREE_CODE (inner_type) != COMPLEX_TYPE) + return; + break; + + default: + return; + } + + /* Extract the components of the two complex values. Make sure and + handle the common case of the same value used twice specially. */ + ac = TREE_OPERAND (rhs, 0); + ar = extract_component (bsi, ac, 0); + ai = extract_component (bsi, ac, 1); + + if (TREE_CODE_CLASS (code) == '1') + bc = br = bi = NULL; + else + { + bc = TREE_OPERAND (rhs, 1); + if (ac == bc) + br = ar, bi = ai; + else + { + br = extract_component (bsi, bc, 0); + bi = extract_component (bsi, bc, 1); + } + } + + switch (code) + { + case PLUS_EXPR: + case MINUS_EXPR: + expand_complex_addition (bsi, inner_type, ar, ai, br, bi, code); + break; + + case MULT_EXPR: + expand_complex_multiplication (bsi, inner_type, ar, ai, br, bi); + break; + + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case RDIV_EXPR: + expand_complex_division (bsi, inner_type, ar, ai, br, bi, code); + break; + + case NEGATE_EXPR: + expand_complex_negation (bsi, inner_type, ar, ai); + break; + + case CONJ_EXPR: + expand_complex_conjugate (bsi, inner_type, ar, ai); + break; + + case EQ_EXPR: + case NE_EXPR: + expand_complex_comparison (bsi, ar, ai, br, bi, code); + break; + + default: + abort (); + } +} + +/* 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. */ + +static void +expand_complex_operations (void) +{ + int old_last_basic_block = last_basic_block; + block_stmt_iterator bsi; + basic_block bb; + + FOR_EACH_BB (bb) + { + 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); + } +} + +struct tree_opt_pass pass_lower_complex = +{ + "complex", /* name */ + NULL, /* gate */ + expand_complex_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_rename_vars + | TODO_ggc_collect | TODO_verify_ssa + | TODO_verify_stmts | TODO_verify_flow /* todo_flags_finish */ +}; |