Hi! During taskloop development, I have noticed a bug in the GIMPLE_OMP_TASK ompexp - unlike GIMPLE_OMP_PARALLEL, where if the parallel body is noreturn, the GOMP_parallel call never returns either, for tasks it really depends on whether the task is included or not. If the library (the usual case) schedules it to run in some other thread, or the same one when it starts waiting on a barrier, then it will return immediately. Without this patch we weren't representing this possible control flow through a cfg edge (from cfg pass till ompexp), which meant that we in that case assumed the GOMP_task call will not return.
Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk. Will backport after a few days to 5 branch, perhaps older branches too. 2015-05-13 Jakub Jelinek <ja...@redhat.com> PR middle-end/66133 * omp-low.c (expand_omp_taskreg): For GIMPLE_OMP_TASK expansion, make sure it is never noreturn, even when the task body does not return. (lower_omp_taskreg): For GIMPLE_OMP_TASK, emit GIMPLE_OMP_CONTINUE right before GIMPLE_OMP_RETURN. (make_gimple_omp_edges): Accept GIMPLE_OMP_CONTINUE as ->cont for GIMPLE_OMP_TASK. For GIMPLE_OMP_RETURN corresponding to GIMPLE_OMP_TASK add an EDGE_ABNORMAL edge from entry to exit. * testsuite/libgomp.c/pr66133.c: New test. --- gcc/omp-low.c.jj 2015-04-21 18:23:24.000000000 +0200 +++ gcc/omp-low.c 2015-05-13 15:41:03.648446718 +0200 @@ -5377,7 +5377,10 @@ expand_omp_taskreg (struct omp_region *r child_cfun = DECL_STRUCT_FUNCTION (child_fn); entry_bb = region->entry; - exit_bb = region->exit; + if (gimple_code (entry_stmt) == GIMPLE_OMP_TASK) + exit_bb = region->cont; + else + exit_bb = region->exit; bool is_cilk_for = (flag_cilkplus @@ -5436,7 +5439,9 @@ expand_omp_taskreg (struct omp_region *r variable. In which case, we need to keep the assignment. */ if (gimple_omp_taskreg_data_arg (entry_stmt)) { - basic_block entry_succ_bb = single_succ (entry_bb); + basic_block entry_succ_bb + = single_succ_p (entry_bb) ? single_succ (entry_bb) + : FALLTHRU_EDGE (entry_bb)->dest; tree arg, narg; gimple parcopy_stmt = NULL; @@ -5524,14 +5529,28 @@ expand_omp_taskreg (struct omp_region *r e = split_block (entry_bb, stmt); gsi_remove (&gsi, true); entry_bb = e->dest; - single_succ_edge (entry_bb)->flags = EDGE_FALLTHRU; + edge e2 = NULL; + if (gimple_code (entry_stmt) == GIMPLE_OMP_PARALLEL) + single_succ_edge (entry_bb)->flags = EDGE_FALLTHRU; + else + { + e2 = make_edge (e->src, BRANCH_EDGE (entry_bb)->dest, EDGE_ABNORMAL); + gcc_assert (e2->dest == region->exit); + remove_edge (BRANCH_EDGE (entry_bb)); + set_immediate_dominator (CDI_DOMINATORS, e2->dest, e->src); + gsi = gsi_last_bb (region->exit); + gcc_assert (!gsi_end_p (gsi) + && gimple_code (gsi_stmt (gsi)) == GIMPLE_OMP_RETURN); + gsi_remove (&gsi, true); + } - /* Convert GIMPLE_OMP_RETURN into a RETURN_EXPR. */ + /* Convert GIMPLE_OMP_{RETURN,CONTINUE} into a RETURN_EXPR. */ if (exit_bb) { gsi = gsi_last_bb (exit_bb); gcc_assert (!gsi_end_p (gsi) - && gimple_code (gsi_stmt (gsi)) == GIMPLE_OMP_RETURN); + && (gimple_code (gsi_stmt (gsi)) + == (e2 ? GIMPLE_OMP_CONTINUE : GIMPLE_OMP_RETURN))); stmt = gimple_build_return (NULL); gsi_insert_after (&gsi, stmt, GSI_SAME_STMT); gsi_remove (&gsi, true); @@ -5552,6 +5571,14 @@ expand_omp_taskreg (struct omp_region *r new_bb = move_sese_region_to_fn (child_cfun, entry_bb, exit_bb, block); if (exit_bb) single_succ_edge (new_bb)->flags = EDGE_FALLTHRU; + if (e2) + { + basic_block dest_bb = e2->dest; + if (!exit_bb) + make_edge (new_bb, dest_bb, EDGE_FALLTHRU); + remove_edge (e2); + set_immediate_dominator (CDI_DOMINATORS, dest_bb, new_bb); + } /* When the OMP expansion process cannot guarantee an up-to-date loop tree arrange for the child function to fixup loops. */ if (loops_state_satisfies_p (LOOPS_NEED_FIXUP)) @@ -11158,6 +11185,10 @@ lower_omp_taskreg (gimple_stmt_iterator gimple_seq_add_stmt (&new_body, gimple_build_label (ctx->cancel_label)); gimple_seq_add_seq (&new_body, par_olist); new_body = maybe_catch_exception (new_body); + if (gimple_code (stmt) == GIMPLE_OMP_TASK) + gimple_seq_add_stmt (&new_body, + gimple_build_omp_continue (integer_zero_node, + integer_zero_node)); gimple_seq_add_stmt (&new_body, gimple_build_omp_return (false)); gimple_omp_set_body (stmt, new_body); @@ -12272,6 +12303,10 @@ make_gimple_omp_edges (basic_block bb, s somewhere other than the next block. This will be created later. */ cur_region->exit = bb; + if (cur_region->type == GIMPLE_OMP_TASK) + /* Add an edge corresponding to not scheduling the task + immediately. */ + make_edge (cur_region->entry, bb, EDGE_ABNORMAL); fallthru = cur_region->type != GIMPLE_OMP_SECTION; cur_region = cur_region->outer; break; @@ -12320,6 +12355,10 @@ make_gimple_omp_edges (basic_block bb, s } break; + case GIMPLE_OMP_TASK: + fallthru = true; + break; + default: gcc_unreachable (); } --- libgomp/testsuite/libgomp.c/pr66133.c.jj 2015-05-13 13:44:05.592658643 +0200 +++ libgomp/testsuite/libgomp.c/pr66133.c 2015-05-13 13:45:13.974586586 +0200 @@ -0,0 +1,35 @@ +/* PR middle-end/66133 */ +/* { dg-do run } */ +/* { dg-options "-O2 -fopenmp" } */ + +#include <stdlib.h> +#include <unistd.h> + +volatile int x; + +__attribute__((noinline)) void +foo (void) +{ + if (x == 0) + { + #pragma omp task + { + usleep (2000); + exit (0); + } + } + else + abort (); +} + +int +main () +{ + #pragma omp parallel num_threads (2) + { + #pragma omp barrier + #pragma omp single + foo (); + } + exit (0); +} Jakub