/* Expand an OpenMP metadirective.
Copyright (C) 2021 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 "target.h"
#include "tree.h"
#include "langhooks.h"
#include "gimple.h"
#include "tree-pass.h"
#include "cgraph.h"
#include "fold-const.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "gimple-walk.h"
#include "gomp-constants.h"
#include "omp-general.h"
#include "diagnostic-core.h"
#include "tree-cfg.h"
#include "cfganal.h"
#include "ssa.h"
#include "tree-into-ssa.h"
#include "cfghooks.h"
static void
omp_expand_metadirective (function *fun, basic_block bb)
{
gimple *stmt = last_stmt (bb);
vec candidates
= omp_resolve_metadirective (stmt);
/* This is the last chance for the metadirective to be resolved. */
if (candidates.is_empty ())
gcc_unreachable ();
auto_vec labels;
for (unsigned int i = 0; i < candidates.length (); i++)
labels.safe_push (candidates[i].directive);
/* Delete BBs for all variants not in the candidate list. */
for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
{
tree label = gimple_omp_metadirective_label (stmt, i);
if (!labels.contains (label))
{
edge e = find_edge (bb, label_to_block (fun, label));
remove_edge_and_dominated_blocks (e);
labels.safe_push (label);
}
}
/* Remove the metadirective statement. */
gimple_stmt_iterator gsi = gsi_last_bb (bb);
gsi_remove (&gsi, true);
if (candidates.length () == 1)
{
/* Special case if there is only one selector - there should be one
remaining edge from BB to the selected variant. */
edge e = find_edge (bb, label_to_block (fun,
candidates.last ().directive));
e->flags |= EDGE_FALLTHRU;
return;
}
basic_block cur_bb = bb;
/* For each candidate, create a conditional that checks the dynamic
condition, branching to the candidate directive if true, to the
next candidate check if false. */
for (unsigned i = 0; i < candidates.length () - 1; i++)
{
basic_block next_bb = NULL;
gcond *cond_stmt = gimple_build_cond_from_tree (candidates[i].selector,
NULL_TREE, NULL_TREE);
gsi = gsi_last_bb (cur_bb);
gsi_insert_seq_after (&gsi, cond_stmt, GSI_NEW_STMT);
if (i < candidates.length () - 2)
{
edge e_false = split_block (cur_bb, cond_stmt);
e_false->flags &= ~EDGE_FALLTHRU;
e_false->flags |= EDGE_FALSE_VALUE;
e_false->probability = profile_probability::uninitialized ();
next_bb = e_false->dest;
}
/* Redirect the source of the edge from BB to the candidate directive
to the conditional. Reusing the edge avoids disturbing phi nodes in
the destination BB. */
edge e = find_edge (bb, label_to_block (fun, candidates[i].directive));
redirect_edge_pred (e, cur_bb);
e->flags |= EDGE_TRUE_VALUE;
if (next_bb)
cur_bb = next_bb;
}
/* The last of the candidates is always static. */
edge e = find_edge (cur_bb, label_to_block (fun,
candidates.last ().directive));
e->flags |= EDGE_FALSE_VALUE;
}
namespace {
const pass_data pass_data_omp_expand_metadirective =
{
GIMPLE_PASS, /* type */
"omp_expand_metadirective", /* name */
OPTGROUP_OMP, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_gimple_lcf, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_update_ssa | TODO_cleanup_cfg, /* todo_flags_finish */
};
class pass_omp_expand_metadirective : public gimple_opt_pass
{
public:
pass_omp_expand_metadirective (gcc::context *ctxt)
: gimple_opt_pass (pass_data_omp_expand_metadirective, ctxt)
{}
/* opt_pass methods: */
virtual bool gate (function *)
{
return (flag_openmp);
}
virtual unsigned int execute (function *fun);
}; // class pass_omp_oacc_kernels_decompose
unsigned int
pass_omp_expand_metadirective::execute (function *fun)
{
basic_block bb;
auto_vec metadirective_bbs;
FOR_EACH_BB_FN (bb, fun)
{
gimple *stmt = last_stmt (bb);
if (stmt && is_a (stmt))
metadirective_bbs.safe_push (bb);
}
if (metadirective_bbs.is_empty ())
return 0;
calculate_dominance_info (CDI_DOMINATORS);
for (unsigned i = 0; i < metadirective_bbs.length (); i++)
omp_expand_metadirective (fun, metadirective_bbs[i]);
free_dominance_info (fun, CDI_DOMINATORS);
mark_virtual_operands_for_renaming (fun);
return 0;
}
} // anon namespace
gimple_opt_pass *
make_pass_omp_expand_metadirective (gcc::context *ctxt)
{
return new pass_omp_expand_metadirective (ctxt);
}