diff --git a/gcc/testsuite/gcc.target/i386/pr88240.c b/gcc/testsuite/gcc.target/i386/pr88240.c new file mode 100644 index 00000000000..5ee02f3193c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr88240.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -mno-sse" } */ + +int flag; +union { double f; unsigned long long i; } u; +void __attribute__((noinline)) +init () +{ + flag = 1; + u.i = 18442936822990639076ULL; +} +unsigned long long __attribute__((noinline)) +test () +{ + if (flag) + return u.i; + else + return u.f; +} +int main() +{ + init (); + if (test () != 18442936822990639076ULL) + __builtin_abort (); + return 0; +} diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c index 0c1654f3580..7d67305bf4b 100644 --- a/gcc/tree-ssa-pre.c +++ b/gcc/tree-ssa-pre.c @@ -3571,6 +3571,16 @@ do_hoist_insertion (basic_block block) continue; } + /* If we end up with a punned expression representation and this + happens to be a float typed one give up - we can't know for + sure whether all paths perform the floating-point load we are + about to insert and on some targets this can cause correctness + issues. See PR88240. */ + if (expr->kind == REFERENCE + && PRE_EXPR_REFERENCE (expr)->punned + && FLOAT_TYPE_P (get_expr_type (expr))) + continue; + /* OK, we should hoist this value. Perform the transformation. */ pre_stats.hoist_insert++; if (dump_file && (dump_flags & TDF_DETAILS)) diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c index 2e925a1afbf..934ae40670d 100644 --- a/gcc/tree-ssa-sccvn.c +++ b/gcc/tree-ssa-sccvn.c @@ -3601,6 +3601,7 @@ vn_reference_insert (tree op, tree result, tree vuse, tree vdef) vr1->vuse = vuse_ssa_val (vuse); vr1->operands = valueize_shared_reference_ops_from_ref (op, &tem).copy (); vr1->type = TREE_TYPE (op); + vr1->punned = 0; ao_ref op_ref; ao_ref_init (&op_ref, op); vr1->set = ao_ref_alias_set (&op_ref); @@ -3660,6 +3661,7 @@ vn_reference_insert_pieces (tree vuse, alias_set_type set, vr1->vuse = vuse_ssa_val (vuse); vr1->operands = valueize_refs (operands); vr1->type = type; + vr1->punned = 0; vr1->set = set; vr1->base_set = base_set; vr1->hashcode = vn_reference_compute_hash (vr1); @@ -4892,6 +4894,7 @@ visit_reference_op_call (tree lhs, gcall *stmt) them here. */ vr2->operands = vr1.operands.copy (); vr2->type = vr1.type; + vr2->punned = vr1.punned; vr2->set = vr1.set; vr2->base_set = vr1.base_set; vr2->hashcode = vr1.hashcode; @@ -4918,10 +4921,11 @@ visit_reference_op_load (tree lhs, tree op, gimple *stmt) bool changed = false; tree last_vuse; tree result; + vn_reference_t res; last_vuse = gimple_vuse (stmt); result = vn_reference_lookup (op, gimple_vuse (stmt), - default_vn_walk_kind, NULL, true, &last_vuse); + default_vn_walk_kind, &res, true, &last_vuse); /* We handle type-punning through unions by value-numbering based on offset and size of the access. Be prepared to handle a @@ -4943,6 +4947,13 @@ visit_reference_op_load (tree lhs, tree op, gimple *stmt) gimple_match_op res_op (gimple_match_cond::UNCOND, VIEW_CONVERT_EXPR, TREE_TYPE (op), result); result = vn_nary_build_or_lookup (&res_op); + if (result + && TREE_CODE (result) == SSA_NAME + && VN_INFO (result)->needs_insertion) + /* Track whether this is the canonical expression for different + typed loads. We use that as a stopgap measure for code + hoisting when dealing with floating point loads. */ + res->punned = true; } /* When building the conversion fails avoid inserting the reference diff --git a/gcc/tree-ssa-sccvn.h b/gcc/tree-ssa-sccvn.h index d68e7c0ffa3..48701c32544 100644 --- a/gcc/tree-ssa-sccvn.h +++ b/gcc/tree-ssa-sccvn.h @@ -145,6 +145,7 @@ typedef struct vn_reference_s alias_set_type set; alias_set_type base_set; tree type; + unsigned punned : 1; vec operands; tree result; tree result_vdef;