Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in (revision 272586)
+++ gcc/Makefile.in (working copy)
@@ -1315,20 +1315,21 @@ OBJS = \
gimple.o \
gimple-builder.o \
gimple-expr.o \
gimple-iterator.o \
gimple-fold.o \
gimple-laddress.o \
gimple-loop-interchange.o \
gimple-loop-jam.o \
gimple-loop-versioning.o \
gimple-low.o \
+ gimple-lower-fenv.o \
gimple-pretty-print.o \
gimple-ssa-backprop.o \
gimple-ssa-evrp.o \
gimple-ssa-evrp-analyze.o \
gimple-ssa-isolate-paths.o \
gimple-ssa-nonnull-compare.o \
gimple-ssa-split-paths.o \
gimple-ssa-store-merging.o \
gimple-ssa-strength-reduction.o \
gimple-ssa-sprintf.o \
Index: gcc/cp/typeck.c
===================================================================
--- gcc/cp/typeck.c (revision 272586)
+++ gcc/cp/typeck.c (working copy)
@@ -5544,20 +5544,47 @@ cp_build_binary_op (const op_location_t
if (TREE_TYPE (cop0) != orig_type)
cop0 = cp_convert (orig_type, op0, complain);
if (TREE_TYPE (cop1) != orig_type)
cop1 = cp_convert (orig_type, op1, complain);
instrument_expr = ubsan_instrument_division (location, cop0, cop1);
}
else if (doing_shift && sanitize_flags_p (SANITIZE_SHIFT))
instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
}
+ // FIXME: vectors (and complex?) as well
+ if (flag_rounding_math && SCALAR_FLOAT_TYPE_P (build_type))
+ {
+ bool do_fenv_subst = true;
+ internal_fn ifn;
+ switch (resultcode)
+ {
+ case PLUS_EXPR:
+ ifn = IFN_FENV_PLUS;
+ break;
+ case MINUS_EXPR:
+ ifn = IFN_FENV_MINUS;
+ break;
+ case MULT_EXPR:
+ ifn = IFN_FENV_MULT;
+ break;
+ case RDIV_EXPR:
+ ifn = IFN_FENV_DIV;
+ break;
+ default:
+ do_fenv_subst = false;
+ }
+ if (do_fenv_subst)
+ return build_call_expr_internal_loc (location, ifn, build_type,
+ 2, op0, op1);
+ }
+
result = build2_loc (location, resultcode, build_type, op0, op1);
if (final_type != 0)
result = cp_convert (final_type, result, complain);
if (instrument_expr != NULL)
result = build2 (COMPOUND_EXPR, TREE_TYPE (result),
instrument_expr, result);
if (!processing_template_decl)
{
Index: gcc/gimple-lower-fenv.cc
===================================================================
--- gcc/gimple-lower-fenv.cc (nonexistent)
+++ gcc/gimple-lower-fenv.cc (working copy)
@@ -0,0 +1,144 @@
+/* Lower correctly rounded operations.
+ Copyright (C) 2019 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 3, 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 COPYING3. If not see
+. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "gimple-iterator.h"
+
+/* Create a pass-through inline asm barrier from IN to OUT. */
+static gasm*
+asm_barrier (tree out, tree in)
+{
+ vec *inputs = NULL, *outputs = NULL;
+ if (out)
+ {
+ vec_safe_push (inputs,
+ build_tree_list (build_tree_list
+ (NULL_TREE, build_string (2, "0")), in));
+ vec_safe_push (outputs,
+ build_tree_list (build_tree_list
+ (NULL_TREE, build_string (3, "=g")),
+ out));
+ }
+ else
+ {
+ vec_safe_push (inputs,
+ build_tree_list (build_tree_list
+ (NULL_TREE, build_string (2, "g")), in));
+ }
+ gasm *g = gimple_build_asm_vec ("", inputs, outputs, NULL, NULL);
+ gimple_asm_set_volatile (g, true);
+ if (out)
+ SSA_NAME_DEF_STMT (out) = g;
+ return g;
+}
+
+/* A simple pass that attempts to fold all fenv internal functions. */
+
+namespace {
+
+const pass_data pass_data_lower_fenv =
+{
+ GIMPLE_PASS, /* type */
+ "lfenv", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_lower_fenv : public gimple_opt_pass
+{
+public:
+ pass_lower_fenv (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_lower_fenv, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual unsigned int execute (function *);
+}; // class pass_lower_fenv
+
+unsigned int
+pass_lower_fenv::execute (function *fun)
+{
+ basic_block bb;
+ FOR_EACH_BB_FN (bb, fun)
+ {
+ gimple_stmt_iterator i;
+ for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
+ {
+ gimple *stmt = gsi_stmt (i);
+ if (gimple_code (stmt) != GIMPLE_CALL
+ || !gimple_call_internal_p (stmt))
+ continue;
+
+ tree_code code;
+ switch (gimple_call_internal_fn (stmt))
+ {
+ case IFN_FENV_PLUS:
+ code = PLUS_EXPR;
+ break;
+ case IFN_FENV_MINUS:
+ code = MINUS_EXPR;
+ break;
+ case IFN_FENV_MULT:
+ code = MULT_EXPR;
+ break;
+ case IFN_FENV_DIV:
+ code = RDIV_EXPR;
+ break;
+ default:
+ continue;
+ }
+
+ tree op0 = gimple_call_arg (stmt, 0);
+ tree op1 = gimple_call_arg (stmt, 1);
+ tree ftype = TREE_TYPE (op0);
+ tree newop0 = make_ssa_name (ftype);
+ tree newop1 = make_ssa_name (ftype);
+ gsi_insert_before (&i, asm_barrier (newop0, op0), GSI_SAME_STMT);
+ gsi_insert_before (&i, asm_barrier (newop1, op1), GSI_SAME_STMT);
+
+ tree lhs = gimple_call_lhs (stmt);
+ tree newlhs = make_ssa_name (ftype);
+ gimple *new_stmt = gimple_build_assign (newlhs, code, newop0, newop1);
+ gsi_insert_before (&i, new_stmt, GSI_SAME_STMT);
+ gsi_replace (&i, asm_barrier (lhs, newlhs), false);
+ unlink_stmt_vdef (stmt);
+ release_ssa_name (gimple_vdef (stmt));
+ }
+ }
+ return 0;
+}
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_lower_fenv (gcc::context *ctxt)
+{
+ return new pass_lower_fenv (ctxt);
+}
Index: gcc/internal-fn.c
===================================================================
--- gcc/internal-fn.c (revision 272586)
+++ gcc/internal-fn.c (working copy)
@@ -2869,20 +2869,46 @@ expand_DIVMOD (internal_fn, gcall *call_
}
/* Expand a NOP. */
static void
expand_NOP (internal_fn, gcall *)
{
/* Nothing. But it shouldn't really prevail. */
}
+/* This should get expanded in the wmul pass. */
+
+static void
+expand_FENV_PLUS (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+static void
+expand_FENV_MINUS (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+static void
+expand_FENV_MULT (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+static void
+expand_FENV_DIV (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
/* Expand a call to FN using the operands in STMT. FN has a single
output operand and NARGS input operands. */
static void
expand_direct_optab_fn (internal_fn fn, gcall *stmt, direct_optab optab,
unsigned int nargs)
{
expand_operand *ops = XALLOCAVEC (expand_operand, nargs + 1);
tree_pair types = direct_internal_fn_types (fn, stmt);
Index: gcc/internal-fn.def
===================================================================
--- gcc/internal-fn.def (revision 272586)
+++ gcc/internal-fn.def (working copy)
@@ -345,16 +345,22 @@ DEF_INTERNAL_FN (FALLTHROUGH, ECF_LEAF |
/* To implement __builtin_launder. */
DEF_INTERNAL_FN (LAUNDER, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
/* Divmod function. */
DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
/* A NOP function with arbitrary arguments and return value. */
DEF_INTERNAL_FN (NOP, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+/* float operations with rounding / exception flags. */
+DEF_INTERNAL_FN (FENV_PLUS, ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (FENV_MINUS, ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (FENV_MULT, ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (FENV_DIV, ECF_LEAF | ECF_NOTHROW, NULL)
+
#undef DEF_INTERNAL_INT_FN
#undef DEF_INTERNAL_FLT_FN
#undef DEF_INTERNAL_FLT_FLOATN_FN
#undef DEF_INTERNAL_SIGNED_OPTAB_FN
#undef DEF_INTERNAL_OPTAB_FN
#undef DEF_INTERNAL_FN
Index: gcc/passes.def
===================================================================
--- gcc/passes.def (revision 272586)
+++ gcc/passes.def (working copy)
@@ -377,20 +377,21 @@ along with GCC; see the file COPYING3.
PUSH_INSERT_PASSES_WITHIN (pass_tm_init)
NEXT_PASS (pass_tm_mark);
NEXT_PASS (pass_tm_memopt);
NEXT_PASS (pass_tm_edges);
POP_INSERT_PASSES ()
NEXT_PASS (pass_simduid_cleanup);
NEXT_PASS (pass_vtable_verify);
NEXT_PASS (pass_lower_vaarg);
NEXT_PASS (pass_lower_vector);
NEXT_PASS (pass_lower_complex_O0);
+ NEXT_PASS (pass_lower_fenv);
NEXT_PASS (pass_sancov_O0);
NEXT_PASS (pass_lower_switch_O0);
NEXT_PASS (pass_asan_O0);
NEXT_PASS (pass_tsan_O0);
NEXT_PASS (pass_sanopt);
NEXT_PASS (pass_cleanup_eh);
NEXT_PASS (pass_lower_resx);
NEXT_PASS (pass_nrv);
NEXT_PASS (pass_cleanup_cfg_post_optimizing);
NEXT_PASS (pass_warn_function_noreturn);
Index: gcc/tree-pass.h
===================================================================
--- gcc/tree-pass.h (revision 272586)
+++ gcc/tree-pass.h (working copy)
@@ -617,20 +617,21 @@ extern rtl_opt_pass *make_pass_shorten_b
extern rtl_opt_pass *make_pass_set_nothrow_function_flags (gcc::context *ctxt);
extern rtl_opt_pass *make_pass_dwarf2_frame (gcc::context *ctxt);
extern rtl_opt_pass *make_pass_final (gcc::context *ctxt);
extern rtl_opt_pass *make_pass_rtl_seqabstr (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_release_ssa_names (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_early_inline (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_local_fn_summary (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_update_address_taken (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_convert_switch (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_lower_vaarg (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_lower_fenv (gcc::context *ctxt);
/* Current optimization pass. */
extern opt_pass *current_pass;
extern bool execute_one_pass (opt_pass *);
extern void execute_pass_list (function *, opt_pass *);
extern void execute_ipa_pass_list (opt_pass *);
extern void execute_ipa_summary_passes (ipa_opt_pass_d *);
extern void execute_all_ipa_transforms (void);
extern void execute_all_ipa_stmt_fixups (struct cgraph_node *, gimple **);