diff options
Diffstat (limited to 'gcc/c-family/c-gimplify.cc')
-rw-r--r-- | gcc/c-family/c-gimplify.cc | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc index ef5c7d9..17b0610 100644 --- a/gcc/c-family/c-gimplify.cc +++ b/gcc/c-family/c-gimplify.cc @@ -41,6 +41,8 @@ along with GCC; see the file COPYING3. If not see #include "c-ubsan.h" #include "tree-nested.h" #include "context.h" +#include "tree-pass.h" +#include "internal-fn.h" /* The gimplification pass converts the language-dependent trees (ld-trees) emitted by the parser into language-independent trees @@ -686,6 +688,14 @@ c_build_bind_expr (location_t loc, tree block, tree body) return bind; } +/* Helper for c_gimplify_expr: test if target supports fma-like FN. */ + +static bool +fma_supported_p (enum internal_fn fn, tree type) +{ + return direct_internal_fn_supported_p (fn, type, OPTIMIZE_FOR_BOTH); +} + /* Gimplification of expression trees. */ /* Do C-specific gimplification on *EXPR_P. PRE_P and POST_P are as in @@ -739,6 +749,75 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED, break; } + case PLUS_EXPR: + case MINUS_EXPR: + { + tree type = TREE_TYPE (*expr_p); + /* For -ffp-contract=on we need to attempt FMA contraction only + during initial gimplification. Late contraction across statement + boundaries would violate language semantics. */ + if (SCALAR_FLOAT_TYPE_P (type) + && flag_fp_contract_mode == FP_CONTRACT_ON + && cfun && !(cfun->curr_properties & PROP_gimple_any) + && fma_supported_p (IFN_FMA, type)) + { + bool neg_mul = false, neg_add = code == MINUS_EXPR; + + tree *op0_p = &TREE_OPERAND (*expr_p, 0); + tree *op1_p = &TREE_OPERAND (*expr_p, 1); + + /* Look for ±(x * y) ± z, swapping operands if necessary. */ + if (TREE_CODE (*op0_p) == NEGATE_EXPR + && TREE_CODE (TREE_OPERAND (*op0_p, 0)) == MULT_EXPR) + /* '*EXPR_P' is '-(x * y) ± z'. This is fine. */; + else if (TREE_CODE (*op0_p) != MULT_EXPR) + { + std::swap (op0_p, op1_p); + std::swap (neg_mul, neg_add); + } + if (TREE_CODE (*op0_p) == NEGATE_EXPR) + { + op0_p = &TREE_OPERAND (*op0_p, 0); + neg_mul = !neg_mul; + } + if (TREE_CODE (*op0_p) != MULT_EXPR) + break; + auto_vec<tree, 3> ops (3); + ops.quick_push (TREE_OPERAND (*op0_p, 0)); + ops.quick_push (TREE_OPERAND (*op0_p, 1)); + ops.quick_push (*op1_p); + + enum internal_fn ifn = IFN_FMA; + if (neg_mul) + { + if (fma_supported_p (IFN_FNMA, type)) + ifn = IFN_FNMA; + else + ops[0] = build1 (NEGATE_EXPR, type, ops[0]); + } + if (neg_add) + { + enum internal_fn ifn2 = ifn == IFN_FMA ? IFN_FMS : IFN_FNMS; + if (fma_supported_p (ifn2, type)) + ifn = ifn2; + else + ops[2] = build1 (NEGATE_EXPR, type, ops[2]); + } + /* Avoid gimplify_arg: it emits all side effects into *PRE_P. */ + for (auto &&op : ops) + if (gimplify_expr (&op, pre_p, post_p, is_gimple_val, fb_rvalue) + == GS_ERROR) + return GS_ERROR; + + gcall *call = gimple_build_call_internal_vec (ifn, ops); + gimple_seq_add_stmt_without_update (pre_p, call); + *expr_p = create_tmp_var (type); + gimple_call_set_lhs (call, *expr_p); + return GS_ALL_DONE; + } + break; + } + default:; } |